首页 > 解决方案 > 如何从 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++cvariadic

解决方案


从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实际定义的位置(在所有传递给他的objlib中)并且什么都没有 - 没有这样的符号。结果我们得到错误未解决的外部符号[_]consoleDebug

未记录的链接器选项中的解决方案/alternatename

/alternatename:sym1=sym2

如果他需要符号并且找不到它,我们会告诉链接器 ( link.exe ) - 尝试使用它。有了这个,我们可以创建下一个解决方案:sym1sym2

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 文件来创建单个jmpshim。说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

推荐阅读