【发布时间】:2021-02-18 06:19:53
【问题描述】:
我正在尝试创建一个要导出的 C++ DLL,并且我需要导出一个类的所有函数。所以我想出了这个想法来删减样板:
#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 宏中还有一个额外的括号。
【问题讨论】:
-
大多数编译器都有在预处理后停止的选项,让您可以准确地看到宏扩展为什么。我建议你尝试一下,看看会发生什么。
-
@Someprogrammerdude 我试过你所说的,显然,一些宏扩展为空。在本例中,它扩展为:
extern "C" __declspec(dllexport) MacroTestObject* __stdcall CreateMacroTestObject WithTwoParams( ) { return new MacroTestObject ( ); } MacroTestObject::MacroTestObject ( ) { }。这根本不是它应该的样子。 -
嘿@wonttellusername,我冒昧地为您的问题创建了一个快速的godbolt示例:godbolt.org/z/o6oeW3提及您使用的编译器可能会很有用,并且可能包括这个godbolt示例所以人们可以更轻松地开始解决您的问题。 (也看看生成的输出,def.似乎在宏扩展中出了点问题)
-
@hassec 我添加了一个编辑。我想我已经解决了问题的主要部分。基本上,
__VA_ARGS__在展开时被算作一个宏参数,所以DECLARE_ARGUMENTS总是展开为DECLARE_ARGUMENTS_0。我对非游戏性编程不是很熟悉,因此我能提供的最佳信息是我正在将 MSVC 与 Visual Studio 2019 结合使用。它是 Rider 自动用于 Unreal Engine 的一个。 -
见godbolt.org/z/r7n8r9,DECLARE_ARGUMENTS 中最后一个关闭“)”似乎太多了。删除该 Clang 后将产生您想要的输出。但是 MSVC 仍然是错误的,因为正如您所说, COUNT_PAIRS 似乎在 MSVC 上被破坏了。不幸的是,我对那个编译器没有任何经验:/