@@ -622,48 +622,79 @@ inline bool pretty_print(std::ostream& stream,
622622
623623#endif
624624
625+ template <typename T, typename ... U>
626+ struct last {
627+ using type = typename last<U...>::type;
628+ };
629+
630+ template <typename T>
631+ struct last <T> {
632+ using type = T;
633+ };
634+
635+ template <typename ... T>
636+ using last_t = typename last<T...>::type;
637+
625638class DebugOutput {
626639 public:
627- DebugOutput (const char * filepath,
628- int line,
629- const char * function_name,
630- const char * expression)
631- : m_use_colorized_output(isColorizedOutputEnabled()),
632- m_filepath (filepath),
633- m_line(line),
634- m_function_name(function_name),
635- m_expression(expression) {
636- const std::size_t path_length = m_filepath.length ();
640+ // Helper alias to avoid obscure type `const char* const*` in signature.
641+ using expr_t = const char *;
642+
643+ DebugOutput (const char * filepath, int line, const char * function_name)
644+ : m_use_colorized_output(isColorizedOutputEnabled()) {
645+ std::string path = filepath;
646+ const std::size_t path_length = path.length ();
637647 if (path_length > MAX_PATH_LENGTH) {
638- m_filepath = " .." + m_filepath.substr (path_length - MAX_PATH_LENGTH,
639- MAX_PATH_LENGTH);
648+ path = " .." + path.substr (path_length - MAX_PATH_LENGTH, MAX_PATH_LENGTH);
649+ }
650+ std::stringstream ss;
651+ ss << ansi (ANSI_DEBUG) << " [" << path << " :" << line << " ("
652+ << function_name << " )] " << ansi (ANSI_RESET);
653+ m_location = ss.str ();
654+ }
655+
656+ template <typename ... T>
657+ auto print (std::initializer_list<expr_t > exprs, T&&... values)
658+ -> last_t<T...> {
659+ if (exprs.size () != sizeof ...(values)) {
660+ std::cerr
661+ << m_location << ansi (ANSI_WARN)
662+ << " The number of arguments mismatch, please check unprotected comma"
663+ << ansi (ANSI_RESET) << std::endl;
640664 }
665+ return print_impl (exprs.begin (), std::forward<T>(values)...);
641666 }
642667
668+ private:
643669 template <typename T>
644- T&& print (const std::string& type , T&& value) const {
670+ T&& print_impl (const expr_t * expr , T&& value) {
645671 const T& ref = value;
646672 std::stringstream stream_value;
647673 const bool print_expr_and_type = pretty_print (stream_value, ref);
648674
649675 std::stringstream output;
650- output << ansi (ANSI_DEBUG) << " [" << m_filepath << " :" << m_line << " ("
651- << m_function_name << " )] " << ansi (ANSI_RESET);
676+ output << m_location;
652677 if (print_expr_and_type) {
653- output << ansi (ANSI_EXPRESSION) << m_expression << ansi (ANSI_RESET)
654- << " = " ;
678+ output << ansi (ANSI_EXPRESSION) << *expr << ansi (ANSI_RESET) << " = " ;
655679 }
656680 output << ansi (ANSI_VALUE) << stream_value.str () << ansi (ANSI_RESET);
657681 if (print_expr_and_type) {
658- output << " (" << ansi (ANSI_TYPE) << type << ansi (ANSI_RESET) << " )" ;
682+ output << " (" << ansi (ANSI_TYPE) << type_name<T>() << ansi (ANSI_RESET)
683+ << " )" ;
659684 }
660685 output << std::endl;
661686 std::cerr << output.str ();
662687
663688 return std::forward<T>(value);
664689 }
665690
666- private:
691+ template <typename T, typename ... U>
692+ auto print_impl (const expr_t * exprs, T&& value, U&&... rest)
693+ -> last_t<T, U...> {
694+ print_impl (exprs, std::forward<T>(value));
695+ return print_impl (exprs + 1 , std::forward<U>(rest)...);
696+ }
697+
667698 const char * ansi (const char * code) const {
668699 if (m_use_colorized_output) {
669700 return code;
@@ -674,15 +705,13 @@ class DebugOutput {
674705
675706 const bool m_use_colorized_output;
676707
677- std::string m_filepath;
678- const int m_line;
679- const std::string m_function_name;
680- const std::string m_expression;
708+ std::string m_location;
681709
682710 static constexpr std::size_t MAX_PATH_LENGTH = 20 ;
683711
684712 static constexpr const char * const ANSI_EMPTY = " " ;
685713 static constexpr const char * const ANSI_DEBUG = " \x1b [02m" ;
714+ static constexpr const char * const ANSI_WARN = " \x1b [33m" ;
686715 static constexpr const char * const ANSI_EXPRESSION = " \x1b [36m" ;
687716 static constexpr const char * const ANSI_VALUE = " \x1b [01m" ;
688717 static constexpr const char * const ANSI_TYPE = " \x1b [32m" ;
@@ -696,14 +725,49 @@ T&& identity(T&& t) {
696725 return std::forward<T>(t);
697726}
698727
728+ template <typename T, typename ... U>
729+ auto identity (T&&, U&&... u) -> last_t<U...> {
730+ return identity (std::forward<U>(u)...);
731+ }
732+
699733} // namespace dbg
700734
701735#ifndef DBG_MACRO_DISABLE
702- // We use a variadic macro to support commas inside expressions (e.g.
703- // initializer lists):
704- #define dbg (...) \
705- dbg::DebugOutput (__FILE__, __LINE__, __func__, #__VA_ARGS__) \
706- .print(dbg::type_name<decltype (__VA_ARGS__)>(), (__VA_ARGS__))
736+
737+ #define DBG_FOREACH_1 (fn, x ) fn(x)
738+ #define DBG_FOREACH_2 (fn, x, ...) fn(x), DBG_FOREACH_1(fn, __VA_ARGS__)
739+ #define DBG_FOREACH_3 (fn, x, ...) fn(x), DBG_FOREACH_2(fn, __VA_ARGS__)
740+ #define DBG_FOREACH_4 (fn, x, ...) fn(x), DBG_FOREACH_3(fn, __VA_ARGS__)
741+ #define DBG_FOREACH_5 (fn, x, ...) fn(x), DBG_FOREACH_4(fn, __VA_ARGS__)
742+ #define DBG_FOREACH_6 (fn, x, ...) fn(x), DBG_FOREACH_5(fn, __VA_ARGS__)
743+ #define DBG_FOREACH_7 (fn, x, ...) fn(x), DBG_FOREACH_6(fn, __VA_ARGS__)
744+ #define DBG_FOREACH_8 (fn, x, ...) fn(x), DBG_FOREACH_7(fn, __VA_ARGS__)
745+ #define DBG_FOREACH_9 (fn, x, ...) fn(x), DBG_FOREACH_8(fn, __VA_ARGS__)
746+ #define DBG_FOREACH_10 (fn, x, ...) fn(x), DBG_FOREACH_9(fn, __VA_ARGS__)
747+ #define DBG_FOREACH_11 (fn, x, ...) fn(x), DBG_FOREACH_10(fn, __VA_ARGS__)
748+ #define DBG_FOREACH_12 (fn, x, ...) fn(x), DBG_FOREACH_11(fn, __VA_ARGS__)
749+ #define DBG_FOREACH_13 (fn, x, ...) fn(x), DBG_FOREACH_12(fn, __VA_ARGS__)
750+ #define DBG_FOREACH_14 (fn, x, ...) fn(x), DBG_FOREACH_13(fn, __VA_ARGS__)
751+ #define DBG_FOREACH_15 (fn, x, ...) fn(x), DBG_FOREACH_14(fn, __VA_ARGS__)
752+ #define DBG_FOREACH_16 (fn, x, ...) fn(x), DBG_FOREACH_15(fn, __VA_ARGS__)
753+ #define DBG_FOREACH_N (_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, \
754+ _14, _15, _16, N, ...) \
755+ N
756+
757+ #define DBG_FOREACH (fn, ...) \
758+ DBG_FOREACH_N (__VA_ARGS__, DBG_FOREACH_16, DBG_FOREACH_15, DBG_FOREACH_14, \
759+ DBG_FOREACH_13, DBG_FOREACH_12, DBG_FOREACH_11, \
760+ DBG_FOREACH_10, DBG_FOREACH_9, DBG_FOREACH_8, DBG_FOREACH_7, \
761+ DBG_FOREACH_6, DBG_FOREACH_5, DBG_FOREACH_4, DBG_FOREACH_3, \
762+ DBG_FOREACH_2, DBG_FOREACH_1, unused) \
763+ (fn, __VA_ARGS__)
764+
765+ #define DBG_STRINGIFY_IMPL (x ) #x
766+ #define DBG_STRINGIFY (x ) DBG_STRINGIFY_IMPL(x)
767+
768+ #define dbg (...) \
769+ dbg::DebugOutput (__FILE__, __LINE__, __func__) \
770+ .print({DBG_FOREACH (DBG_STRINGIFY, __VA_ARGS__)}, __VA_ARGS__)
707771#else
708772#define dbg (...) dbg::identity(__VA_ARGS__)
709773#endif // DBG_MACRO_DISABLE
0 commit comments