Почему я получаю «Ошибка: символ уже определен» при создании экземпляра шаблона C с типом функции

#c #templates #dll #shared-libraries

#c #шаблоны #dll #совместно используемые библиотеки #общие библиотеки

Вопрос:

У меня есть простая библиотека DLL с 4 функциями, 2 из которых используют __stdcall и 2 соглашения о вызовах __cdecl по умолчанию.

В моем исполняемом файле я хочу вызвать все эти 4 функции, и я использую шаблон для этого. Код следующий:

 #include <windows.h>
#include <stdio.h>

template<typename FuncType>
void CallFunction( HMODULE hModule, const char * name )
{
    FuncType function = (FuncType)GetProcAddress( hModule, name );
    if (function)
        printf("result: %d.n", function(1,2) );
    else
        printf("%s not found (%lu)n", name, GetLastError());
}

typedef int (* FuncType1)(int, int);
typedef int (__stdcall * FuncType2)(int, int);

int main(int argc, char** argv)
{
    HMODULE hModule = LoadLibrary( TEXT("library.dll") );
    if (hModule) {
        CallFunction<FuncType1>( hModule, "File1_Funkce1" );
        CallFunction<FuncType2>( hModule, "File1_Funkce2" );
        CallFunction<FuncType1>( hModule, "File2_Funkce1" );
        CallFunction<FuncType2>( hModule, "File2_Funkce2" );

        FreeLibrary( hModule );
    }
    else {
        printf("library not foundn");
    }
    return 0;
}
  

Это хорошо компилируется с помощью компилятора Visual Studio, но выдает ошибку с MinGW:
Error: symbol '__Z12CallFunctionIPFiiiEEvP11HINSTANCE__PKc' is already defined . Я не понимаю, почему использование шаблонов таким образом приведет к появлению нескольких определений, поскольку вы можете обычно создавать экземпляры vector<int> и vector<char> несколько раз в одной и той же единице перевода, и ошибок не возникает.

Есть идеи?

Редактировать: мои команды компиляции были просто:

 cl file.cpp
g   file.cpp -o file.exe
  

Комментарии:

1. Пожалуйста, опубликуйте свои команды компиляции / ссылки для mingw

2. Искаженное имя, на которое он жалуется, таково: void CallFunction<int (*)(int, int)>(HINSTANCE_ , char const )

Ответ №1:

Кажется, что mingw игнорирует __stdcall при искажении имен шаблонов и вызывает конфликты имен. Вы можете избежать этого, закодировав эти указатели в их собственных типах:

 template<typename Func>
struct StdCall;

template<typename R, typename... Params>
struct StdCall<R(Params...)>
{
    using type = R(__stdcall *)(Params...);
};

template<typename Func>
struct Cdecl;

template<typename R, typename... Params>
struct Cdecl<R(Params...)>
{
    using type = R(*)(Params...);
};
  

И затем вы вызываете их:

 CallFunction<StdCall<int(int,int)>>();
CallFunction<Cdecl<int(int,int)>>();
  

Вам пришлось бы изменить, CallFunction чтобы вызвать ::type , хотя:

 template<typename FuncType>
void CallFunction( )
{
    using Func = typename FuncType::type;
}
  

Комментарии:

1. о, вау, это сложный обходной путь… это ошибка в GCC или просто недостаток его дизайна?

2. @Youda008 ну, вы могли бы создавать StdCall и Cdecl не шаблоны и просто кодировать int(int,int) непосредственно как их типы, но это довольно жестко. Что касается того, является ли это ошибкой, я думаю, что это так, но я не уверен, находится ли это на уровне mingw или на уровне gcc.