Статическая переменная-член в шаблоне с несколькими DLL

Мой код построен на нескольких файлах .dll, и у меня есть класс шаблона со статической переменной-членом.

Я хочу, чтобы один и тот же экземпляр этой статической переменной-члена был доступен во всех библиотеках DLL, но это не работает: я вижу разные экземпляры (разные значения) в каждой из них.

Когда я не использую шаблоны, нет проблем: инициализируйте статический член в одном из исходных файлов и используйте директивы __declspec (dllexport) и __declspec (dllimport) в классе. Но с шаблонами это не работает. Есть ли способ заставить его работать?

Я видел некоторые предлагаемые решения, в которых используется «extern», но я думаю, что не могу его использовать, потому что мой код должен работать с Visual Studio 2002 и 2005.

Спасибо.

Уточнение: я хочу иметь отдельный экземпляр статической переменной для каждого типа создания экземпляра шаблона. Но если я создаю экземпляр шаблона с одним и тем же типом в двух разных dll, я хочу, чтобы в них обоих была одна и та же переменная.


person Igor Oks    schedule 29.12.2008    source источник


Ответы (7)


Создайте специализацию шаблона, а затем экспортируйте статические элементы специализации.

person chappar    schedule 29.12.2008
comment
Спасибо, это работает. Но таким образом я должен создать специализацию для каждого типа, и я теряю весь смысл шаблонов. Есть ли решение без него? - person Igor Oks; 29.12.2008
comment
см. ссылку в упомянутом решении, чтобы сохранить гибкость шаблона - person balint.miklos; 30.06.2009

Также существует следующее решение:

  • в библиотеке: явно создайте экземпляр некоторой специализации шаблона и поделитесь им с dllexport
  • in the main program:
    • if the specialization is available it will be used from the library
    • если специализация недоступна, она компилируется в основной программе

Подробное описание того, как это сделать:

Блог Anteru Явное создание шаблона

person balint.miklos    schedule 30.06.2009

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

person Judge Maygarden    schedule 29.12.2008
comment
ааа, вот что он имел в виду! я смотрел на его вопрос, не зная, что он имеет в виду. Я думаю, ты справился. Ницца - person Johannes Schaub - litb; 29.12.2008
comment
Нет, я хочу иметь отдельный экземпляр статической переменной для каждого типа создания экземпляра шаблона. Но если я создаю экземпляр шаблона с одним и тем же типом в двух разных dll, я хочу, чтобы в них обоих была одна и та же переменная. - person Igor Oks; 29.12.2008

Кажется, есть способ сделать это с меньшими ограничениями для кода, который использует класс шаблона.

Сделайте статический член указателем. Создайте глобальную карту с фиксированным известным типом, которую можно экспортировать из DLL. Карта использует typeid () класса в качестве ключа и адрес «глобальной переменной для класса» в качестве значения. Инициализируйте статический член с помощью функции, которая проверяет, существует ли класс уже на карте, и, если да, заставляет вторую версию класса (во второй DLL) указывать на статическую переменную первой версии класса.

Таким образом, каждая DLL имеет отдельный статический объект, но каждая DLL также имеет указатель, и все указатели указывают на один и тот же статический объект.

Вот некоторый псевдокод, предполагающий, что статический тип такой же, как параметр шаблона (но должен быть легко адаптирован для других случаев).

map<string,void*> dllexport the_map;  // instantiate this once in a single DLL

T *set_the_global(T *candidate) {
  map<string,void*>::iterator r = the_map.find(string(typeid(the_class<T>).name()));
  if(r == the_map.end()) {
    the_map[string(typeid(the_class<T>).name())] = (void*)candidate;
    return candidate;  // new class: use it as global storage location
  } else {
    return (T*)(r->second);  // class already has global storage location
  }
}

template <class T> class the_class {
  virtual void something();  // so RTTI exists
  static T *the_global;  // use this! always points to the same object
  static T one_per_dll;  // only used in initialisation
};
template<class T> the_class<T>::one_per_dll;
template<class T> the_class<T>::the_global = set_the_global(&the_class<T>::one_per_dll)
person James    schedule 18.12.2009

