c++ - 如何从 C 代码中调用具有可变数量参数的 C++ 函数
问题描述
我必须使用第三方 C++ 库(我无法更改)并从 C 代码调用它的 API。
对于大多数库 API,我使用包装器,如本文所述: 如何从 C 调用 C++ 函数?
但是有一个 API 接受可变数量的参数。
这是它的定义(来自库提供的头文件):
void consoleDebug(char* format, ...);
我看不到如何为这个 API 编写包装函数。
我试过这个但它不起作用:
extern "C" {
void wrapper_consoleDebug(char * format, ...)
{
va_list argptr;
va_start(argptr,format);
consoleDebug(format, argptr);
va_end(argptr);
}
}
欢迎任何想法!谢谢!
解决方案
从c调用c++函数的问题,它使用了不同的装饰。
接下来是cl.exe (msvc) + link.exe工具集,但认为其他编译器/链接器具有模拟功能
例如,当您在c++函数中编译时
void consoleDebug(char* format, ...)
在obj(或静态lib)文件中将是?consoleDebug@@YAXPEADZZ
符号。但是当您使用c单元中的相同功能时 - 在目标文件中将_consoleDebug
(对于x86)或consoleDebug
(其他平台)
如果我们在c文件中声明
void consoleDebug(char* format, ...)
并且 do call - in obj将存储使用的外部符号consoleDebug
(或_consoleDebug
)。当链接器将是构建代码时 - 它会搜索 -[_]consoleDebug
实际定义的位置(在所有传递给他的obj和lib中)并且什么都没有 - 没有这样的符号。结果我们得到错误未解决的外部符号[_]consoleDebug
未记录的链接器选项中的解决方案/alternatename
:
/alternatename:sym1=sym2
如果他需要符号并且找不到它,我们会告诉链接器 ( link.exe ) - 尝试使用它。有了这个,我们可以创建下一个解决方案:sym1
sym2
1 - 我们需要准确地知道C++中的符号名称- 我们可以使用__FUNCDNAME__
宏来获取它:
例如:
#define _GET_NAMES_
#ifdef _GET_NAMES_
void consoleDebug(char* format, ...)
{
#pragma message(__FUNCSIG__ ";\r\n")
#pragma message("__pragma(comment(linker, \"/alternatename:" __FUNCTION__ "=" __FUNCDNAME__ "\"))")
}
#endif // _GET_NAMES_
这是临时的假代码,只需要打印__FUNCDNAME__
然后在c文件中我们声明
void __cdecl consoleDebug(char *,...);
#ifdef _X86_
__pragma(comment(linker, "/alternatename:_consoleDebug=?consoleDebug@@YAXPADZZ"))
#else
__pragma(comment(linker, "/alternatename:consoleDebug=?consoleDebug@@YAXPEADZZ"))
#endif
并且可以免费使用consoleDebug
如果我们在c++中有多个具有相同短名称的函数,比如说
void consoleDebug(char* format, ...);
void consoleDebug(wchar_t* format, ...);
这也很容易,只需在c代码中为这 2 个 api 命名一点不同:
void __cdecl consoleDebugA(char *,...);
#ifdef _X86_
__pragma(comment(linker, "/alternatename:_consoleDebugA=?consoleDebug@@YAXPADZZ"))
#else
__pragma(comment(linker, "/alternatename:consoleDebugA=?consoleDebug@@YAXPEADZZ"))
#endif
void __cdecl consoleDebugW(wchar_t *,...);
#ifdef _X86_
__pragma(comment(linker, "/alternatename:_consoleDebugW=?consoleDebug@@YAXPA_WZZ"))
#else
__pragma(comment(linker, "/alternatename:consoleDebugW=?consoleDebug@@YAXPEA_WZZ"))
#endif
在此之后我们可以简单地调用
consoleDebugA("str %u\n", 1);
consoleDebugW(L"str %u\n", 2);
从c代码。
不需要任何垫片/包装器代码。如果您使用的不是cl/link而是其他工具链并且找不到/alternatename
名称选项的模拟 - 可能使用asm 文件来创建单个jmp
shim。说x64
extern ?consoleDebug@@YAXPEADZZ:proc
extern ?consoleDebug@@YAXPEA_WZZ:proc
_TEXT segment 'CODE'
consoleDebugA proc
jmp ?consoleDebug@@YAXPEADZZ
consoleDebugA endp
consoleDebugW proc
jmp ?consoleDebug@@YAXPEA_WZZ
consoleDebugW endp
_TEXT ENDS
END
推荐阅读
- python-3.x - 创建.exe时如何提高GUI的启动速度?
- jquery - jquery遍历分页表
- angular - rxjs 5 用于 map 、 catch 、 throw 转换为 6.5 语法以从后端获取响应 json
- postgresql - PostgreSQL 约束和主键同名
- java - 如何修复 Flink 错误:无法推断 FlinkKafkaConsumer011<> 的类型参数
- c# - 如何通过 C# 中的 lambda 表达式将元组的一项匹配到另一个列表来过滤元组列表?
- c# - Tag Helper vs HTML Helper “使用 IHtmlHelper.Partial 可能导致应用程序死锁”
- javascript - 到达底部页脚时如何停止固定侧边栏的位置
- excel - Excel VBA,错误 438“对象不支持 WorksheetFunction 上的此属性或方法
- javascript - PHP 正则表达式到 Javascript 正则表达式,用于 HTML 元素作为字符串