[英]How to call C++ functions with variable number of arguments from C code
我必须使用第三方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, ...)
并且调用 - 在obj中将存储外部符号consoleDebug
(或_consoleDebug
)使用。 当链接器将构建代码 - 它将搜索 - 其中[_]consoleDebug
实际定义(在所有obj和lib传递给他)并且没有任何东西 - 没有这样的符号。 结果我们得到错误未解析的外部符号[_]consoleDebug
这里的解决方案是未记录的链接器选项/alternatename
:
/alternatename:sym1=sym2
如果他需要sym1
符号而无法找到它,我们会告诉链接器( link.exe ) - 请尝试使用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
name选项 - 可能使用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
谢谢你的帮助!
我尝试了Sam Varshavchik的建议并且它起作用(至少在我的情况下)! 更确切地说,这就是我所做的:
// wrapper.cpp
extern "C" { void (*wrapper_consoleDebug)(char * format, ...) = consoleDebug;}
// wrapper.h
extern void (*wrapper_consoleDebug)(char * format, ...);
// C file
#include "wrapper.h"
// in my code
wrapper_consoleDebug("my logger is %s","great");
我还没有尝试其他建议,但我猜他们也会工作。
再次感谢!
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.