Шаблон, статика и длл - PullRequest
       13

Шаблон, статика и длл

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

Я пытаюсь экспортировать шаблон функции со статической переменной в определении.

.dll / foo.h:

#ifdef _DLL
#define API __declspec(dllexport)   
#else  
#define API __declspec(dllimport)   
#endif  

class API Foo
{
  public:
  template<typename T>
  static T& Get()
  {
    static T _instance;
    return _instance;
  }

  static void Set();
}

Я хочу, чтобы вызовы, сделанные .dll и .exe, ссылались на один и тот же объект "_instance". Я знаю, что могу сделать это, определив статическую переменную в .cpp. Но в этом случае я имею дело с шаблонами, поэтому я застрял.

Edit: Пример того, что происходит ..

.dll / foo.cpp:

void Foo::Set()
{
   Foo::Get<int>() = 10;
}

.exe / main.cpp:

int main()
{
  auto & x = Foo::Get<int>();
  x = 3;
  std::cout << x; // 3
  Foo::Set();
  std::cout << x; // 3 (I want it to be 10)
}

Ответы [ 2 ]

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

вам нужно отдельная отметка каждый шаблон с API (__declspec(dllexport) или __declspec(dllimport)) и не указывать его в коде класса.

Файл Foo.h:

#ifdef _DLL
#define API __declspec(dllexport)   
#else  
#define API __declspec(dllimport)   
#endif  

class API Foo
{
public:
    template<typename T> 
    API static T& Get();

    static void Set();
};

обратите внимание, что мы разделяем метку Get() на API, несмотря на то, что все классы Foo также помечены API (метка класса фактически не влияет на функцию шаблона, поэтому нужно пометить ее отдельно). и никакой реализации Get здесь - экспортируемые функции в любом случае не могут быть встроенными.

поэтому код dll (Foo.cpp) должен выглядеть так:

#include "foo.h"

template<typename T>    
API T& Foo::Get()
{
    __pragma(message("__imp_" __FUNCDNAME__)) // for debug
    static T _instance;
    return _instance;
}

void Foo::Set()
{
    Foo::Get<int>() = 10;
}

обратите внимание, что мы снова явно используем API (__declspec(dllexport)) в реализации тела функции. это очень важно - компилятор не предупреждает вас, если вы пропустите API здесь, но без этого - Get не будет экспортировано.

точно, что на данный момент все правильно - скопируйте строку, созданную __pragma(message("__imp_" __FUNCDNAME__)) (она будет выглядеть как __imp_??$Get@H@Foo@@SAAEAHXZ) и выполните поиск точно (от символа к символу) этого строка в созданном файле .lib - после сборки dll. если он существует - все в порядке, иначе бессмысленно продолжать (с exe)

и в exe:

#include "../foo_dll/foo.h"

Foo::Get<int>() = 3;
Foo::Set();
if (Foo::Get<int>() != 10)
{
    __debugbreak();
}
0 голосов
/ 09 сентября 2018

В общем случае вы вступили на путь боли. Вы должны контролировать процесс создания экземпляров. Это можно сделать, но я подозреваю, что переосмысление реальной проблемы, которую вы пытаетесь решить с помощью этого Foo, может быть более полезным. Вы можете оставить реализацию Get из Foo неэкспонированной в Foo.h.

#ifdef _DLL
#define API __declspec(dllexport)   
#else  
#define API __declspec(dllimport)   
#endif  

template <class T>
class API Foo{
public:
   static T &Get();
};

Тогда есть какой-то другой заголовок, который реализует его как Foo.impl

template <class T>
T &Foo<T>::Get() {
    static T _instance;
    return _instance;
};

В DLL вы можете создать его в файле ConcreteClass.cpp для того, что вы, возможно, сделали целым Foo своим:

 #include "Foo.h"
 #include "Foo.impl"

 void dummy_instantiation() {
     Foo<ConcreteClass>::Get();
 }

Если вы теперь включите "Foo.h" только в основную программу и свяжете ее с библиотекой экспорта DLL, тогда для нее будет определен символ Foo<ConcreteClass>::Get();, поэтому компоновщик не будет жаловаться.

...