C++元编程

3

在我的 C++ 项目开发过程中,经常需要进行调试,我通常使用这个宏来完成

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

但很多时候我需要像这样做一些事情

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

但理想情况下,可能只需编写DBUG(a, b, c)DBG(a, b, c, d, e)等代码即可实现更多变量的功能。经过一些研究,这似乎是元编程或更具体地说是代码生成的问题,但由于我在这些领域的知识有限,因此我找不到解决方法。
如果可能的话,我希望不使用Boost或其他外部库,并使用C++98的功能来解决该问题,尽管如果不可能,我愿意使用C++11

这个问题的标题有点含糊不清,如果您有更好的想法,请编辑它。 - user2833292
在 Google 上搜索可变参数宏,那应该会对你有所帮助(并避免使用 endl,可能通过使用预处理器来连接字符串减少调用 operator<< 的次数...)。 - David Rodríguez - dribeas
使用可变参数宏,是否可以像这样做 #define DBUG1(...) DBUG(arg1);DBUG(arg2);...;DBUG(argk); - user2833292
为什么不使用调试器?它们非常擅长显示变量的值。 - DanielKO
没错,我可以使用“gdb”,但是我更喜欢使用老派的打印变量来进行调试。两种方法各有优劣。 - user2833292
只是让你知道,这不是元编程... - SwiftMango
4个回答

7

我不喜欢被限制在特定数量的参数上。我没有找到一个好的方法来静态解码名称,所以这些名称将被组合为逗号分隔的字符串,然后在运行时进行解码。总体而言,这可能有些过于繁重,但至少它按要求执行并且没有参数数量的限制(除了编译器的限制):

#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();
}

这段代码使用了2011年C++修订版引入的多个功能特性。


太棒了!聪明地使用了宏和模板,虽然我需要花更多的时间来完全理解代码。可能还值得一提的是,这仅适用于 C++11 - user2833292
如果你问我,这是一个非常出色的运行时解决方案。现在我想知道是否有可能使用其中一种编译时字符串库在编译时处理名称。 - chris

4
这里有一个解决方案,改编自这个答案。您需要通过更改CHOOSERDBG宏来支持最大数量的参数,并添加适当的DBG#宏。它也需要C++ 11。
#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);
}

输出:

a: 0
b: 1
c: 5


是的,只要我给DBUG宏提供最多三个参数,它就可以工作。但是,是否可能使其适用于任意数量的参数,例如DBUG(a, b, c, d, e) - user2833292
@user2833292,除非你扩展宏定义(所以从不是任意的)。宏是相当原始的。它也适用于小于最大值的任何数字。 - chris
那么,通过使用模板,是否有可能实现图灵完备性,我记得读过 C++ 模板是图灵完备的。 - user2833292
@user2833292:图灵完备并不意味着能够找出你在参数中使用的变量/表达式的名称。 - DanielKO
@DanielKO 好的,我可能理解有误,我们能够同时使用宏和模板来实现这个吗? - user2833292

0

你可以按照以下方式使用自己的DBUG(a)

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


-1

使用一些好用的 C++11 可变参数模板怎么样?

#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);
}

不符合原帖要求,即打印变量名和其值。 - DanielKO
如果我们能够打印变量的名称,那就完美了。 - user2833292
我的错。对不起。 - hauzer

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接