我必须使用第三方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);
}
}
任何想法都欢迎!谢谢!
答案 0 :(得分:1)
调用 c ++ 函数的问题,它使用了不同的修饰。
接下来所有 cl.exe(msvc)+ link.exe 工具集,但认为其他编译器/链接器都具有模拟功能
例如,当您在 c ++ 函数
中进行编译时 void consoleDebug(char* format, ...)
将是?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");
我还没有尝试其他建议,但是我想它们也会起作用。
再次感谢!