Пока что у меня есть:
// TypeCounter.h
template <typename Base>
class Counter : public Base
{
protected:
static int typeIndexCounter;
};
template <typename T, typename Base>
class Bridge : public Counter<Base>
{
public:
virtual ~Bridge() {}
virtual int GetTypeIndex() const
{
return TypeIndex();
}
static int TypeIndex()
{
static int typeIndex = typeIndexCounter++;
return typeIndex;
}
};
// static variable definition
template <typename Base>
int Counter<Base>::typeIndexCounter;
Вариант использования похож на:
class SBase
{ ... }
class S0 : public Bridge<S0, SBase>
{ ... }
class S1 : public Bridge<S1, SBase>
{ ... }
class S2 : public Bridge<S2, SBase>
{ ... }
Так что для каждого вида Base
класс, я могу посчитать, сколько у него производных (если оно когда-либо создавалось, конечно).
Проблема в том, что это Counter
класс находится в DLL.
Если у меня есть несколько пользователей этой DLL, то каждый пользователь будет создавать свои собственные Bridge
а также Counter
тогда TypeIndex()
может быть разным среди этих пользователей для одного и того же T
,
Я пытался поставить __declspec(dllexport)
до Counter
а также Bridge
, но есть ошибки компиляции на стороне пользователя (сторона пользователя изменится export
в import
, определив макрос).
Несмотря на ошибки, это также неправильный подход, так как не следует экспортировать шаблон класса.
Поэтому я хочу убедиться, что TypeIndex()
сохраняйте то же самое наверняка T
, даже если есть много экземпляров для одного и того же T
Как я могу достичь этого?
Спасибо!
Используйте карту для хранения информации. Это не включает никакого встроенного кода (за исключением миксина CRTP для регистрации и подсчета), но он должен избегать несовпадения различных частей.
Тем не менее, когда вы пишете «пользователи», вы не имеете в виду пользователей в смысле людей, которые имеют логин, верно? Причина в том, что это всегда только подсчет в одном процессе, разные процессы одного и того же или разных пользователей не влияют на это.
Это правда, что вы не можете экспортировать шаблон класса. Это даже бесполезно, поскольку у вас есть весь шаблон класса, записанный в заголовочном файле, поэтому для использования кода фактически не требуется экспорта.
Вместо этого вы должны экспортировать экземпляр класса, а затем использовать его с ключевым словом «extern»:
Это заголовочный файл при компиляции библиотеки:
class SBase{ ... }
template class __declspec(dllexport) Bridge<S0, SBase>;
class S0 : public Bridge<S0, SBase>
{ ... }
template class __declspec(dllexport) Bridge<S1, SBase>;
class S1 : public Bridge<S1, SBase>
{ ... }
template class __declspec(dllexport) Bridge<S2, SBase>;
class S2 : public Bridge<S2, SBase>
{ ... }
И это заголовочный файл при построении клиентского кода:
class SBase{ ... }
extern template class __declspec(dllimport) Bridge<S0, SBase>;
class S0 : public Bridge<S0, SBase>
{ ... }
extern template class __declspec(dllimport) Bridge<S1, SBase>;
class S1 : public Bridge<S1, SBase>
{ ... }
extern template class __declspec(dllimport) Bridge<S2, SBase>;
class S2 : public Bridge<S2, SBase>
{ ... }
Теперь компоновщик будет искать эти классы во всей вашей программе, а не только в модуле компиляции.
Это должно быть определено по типу, чтобы получить разные выходные данные с использованием одного и того же кода:
#ifdef LIBRARY_BUILD
#define EXPORT __declspec(dllexport)
#define TEMPLATE_EXPORT
#else
#define EXPORT __declspec(dllimport)
#define TEMPLATE_EXPORT extern
#endif
class SBase{ ... }
TEMPLATE_EXPORT template class EXPORT Bridge<S0, SBase>;
class S0 : public Bridge<S0, SBase>
{ ... }
TEMPLATE_EXPORT template class EXPORT Bridge<S1, SBase>;
class S1 : public Bridge<S1, SBase>
{ ... }
TEMPLATE_EXPORT template class EXPORT Bridge<S2, SBase>;
class S2 : public Bridge<S2, SBase>
{ ... }
Это должно быть сделано во ВСЕХ шаблонных классах со статическими членами, поэтому у всех шаблонных классов есть только ОДНО экземпляры членов, а не по одному на модуль компиляции.
Вы можете получить дополнительную информацию здесь:
классы и статические переменные в общих библиотеках
РЕДАКТИРОВАТЬ:
Перечитав ваш вопрос, я бы объявил extern вашего базового класса шаблона (так как это класс со статическим членом), а не конкретные:
TEMPLATE_EXPORT template class EXPORT Counter<SBase>;
Итак, теперь вы можете иметь любой производный класс SBase, и все будут полагаться на один и тот же класс Counter, без необходимости объявлять себя как extern.
Конечно, если SBase находится в какой-то библиотеке, он также должен быть объявлен как extern (обычным образом, просто __declspec (dllexport), так как это не шаблон).