Как вызывать функции C ++ с переменным числом аргументов из кода C - PullRequest
0 голосов
/ 19 сентября 2018

Мне нужно использовать стороннюю библиотеку C ++ (которую я не могу изменить) и вызывать ее API из кода C.

Для большинства библиотечных 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);
}
}

Любая идея приветствуется!Спасибо!

Ответы [ 2 ]

0 голосов
/ 19 сентября 2018

Спасибо за вашу помощь!

Я попробовал предложение от Сэма Варшавчика, и оно работает (по крайней мере, в моем случае)!Точнее, вот что я сделал:

// 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");

Другие предложения я еще не пробовал, но, думаю, они тоже подойдут.

Еще раз спасибо!

0 голосов
/ 19 сентября 2018

проблема вызова c ++ функций из c в том, что он использовал разные декорации.

все следующее для cl.exe (msvc) + link.exe набора инструментов, но думаю, что другие компиляторы / компоновщики имеют аналоговые функции

, например, при компиляции в c ++ function

void consoleDebug(char* format, ...)

в obj (или статический lib ) файл будет иметь символ ?consoleDebug@@YAXPEADZZ.но когда вы используете ту же функцию из c unit - в объектном файле будет _consoleDebug (для x86 ) или consoleDebug (для других платформ)

, если мыобъявить в c файл

void consoleDebug(char* format, ...)

и сделать вызов - в obj будет сохранен этот внешний символ consoleDebug (или * 1041)*) используемый.когда компоновщик будет кодом сборки - он будет искать - где реально определено [_]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, ...);

этоТакже легко работать, нужно только немного другое имя это 2 API в C код:

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 файл для создания одиночной jmp прокладки.скажем за 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
...