4

Consider:

#include <bits/stdc++.h>
using namespace std;

#define __deb(X...) (cout << "[" << #X << "]:" << X)
template <typename... type>
void debug(type &&... args)
{
    ((__deb(args)), ...);
}

int main()
{
    int a = 1, b = 3;
    debug(a,b);
    return 0;
}

I got output like [args]:1[args]:3, but I wanted output like [a]:1[b]:3.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
  • 4
    `debug` function cannot know the names of the variables you passed as its arguments. You will have to revert to macros or compiler-specific intrinsics. – Quimby Oct 05 '20 at 17:40
  • 3
    Warning: Do not use `#include ` ([why](https://stackoverflow.com/questions/31816095/why-should-i-not-include-bits-stdc-h)) and avoid using `using namespace std;` ([why](https://stackoverflow.com/questions/1452721/why-is-using-namespace-std-considered-bad-practice)). Using the two together can wreck havoc on even the simplest of programs. Use with caution. – user4581301 Oct 05 '20 at 17:42
  • 3
    Unrelated to the question, but identifiers with double-underscores are reserved by the language for the implementation. Naming a macro `__deb` is not guaranteed to work correctly or be portable – Human-Compiler Oct 05 '20 at 17:43
  • 1
    Have you considered using the stringizing operator `#` in a macro that sits between callers and your `debug()`? – Tanveer Badar Oct 05 '20 at 17:55

3 Answers3

11

One way could be to quote all the macro arguments using #__VA_ARGS__ and parse that string in the C++ function.

Example:

#include <iostream>
#include <sstream>
#include <string>
#include <utility>

template<typename T, typename... Args>
std::string debug_detail(const char* names, T&& var, Args&&... args) {
    std::ostringstream builder;

    // find variable end
    const char* end = names;
    while(*end != ',' && *end != '\0') ++end;

    // display one variable
    (builder << ' ').write(names, end - names) << '=' << var;

    // continue parsing?
    if constexpr(sizeof...(Args) > 0) {
        // recursively call debug_detail() with the new beginning for names
        builder << debug_detail(end + 1, std::forward<Args>(args)...);
    }

    return builder.str();
}

template<typename... Args>
void debug_entry(const char* file, int line, const char* func,
                 const char* names, Args&&... args) {
    std::ostringstream retval;

    // common debug info
    retval << file << '(' << line << ") " << func << ':';

    // add variable info
    retval << debug_detail(names, std::forward<Args>(args)...) << '\n';

    std::cout << retval.str();
}

// the actual debug macro
#define debug(...) \
    debug_entry(__FILE__,__LINE__,__func__,#__VA_ARGS__,__VA_ARGS__)

int main() {
    int foo = 1;
    const double bar = 2;
    const std::string Hello = "world";

    debug(foo,bar,Hello);
}

Possible output:

example.cpp(49) main: foo=1 bar=2 Hello=world

Demo


A C++20 version using std::source_location:

#include <source_location>

template<typename... Args>
void debug_entry(const std::source_location location,
                 const char* names, Args&&... args) {
    std::ostringstream retval;

    // common debug info
    retval << location.file_name() << '(' << location.line() << ','
           << location.column() << ") " << location.function_name() << ':';

    // add variable info
    retval << debug_detail(names, std::forward<Args>(args)...) << '\n';

    std::cout << retval.str();
}

// the actual debug macro
#define debug(...) \
    debug_entry(std::source_location::current(), #__VA_ARGS__,__VA_ARGS__)

Demo

Ted Lyngmo
  • 93,841
  • 5
  • 60
  • 108
2

Here's my humble attempt, which uses a macro FOO to create a pair of the variable name and its value, and passes the arguments to a variadic function:

#include <utility>
#include <iostream>

#define FOO(var) std::make_pair(std::string(#var), var)

template <typename T>
void __deb(std::pair<std::string, T> arg) { std::cout << "[" << arg.first << "]:" << arg.second; }

template <typename... type>
void debug(std::pair<std::string, type> &&... args)
{
    (__deb(args), ...);
}

int main()
{
    int a = 1, b = 3;
    debug(FOO(a), FOO(b));
}

Demo


Alternatively, to avoid having a macro call FOO for each variable in debug, you could define debug as a macro that accepts #__VA_ARGS__ (string of arguments) and __VA_ARGS__ (argument values). Then parse each variable name and value:

#include <iostream>
#include <sstream>
#include <stdio.h>

#define debug(...) debug_print(#__VA_ARGS__,__VA_ARGS__)

template <typename T>
void __deb(std::istringstream &ss, T arg)
{
    //Extract name from stream
    std::string name;
    std::getline(ss, name, ',');
    //trim leading space
    const auto pos(name.find_first_not_of(" "));
    name.erase(0, pos);
    std::cout << "[" << name << "]:" << arg;
}

template <typename... type>
void debug_print(const char* names, type&&...args)
{
    std::istringstream ss(names);
    (__deb(ss, args), ...);
}


int main()
{
    int a = 1, b = 3, c = 4;
    debug(a, b, c);
}

Demo

jignatius
  • 6,304
  • 2
  • 15
  • 30
1

The problem is that the MACRO is used in the context of void debug(type &&... args), which is not familiar with the names a and b.

A possible solution to your problem is to implement a bigger MACRO which gats several vars and calls a sub-MACRO which handles a single var (which you already implemented).

This way the initial MACRO call will happen in the context of the calling function which has the wanted vars

Dniel BV
  • 68
  • 7
  • Is it possible to do that for variadic macros? – Tanveer Badar Oct 05 '20 at 17:56
  • Well once I did something close but it could only handle up to 5 vars (since you can't really handle unknown number of vars in a MACRO) One way to handle that is something like `MACRO(a,b,c,d,e) _deb(a) MACRO(b,c,d,e) MACRO(a,b,c,d) _deb(a) MACRO(b, c, d) MACRO(a,b,c) _deb(a) MACRO(b,c) MACRO(a,b) _deb(a) MACRO(b) MACRO(a) _deb(a) #define _deb(a) whatever` – Dniel BV Oct 05 '20 at 18:00
  • Yeah, that's what I suspected. In that case, pointing the OP to libraries like boost::test or similar which use macros to do all sorts of tricks might be a good idea. – Tanveer Badar Oct 05 '20 at 18:01