Tomographer  v5.4
Tomographer C++ Framework Documentation
fmt.h
Go to the documentation of this file.
1 /* This file is part of the Tomographer project, which is distributed under the
2  * terms of the MIT license.
3  *
4  * The MIT License (MIT)
5  *
6  * Copyright (c) 2016 ETH Zurich, Institute for Theoretical Physics, Philippe Faist
7  * Copyright (c) 2017 Caltech, Institute for Quantum Information and Matter, Philippe Faist
8  *
9  * Permission is hereby granted, free of charge, to any person obtaining a copy
10  * of this software and associated documentation files (the "Software"), to deal
11  * in the Software without restriction, including without limitation the rights
12  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13  * copies of the Software, and to permit persons to whom the Software is
14  * furnished to do so, subject to the following conditions:
15  *
16  * The above copyright notice and this permission notice shall be included in
17  * all copies or substantial portions of the Software.
18  *
19  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
25  * SOFTWARE.
26  */
27 
28 #ifndef TOMOGRAPHER_TOOLS_FMT_H
29 #define TOMOGRAPHER_TOOLS_FMT_H
30 
37 #include <cstdio>
38 #include <cstdarg>
39 
40 #include <chrono>
41 #include <string>
42 #include <sstream> // stringstream
43 #include <iostream>
44 #include <utility> // std::declval
45 #include <vector>
46 #include <iomanip>
47 #include <functional>
48 
49 #include <tomographer/tools/cxxutil.h> // PRINTFN_ARGS_SAFE
50 
51 
52 namespace Tomographer {
53 namespace Tools {
54 
55 
56 //
57 // =============================================================================
58 //
59 // Formatting Utilities
60 //
61 
62 
68 class TOMOGRAPHER_EXPORT BadFmtsFormat : public std::exception
69 {
70  std::string msg;
71 public:
73  BadFmtsFormat(const std::string& msg_) : msg(msg_) { }
74  ~BadFmtsFormat() throw() { }
75 
76  const char * what() const throw() {
77  return msg.c_str();
78  }
79 };
80 
81 
82 
87 inline std::string vfmts(const char* fmt, va_list vl)
88 {
89  // check out printf() formatting for std::string:
90  // http://stackoverflow.com/a/10150393/1694896
91  // http://stackoverflow.com/a/26197300/1694896
92 
93  // on MinGW, can't find std::vsnprintf() because for some reason it's declared in the
94  // global scope... so just import std namespace here and use generic vsnprintf() call.
95  using namespace std;
96 
97  std::size_t size = 10;
98  char * buffer = new char[size];
99  va_list ap1;
100  va_copy(ap1, vl);
101  int nsize = vsnprintf(buffer, size, fmt, ap1);
102  if (nsize < 0) {
103  // failure: bad format probably
104  throw BadFmtsFormat("vsnprintf("+std::string(fmt)+") failure: code="+std::to_string(nsize));
105  }
106  if(size <= (std::size_t)nsize) {
107  // buffer too small: delete buffer and try again
108  delete[] buffer;
109  size = (std::size_t)nsize+1; // +1 for "\0"
110  buffer = new char[size];
111  nsize = vsnprintf(buffer, size, fmt, vl);
112  }
113  std::string ret(buffer);
114  delete[] buffer;
115  va_end(ap1);
116  return ret;
117 }
118 
119 
124 inline std::string fmts(const char * fmt, ...) PRINTF1_ARGS_SAFE;
125 
126 // definition. It seems definitions cannot have function attributes (here
127 // PRINTF1_ARGS_SAFE), so that's why it's separate
128 inline std::string fmts(const char * fmt, ...)
129 {
130  va_list ap;
131  va_start(ap, fmt);
132  std::string result = vfmts(fmt, ap);
133  va_end(ap);
134  return result;
135 }
136 
137 
149 #define streamstr(tokens) \
150  dynamic_cast<std::ostringstream&>( \
151  std::ostringstream().seekp(0) << tokens \
152  ).str() \
153 
154 
161 #define streamcstr(tokens) streamstr(tokens).c_str()
162 
163 
164 
165 //
166 // utility to stream an object, if possible
167 //
168 
169 namespace tomo_internal {
170 // see http://stackoverflow.com/a/9154394/1694896
171 // see sfinae_yes and sfinae_no in cxxutil.h
172 template<typename T> static auto test_has_stream_op(int)
173  -> typename sfinae_yes<decltype(*((std::ostream*)(NULL)) << *((T*)(NULL)))>::yes&;
174 template<typename T> static auto test_has_stream_op(long)
175  -> typename sfinae_no<>::no&;
176 // template<typename T, int dummy_value,
177 // typename EnabledIfType = decltype(std::declval<std::ostream&>() << std::declval<T>())>
178 // struct test_has_stream_op { static constexpr bool value = true; };
179 // template<typename T, long dummy_value>
180 // struct test_has_stream_op<T,dummy_value,void> { static constexpr bool value = false; };
181 // template<typename T>
182 // struct has_stream_op {
183 // static constexpr bool value = test_has_stream_op<T, 0>::value;
184 // };
185 } // namespace tomo_internal
186 
187 
198 template<typename T>
199 struct TOMOGRAPHER_EXPORT hasOStreamOp {
200  static constexpr bool value = (sizeof(tomo_internal::test_has_stream_op<T>(0))
201  == sizeof(typename tomo_internal::sfinae_yes<>::yes));
202 };
203 
204 
205 namespace tomo_internal {
207 template<typename T_>
208 struct StreamIfPossibleWrapper
209 {
211  typedef T_ T;
215  StreamIfPossibleWrapper(
216  const T& obj_,
217  std::string before_,
218  std::string after_,
219  std::string alternative_
220  )
221  : obj(obj_), before(before_), after(after_), alternative(alternative_)
222  {
223  }
224 
226  const T & obj;
227 
229  const std::string before;
231  const std::string after;
233  const std::string alternative;
234 
235  // internal :
236 
239  void stream_into(OStream&& stream) const
240  {
241  stream << alternative;
242  }
245  void stream_into(OStream&& stream) const
246  {
247  stream << before << obj << after;
248  }
249 };
251 template<typename T>
252 inline std::ostream & operator<<(std::ostream & stream, const StreamIfPossibleWrapper<T>& wobj)
253 {
254  wobj.stream_into(stream);
255  return stream;
256 }
258 template<typename T>
259 inline std::ostream&& operator<<(std::ostream&& stream, const StreamIfPossibleWrapper<T>& wobj)
260 {
261  wobj.stream_into(stream);
262  return std::move(stream);
263 }
264 }
265 
266 
267 
268 #ifndef TOMOGRAPHER_PARSED_BY_DOXYGEN
270 inline std::string streamIfPossible(const T& /*obj*/)
271 {
272  // obj is not streamable
273  return std::string("<")+typeid(T).name()+std::string(">");
274 }
276 inline const T& streamIfPossible(const T& obj)
277 {
278  return obj;
279 }
280 template<typename T>
281 inline tomo_internal::StreamIfPossibleWrapper<T>
282 streamIfPossible(const T& obj, std::string before,
283  std::string after = std::string(),
284  std::string alternative = "<"+std::string(typeid(T).name())+">")
285 {
286  return tomo_internal::StreamIfPossibleWrapper<T>(obj, std::move(before),
287  std::move(after), std::move(alternative));
288 }
289 #else
290 
310 template<typename T>
311 inline _Unspecified streamIfPossible(const T& obj)
312 {
313 }
345 template<typename T>
346 inline _Unspecified streamIfPossible(const T& obj,
347  std::string before,
348  std::string after = std::string(),
349  std::string alternative = "<"+std::string(typeid(T).name())+">")
350 {
351 }
352 #endif
353 
354 
355 
356 
357 
358 
359 
360 // =============================================================================
361 
362 
370 inline std::string fmtDuration(double seconds)
371 {
372  // split `seconds' into integral part and fractional part
373  double dt_i_d;
374  double dt_f = std::modf(seconds, &dt_i_d);
375  int dt_i = (int)(dt_i_d+0.5);
376 
377  return fmts("%d:%02d:%02d.%03d", dt_i/3600, (dt_i/60)%60, dt_i%60, (int)(dt_f*1000+0.5));
378 };
379 
389 template<typename Rep, typename Period>
391 {
392  typedef std::chrono::duration<Rep, Period> Duration;
393 
394  // delta-time, in seconds and fraction of seconds
395  double seconds = (double)dt.count() * Duration::period::num / Duration::period::den ;
396 
397  return fmtDuration(seconds);
398 };
399 
400 
401 
402 
403 
407 class TOMOGRAPHER_EXPORT ConsoleFormatterHelper
408 {
409 public:
417  ConsoleFormatterHelper(int width = 0)
418  : _columns((std::size_t)Tomographer::Tools::getWidthForTerminalOutput(width))
419  {
420  }
421 
423  inline std::size_t columns() const { return _columns; }
424 
432  {
433  if (x.size() > columns()) {
434  return std::move(x) + "\n";
435  }
436  const std::size_t r = columns() - x.size();
437  const std::size_t rleft = r/2;
438  const std::size_t rright = r - rleft; // may differ from r/2 if r is odd
439  return std::string(rleft, ' ') + std::move(x) + std::string(rright, ' ') + "\n";
440  }
441 
449  {
450  if (x.size() > columns()) {
451  return std::move(x) + "\n";
452  }
453  const std::size_t r = columns() - x.size();
454  return std::string(r, ' ') + std::move(x) + "\n";
455  }
456 
464  {
465  if (x.size() > columns()) {
466  return std::move(x) + "\n";
467  }
468  const std::size_t r = columns() - x.size();
469  return std::move(x) + std::string(r, ' ') + "\n";
470  }
471 
472  inline std::string hrule(char pattern = '-')
473  {
474  // remember that std::string(10, '-') constructs 10 copies of '-', that is : "----------".
475  return std::string(columns(), pattern) + std::string("\n");
476  }
477 
478 private:
479  const std::size_t _columns;
480 };
481 
482 
483 // =============================================================================
484 
485 // Helper
486 
487 namespace tomo_internal {
488 class LazyOStreamCallback {
489 public:
490  template<typename CallFn>
491  LazyOStreamCallback(CallFn && fn_) : fn(fn_) { }
492 
494 };
495 inline std::ostream & operator<<(std::ostream & stream, const LazyOStreamCallback & lazy)
496 {
497  lazy.fn(stream);
498  return stream;
499 }
500 } // tomo_internal
501 
502 
503 // =============================================================================
504 
505 // Simple word wrapping
506 
508 {
509 public:
511  std::string init_ = "",
512  int indent_ = 0,
513  int first_indent_ = 0,
514  bool hard_wrap_long_lines_ = false,
515  bool obey_newlines_ = false)
516  : w(w_),
517  init(std::move(init_)),
518  indent(indent_),
519  first_indent(first_indent_),
520  hard_wrap_long_lines(hard_wrap_long_lines_),
521  obey_newlines(obey_newlines_)
522  {
523  }
524 
525  std::vector<std::string> rawWrapLines(const std::string & text) const
526  {
528  std::size_t pos = 0;
529 
530  std::string startline;
531  int startline_len = first_indent + (int)init.size();
532 
533  auto finish_line = [&output,&startline,&startline_len,this](std::string linetoend = "") {
534  std::string full_line = std::move(startline) + std::move(linetoend);
535  if (full_line.size()) {
536  output.push_back(full_line);
537  }
538  startline = "";
539  startline_len = indent;
540  };
541  auto add_startline = [&startline,&startline_len](std::string morestartline) {
542  startline += std::move(morestartline);
543  startline_len += morestartline.size();
544  };
545 
546  while (pos < text.size()) {
547 
548  std::size_t this_w = (std::size_t)std::max((int)w - startline_len, 1);
549 
550  // build the next line of the output
551 
552  if (text[pos] == ' ' || text[pos] == '\n') {
553  bool new_paragraph = false;
554  while (pos < text.size() && (text[pos] == ' ' || text[pos] == '\n')) {
555  if (text[pos] == '\n') {
556  new_paragraph = true;
557  }
558  ++ pos; // ignore leading spaces
559  }
560  if (new_paragraph) {
561  finish_line();
562  output.push_back("");
563  continue;
564  }
565  }
566 
567  std::size_t nxtpos = text.find_first_of('\n', pos);
568  if (nxtpos != std::string::npos && nxtpos < pos+this_w) { // have a newline on this line
569  if (obey_newlines) {
570  // hard newline within next line, break there
571  finish_line( text.substr(pos, nxtpos-pos) );
572  pos = nxtpos+1;
573  continue;
574  } else {
575  // might be a paragraph ending, so continue re-parsing from here
576  add_startline( text.substr(pos, nxtpos-pos) );
577  pos = nxtpos+1;
578  continue;
579  }
580  }
581 
582  if (text.size() < pos + this_w) {
583  // fits in one line
584  finish_line( text.substr(pos) );
585  pos = text.size();
586  break;
587  }
588 
589  // starting at this_w from pos, look back for first space or newline
590  std::size_t lpos = text.find_last_of(" \n", pos+this_w);
591  if (lpos == std::string::npos || lpos < pos) { // not found within this line
592  // line too long with no spaces
593  if (hard_wrap_long_lines) {
594  finish_line( text.substr(pos, this_w) );
595  pos += this_w;
596  continue;
597  }
598  // otherwise, look for next space and break there
599  std::size_t nxtpos = text.find_first_of(" \n", pos+this_w);
600  if (nxtpos == std::string::npos) {
601  // all up to the end is one super long word
602  finish_line( text.substr(pos) );
603  pos = text.size();
604  break;
605  }
606  // ok, wrapping a bit further off the line keeping the long word intact
607  finish_line( text.substr(pos, nxtpos-pos) );
608  pos = nxtpos+1;
609  continue;
610  }
611  // wrap line normally at last space
612  finish_line( text.substr(pos, lpos-pos) );
613  pos = lpos+1;
614 
615  continue;
616  }
617  finish_line();
618 
619  // if last line is new paragraph, remove it
620  if (output.size() && output.back().size() == 0) {
621  output.pop_back();
622  }
623 
624  return output;
625  }
626 
627  tomo_internal::LazyOStreamCallback wrapped(const std::string & text) const
628  {
629  std::vector<std::string> lines = rawWrapLines(text);
630  return tomo_internal::LazyOStreamCallback([lines,this](std::ostream & stream) {
631  for (std::size_t k = 0; k < lines.size(); ++k) {
632  if (k == 0) {
633  stream << std::string((std::size_t)first_indent, ' ') << init << lines[0] << "\n";
634  } else {
635  stream << std::string((std::size_t)indent, ' ') << lines[k] << "\n";
636  }
637  }
638  });
639  }
640 
641  std::size_t getWidth() const { return w; }
642  std::string getInit() const { return init; }
643  int getIndent() const { return indent; }
644  int getFirstIndent() const { return first_indent; }
645  bool getHardWrapLongLines() const { return hard_wrap_long_lines; }
646  bool getObeyNewlines() const { return obey_newlines; }
647 
648 private:
649  std::size_t w;
650  std::string init;
651  int indent;
652  int first_indent;
653  bool hard_wrap_long_lines;
654  bool obey_newlines;
655 };
656 
657 
658 // =============================================================================
659 
660 // Format footnotes in std::ostream
661 
662 
668 {
669 private:
671 public:
672  FmtFootnotes() : ftlist{}
673  {
674  }
675 
676  tomo_internal::LazyOStreamCallback addFootNote(std::string footnote)
677  {
678  return tomo_internal::LazyOStreamCallback([footnote,this](std::ostream & stream) {
679  stream << "[" << std::to_string(ftlist.size()+1) << "]";
680  ftlist.push_back(footnote);
681  }) ;
682  }
683 
684  void addSilentFootNote(std::size_t no, std::string footnote)
685  {
686  (void)no; // silence "unused" warning
687 
688  // Footnotes numbering starts at 1
689  tomographer_assert(no-1 == ftlist.size()
690  && "Your footnotes have changed numbering! Check your silent footnotes.") ;
691 
692  ftlist.push_back(footnote);
693  }
694 
695  const std::vector<std::string> & getFootNotes() const { return ftlist; }
696 
697  tomo_internal::LazyOStreamCallback wrapped(std::size_t width = 80) const
698  {
699  return tomo_internal::LazyOStreamCallback([width,this](std::ostream & stream) {
700  const std::vector<std::string> & ftlist = getFootNotes();
701  int w = (int)(std::ceil(std::log10((double)ftlist.size()))+0.01);
702  for (std::size_t k = 0; k < ftlist.size(); ++k) {
703  std::string head = "["+std::to_string(1+k)+"] ";
704  SimpleWordWrapper wrapper(width, head, w+4, 1);
705  stream << wrapper.wrapped(ftlist[k]);
706  }
707  });
708  }
709 };
710 
711 inline std::ostream & operator<<(std::ostream & stream, const FmtFootnotes & f)
712 {
713  const std::vector<std::string> & ftlist = f.getFootNotes();
714  int w = (int)(std::ceil(std::log10((double)ftlist.size()))+0.01);
715  const std::string nlindent = "\n" + std::string((std::size_t)w+4, ' ');
716  for (std::size_t k = 0; k < ftlist.size(); ++k) {
717  std::string s = ftlist[k];
718 
719  // string replace "\n" by "\n<indent spaces>" -- see https://stackoverflow.com/a/3418285/1694896
720  size_t start_pos = 0;
721  while (s.size() && (start_pos = s.find("\n", start_pos)) != std::string::npos) {
722  s.replace(start_pos, 1, nlindent);
723  start_pos += nlindent.length();
724  }
725 
726  // output this footnote
727  stream << " [" << std::setw(w) << (k+1) << "] " << s << "\n";
728  }
729 
730  return stream;
731 }
732 
733 
734 
735 
736 } // namespace Tools
737 } // namespace Tomographer
738 
739 
740 #endif
std::string centerLine(std::string x)
Produce a centered string.
Definition: fmt.h:431
Base namespace for the Tomographer project.
Definition: densellh.h:45
std::ostream & operator<<(std::ostream &str, const StoreIfEnabled< T, false > &)
C++ Stream operators for StoreIfEnabled.
Definition: cxxutil.h:294
T ceil(T... args)
std::size_t columns() const
The number of character columns (as specified to the constructor or detected)
Definition: fmt.h:423
std::string vfmts(const char *fmt, va_list vl)
printf- formatting to a std::string, with va_list pointer
Definition: fmt.h:87
BadFmtsFormat(const std::string &msg_)
Construct an exception for bad printf formatting. Provide an error message here.
Definition: fmt.h:73
T to_string(T... args)
STL namespace.
std::string fmtDuration(double seconds)
Format a number of seconds into a human-readable string.
Definition: fmt.h:370
T modf(T... args)
T log10(T... args)
Exception for bad printf format.
Definition: fmt.h:68
T setw(T... args)
std::string leftLine(std::string x)
Produce a left-aligned string.
Definition: fmt.h:463
STL class.
T find_last_of(T... args)
T push_back(T... args)
Minimal tool for formatting console stuff with fixed line width.
Definition: fmt.h:407
T replace(T... args)
T pop_back(T... args)
T find_first_of(T... args)
_Unspecified streamIfPossible(const T &obj)
Utility to stream an object, but only if "<<" overload exists.
Definition: fmt.h:311
Some C++ utilities, with a tad of C++11 tricks.
T max(T... args)
STL class.
T move(T... args)
T vsnprintf(T... args)
T find(T... args)
T size(T... args)
std::string rightLine(std::string x)
Produce a right-aligned string.
Definition: fmt.h:448
std::string fmts(const char *fmt,...)
printf- format to a std::string
Definition: fmt.h:128
ConsoleFormatterHelper(int width=0)
Constructor.
Definition: fmt.h:417
Traits class to see whether an object exposes a "<<" operator overload for std::ostream.
Definition: fmt.h:199
VarValueDecoder< T >::RetType value(const Var &var)
Access the value of the given variable, as a C++ type.
Definition: ezmatio.h:878
T back(T... args)
Helper to format footnotes in Fmt.
Definition: fmt.h:667
int getWidthForTerminalOutput(int max_width=0)
Return a suitable width for displaying stuff on the standard output.
Definition: cxxutil.h:569
T substr(T... args)
STL class.
#define tomographer_assert(...)
Assertion test macro.
Definition: cxxdefs.h:84