DebugPrinter.hpp
Go to the documentation of this file.
1 
62 // ToDo: constexpr DebugPrinter for compile-time debugging
63 // when setting a "constexpr string" as output
64 
65 #ifndef DEBUGPRINTER_HEADER
66 #define DEBUGPRINTER_HEADER
67 
68 #include <iostream>
69 
70 #ifdef NDEBUG
71 #define DEBUGPRINTER_OFF
72 #endif
73 
74 #ifndef DEBUGPRINTER_OFF
75 
76 #include <iomanip>
77 #include <fstream>
78 #include <sstream>
79 #include <typeinfo>
80 #include <cstdlib>
81 #include <algorithm>
82 #include <stdexcept>
83 #include <memory>
84 #include <type_traits>
85 
86 #ifndef DEBUGPRINTER_NO_EXECINFO
87 #include <execinfo.h>
88 #endif // DEBUGPRINTER_NO_EXECINFO
89 
90 #ifndef DEBUGPRINTER_NO_CXXABI
91 #include <cxxabi.h>
92 #endif // DEBUGPRINTER_NO_CXXABI
93 
94 #ifndef DEBUGPRINTER_NO_SIGNALS
95 #include <signal.h>
96 #include <map>
97 #endif // DEBUGPRINTER_NO_SIGNALS
98 
99 #if defined (WIN32) || defined (_WIN32) // TODO: this can be improved a lot
100 #define DEBUGPRINTER_DIRSEP '\\'
101 #else
102 #define DEBUGPRINTER_DIRSEP '/'
103 #endif
104 
105 #endif // DEBUGPRINTER_OFF
106 
108 namespace fsc {
109 
110 #ifndef DEBUGPRINTER_OFF
111 
155 
156  public:
157 
158 /*******************************************************************************
159  * DebugPrinter ctor and friends
160  */
161 
164 
165  operator=(std::cout);
166  set_precision(5);
167  set_color("0;31");
168 
169  #ifndef DEBUGPRINTER_NO_SIGNALS
170  struct sigaction act;
171  act.sa_handler = signal_handler;
172  for(auto const & sig: sig_names()) {
173  sigaction(sig.first, &act, NULL);
174  }
175  #endif // DEBUGPRINTER_NO_SIGNALS
176 
177  }
178 
180  DebugPrinter(const DebugPrinter &) = delete;
181 
183  DebugPrinter(DebugPrinter &&) = delete;
184 
185  template <typename T>
186  friend inline DebugPrinter & operator<<(DebugPrinter &, const T&);
187 
188  friend inline DebugPrinter & operator<<(DebugPrinter &,
189  std::ostream& (*pf)(std::ostream&));
190 
191 /*******************************************************************************
192  * DebugPrinter setters
193  */
194 
204  inline void operator=(std::ostream & os) noexcept { outstream = &os; }
205 
220  template <typename T>
221  inline auto operator=(T && os)
222  -> std::enable_if_t<std::is_move_assignable<T>::value
223  && std::is_rvalue_reference<decltype(os)>::value> {
224  outstream_mm = std::shared_ptr<std::ostream>(new T(std::move(os)));
225  outstream = outstream_mm.get();
226  }
227 
235  inline void set_precision(const std::streamsize prec) noexcept { prec_ = prec; }
236 
249  inline void set_color(const std::string str) { // no chaining <- returns void
250  if(is_number(str.substr(0,1)) && is_number(str.substr(2,2))) {
251  hcol_ = "\033[" + str + "m";
252  hcol_r_ = "\033[0m";
253  } else
254  throw std::runtime_error("DebugPrinter error: invalid set_color() argument");
255  }
263  inline void set_color() noexcept { // no chaining <- returns void
264  hcol_ = "";
265  hcol_r_ = "";
266  }
267 
268 /*******************************************************************************
269  * DebugPrinter parentheses operators
270  */
271 
283  template <typename T, typename U>
284  inline void operator()(const T & label, U const & obj,
285  const std::string sc = ": ") const {
286  print_stream_impl< has_stream<T> && has_stream<U> >(label, obj, sc);
287  }
299  template <typename T>
300  inline void operator()(const T& obj) const { operator()(">>>", obj, " "); }
301 
302 /*******************************************************************************
303  * DebugPrinter info on type and RTTI (stack)
304  */
305 
306  #ifndef DEBUGPRINTER_NO_EXECINFO
307 
324  void stack(
325  const int backtrace_size = max_backtrace,
326  const bool compact = false,
327  const int begin = 1 /* should stay 1 except for special needs */
328  ) const {
329 
330  std::ostream & out = *outstream;
331 
332  using uint = unsigned int;
333 
334  uint end = begin + backtrace_size; // don't pull more than necessary
335  if(end > max_backtrace)
336  end = max_backtrace;
337 
338  void * stack[max_backtrace];
339  uint r = backtrace(stack, end);
340  if(end == max_backtrace) // prettiness hack: ignore binary line
341  --r;
342  end = r; // update to received end
343  char ** symbols = backtrace_symbols(stack, end);
344 
345  if(compact == false)
346  out << "DebugPrinter obtained " << end-begin << " stack frames:"
347  << std::endl;
348  if(!symbols) return;
349 
350  #ifndef DEBUGPRINTER_NO_CXXABI
351 
352  for(uint i = begin; i < end; ++i) {
353  std::string line = std::string(symbols[i]);
354  std::string prog = prog_part(line);
355  std::string mangled = mangled_part(line);
356  std::string offset = offset_part(line);
357  std::string mainoffset = address_part(line);
358  if(mangled == "")
359  std::cerr << "DebugPrinter error: No dynamic symbol (you probably didn't compile with -rdynamic)"
360  << std::endl;
361  int status;
362  std::string demangled = demangle(mangled, status);
363  switch (status) {
364  case -1:
365  out << "DebugPrinter error: Could not allocate memory!" << std::endl;
366  break;
367  case -3:
368  out << "DebugPrinter error: Invalid argument to demangle()" << std::endl;
369  break;
370  case -2: // invalid name under the C++ ABI mangling rules
371  demangled = mangled;
372  // fallthrough
373  default:
374  if(compact == false)
375  out << " " << prog << ": " << demangled << "\t+"
376  << offset << "\t[+" << mainoffset << "]"<< std::endl;
377  else
378  out << demangled << std::endl;
379  }
380  }
381  if(compact == false) out << std::endl;
382 
383  #else // DEBUGPRINTER_NO_CXXABI
384 
385  for(uint i = begin; i < end; ++i) {
386  if(compact == false)
387  out << " " << symbols[i] << std::endl;
388  else
389  out << mangled_part(std::string(symbols[i])) << std::endl;
390  }
391 
392  if(compact == false) out << std::endl;
393  out << "echo '' && c++filt";
394  for(uint i = begin; i < end; ++i)
395  out << " " << mangled_part(std::string(symbols[i]));
396  out << " && echo ''" << std::endl;
397  if(compact == false) out << std::endl;
398 
399  #endif // DEBUGPRINTER_NO_CXXABI
400 
401  free(symbols);
402  }
403 
404  #else // DEBUGPRINTER_NO_EXECINFO
405 
406  void stack(...) const {
407  *outstream << "DebugPrinter::stack() not available" << std::endl;
408  }
409 
410  #endif // DEBUGPRINTER_NO_EXECINFO
411 
412 /*******************************************************************************
413  * DebugPrinter "private" parts
414  */
415 
417  struct detail {
418 
419  DebugPrinter & super;
420 
421  // Simulate method specialisation through overloading
422  template<typename T> struct fwdtype {};
423 
424  // Specialisation for type printing, used through dout_TYPE and dout_TYPE_OF
425  #define DEBUGPRINTER_TYPE_SPEC(mods) \
426  template <typename T> \
427  inline void type(detail::fwdtype<T mods>, \
428  const std::string valness = "", \
429  const std::string expr = "") const { \
430  int dummy; \
431  std::string info = ""; \
432  if(expr != "") \
433  info = " {" + valness + " " + expr + "}"; \
434  auto mod = super.mod_split(std::string(#mods)); \
435  super << mod.first << super.demangle(typeid(T).name(), dummy) \
436  << mod.second << info << std::endl; \
437  } //
438  DEBUGPRINTER_TYPE_SPEC()
439  DEBUGPRINTER_TYPE_SPEC(&)
440  DEBUGPRINTER_TYPE_SPEC(&&)
441  DEBUGPRINTER_TYPE_SPEC(const)
442  DEBUGPRINTER_TYPE_SPEC(const &)
443  DEBUGPRINTER_TYPE_SPEC(const &&)
444  DEBUGPRINTER_TYPE_SPEC(volatile)
445  DEBUGPRINTER_TYPE_SPEC(volatile &)
446  DEBUGPRINTER_TYPE_SPEC(volatile &&)
447  DEBUGPRINTER_TYPE_SPEC(const volatile)
448  DEBUGPRINTER_TYPE_SPEC(const volatile &)
449  DEBUGPRINTER_TYPE_SPEC(const volatile &&)
450  #undef DEBUGPRINTER_TYPE_SPEC
451 
452  // Get valueness of provided variable
453  template<typename T>
454  const std::string valueness(T &&) const noexcept {
455  return super.valueness_impl(fwdtype<T>());
456  }
457 
458  // Was used to print types and vars through same iface, but loses cv + ref
459  // We cannot implement a function like sizeof() and typeid() that takes
460  // types and instances.
461  // ToDo: or can we?
462  // try appending #def TYPE TYPE_IMPL(input x) and abuse comma split
463  // and typeid/sizeof for x
464  inline void type_name(const std::string & name,
465  const std::string & traits = "") {
466  int dummy;
467  *(super.outstream) << traits << super.demangle(name, dummy) << std::endl;
468  }
469 
470  inline void pause(std::string reason) const {
471  if(reason != "") reason = " (" + reason + ")";
472  std::cout << "DebugPrinter paused" << reason
473  << ". Press ENTER to continue." << std::flush;
474  std::cin.clear();
475  std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
476  }
477  template <typename T>
478  static const T & pausecheck(const T & t) {
479  return t;
480  }
481  static bool pausecheck() { return true; }
482 
483  inline std::string filemacro_name(const std::string str) const {
484  return str.substr(str.rfind(DEBUGPRINTER_DIRSEP)+1);
485  }
486 
487  } const detail_{*this};
489 
490 /*******************************************************************************
491  * DebugPrinter private parts
492  */
493 
494  private:
495 
496  std::ostream * outstream; // output stream
497  std::shared_ptr<std::ostream> outstream_mm; // managed output stream
498  std::streamsize prec_; // precision
499  std::string hcol_; // highlighting color
500  std::string hcol_r_; // neutral color
501 
502  static const unsigned int max_backtrace = 50;
503  static const unsigned int max_demangled = 4096;
504 
505  #ifndef DEBUGPRINTER_NO_SIGNALS
506  using sig_type = int;
507  static std::map<sig_type, std::string> sig_names() {
508  std::map<sig_type, std::string> res;
509  res[SIGABRT] = "SIGABRT";
510  res[SIGFPE] = "SIGFPE";
511  res[SIGSEGV] = "SIGSEGV";
512  res[SIGSYS] = "SIGSYS";
513  return res;
514  }
515  //~ static DebugPrinter & static_init() { static DebugPrinter d; return d; }
516  static void signal_handler(int signum) {
517  static DebugPrinter d;
518  d << "DebugPrinter handler caught signal "
519  << sig_names()[signum] << " (" << signum << ")" << std::endl;
520  //~ static_init().stack(max_backtrace, false, 3);
521  d.stack(max_backtrace, false, 3);
522  struct sigaction act;
523  act.sa_handler = SIG_DFL;
524  sigaction(signum, &act, NULL);
525  }
526  #endif // DEBUGPRINTER_NO_SIGNALS
527 
528  #ifndef DEBUGPRINTER_NO_CXXABI
529 
530  inline std::string demangle(const std::string & str, int & status) const {
531  std::unique_ptr<char, void(*)(void*)> // needs to call free, not delete
532  dmgl(abi::__cxa_demangle(str.c_str(), 0, 0, &status), std::free);
533  return (status==0) ? dmgl.get() : str; // calls string ctor for char*
534  }
535 
536  #else // DEBUGPRINTER_NO_CXXABI
537 
538  inline std::string demangle(const std::string & str, int &) const noexcept {
539  return str;
540  }
541 
542  #endif // DEBUGPRINTER_NO_CXXABI
543 
544  // Fetch different parts from a stack trace line
545  #ifdef __APPLE__
546  inline std::string prog_part(const std::string str) const {
547  std::stringstream ss(str);
548  std::string res;
549  ss >> res; ss >> res;
550  return res;
551  }
552  inline std::string mangled_part(const std::string str) const {
553  std::stringstream ss(str);
554  std::string res;
555  ss >> res; ss >> res; ss >> res; ss >> res;
556  return res;
557  }
558  inline std::string offset_part(const std::string str) const {
559  std::stringstream ss(str);
560  std::string res;
561  ss >> res; ss >> res; ss >> res; ss >> res; ss >> res; ss >> res;
562  return res;
563  }
564  inline std::string address_part(const std::string str) const {
565  std::stringstream ss(str);
566  std::string res;
567  ss >> res; ss >> res; ss >> res;
568  return res;
569  }
570  #else
571  inline std::string prog_part(const std::string str) const {
572  return str.substr(0, str.find("("));
573  }
574  inline std::string mangled_part(const std::string str) const {
575  std::string::size_type pos = str.find("(") + 1;
576  if(str.find("+", pos) == std::string::npos) return "";
577  return str.substr(pos, str.find("+", pos) - pos);
578  }
579  inline std::string offset_part(const std::string str) const {
580  std::string::size_type pos = str.find("+");
581  if(pos == std::string::npos) return "";
582  else return str.substr(pos+1, str.find(")", pos) - pos-1);
583  }
584  inline std::string address_part(const std::string str) const {
585  std::string::size_type pos = str.find("[");
586  if(pos == std::string::npos) return "";
587  else return str.substr(pos+1, str.find("]", pos) - pos-1);
588  }
589  #endif
590 
591  // Used for set_color validation
592  bool is_number(const std::string& s) const {
593  return !s.empty() && std::find_if(s.begin(), s.end(),
594  [](char c) { return !std::isdigit(c); }) == s.end();
595  }
596 
597  // Implementation of operator()
598  template <bool B, typename U, typename V>
599  std::enable_if_t<!B>
600  print_stream_impl(const U& label, const V& obj, const std::string&) const {
601  auto typ = [this](const auto & obj) // careless (dummy) demangle wrapper
602  { int dummy = 0; return demangle(typeid(obj).name(), dummy); };
603  *outstream << "DebugPrinter error: object of type "
604  << ( has_stream<U> ? typ(obj) : typ(label) ) << std::endl
605  << " has no suitable " << typ(*outstream)
606  << " operator<< overload." << std::endl;
607  }
608  template <bool B, typename U, typename V>
609  std::enable_if_t<B>
610  print_stream_impl(const U& label, const V& obj, const std::string& sc) const {
611  *outstream << hcol_ << label << sc << obj
612  << hcol_r_ << std::endl;
613  }
614 
615 
616  // ToDo: specialise on m_and (break on false before evaluating rest, faster compile)
617  // and use in print_stream_impl instead of has_stream && has_stream
618  template <bool B, typename... T>
619  struct m_and_impl {
620  using type = std::integral_constant<bool , B>;
621  };
622 
623  template <bool B, typename T, typename... U>
624  struct m_and_impl<B, T, U...> {
625  using type = typename m_and_impl<B && T::value, U...>::type;
626  };
627  template <typename... T>
628  struct m_and : public m_and_impl<true, T...>::type {
629  };
630 
631  template<typename T, typename S>
632  struct has_stream_impl {
633  template <typename T_, typename S_>
634  static auto check(T_ && t, S_ && s) -> decltype( // test for any expression:
635  s << t,
636  std::true_type());
637  static std::false_type check(...);
638  using type = decltype(check( std::declval<T>(), std::declval<S>()));
639  };
640  template<typename T, typename S = std::ostream&>
641  struct has_stream_t : public has_stream_impl<T, S>::type {}; // ::type stl boolean has ::value
642  template<typename T, typename S = std::ostream&>
643  static constexpr bool has_stream = has_stream_impl<T, S>::type::value;
644 
645 
646 
647  // Used to split mods for type()/type_of()
648  std::pair<std::string, std::string> mod_split(const std::string & s) const {
649  auto pos = s.find('&');
650  pos = pos==std::string::npos ? s.size() : pos;
651  std::string first = s.substr(0, pos), second = s.substr(pos);
652 
653  if(first.size() > 0 && first.back()!= ' ') first += " ";
654  if(second.size() > 0) second = " " + second;
655  return std::make_pair(first, second);
656  }
657 
658  // Used for valueness printing
659  template <typename T>
660  const std::string valueness_impl(detail::fwdtype<T>) const noexcept
661  { return "r-value"; }
662  template <typename T>
663  const std::string valueness_impl(detail::fwdtype<T&>) const noexcept
664  { return "l-value"; }
665 
666 };
667 
668 /*******************************************************************************
669  * std::ostream overloads
670  */
671 
673 template <typename T>
674 DebugPrinter & operator<<(DebugPrinter & d, const T& output) {
675  std::ostream & out = *d.outstream;
676  std::streamsize savep = out.precision();
677  std::ios_base::fmtflags savef =
678  out.setf(std::ios_base::fixed, std::ios::floatfield);
679  out << std::setprecision(static_cast<int>(d.prec_)) << std::fixed << output
680  << std::setprecision(static_cast<int>(savep));
681  out.setf(savef, std::ios::floatfield);
682  out.flush();
683  return d;
684 }
685 
688  std::ostream& (*pf)(std::ostream&)) {
689  std::ostream & out = *d.outstream;
690  out << pf;
691  return d;
692 }
693 
695 template <typename T>
696 inline DebugPrinter & operator,(DebugPrinter & d, const T& output) {
697  return operator<<(d, output);
698 }
699 
702  std::ostream& (*pf)(std::ostream&)) {
703  return operator<<(d, pf);
704 }
705 
706 /*******************************************************************************
707  * Namespace Globals
708  */
709 
710 // Heap allocate => no destructor call at program exit (wiped by OS).
711 // => no management of std::ostream& possible
712 // (stop BLC design like shared_ptr on DebugPrinter::outstream)
714 static DebugPrinter& dout = *new DebugPrinter;
715 
716 /*******************************************************************************
717  * Macros
718  */
719 
731 #define dout_HERE fsc::dout(fsc::dout.detail_.filemacro_name(__FILE__), \
732  std::to_string(__LINE__) \
733  + " (" + std::string(__func__) + ")",":"); //
734 
750 #define dout_FUNC fsc::dout.stack(1, true);
751 
768 #define dout_VAL(...) fsc::dout(#__VA_ARGS__, (__VA_ARGS__), " = ");
769 
779 #define dout_TYPE(...) fsc::dout.detail_.type( \
780  fsc::DebugPrinter::detail::fwdtype<__VA_ARGS__>() \
781 ); //
782 
795 #define dout_TYPE_OF(...) fsc::dout.detail_.type( \
796  fsc::DebugPrinter::detail::fwdtype<decltype(__VA_ARGS__)>() \
797  , fsc::dout.detail_.valueness(__VA_ARGS__) \
798  , #__VA_ARGS__ \
799 ); //
800 
819 #define dout_STACK fsc::dout.stack();
820 
835 #define dout_PAUSE(...) \
836  if(fsc::dout.detail_.pausecheck(__VA_ARGS__)) \
837  fsc::dout.detail_.pause(#__VA_ARGS__); //
838 
843 #else // DEBUGPRINTER_OFF
844 
845 class DebugPrinter {
846  public:
847  inline void operator=(std::ostream &) noexcept {}
848  inline void operator=(std::ostream &&) {}
849  inline void set_precision(const int) noexcept {}
850  inline void set_color(...) noexcept {}
851  inline void operator()(...) const {}
852  inline void stack(...) const {}
853 };
854 
855 template <typename T>
856 inline DebugPrinter & operator<<(DebugPrinter & d, const T&) {return d;}
858  std::ostream& (*)(std::ostream&)) {return d;}
859 template <typename T>
860 inline DebugPrinter & operator,(DebugPrinter & d, const T&) {return d;}
861 inline DebugPrinter & operator,(DebugPrinter & d,
862  std::ostream& (*)(std::ostream&)) {return d;}
863 
864 static DebugPrinter dout;
865 
866 #define dout_HERE ;
867 #define dout_FUNC ;
868 #define dout_VAL(...) ;
869 #define dout_TYPE(...) ;
870 #define dout_TYPE_OF(...) ;
871 #define dout_STACK ;
872 #define dout_PAUSE(...) ;
873 
874 #endif // DEBUGPRINTER_OFF
875 
877 namespace detail {
878  namespace dont_even_ask {
879 
880  inline void function() { (void)dout; }
881 
882  }
883 }
885 
886 } // namespace fsc
887 
888 #endif // DEBUGPRINTER_HEADER
889 
void operator()(const T &label, U const &obj, const std::string sc=": ") const
Print highlighted label and object.
DebugPrinter()
Constructor for dout and user specified DebugPrinter objects.
Class for global static dout object.
void set_color(const std::string str)
Highlighting color.
DebugPrinter & operator,(DebugPrinter &d, const T &output)
operator, overload for std::ostream
void stack(const int backtrace_size=max_backtrace, const bool compact=false, const int begin=1) const
Print a stack trace.
friend DebugPrinter & operator<<(DebugPrinter &, const T &)
operator<< overload for std::ostream
auto operator=(T &&os) -> std::enable_if_t< std::is_move_assignable< T >::value &&std::is_rvalue_reference< decltype(os)>::value >
Assignment operator for moving streams.
void operator()(const T &obj) const
Print highlighted object.
General fsc namespace.
void set_color() noexcept
Remove highlighting color.
void operator=(std::ostream &os) noexcept
Assignment operator for changing streams.
void set_precision(const std::streamsize prec) noexcept
Number of displayed decimal digits.