【问题标题】:Writing a function using a macro with variable number of arguments使用具有可变数量参数的宏编写函数
【发布时间】:2011-07-22 08:07:11
【问题描述】:

如何编写具有可变数量参数的宏来定义函数?假设我们用 2 个参数定义类 class1,用三个参数定义类 class2。

class class1 {
public:
   int arg1;
   int arg2;
   class1(int x1, int x2): arg1(x1), arg2(x2) {}
};
class class2 {
public:
   int arg1;
   int arg2;
   int arg3;
   class1(int x1, int x2, int x3): arg1(x1), arg2(x2), arg3(x3) {}
};

对于我定义的每个类,甚至是在我想要编写以下内容之前已经定义的类:

template<> inline void writeInfo<class1>(const class1& obj, FILE* fp) {
    writeAmount(2, fp);
    writeName("arg1", fp);
    writeInfo(obj.arg1, fp);
    writeName("arg2", fp);
    writeInfo(obj.arg2, fp);
}
template<> inline void writeInfo<class2>(const class2& obj, FILE* fp) {
    writeAmount(3, fp);
    writeName("arg1", fp);
    writeInfo(obj.arg1, fp);
    writeName("arg2", fp);
    writeInfo(obj.arg2, fp);
    writeName("arg3", fp);
    writeInfo(obj.arg3, fp);
}

我们不需要关心writeAmount、writeName 或writeInfo 的定义。我想做的是写这样的东西:

MACROWRITEINFO(class1, 2, arg1, arg2);
MACROWRITEINFO(class2, 3, arg1, arg2, arg3);

是否可以创建这样的宏,以便它可以扩展到上述模板定义?我在很多地方都读到宏是邪恶的,但在这种情况下,我相信它们非常有帮助,因为它们会减少我输入的代码量,从而减少我在创建模板函数。

【问题讨论】:

  • 您可能想查看 Boost Serialization。这看起来像是在重新发明轮子。
  • 您还可以查看 Boost Preprocessor,它应该为您提供创建此类宏的工具。
  • @visitor 和 MSalter,你们谁能给我看一个使用 Boost 预处理器或序列化的例子吗?很抱歉听起来很懒惰而不是用谷歌搜索,但我想看看如何应用它而无需对此进行大量研究。

标签: c++


【解决方案1】:

首先,您应该改进您的格式/代码。您的代码在类定义后缺少“类”关键字和分号 - 当您发布 sn-p 时,请确保它是正确的代码,因为有些人(即我)会尝试编译它。

其次,不要使用函数模板特化。如果宏是邪恶的,那么它们一定是撒旦的化身。只需坚持旧的重载即可。详情请见here。

至少 - 一个答案。如果 all args 的类型相同,您可能会弄乱可变参数宏 - 例如,您可以在 writeInfo 函数中创建一个数组并迭代元素。由于这里显然不是这种情况,您可以为不同数量的参数定义 MACROWRITEINFO 宏的许多变体,使用一些通用块来减少代码重复。例如:

#define MACROWRITEINFO_BEGIN(type, amount)  \
void writeInfo(const type& obj, FILE* fp)   \
{                                           \
    writeAmount(amount, fp);

#define MACROWRITEINFO_NAMEINFO(name)       \
    writeName(#name, fp);                   \
    writeInfo(obj.##name, fp);

#define MACROWRITEINFO_END()                \
}

使用这些,您现在可以根据参数数量定义变体。

#define MACROWRITEINFO1(type, arg1) \
    MACROWRITEINFO_BEGIN(type, 1)   \
    MACROWRITEINFO_NAMEINFO(arg1)   \
    MACROWRITEINFO_END()

#define MACROWRITEINFO2(type, arg1, arg2) \
    MACROWRITEINFO_BEGIN(type, 2)   \
    MACROWRITEINFO_NAMEINFO(arg1)   \
    MACROWRITEINFO_NAMEINFO(arg2)   \
    MACROWRITEINFO_END()

等等……

编辑: 好吧,我想是可以在这里使用可变参数宏。看看这个SO question。这是纯粹的疯狂,但你应该能够实现你想要的。

编辑: 我的想法是将可变参数扩展为数组,然后对其进行迭代;如果它们是同一类型,比如说 int,你可以这样写:

#define VAARGSSAMPLE(...) \
    int args[] = { __VA_ARGS__ }; \
    for (int i = 0; i < sizeof(args)/sizeof(int); ++i) \
    { \
        printf("%d\n", args[i]); \
    }

VAARGSSAMPLE(1, 5, 666);

因此,如果所有变量都属于同一类型,则可以将它们放入数组中。但他们不是,所以它不会做。如果您真的非常想坚持使用可变参数,请转到我的第一次编辑。

【讨论】:

  • 感谢您指出我的错误并显示链接有关功能模板专业化的详细信息,当然感谢您的回答。关于“如果所有 args 都属于同一类型,你可以乱用可变参数宏”,我们在这里写的只是“arg1”,“arg2”,......它们是什么类型有关系吗?它们应该由宏写下来。你能写一个可变参数宏吗?我似乎找不到如何遍历数组。
  • 我想我会坚持你的第一次编辑。我找到了interesting 的东西,但这需要我提前知道变量类型。将变量类型存储在变量中的任何方式?无论如何,我想我会坚持你的第一个答案,并避免使用模板专业化。
  • 澄清一下:模板 type 特化很棒。模板函数特化是纯粹的邪恶。
【解决方案2】:

我认为使用宏无法做到这一点。您可以使用可变参数(variadic),但不能生成依赖于参数的代码。

我建议您创建一个 DSL(例如简单的 xml..)并从中生成代码。这是更清洁和良好的做法。

你可以这样做:

<writeInfos>
    <writeInfo class="class1" amount="3">
        <arguments>
            <argument>arg1</argument>
            <argument>arg2</argument>
        </arguments>
    </writeInfo>
</writeInfos>

然后从中创建源代码。您应该在构建过程中添加此步骤。

但是你也可以定义一些更简单的东西..你可以把你的 MACROWRITEINFO “函数”放在一个文本文件中并自己解析它..

【讨论】:

  • 我对 DSL 不熟悉...你能给我看一个适用于我的问题的例子吗?
  • 对不起,我不清楚。领域特定语言是一个通用的表达方式。您可以自己定义它..例如使用带有所有参数的 xml。我会更新我的答案。
  • 我想我更喜欢第二种选择。将 MACROWRITEINFO 函数放在另一个文件中并解析它以创建一个可以包含到主文件中的 cpp 文件。不过不是很满意。我想我现在就继续写整个函数。
猜你喜欢
  • 1970-01-01
  • 2011-12-24
  • 2018-07-04
  • 2011-07-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多