3

During development of my project in C++ I have a frequent need of debugging and I usually use this macro to do it

#define DBUG(a) {std::cout << #a << " : " << a << std::endl;};

But many times I need to do something like this

int a;
std :: string b;
double c;
...
...
DBG(a); DBG(b); DBG(c);

But ideally it might be possible to just write DBUG(a, b, c) or DBG(a, b, c, d, e) for more variables to achieve something like this. After some research this looked like a problem in meta-programming or more specifically code-generation, but because of my limited knowledge in these areas I could not find a way to go about it.

If possible I would like to solve this without using Boost or other external libraries, and using the features in C++98 although if it is not possible I'm willing to use C++11.

4 Answers4

7

I don't like a limitation to a specific number of arguments. I didn't find a nice approach which decodes the name statically so the names are put together as a comma separated string and then decoded at run-time. Overall, that may be a bit too heavy-weight but, at least, it does as was asked and has not limitation on the number of arguments (other than compiler limitations, that is):

#include <iostream>
#include <sstream>
#include <iterator>
#include <algorithm>
#include <tuple>
#include <vector>
#include <type_traits>
#include <stdlib.h>

template <int I, int S, typename... V>
typename std::enable_if<I == S>::type
debug_var(std::vector<std::string> const&, std::tuple<V...> const&)
{
}

template <int I, int S, typename... V>
typename std::enable_if<I != S>::type
debug_var(std::vector<std::string> const& n, std::tuple<V...> const& v)
{
    std::cout << n[I] << '=' << std::get<I>(v) << ' ';
    debug_var<I + 1, S>(n, v);
}

template <typename... V>
void debug(std::vector<std::string> const& n, std::tuple<V...> const& v)
{
    debug_var<0, sizeof...(V)>(n, v);
    std::cout << '\n' << std::flush;
}

std::vector<std::string> debug_names(char const* names)
{
    std::vector<std::string> result;
    std::istringstream in(names);
    for (std::string name; std::getline(in >> std::ws, name, ','); ) {
        result.push_back(name);
    }
    return result;
}

#define DEBUG(...) debug(debug_names(#__VA_ARGS__), std::tie(__VA_ARGS__));

int main()
{
    int a=1, b=2;
    DEBUG(a, b);
    DEBUG();
}

The code uses several features which were introduced by the 2011 revision of C++.

Dietmar Kühl
  • 150,225
  • 13
  • 225
  • 380
  • Great! Clever use both macros and templates, although I'll have to spend a little more time to fully understand the code. It might be relevant to also mention that this works only for `C++11`. – user2833292 Oct 01 '13 at 05:05
  • Brilliant runtime solution if you ask me. Now I have to wonder if it's possible to process the names at compile time with one of those compile time string libraries. – chris Oct 01 '13 at 13:28
4

Here's one solution adapted from this answer. You have to define your macros to support up to a maximum number of parameters by changing the CHOOSER and DBG macros, as well as adding appropriate DBG# macros. It does require C++11, too.

#include <iostream>

#define DBG1(a) std::cout << #a ": " << a << "\n"
#define DBG2(a, b) DBG1(a); DBG1(b)
#define DBG3(a, b, c) DBG2(a, b); DBG1(c)

#define CHOOSER(a, b, c, CHOICE, ...) CHOICE
#define DBG(...) CHOOSER(__VA_ARGS__, DBG3, DBG2, DBG1)(__VA_ARGS__)

int main() {
    int a{}, b{1}, c{5};
    DBG(a, b, c);
}

Output:

a: 0
b: 1
c: 5

Community
  • 1
  • 1
chris
  • 60,560
  • 13
  • 143
  • 205
  • Yes, this works as long as I give up-to three arguments to `DBUG` macro, is it possible to make it work for arbitrary number of arguments like `DBUG(a, b, c, d, e)`. – user2833292 Oct 01 '13 at 04:11
  • @user2833292, Not unless you expand the macro definitions (so never arbitrary). Macros are pretty primitive. It will work with any number less than the maximum as well, though. – chris Oct 01 '13 at 04:12
  • Then is it possible by using templates, I remember reading `C++` templates are Turing complete. – user2833292 Oct 01 '13 at 04:21
  • @user2833292: Turing complete doesn't imply being able to figure out the name of the variable/expression you are using in the argument. – DanielKO Oct 01 '13 at 04:39
  • @DanielKO Ok, I could have misunderstood, but then can we couple both macros and templates to achieve this. – user2833292 Oct 01 '13 at 04:43
0

you may use your own DBUG(a) the following way

DBUG(a << " " << b << " " << c);

aah134
  • 860
  • 12
  • 25
-1

How about using some good ol' C++11 variadic templates?

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


template <typename T>
std::string make_string(const T& t)
{
    std::ostringstream oss;
    oss << t;
    return oss.str();
}

template <typename Thead, typename ... Ttail>
std::string make_string(const Thead& head, const Ttail& ... tail)
{
    return make_string(head) + make_string(tail...);
}

void debug(const std::string& msg)
{
    std::cout << "DEBUG: " << msg << std::endl;
}

void debug(void)
{
    std::cout << "DEBUG!" << std::endl;
}

template <typename ... Targs>
void debug(const Targs& ... args)
{
    debug(make_string(args...));
}



int main(void)
{
    int z;
    debug("We're gonna crash: ", &z, "!");
    debug();
    debug(3.14);
}
hauzer
  • 258
  • 3
  • 12