Вы уже пробовали это использование:

#pragma data_seg(".JOE")
HWND hWndServer = NULL;
HHOOK hook = NULL;
#pragma data_seg()
#pragma comment(linker, "/section:.JOE,rws")  

Обратите внимание, что переменная требует инициализации луча.

Дополнительная информация: http://msdn.microsoft.com/en-us/library/ms997537.aspx http://www.flounder.com/hooks.htm

Удачи.

person lsalamon    schedule 29.12.2008

Я вижу два исправления этой проблемы.

Во-первых, вы используете другой класс, не являющийся шаблоном, для хранения этого статического значения - или делаете его глобальным? - и экспортировать это из dll.

Другой вариант немного сложнее, поскольку вы создаете экземпляр шаблона в коде и экспортируете созданное значение (я) шаблона. Итак, чтобы привести пример, скажем, у меня был особый тип шаблонного класса связанного списка, и мне нужно было, чтобы статическое значение было общим для всех библиотек DLL. Я написал код для создания шаблона, но на самом деле он используется только для небольшого количества типов. Я бы создал экземпляры классов как таковых:

template <class T> class Foo;
template<> class Foo<int> {};

Затем вы можете экспортировать статические переменные, содержащиеся внутри.

__declspec(dllexport) int Foo<int>::StaticMember = 0;

(Или что-то в этом роде, я немного устарел с экспортом / импортом dll.)

Хотя реальный вопрос заключается в том, зачем вам это нужно, поскольку технически DLL может использоваться во всех процессах только с одной копией, хранящейся в памяти. Вы действительно хотите, чтобы была только одна версия статики для всех процессов или одна для каждого процесса?

person Daemin    schedule 29.12.2008
comment
вы можете явно создать экземпляр только static: template int SpecialFooList ‹int› :: staticMember; в одном файле cpp, тогда вам не нужно специализировать весь шаблон. то, что вы делаете в настоящее время, не соответствует C ++, и я не уверен, что это должно означать :) - person Johannes Schaub - litb; 29.12.2008
comment
если вы хотите явно создать экземпляр всего шаблона, вы делаете это с помощью класса шаблона SpecialFooList ‹int›; - person Johannes Schaub - litb; 29.12.2008
comment
Хорошо, исправлены (серьезные) ошибки кодирования, спасибо за внимание. - person Daemin; 29.12.2008
comment
Спасибо. Я попытался создать только статический экземпляр, но это не решило проблему (другая dll не находит его во время связывания). Даже после исправления вы по-прежнему создаете экземпляр шаблона, а не статического объекта. Вы уверены, что он работает? Относительно вашего вопроса: я использую только один процесс - person Igor Oks; 29.12.2008
comment
я вне игры. Я понятия не имею, как все работает в DLL Windows :) удачи. Я поиграю с ними в следующий раз. - person Johannes Schaub - litb; 29.12.2008
comment
litb: спасибо! Кроме того, есть идеи, как заставить это работать с файлами unix .so? - person Igor Oks; 29.12.2008
comment
Игорь: Просто мое мнение, что я бы избегал наличия статических членов внутри dll, которые также доступны извне. Значит ли это, что единовременно будет работать только один экземпляр вашего процесса? - person Daemin; 01.01.2009

До того, как extern template экземпляры были приняты в черновой вариант стандарта, похоже, что Microsoft реализовала расширение для компилятора VC ++.

Компилятор VC ++ выдаст предупреждение, если используется нестандартное расширение; VS.NET (2003) и выше относятся к этому предупреждению < / a> описание для подробностей. Это предупреждение также отображается для VS 6.0.

Я лично никогда не пытался использовать это расширение, поэтому я не могу поручиться за это предложение. Очевидно, я ограничиваю этот ответ Microsoft Visual Studio (я видел ваш комментарий относительно Unix), но я публикую в надежде, что он может оказаться полезным.

person Henk    schedule 29.12.2008