ptc-print
A C++17 header-only library for custom printing to the output stream (inspired by the Python print function).
print.hpp
Go to the documentation of this file.
1 //====================================================
2 // File data
3 //====================================================
11 //====================================================
12 // Preprocessor directives
13 //====================================================
14 #pragma once
15 #ifndef PYTHON_TO_CPP_PRINT_HPP
16 #define PYTHON_TO_CPP_PRINT_HPP
17 
18 //====================================================
19 // Headers
20 //====================================================
21 
22 // Standard headers
23 #include <iostream>
24 #include <sstream>
25 #include <string>
26 #include <string_view>
27 #include <type_traits>
28 #include <mutex>
29 #include <utility>
30 #include <locale>
31 #include <codecvt>
32 
33 // Extra types headers
34 #ifndef PTC_DISABLE_STD_TYPES_PRINTING
35 #include <stddef.h>
36 #include <complex>
37 #include <array>
38 #include <stack>
39 #include <queue>
40 #include <chrono>
41 #include <unordered_map>
42 #include <typeindex>
43 #include <iterator>
44 #include <optional>
45 #include <ratio>
46 #include <algorithm>
47 #endif
48 
49 //====================================================
50 // Namespaces
51 //====================================================
52 using namespace std::literals::string_literals;
53 
54 namespace ptc
55  {
56  //====================================================
57  // Enum classes
58  //====================================================
59 
60  // mode
65  enum class mode { str };
66 
67  // ANSI_pos
72  enum class ANSI { first, generic };
73 
74  //====================================================
75  // Helper tools
76  //====================================================
77 
78  namespace
79  {
80  // StringConverter
88  template <class CharT>
89  std::conditional_t<std::is_same_v<CharT, char>, const std::basic_string<CharT>&, std::basic_string<CharT>>
90  StringConverter( const std::string& input_str )
91  {
92  if constexpr( std::is_same_v <CharT, char> )
93  {
94  return input_str;
95  }
96  else if constexpr( std::is_same_v <CharT, wchar_t> )
97  {
98  static std::wstring_convert <std::codecvt_utf8_utf16 <wchar_t>> converter_wchar_t;
99  return converter_wchar_t.from_bytes( input_str );
100  }
101  #ifndef __APPLE__
102  #if ( __cplusplus >= 202002L )
103  else if constexpr( std::is_same_v <CharT, char8_t> )
104  {
105  return reinterpret_cast <const char8_t*>( input_str.c_str() );
106  }
107  #endif
108  else if constexpr( std::is_same_v <CharT, char16_t> )
109  {
110  static std::wstring_convert <std::codecvt_utf8_utf16 <char16_t>, char16_t> converter_16_t;
111  return converter_16_t.from_bytes( input_str );
112  }
113  else if constexpr( std::is_same_v <CharT, char32_t> )
114  {
115  static std::wstring_convert <std::codecvt_utf8_utf16 <char32_t>, char32_t> converter_32_t;
116  return converter_32_t.from_bytes( input_str );
117  }
118  #endif
119  else
120  {
121  return StringConverter<CharT>( "" );
122  }
123  }
124 
125  #ifndef PTC_DISABLE_STD_TYPES_PRINTING
126 
127  // is_streamable
133  template <class T, class T_str>
134  struct is_streamable
135  {
136  static std::false_type test( ... );
137 
138  template<class U>
139  static auto test( const U& u ) -> decltype( std::declval <std::basic_ostream<T_str>&>() << u, std::true_type{} );
140 
141  static constexpr bool value = decltype( test( std::declval <T>() ) )::value;
142  };
143 
144  template<class T, class T_str>
145  inline constexpr bool is_streamable_v = is_streamable<T, T_str>::value;
146 
147  // Helper function for container adaptors printing
156  template <class Container, class T_str>
157  inline void print_adaptor( std::basic_ostream<T_str>& os, const Container& container )
158  {
159  typename Container::const_iterator beg = container.begin();
160  std::basic_string<T_str> separator = StringConverter<T_str>( ""s );
161 
162  while( beg != container.end() )
163  {
164  os << separator << *beg++;
165  separator = StringConverter<T_str>( ", "s );
166  }
167  }
168 
169  // container_mod overload for std::stack hacked printing
178  template <class Type, class Container>
179  const Container& container_mod( const std::stack<Type, Container>& stack )
180  {
181  struct HackedStack : private std::stack<Type, Container>
182  {
183  static const Container& container( const std::stack<Type, Container>& stack )
184  {
185  return stack.*&HackedStack::c;
186  }
187  };
188 
189  return HackedStack::container( stack );
190  }
191 
192  // container_mod overload for std::priority_queue hacked printing
201  template < class Type, class Container >
202  const Container& container_mod( const std::priority_queue<Type, Container>& priority_queue )
203  {
204  struct HackedQueue : private std::priority_queue<Type, Container>
205  {
206  static const Container& container( const std::priority_queue<Type, Container>& priority_queue )
207  {
208  return priority_queue.*&HackedQueue::c;
209  }
210  };
211 
212  return HackedQueue::container( priority_queue );
213  }
214 
215  //====================================================
216  // Operator << overloads for stdlib types
217  //====================================================
218 
219  #if defined( __GNUC__ ) && ( __GNUC___ <= 9 )
220 
221  // Overload for std::nullptr_t
229  template <class T_str>
230  inline std::basic_ostream<T_str>& operator << ( std::basic_ostream<T_str>& os, std::nullptr_t )
231  {
232  os << "nullptr";
233  return os;
234  }
235 
236  #endif
237 
238  // Overload for std::complex
248  template <class T_str, class T_cmplx>
249  inline std::basic_ostream<T_str>& operator << ( std::basic_ostream<T_str>& os, const std::complex<T_cmplx>& number )
250  {
251  os << number.real() << '+' << number.imag() << 'j';
252 
253  return os;
254  }
255 
256  // Helper overload for std::vector and std::map
267  template <class T_str, class T, class U>
268  inline std::basic_ostream<T_str>& operator <<( std::basic_ostream<T_str>& os, const std::pair <T, U>& p )
269  {
270  os << '[' << p.first << ", " << p.second << ']';
271 
272  return os;
273  }
274 
275  // Overload for all containers printing
287  template <template <typename, typename...> class ContainerType, typename ValueType, typename... Args, class T_str>
288  std::enable_if_t< ! is_streamable_v <ContainerType <ValueType, Args...>, T_str>, std::basic_ostream<T_str>&>
289  operator <<( std::basic_ostream<T_str>& os, const ContainerType<ValueType, Args...>& container )
290  {
291  static bool constexpr is_stack = std::is_same_v <ContainerType<ValueType, Args...>, std::stack<ValueType>>;
292  static bool constexpr is_pqueue = std::is_same_v <ContainerType<ValueType, Args...>, std::priority_queue<ValueType>>;
293 
294  os << '[';
295  if constexpr ( ! is_stack && ! is_pqueue )
296  {
297  std::basic_string<T_str> separator = StringConverter<T_str>( ""s );
298  for ( const auto& elem: container )
299  {
300  os << separator;
301  os << elem;
302  separator = StringConverter<T_str>( ", "s );
303  }
304  }
305  else
306  {
307  print_adaptor( os, container_mod( container ) );
308  }
309  os << ']';
310 
311  return os;
312  }
313 
314  // Overload for std::array printing
325  template <class T_str, class T, size_t T_no>
326  std::basic_ostream<T_str>& operator <<( std::basic_ostream<T_str>& os, const std::array<T, T_no>& container )
327  {
328  std::basic_string<T_str> separator = StringConverter<T_str>( ""s );
329 
330  os << '[';
331  std::copy
332  (
333  std::begin( container ),
334  std::prev( container.end() ),
335  std::ostream_iterator <T> { os, ", " }
336  );
337  os << container.back();
338  os << ']';
339 
340  return os;
341  }
342 
343  // Overload for C arrays
354  template <class T_str, class T1, size_t arrSize,
355  typename = std::enable_if_t< ! std::is_same <T1,char>::value>>
356  std::basic_ostream<T_str>& operator <<( std::basic_ostream<T_str>& os, const T1( & arr )[ arrSize ] )
357  {
358  os << '[';
359  if ( arrSize )
360  {
361  std::basic_string<T_str> separator = StringConverter<T_str>( ""s );
362  for ( const auto& elem: arr )
363  {
364  os << separator;
365  os << elem;
366  separator = StringConverter<T_str>( ", "s );
367  }
368  }
369  os << ']';
370 
371  return os;
372  }
373 
374  // Overload for std::chrono::duration objects
385  template <class T_str, class T_time, class int_type>
386  std::basic_ostream<T_str>& operator <<( std::basic_ostream<T_str>& os, const std::chrono::duration<int_type, T_time>& val )
387  {
388  if constexpr( ! std::is_same_v<std::chrono::duration<int_type, T_time>, std::chrono::duration<int_type>> )
389  {
390  static std::unordered_map<std::type_index, std::basic_string_view<T_str>> time_map
391  {
392  { typeid( std::nano ), StringConverter<T_str>( "ns" ) },
393  { typeid( std::micro ), StringConverter<T_str>( "us" ) },
394  { typeid( std::milli ), StringConverter<T_str>( "ms" ) },
395  { typeid( std::ratio<60> ), StringConverter<T_str>( "min" ) },
396  { typeid( std::ratio<3600> ), StringConverter<T_str>( "h" ) }
397  };
398 
399  #if ( __cplusplus >= 202002L )
400  time_map.insert( { typeid( std::ratio<86400> ), StringConverter<T_str>( "d" ) } );
401  time_map.insert( { typeid( std::ratio<604800> ), StringConverter<T_str>( "w" ) } );
402  time_map.insert( { typeid( std::ratio<2629746> ), StringConverter<T_str>( "mos" ) } );
403  time_map.insert( { typeid( std::ratio<31556952> ), StringConverter<T_str>( "y" ) } );
404  #endif
405 
406  os << val.count() << time_map[ typeid( T_time ) ];
407  }
408  else if constexpr( std::is_same_v<std::chrono::duration<int_type, T_time>, std::chrono::duration<int_type>> )
409  {
410  os << val.count() << 's';
411  }
412 
413  return os;
414  }
415 
416  // Overload for std::optional
426  template <class T_str, class T>
427  std::basic_ostream<T_str>& operator <<( std::basic_ostream<T_str>& os, std::optional<T> const& opt )
428  {
429  if ( opt ) os << opt.value();
430  else os << "nullopt";
431 
432  return os;
433  }
434 
435  // Overload for std::tuple
436  template<std::size_t...>
437  struct seq{};
438 
439  template<std::size_t N, std::size_t... Is>
440  struct gen_seq: gen_seq<N-1, N-1, Is...>{};
441 
442  template<std::size_t... Is>
443  struct gen_seq<0, Is...> : seq<Is...>{};
444 
445  template<class T_str, class Tuple, std::size_t... Is>
446  void print_tuple( std::basic_ostream<T_str>& os, const Tuple& tup, seq<Is...> )
447  {
448  using swallow = int[];
449  ( void )swallow{ 0, ( void( os << ( Is == 0 ? "" : ", " ) << std::get<Is>( tup ) ), 0 )... };
450  }
451 
452  template<class T_str, class... Args>
453  std::basic_ostream<T_str>& operator<<( std::basic_ostream<T_str>& os, const std::tuple<Args...>& tup )
454  {
455  os << "(";
456  print_tuple( os, tup, gen_seq<sizeof...( Args )>() );
457  os << ")";
458 
459  return os;
460  }
461 
462  #endif
463 
464  //====================================================
465  // Functions used to select printing format
466  //====================================================
467 
468  // ptr
477  template <class T_str = char, class T>
478  std::basic_string<T_str> ptr( T* ptr )
479  {
480  static std::basic_ostringstream<T_str> oss;
481  oss.str( StringConverter<T_str>( ""s ) );
482  oss.clear();
483 
484  oss << "Value: " << ptr << "\n";
485  oss << "Address: " << &ptr;
486 
487  return oss.str();
488  }
489  }
490 
491  //====================================================
492  // ptc_print class
493  //====================================================
499  template <class T_str>
500  struct Print
501  {
502  //====================================================
503  // Public constructors and destructor
504  //====================================================
505 
506  // Default constructor
511  explicit Print():
512  end( StringConverter<T_str>( "\n"s ) ),
513  sep( StringConverter<T_str>( " "s ) ),
514  flush( false )
515  {
516  #ifdef PTC_ENABLE_PERFORMANCE_IMPROVEMENTS
517  performance_options();
518  #endif
519  }
520 
521  //====================================================
522  // Public structs
523  //====================================================
524 
525  // select_cout
531  template <class T>
532  struct select_cout
533  {
534  static std::basic_ostream<T> &cout;
535  };
536 
537  #ifdef PTC_ENABLE_PERFORMANCE_IMPROVEMENTS
538 
539  // select_cin
545  template <class T>
546  struct select_cin
547  {
548  static std::basic_istream<T> &cin;
549  };
550 
551  #endif
552 
553  //====================================================
554  // Public setters
555  //====================================================
556 
557  // setEnd
564  template <class T>
565  inline void setEnd( const T& end_val )
566  {
567  end = end_val;
568  }
569 
570  // setSep
577  template <class T>
578  inline void setSep( const T& sep_val )
579  {
580  sep = sep_val;
581  }
582 
583  // setFlush
589  inline void setFlush( const bool& flush_val )
590  {
591  flush = flush_val;
592  }
593 
594  // setPattern
601  template <class T>
602  inline void setPattern( const T& pattern_val )
603  {
604  pattern = pattern_val;
605  }
606 
607  //====================================================
608  // Public getters
609  //====================================================
610 
611  #ifdef PTC_ENABLE_GETTERS_FOR_UNIT_TESTS
612 
613  // getEnd
619  inline const auto& getEnd() const
620  {
621  return end;
622  }
623 
624  // getSep
630  inline const auto& getSep() const
631  {
632  return sep;
633  }
634 
635  // getFlush
641  inline const bool& getFlush() const
642  {
643  return flush;
644  }
645 
646  // getPattern
652  inline const auto& getPattern() const
653  {
654  return pattern;
655  }
656 
657  #endif
658 
659  //====================================================
660  // Public operator () overloads
661  //====================================================
662 
663  // General case
672  template <class T, class... Args>
673  void operator()( T&& first, Args&&... args ) const
674  {
675  if constexpr ( std::is_base_of_v <std::basic_ostream<T_str>, std::remove_reference_t<T>> )
676  {
677  print_backend( std::forward<T>( first ), std::forward<Args>( args )... );
678  }
679  else
680  {
681  print_backend( select_cout<T_str>::cout, std::forward<T>( first ), std::forward<Args>( args )... );
682  }
683  }
684 
685  // String initialization case
694  template <class... Args>
695  const std::basic_string<T_str> operator()( mode&& first, Args&&... args ) const
696  {
697  if constexpr( sizeof...( args ) > 0 )
698  {
699  switch( first )
700  {
701  case mode::str:
702  {
703  static std::basic_ostringstream<T_str> oss;
704  oss.str( StringConverter<T_str>( ""s ) );
705  oss.clear();
706  print_backend( oss, std::forward<Args>( args )... );
707 
708  return oss.str();
709  }
710  }
711  }
712 
713  return StringConverter<T_str>( "" );
714  }
715 
716  // No arguments case
722  inline void operator () ( std::basic_ostream<T_str>& os = select_cout<T_str>::cout ) const
723  {
724  os << end;
725  if ( flush ) os << std::flush;
726  }
727 
728  private:
729 
730  //====================================================
731  // Private methods
732  //====================================================
733 
734  // is_escape
744  template <typename T>
745  static constexpr bool is_escape( const T& str, const ANSI& flag )
746  {
747  if constexpr( std::is_convertible_v <T, std::basic_string_view<T_str>> && ! std::is_same_v<T, std::nullptr_t> )
748  {
749  switch( flag )
750  {
751  case( ANSI::first ):
752  {
753  return ( std::basic_string_view<T_str>( str ).length() < 7 ) && ( str[0] == '\033' );
754  }
755  case( ANSI::generic ):
756  {
757  return ( std::basic_string_view<T_str>( str ).find( '\033' ) != std::basic_string_view<T_str>::npos );
758  }
759  }
760  }
761  return false;
762  }
763 
764  // print_backend
775  template <class T_os, class T, class... Args>
776  void print_backend( T_os&& os, T&& first, Args&&... args ) const
777  {
778  std::lock_guard <std::mutex> lock{ mutex_ };
779 
780  // Printing the first argument
781  if( is_escape( first, ANSI::first ) || pattern.empty() ) os << first;
782  else os << pattern << first << pattern;
783 
784  // Printing all the other arguments
785  if constexpr( sizeof...( args ) > 0 )
786  {
787  if ( is_escape( first, ANSI::first ) )
788  {
789  if( pattern.empty() ) ( ( os << args << sep ), ...);
790  else ( ( os << pattern << args << pattern << sep ), ...);
791  }
792  else
793  {
794  if( pattern.empty() ) ( ( os << sep << args ), ...);
795  else ( ( os << sep << pattern << args << pattern ), ...);
796  }
797  }
798  os << end;
799 
800  // Resetting the stream from ANSI escape sequences
801  if constexpr( sizeof...( args ) > 0 )
802  {
803  if ( is_escape( first, ANSI::generic ) || ( ( is_escape( args, ANSI::generic ) ) || ...) )
804  {
805  os << "\033[0m";
806  }
807  }
808  else
809  {
810  if ( is_escape( first, ANSI::generic ) )
811  {
812  os << "\033[0m";
813  }
814  }
815 
816  // Other operations
817  if ( flush ) os << std::flush;
818  }
819 
820  // performance_options
825  inline void performance_options() const
826  {
827  std::lock_guard <std::mutex> lock{ mutex_ };
828 
829  std::ios_base::sync_with_stdio( false );
830  select_cout<T_str>::cout.tie( nullptr );
831 
832  #ifdef PTC_ENABLE_PERFORMANCE_IMPROVEMENTS
833  select_cin<T_str>::cin.tie( nullptr );
834  #endif
835  }
836 
837  //====================================================
838  // Private attributes
839  //====================================================
840  std::basic_string<T_str> end, sep, pattern;
841  static std::mutex mutex_;
842  bool flush;
843  };
844 
845  //====================================================
846  // Other steps
847  //====================================================
848 
849  // Print structs specializations
850  template <> template <> inline std::ostream &Print<char>::select_cout <char>::cout = std::cout;
851  template <> template <> inline std::wostream &Print<wchar_t>::select_cout <wchar_t>::cout = std::wcout;
852 
853  #ifdef PTC_ENABLE_PERFORMANCE_IMPROVEMENTS
854  template <> template <> inline std::istream &Print<char>::select_cin <char>::cin = std::cin;
855  template <> template <> inline std::wistream &Print<wchar_t>::select_cin <wchar_t>::cin = std::wcin;
856  #endif
857 
858  // Print::mutex_ definiton
859  template <class T_str> inline std::mutex Print <T_str>::mutex_;
860 
861  // Print objects initialization
862  inline Print <char> print; // char
863  inline Print <wchar_t> wprint; // wchar_t
864 
865  #if ! defined( PTC_ENABLE_PERFORMANCE_IMPROVEMENTS ) && ! defined( __APPLE__ )
866  #if ( __cplusplus >= 202002L )
867  inline Print <char8_t> print8; // char8_t
868  #endif
869  inline Print <char16_t> print16; // char16_t
870  inline Print <char32_t> print32; // char32_t
871  #endif
872  }
873 
874 #endif
Definition: print.hpp:55
Print< wchar_t > wprint
Definition: print.hpp:863
mode
Enum class used to set up the "str" mode to pass a string with the content of the print function.
Definition: print.hpp:65
ANSI
Enum class used to switch among ANSI escape configurations in the "is_escape" function.
Definition: print.hpp:72
Print< char > print
Definition: print.hpp:862
Print< char32_t > print32
Definition: print.hpp:870
Print< char16_t > print16
Definition: print.hpp:869
Struct used to define a way to template the choice of the "std::cout" object in order to be "std::cou...
Definition: print.hpp:533
static std::basic_ostream< T > & cout
Definition: print.hpp:534
Class used to construct the print function.
Definition: print.hpp:501
Print()
Default constructor of the Print class. It initializes the basic class members and enable (if require...
Definition: print.hpp:511
void setEnd(const T &end_val)
Setter used to set the value of the "end" variable. Templated type is required in order to allow also...
Definition: print.hpp:565
void setSep(const T &sep_val)
Setter used to set the value of the "sep" variable. Templated type is required in order to allow also...
Definition: print.hpp:578
void setFlush(const bool &flush_val)
Setter used to set the value of the "flush" variable. Templated type is required in order to allow al...
Definition: print.hpp:589
void operator()(T &&first, Args &&... args) const
Frontend implementation of the () operator overload to print to the output stream....
Definition: print.hpp:673
const std::basic_string< T_str > operator()(mode &&first, Args &&... args) const
Frontend implementation of the () operator overload to initialize its content with an std::string obj...
Definition: print.hpp:695
void setPattern(const T &pattern_val)
Setter used to set the value of the "pattern" variable. Templated type is required in order to allow ...
Definition: print.hpp:602