繁体   English   中英

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

[英]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实际定义(在所有objlib传递给他)并且没有任何东西 - 没有这样的符号。 结果我们得到错误未解析的外部符号[_]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM