编译似乎在预处理器宏扩展之前开始

Compilation seems to start before Preprocessor macro expansion

我正在尝试创建要导出的 C++ DLL,我需要导出 class 的所有函数。所以我想出了这个想法来剪掉样板文件:

#define CONCATENATE_WRAPPED(arg1, arg2) CONCATENATE_WRAPPED_1(arg1, arg2)
#define CONCATENATE_WRAPPED_1(arg1, arg2) CONCATENATE_WRAPPED_2(arg1, arg2)
#define CONCATENATE_WRAPPED_2(arg1, arg2) CONCATENATE_WRAPPED_3(arg1, arg2)
#define CONCATENATE_WRAPPED_3(arg1, arg2) CONCATENATE_WRAPPED_4(arg1, arg2)
#define CONCATENATE_WRAPPED_4(arg1, arg2) arg1##arg2

// Counts the number of pairs:
#define PAIR_SEQUENCER(_1, _1x, _2, _2x, _3, _3x, _4, _4x, N, ...) N
#define COUNT_PAIRS(...) PAIR_SEQUENCER(__VA_ARGS__, 4, x, 3, x, 2, x, 1, 0, 0, x)

// Internal for declaring arguments:
#define DECLARE_ARGUMENTS_0(...)

#define DECLARE_ARGUMENTS_1(typeName, varName, ...)\
    typeName varName

#define DECLARE_ARGUMENTS_2(typeName, varName, ...)\
    typeName varName,\
    DECLARE_ARGUMENTS_1(__VA_ARGS__)

#define DECLARE_ARGUMENTS_3(typeName, varName, ...)\
    typeName varName,\
    DECLARE_ARGUMENTS_2(__VA_ARGS__)

// Internal for passing arguments:
#define PASS_ARGUMENTS_0(...)

#define PASS_ARGUMENTS_1(typeName, varName, ...)\
    varName\
    PASS_ARGUMENTS_0(__VA_ARGS__)

#define PASS_ARGUMENTS_2(typeName, varName, ...)\
    varName, \
    PASS_ARGUMENTS_1(__VA_ARGS__)

#define PASS_ARGUMENTS_3(typeName, varName, ...)\
    varName, \
    PASS_ARGUMENTS_2(__VA_ARGS__)

// Macro to call when declaring parameters and will adjust depending on the number of params, so that
// DECLARE_ARGUMENTS(int, a, float, b) expands to (int a, float b) for up to three pairs.
#define DECLARE_ARGUMENTS(...) ( CONCATENATE_WRAPPED(DECLARE_ARGUMENTS_, COUNT_PAIRS(__VA_ARGS__))(__VA_ARGS__) ))

// Macro to call when passing parameters and will adjust depending on the number of params, so that
// PASS_ARGUMENTS(int, a, float, b) expands to (a, b) for up to three pairs.
#define PASS_ARGUMENTS(...) ( CONCATENATE_WRAPPED(PASS_ARGUMENTS_, COUNT_PAIRS(__VA_ARGS__))(__VA_ARGS__) )

排除所有这些上下文,这就是我的主要宏的样子:

#define IMPLEMENT_CONSTRUCTOR(typeName, functionName, ...) \
    extern "C" __declspec(dllexport) typeName* __stdcall functionName DECLARE_ARGUMENTS(__VA_ARGS__) \
    { \
        return new typeName PASS_ARGUMENTS(__VA_ARGS__); \
    } \
    typeName::typeName DECLARE_ARGUMENTS(__VA_ARGS__)

// the intention is so that
IMPLEMENT_CONSTRUCTOR(MacroTestObject, CreateMacroTestObjectWithTwoParams, int, TwoParamFirst, float, TwoParamSecond)
{ }
//expands to
extern "C" __declspec(dllexport) MacroTestObject* __stdcall CreateMacroTestObjectWithTwoParams(int TwoParamFirst, float TwoParamSecond)
{
    return new MacroTestObject(TwoParamFirst, TwoParamSecond);
}

MacroTestObject::MacroTestObject(int TwoParamFirst, float TwoParamSecond)
{ }

但问题似乎是 C++ 编译器甚至在所有宏展开之前就开始编译,这会疯狂地抛出编译时错误,显示如下内容:

0><Project>\MacroTestObject.cpp(4,1): Error C2512 : 'MacroTestObject::MacroTestObject': no appropriate default constructor available
0><Project>\MacroTestObject.cpp(5,1): Error C2511 : 'MacroTestObject::MacroTestObject(void)': overloaded member function not found in 'MacroTestObject'

但是当我使用 (Rider for Unreal Engine) 的“替换宏调用和所有嵌套调用”功能时,宏扩展得很好。

谁能帮我解决这个问题,我不知道为什么会这样,而且我似乎无法在网上找到任何特定的帮助。

此 DLL 旨在作为本地插件在 Unity3D 中使用,据我所知,使用 C++/CLI 是不可能的。我也不认为 COM 互操作是一个选项,因为我读到的所有关于它的信息都暗示我需要创建 TLB 文件,而且由于 Unity 处理 C# 编译,这似乎也不可能。

编辑: 我想我有点明白为什么这会导致问题。

// Something like
COUNT_PAIRS(a, b, c, d, e, f)
// always expands to
0
// because __VA_ARGS__ is considered as a single argument
// and a recount is not triggered. I tried using CONCATENATE_WRAPPED
// in different places but unfortunately it has been no help.

有人可以帮我解决这个问题吗?

编辑 2:

问题解决,切换到Clang。此外,DECLARE_ARGUMENTS 宏中有一个额外的括号。

所以我认为 this version 现在应该可以正常工作了。

我发现了两个问题:

  1. DECLARE_ARGUMENT 中已经提到的附加“)”,这是一个简单的修复
  2. 事实上 MSVC 似乎 __VA_ARGS__ 与 gcc 和 clang 不同,它不会将参数扩展到自身,而是将其视为一个。这就是为什么例如COUNT_PAIRS 没有正常工作。修复来自此 SO Question

我建议在 Godbolt 中尝试一下,看看它是如何工作的 ;) 希望这能解决您的问题

编辑:我也刚刚看到 here Visual Studio 显然更新了他们的预处理器,所以根据使用的版本,可以避免所有丑陋的东西 EXPAND使用编译器开关调用 /Zc:preprocessor