SourceXtractorPlusPlus  0.12
Please provide a description of the project.
ProgressNCurses.cpp
Go to the documentation of this file.
1 
18 #include "SEMain/ProgressNCurses.h"
19 
20 #include <poll.h>
21 #include <semaphore.h>
22 #include <ncurses.h>
23 #include <fcntl.h>
24 #include <readline/readline.h>
25 #include <csignal>
26 #include <chrono>
27 #include <iostream>
28 #include <iomanip>
29 #include <mutex>
30 #include <boost/algorithm/string/trim.hpp>
31 #include <boost/thread.hpp>
32 
33 
35 
36 
37 namespace SourceXtractor {
38 
39 // Signal handlers
40 static struct sigaction sigterm_action, sigstop_action, sigcont_action, sigwich_action;
42 
43 // Used for sending signals to the UI thread
44 static int signal_fds[2];
45 
46 // Used by the UI thread to notify that is is done
47 static struct ncurses_done {
48  sem_t m_semaphore;
50  sem_init(&m_semaphore, 0, 1);
51  }
52 } ncurses_done;
53 
54 // Forward declaration of signal handlers
55 static void handleTerminatingSignal(int s);
56 static void handleStopSignal(int s);
57 static void handleContinuationSignal(int s);
58 static void handleResizeSignal(int);
59 
60 
70 static int interceptFileDescriptor(int old_fd, int *backup_fd) {
71  int pipe_fds[2];
72 
73  *backup_fd = dup(old_fd);
74  if (*backup_fd < 0) {
76  }
77 
78  if (pipe(pipe_fds) < 0) {
80  }
81 
82  int flags = fcntl(pipe_fds[0], F_GETFL, 0);
83  if (fcntl(pipe_fds[0], F_SETFL, flags | O_NONBLOCK) < 0) {
85  }
86 
87  if (dup2(pipe_fds[1], old_fd) < 0) {
89  }
90  close(pipe_fds[1]);
91 
92  return pipe_fds[0];
93 }
94 
103 static void override_rl_display(void) {
104 }
105 
109 class Screen : public boost::noncopyable {
110 public:
111 
119  Screen(FILE *outfd, FILE *infd) {
120  if (pipe(signal_fds) < 0) {
122  }
123 
124  m_old_redisplay = rl_redisplay_function;
125  rl_redisplay_function = override_rl_display;
126 
127  // Tell readline to leave the terminal be
128  rl_catch_signals = 0;
129  rl_deprep_term_function = NULL;
130  rl_prep_term_function = NULL;
131 
132  // It seems like the readline in MacOSX is not the "real" readline, but a compatibility
133  // layer which misses some things, like the following:
134 #ifndef __APPLE__
135  rl_catch_sigwinch = 0;
136 #endif
137 
138  // Setup signal handlers
139  ::memset(&sigterm_action, 0, sizeof(sigterm_action));
140  ::memset(&sigstop_action, 0, sizeof(sigstop_action));
141  ::memset(&sigcont_action, 0, sizeof(sigcont_action));
142  ::memset(&sigwich_action, 0, sizeof(sigwich_action));
143 
144  // Termination
145  sigterm_action.sa_handler = &handleTerminatingSignal;
146  ::sigaction(SIGINT, &sigterm_action, &prev_signal[SIGINT]);
147  ::sigaction(SIGTERM, &sigterm_action, &prev_signal[SIGTERM]);
148  ::sigaction(SIGABRT, &sigterm_action, &prev_signal[SIGABRT]);
149  ::sigaction(SIGSEGV, &sigterm_action, &prev_signal[SIGSEGV]);
150  ::sigaction(SIGHUP, &sigterm_action, &prev_signal[SIGHUP]);
151 
152  // bg
153  sigstop_action.sa_handler = &handleStopSignal;
154  ::sigaction(SIGTSTP, &sigstop_action, &prev_signal[SIGTSTP]);
155 
156  // fg
157  sigcont_action.sa_handler = &handleContinuationSignal;
158  ::sigaction(SIGCONT, &sigcont_action, &prev_signal[SIGCONT]);
159 
160  // Resizing
161  // Some versions of ncurses handle this by themselves, some other do not, so
162  // we do it ourselves in anycase
163  sigwich_action.sa_handler = &handleResizeSignal;
164  ::sigaction(SIGWINCH, &sigwich_action, &prev_signal[SIGWINCH]);
165 
166  // Enter ncurses
167  initscr();
168  m_screen = newterm(nullptr, outfd, infd);
169  set_term(m_screen);
170 
171  // Hide cursor
172  curs_set(0);
173 
174  // Get input without echo, but leave Ctrl+<Key> to the terminal
175  cbreak();
176  keypad(stdscr, TRUE);
177  noecho();
178 
179  // Setup colors
180  use_default_colors();
181  start_color();
182  }
183 
187  virtual ~Screen() {
188  // Exit ncurses
189  endwin();
190  delscreen(m_screen);
191  rl_redisplay_function = m_old_redisplay;
192  // Restore signal handlers
193  ::sigaction(SIGINT, &prev_signal[SIGINT], nullptr);
194  ::sigaction(SIGTERM, &prev_signal[SIGTERM], nullptr);
195  ::sigaction(SIGABRT, &prev_signal[SIGABRT], nullptr);
196  ::sigaction(SIGSEGV, &prev_signal[SIGSEGV], nullptr);
197  ::sigaction(SIGHUP, &prev_signal[SIGHUP], nullptr);
198  ::sigaction(SIGCONT, &prev_signal[SIGCONT], nullptr);
199  ::sigaction(SIGWINCH, &prev_signal[SIGWINCH], nullptr);
200  close(signal_fds[0]);
201  close(signal_fds[1]);
202  }
203 
207  short initColor(short fg, short bg) {
208  init_pair(m_color_idx, fg, bg);
209  return m_color_idx++;
210  }
211 
212 private:
213  short m_color_idx = 1;
214  SCREEN *m_screen;
215  rl_voidfunc_t* m_old_redisplay;
216 };
217 
234 static void handleTerminatingSignal(int s) {
235  // Restore handler (so if we get stuck somewhere, and second
236  // signal occurs, like a SIGTERM, the process is killed for good)
237  sigaction(s, &prev_signal[s], nullptr);
238 
239  // Notify
240  if (write(signal_fds[1], &s, sizeof(s)) == sizeof(s)) {
241  close(signal_fds[1]);
242  // Wait for UI thread to be done
243 #if _POSIX_C_SOURCE >= 200112L
244  timespec timeout;
245  clock_gettime(CLOCK_REALTIME, &timeout);
246  timeout.tv_sec += 5;
247  sem_timedwait(&ncurses_done.m_semaphore, &timeout);
248 #else
249  // MacOSX does not have timedwait
250  int timeout = 5;
251  while(timeout > 0 && sem_trywait(&ncurses_done.m_semaphore) < 0) {
252  sleep(1);
253  --timeout;
254  }
255 #endif
256  }
257 
258  // Call the previous handler
259  raise(s);
260 }
261 
265 static void handleStopSignal(int s) {
266  // Restore handler
267  sigaction(s, &prev_signal[s], nullptr);
268 
269  // Exit ncurses
270  endwin();
271 
272  // Trigger the previous handler
273  raise(s);
274 }
275 
279 static void handleContinuationSignal(int) {
280  // Restore handlers
281  sigaction(SIGCONT, &sigcont_action, nullptr);
282  sigaction(SIGTSTP, &sigstop_action, nullptr);
283 }
284 
288 static void handleResizeSignal(int s) {
289  if (write(signal_fds[1], &s, sizeof(s)) < 0) {
290  // Just ignore
291  }
292 }
293 
297 class LogWidget {
298 private:
299  WINDOW *m_pad, *m_scroll;
300  // Screen coordinates!
303  // Number of total lines being written so far
305  // Last line being *displayed*
307  // Colors
309 
310  static const int BUFFER_INCREASE_STEP_SIZE = 10, BUFFER_MAX_SIZE = 16384;
311 
312 public:
313 
329  LogWidget(int display_height, int display_width, int display_y, int display_x, short bar_color, short ind_color)
330  : m_pad(newpad(BUFFER_INCREASE_STEP_SIZE, display_width)),
331  m_scroll(newpad(display_height, 1)),
332  m_display_height(display_height), m_display_width(display_width), m_display_y(display_y), m_display_x(display_x),
333  m_written_lines(0), m_active_line(0), m_scroll_bar_color(bar_color), m_scroll_ind_color(ind_color) {
334  scrollok(m_pad, TRUE);
335  }
336 
340  virtual ~LogWidget() {
341  delwin(m_pad);
342  delwin(m_scroll);
343  }
344 
348  void write(const char *data, ssize_t nchars) {
349  while (nchars > 0) {
350  if (*data == '\n') {
351  // If the current line is the last one, follow the log
353  ++m_active_line;
354  }
356  // Increase buffer if we ran out of lines on the pad
357  if (getmaxy(m_pad) <= m_written_lines) {
359  }
360  }
361  waddch(m_pad, *data);
362  ++data, --nchars;
363  }
364  drawLog();
365  drawScroll();
366  }
367 
371  void resize(int display_height, int display_width) {
372  m_display_height = display_height;
373  m_display_width = display_width;
374 
375  // Resize to make place for the new width only if it is bigger.
376  // Note that the pad height depends on the number of written lines, not displayed size!
377  if (display_width > getmaxx(m_pad)) {
378  wresize(m_pad, getmaxy(m_pad), display_width);
379  }
380  wresize(m_scroll, display_height, 1);
381  drawLog();
382  drawScroll();
383  }
384 
389  void scrollText(int d) {
390  m_active_line += d;
391  if (m_active_line > getcury(m_pad) + 1) {
392  m_active_line = getcury(m_pad) + 1;
393  }
396  }
399  }
400  drawLog();
401  drawScroll();
402  }
403 
407  void handleKeyPress(int key) {
408  switch (key) {
409  case KEY_DOWN:
410  scrollText(1);
411  break;
412  case KEY_UP:
413  scrollText(-1);
414  break;
415  case KEY_NPAGE:
416  scrollText(LINES);
417  break;
418  case KEY_PPAGE:
419  scrollText(-LINES);
420  break;
421  }
422  }
423 
428  // Scan line by line
429  std::vector<std::string> term_lines;
430  for (int i = 0; i < m_written_lines; ++i) {
431  // Note: We do not want the '\0' to be part of the final string, so we use the string constructor to prune those
432  std::vector<char> buffer(m_display_width + 1, '\0');
433  mvwinnstr(m_pad, i, 0, buffer.data(), m_display_width - 2);
434  term_lines.emplace_back(buffer.data());
435  boost::algorithm::trim(term_lines.back());
436  }
437  // Prune trailing empty lines
438  while (!term_lines.empty() && term_lines.back().empty()) {
439  term_lines.pop_back();
440  }
441  return term_lines;
442  }
443 
444 private:
448  void drawScroll() const {
449  werase(m_scroll);
450 
451  int max_selectable_line = m_written_lines;
452  int min_selectable_line = std::min(m_written_lines, m_display_height);
453  int displayed_line_offset = m_active_line - min_selectable_line;
454  float p = std::max(0.f, std::min(1.f, displayed_line_offset / float(max_selectable_line - min_selectable_line)));
455 
456  int scroll_marker_pos = p * (m_display_height - 1);
457  for (int i = 0; i < m_display_height; ++i) {
458  if (i == scroll_marker_pos)
459  waddch(m_scroll, ACS_CKBOARD | COLOR_PAIR(m_scroll_ind_color));
460  else
461  waddch(m_scroll, '|' | COLOR_PAIR(m_scroll_bar_color));
462  }
463  pnoutrefresh(m_scroll,
464  0, 0,
467  );
468  }
469 
473  void drawLog() const {
474  int pad_y = std::max(m_active_line - m_display_height, 0);
475  pnoutrefresh(m_pad,
476  pad_y, 0, // Pad coordinates
477  m_display_y, m_display_x, // Start screen coordinates
478  m_display_y + m_display_height - 1, m_display_x + m_display_width - 2 // End screen coordinates
479  );
480  }
481 };
482 
486 class ProgressWidget : public boost::noncopyable {
487 public:
503  ProgressWidget(int height, int width, int y, int x, short done_color, short progress_color)
504  : m_window(newwin(height, width, y, x)), m_started(std::chrono::steady_clock::now()),
505  m_done_color(done_color), m_progress_color(progress_color) {
506  }
507 
512  delwin(m_window);
513  }
514 
522  void move(int y, int x) {
523  mvwin(m_window, y, x);
524  wnoutrefresh(m_window);
525  }
526 
534  void resize(int height, int width) {
535  wresize(m_window, height, width);
536  wnoutrefresh(m_window);
537  }
538 
542  unsigned getHeight() const {
543  return getmaxy(m_window);
544  }
545 
549  void update(const std::list<ProgressInfo>& info) {
550  // Precalculate layout, so labels are aligned
551  size_t value_position = sizeof("Elapsed");
552 
553  for (auto& entry: info) {
554  if (entry.m_label.size() > value_position) {
555  value_position = entry.m_label.size();
556  }
557  }
558  value_position++; // Plus space
559 
560  // Width of the bar is the with of the windows - a space - two brackets []
561  size_t bar_width = getmaxx(m_window) - 2 - value_position;
562 
563  // Elapsed
564  auto now = std::chrono::steady_clock::now();
565  auto elapsed = now - m_started;
566 
567  // Restore position to the beginning
568  werase(m_window);
569 
570  // Now, print the actual progress
571  int line = 0;
572  for (auto& entry : info) {
573  drawProgressLine(value_position, bar_width, line, entry.m_label, entry.m_total, entry.m_done);
574  ++line;
575  }
576 
577  // Elapsed time
578  drawElapsed(value_position, elapsed, line);
579 
580  // Flush
581  wnoutrefresh(m_window);
582  }
583 
584 private:
588  void drawElapsed(size_t value_position, const std::chrono::steady_clock::duration& elapsed, int line) const {
591  auto s = std::chrono::duration_cast<std::chrono::seconds>(elapsed - h - m);
592  std::ostringstream elapsed_str;
593  elapsed_str.fill('0');
594  elapsed_str << std::setw(2) << h.count() << ':' << std::setw(2) << m.count() << ':' << std::setw(2) << s.count();
595 
596  wattron(m_window, A_BOLD);
597  mvwaddstr(m_window, line, 0, "Elapsed");
598  wattroff(m_window, A_BOLD);
599  mvwaddstr(
600  m_window,
601  line, value_position + 1,
602  elapsed_str.str().c_str()
603  );
604  }
605 
609  void drawProgressLine(int value_position, int bar_width, int line, const std::string& label,
610  int total, int done) const {
611  // Label
612  wattron(m_window, A_BOLD);
613  mvwaddstr(m_window, line, 0, label.c_str());
614  wattroff(m_window, A_BOLD);
615 
616  // Total number is unknown
617  if (total <= 0) {
618  mvwprintw(m_window, line, value_position + 1, "%d", done);
619  return;
620  }
621 
622  // Otherwise, report progress as a bar
623  float ratio = done / static_cast<float>(total);
624  // This can happens sometimes, as a measurement could be notified before the deblending, for instance
625  if (ratio > 1)
626  ratio = 1.;
627 
628  // Build the report string
630  bar << done << " / " << total << " (" << std::fixed << std::setprecision(2) << ratio * 100. << "%)";
631 
632  // Attach as many spaces as needed to fill the screen width, minus brackets
633  bar << std::string(bar_width - bar.str().size(), ' ');
634 
635  // Print label
636  wattron(m_window, A_BOLD);
637  mvwaddstr(m_window, line, 0, label.c_str());
638  wattroff(m_window, A_BOLD);
639  mvwaddch(m_window, line, value_position, '[');
640 
641  // Completed
642  auto bar_content = bar.str();
643  int completed = bar_content.size() * ratio;
644 
645  wattron(m_window, COLOR_PAIR(m_done_color));
646  waddstr(m_window, bar_content.substr(0, completed).c_str());
647  wattroff(m_window, COLOR_PAIR(m_done_color));
648 
649  // Rest
650  wattron(m_window, COLOR_PAIR(m_progress_color));
651  waddstr(m_window, bar_content.substr(completed).c_str());
652  wattroff(m_window, COLOR_PAIR(2));
653 
654  // Closing bracket
655  waddch(m_window, ']');
656  }
657 
658  WINDOW *m_window;
659  std::chrono::steady_clock::time_point m_started;
661 };
662 
674 private:
678 
679  // stderr intercept
682  // stdout intercept
684 
685  // Used to recover log into the standard output
687 
688  std::atomic_bool m_trigger_resize, m_exit_loop;
689 
693  void uiThread() {
694  sem_wait(&ncurses_done.m_semaphore);
695  // SIGTERM, SIGINT and SIGHUP should not be handled by this thread, or we will not be able to properly
696  // exit ncurses.
697  // Hopefully there should be no SIGABRT or SIGSEGV here. If there were, we will exit but we will not be able
698  // to restore the terminal state. Having an abort or a segmentation fault is a bug anyway.
699  sigset_t set;
700  sigaddset(&set, SIGTERM);
701  sigaddset(&set, SIGINT);
702  sigaddset(&set, SIGHUP);
703  pthread_sigmask(SIG_BLOCK, &set, nullptr);
704  // Enter ncurses
705  ncursesMode();
706  // Recover file descriptors
707  dup2(m_stderr_original, STDERR_FILENO);
708  dup2(m_stdout_original, STDOUT_FILENO);
709  // Dump recovered text
710  for (const auto& line : m_log_text) {
711  std::cerr << line << std::endl;
712  }
713  sem_post(&ncurses_done.m_semaphore);
714  }
715 
716  void handleSignal(const struct pollfd& poll_fd, LogWidget& logWidget) {
717  if (poll_fd.revents & POLLIN) {
718  int signal_no;
719  if (read(signal_fds[0], &signal_no, sizeof(signal_no)) > 0 && signal_no == SIGWINCH) {
720  m_trigger_resize = true;
721  endwin();
722  refresh();
723  clear();
724  }
725  else {
726  char buf[64];
727  logWidget.write(buf, snprintf(buf, sizeof(buf), "Caught signal %s\n", strsignal(signal_no)));
728  m_exit_loop = true;
729  }
730  }
731  }
732 
733  void pipeToLog(const struct pollfd& poll_fd, int pipe, LogWidget& out) {
734  if (poll_fd.revents & POLLIN) {
735  ssize_t nbytes;
736  char buf[64];
737  while ((nbytes = read(pipe, &buf, sizeof(buf))) > 0) {
738  out.write(buf, nbytes);
739  }
740  }
741  }
742 
743  void handleKeyPress(const struct pollfd& poll_fd, LogWidget& logWidget) const {
744  if (poll_fd.revents & POLLIN) {
745  int key = wgetch(stdscr);
746  if (key != KEY_RESIZE) {
747  logWidget.handleKeyPress(key);
748  }
749  }
750  }
751 
755  void ncursesMode() {
756  Screen screen(m_stderr, stdin);
757 
758  // Log area
759  LogWidget logWidget(
760  LINES - 1, COLS, 0, 0,
761  screen.initColor(COLOR_WHITE, COLOR_BLACK), screen.initColor(COLOR_WHITE, COLOR_WHITE)
762  );
763 
764  // Progress widget
765  ProgressWidget progressWidget(
766  1, COLS, LINES - 1, 0,
767  screen.initColor(COLOR_WHITE, COLOR_GREEN), screen.initColor(COLOR_WHITE, COLOR_BLACK)
768  );
769 
770  // File descriptors to watch for
771  struct pollfd poll_fds[] = {
772  {m_stderr_pipe, POLLIN, 0},
773  {m_stdout_pipe, POLLIN, 0},
774  {STDIN_FILENO, POLLIN, 0},
775  {signal_fds[0], POLLIN, 0}
776  };
777 
778  // Event loop
779  m_exit_loop = false;
780 
781  do {
782  // There has been a signal
783  handleSignal(poll_fds[3], logWidget);
784 
785  // Resize widgets if needed
786  if (m_trigger_resize) {
788  progressWidget.move(LINES - m_progress_info.size() - 1, 0);
789  progressWidget.resize(m_progress_info.size() + 1, COLS);
790  logWidget.resize(LINES - progressWidget.getHeight(), COLS);
791  m_trigger_resize = false;
792  }
793 
794  // There is output/error to redirect
795  pipeToLog(poll_fds[0], m_stderr_pipe, logWidget);
796  pipeToLog(poll_fds[1], m_stdout_pipe, logWidget);
797 
798  // There is a key to read
799  handleKeyPress(poll_fds[2], logWidget);
800 
801  {
803  progressWidget.update(m_progress_info);
804  }
805 
806  // Update screen
807  doupdate();
808 
809  // Wait for events
810  if (poll(poll_fds, 4, 1000) < 0) {
811  // poll may return with EINTR if a signal happened halfway
812  m_exit_loop = (errno != EINTR);
813  }
814  } while (!m_exit_loop && !boost::this_thread::interruption_requested());
815  m_log_text = logWidget.getText();
816  }
817 
818 public:
827  int new_stderr_fd = dup(m_stderr_original);
828  if (new_stderr_fd < 0) {
830  }
831  m_stderr = fdopen(new_stderr_fd, "w");
832  m_ui_thread = Euclid::make_unique<boost::thread>(std::bind(&Dashboard::uiThread, this));
833  }
834 
840  if (m_ui_thread) {
841  try {
842  m_ui_thread->interrupt();
843  if (m_ui_thread->joinable()) {
844  m_ui_thread->join();
845  }
846  }
847  catch (...) {
848  // Ignore
849  }
850  }
851  fclose(m_stderr);
852  // Unneeded duplicates now
853  close(m_stderr_original);
854  close(m_stdout_original);
855  close(m_stderr_pipe);
856  close(m_stdout_pipe);
857  }
858 
862  void update(const std::list<ProgressInfo>& info) {
864  m_trigger_resize = (m_progress_info.size() != info.size()) | m_trigger_resize;
865  m_progress_info = info;
866  }
867 };
868 
870  m_dashboard = make_unique<Dashboard>();
871 }
872 
874 }
875 
877  return isatty(STDERR_FILENO);
878 }
879 
881  if (m_dashboard)
882  m_dashboard->update(info);
883 }
884 
885 void ProgressNCurses::handleMessage(const bool& done) {
886  if (done && m_dashboard)
887  m_dashboard.reset(nullptr);
888 }
889 
890 } // end SourceXtractor
static std::map< int, struct sigaction > prev_signal
rl_voidfunc_t * m_old_redisplay
T empty(T... args)
ProgressWidget(int height, int width, int y, int x, short done_color, short progress_color)
static const int BUFFER_MAX_SIZE
std::shared_ptr< DependentParameter< std::shared_ptr< EngineParameter > > > x
T generic_category(T... args)
constexpr double s
T endl(T... args)
STL namespace.
void pipeToLog(const struct pollfd &poll_fd, int pipe, LogWidget &out)
T duration_cast(T... args)
void drawElapsed(size_t value_position, const std::chrono::steady_clock::duration &elapsed, int line) const
static const int BUFFER_INCREASE_STEP_SIZE
constexpr double bar
STL class.
static void override_rl_display(void)
std::shared_ptr< DependentParameter< std::shared_ptr< EngineParameter > > > y
T setw(T... args)
T fclose(T... args)
static void handleTerminatingSignal(int s)
STL class.
T min(T... args)
static void handleStopSignal(int s)
static struct sigaction sigterm_action sigstop_action sigcont_action sigwich_action
T data(T... args)
constexpr double m
void write(const char *data, ssize_t nchars)
std::chrono::steady_clock::time_point m_started
void resize(int display_height, int display_width)
T pop_back(T... args)
T bind(T... args)
static int interceptFileDescriptor(int old_fd, int *backup_fd)
STL class.
T max(T... args)
void drawProgressLine(int value_position, int bar_width, int line, const std::string &label, int total, int done) const
T fixed(T... args)
void handleMessage(const std::list< ProgressInfo > &info) override
static struct SourceXtractor::ncurses_done ncurses_done
void handleSignal(const struct pollfd &poll_fd, LogWidget &logWidget)
static void handleResizeSignal(int)
Wrap the terminal into a singleton.
T size(T... args)
LogWidget(int display_height, int display_width, int display_y, int display_x, short bar_color, short ind_color)
static int signal_fds[2]
T c_str(T... args)
Set of progress bars/information entries.
T back(T... args)
void update(const std::list< ProgressInfo > &info)
short initColor(short fg, short bg)
static void handleContinuationSignal(int s)
T fill(T... args)
std::unique_ptr< Dashboard > m_dashboard
T setprecision(T... args)
std::vector< std::string > getText()
T snprintf(T... args)
void update(const std::list< ProgressInfo > &info)
void resize(int height, int width)
void handleKeyPress(const struct pollfd &poll_fd, LogWidget &logWidget) const
std::unique_ptr< boost::thread > m_ui_thread
Screen(FILE *outfd, FILE *infd)
std::unique_ptr< T > make_unique(Args &&... args)
T emplace_back(T... args)