编译似乎在预处理器宏扩展之前开始
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 现在应该可以正常工作了。
我发现了两个问题:
DECLARE_ARGUMENT
中已经提到的附加“)”,这是一个简单的修复
- 事实上 MSVC 似乎
__VA_ARGS__
与 gcc 和 clang 不同,它不会将参数扩展到自身,而是将其视为一个。这就是为什么例如COUNT_PAIRS
没有正常工作。修复来自此 SO Question
我建议在 Godbolt 中尝试一下,看看它是如何工作的 ;)
希望这能解决您的问题
编辑:我也刚刚看到 here Visual Studio 显然更新了他们的预处理器,所以根据使用的版本,可以避免所有丑陋的东西 EXPAND
使用编译器开关调用 /Zc:preprocessor
我正在尝试创建要导出的 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 现在应该可以正常工作了。
我发现了两个问题:
DECLARE_ARGUMENT
中已经提到的附加“)”,这是一个简单的修复- 事实上 MSVC 似乎
__VA_ARGS__
与 gcc 和 clang 不同,它不会将参数扩展到自身,而是将其视为一个。这就是为什么例如COUNT_PAIRS
没有正常工作。修复来自此 SO Question
我建议在 Godbolt 中尝试一下,看看它是如何工作的 ;) 希望这能解决您的问题
编辑:我也刚刚看到 here Visual Studio 显然更新了他们的预处理器,所以根据使用的版本,可以避免所有丑陋的东西 EXPAND
使用编译器开关调用 /Zc:preprocessor