如何使用C代码中可变数量的参数调用C ++函数

时间:2018-09-18 21:48:43

标签: c++ c variadic

我必须使用第三方C ++库(无法更改)并从C代码调用其API。

对于大多数库API,我使用包装器,如本文所述: How to call C++ function from 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);
}
}

任何想法都欢迎!谢谢!

2 个答案:

答案 0 :(得分:1)

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

与此相关的是链接器( 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 文件创建单个{{ 1}}垫片。对 x64

jmp

答案 1 :(得分:1)

感谢您的帮助!

我尝试了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");

我还没有尝试其他建议,但是我想它们也会起作用。

再次感谢!

相关问题