Имеется DLL A и использующая ее DLL B. В DLL A помимо прочих экспортируемых классов и функций в одном из заголовочных файлов определен шаблон класса следующего вида (несущественные части кода опущены):
template<class T>
class CSomeTemplate
{
// ....public:
static int GetId()
{
static int nId = 0;
if (0 == nId)
{
nId = GenerateUniqueId();
}
return nId;
}
// ...
};
Классы, инстанцированные из этого шаблона, используются как в DLL A, так и в DLL B. Возникает проблема в том, что локальная статическая переменная nId, определенная в CSomeTemplate<T>::GetId(), существует в двух экземплярах (по одному в каждой из DLL) для одного и того же инстанцированного класса. Отсюда нарушение логики работы и появление багов. Это можно как-нибудь исправить?
Заранее спасибо.
Здравствуйте, Аноним, Вы писали:
А>Имеется DLL A и использующая ее DLL B. В DLL A помимо прочих экспортируемых классов и функций в одном из заголовочных файлов определен шаблон класса следующего вида (несущественные части кода опущены): А>... А>Классы, инстанцированные из этого шаблона, используются как в DLL A, так и в DLL B. Возникает проблема в том, что локальная статическая переменная nId, определенная в CSomeTemplate<T>::GetId(), существует в двух экземплярах (по одному в каждой из DLL) для одного и того же инстанцированного класса. Отсюда нарушение логики работы и появление багов. Это можно как-нибудь исправить?
Тут существенно не то, что эти классы являются шаблонными — та же проблема будет и с нешаблонными классами, если их не определить как экспортируемо-импортируемые.
Для того, чтоб шаблонные классы сделать экспортируемыми, в заголовочном файле нужно оставить только определение самомго шаблонного класса, а определение его функций-членов и членов-данных убрать из заголовочного файла в CPP-файл(ы), и обязательно выполнить явные инстанцирования для всех типов-параметров, с которыми предполагается данный шаблон экспортировать. Для обеспечения же экспорта-импорта можно применить распространенный прием с __declspec(export/import) + соответствующие макросы, если речь о MSVC.
--
Справедливость выше закона. А человечность выше справедливости.
Здравствуйте, Аноним, Вы писали:
А>Это можно как-нибудь исправить?
В общем случае нельзя, так как в A не знают полный список типов, с которыми будет использовать этот код B.
но в данном конкретном случае есть выход -- можно завести какую-то runtime генерилку id.
Скажем такую:
int id_for_raw_name( const std::string& raw_name ) // это экспортируем
{
static std::map<std::string, int> ids;
return ids[raw_name]++;
}
// а это, в хедереtemplate<typename T>
struct TypeId { static int Get() { return id_for_raw_name( typeid( T ).raw_name() ); } };
Или как-то так, с точностью до синтаксиса и идентификаторов
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[2]: Шаблон класса и DLL
От:
Аноним
Дата:
22.01.09 14:19
Оценка:
Здравствуйте, Erop, Вы писали:
E>В общем случае нельзя, так как в A не знают полный список типов, с которыми будет использовать этот код B.
E>но в данном конкретном случае есть выход -- можно завести какую-то runtime генерилку id.
E>Скажем такую:
E>int id_for_raw_name( const std::string& raw_name ) // это экспортируем
E>{
E> static std::map<std::string, int> ids;
E> return ids[raw_name]++;
E>}
E>// а это, в хедере
E>template<typename T>
E>struct TypeId { static int Get() { return id_for_raw_name( typeid( T ).raw_name() ); } };
E>
Или как-то так, с точностью до синтаксиса и идентификаторов
Спасибо за идею. Вроде понял Вопрос только по генерилке — в приведенной реализации она при каждом вызове увеличивает id, назначенный тому или иному типу, в то время, как должна возвращать одно и то же значение, назначенное при первом вызове для данного типа. Ну, впрочем, это уже детали реализации, главное, идея.