Трабла с темплейтами (программистам C++)
May. 22nd, 2008 10:30 pm![[personal profile]](https://www.dreamwidth.org/img/silk/identity/user.png)
Возникает ошибка линковки:
error LNK2005: public: __thiscall ArrayHolder<char>::ArrayHolder<char>(void)" (??0?$ArrayHolder@D@@QAE@XZ) already defined in excel.obj
Причины я знаю (ниже), вопрос в том, как их культурно лечить.
1. Есть DLL и прога, которая ее юзает (то же самое, когда другая DLL, юзает первую DLL).
2. В одном из хэдеров DLL (arrayholder.h) определен некий темплейтный класс:
template <class T>
class ArrayHolder<T>
{
public:
ArrayHolder() {}
...
};
3. В одном из хэдеров DLL (someclass.h) определен некий класс, унаследованный от ArrayHolder<char> - не напрямую а через цепочку, но это неважно:
class EXPORTIMPORT SomeClass: public ArrayHolder<char>
{
...
};
У этого класса в someclass.cpp определена куча функций, которые используются как в DLL, так и в проге.
4. В проге, в некоем модуле excel.cpp создается и используется объект типа ArrayHolder<char>.
Проблема, похоже, в следующем.
Когда компилируется DLL, компилятор создает код конструктора ArrayHolder::ArrayHolder() внутри DLL, и его код экспортируется наружу вместе с конструктором SomeClass,
Когда компилируется excel.cpp, компилятор не знает о том, что ArrayHolder::ArrayHolder() уже есть в DLL-ке, и создает его еще раз. Ошибка обнаруживается на этапе линковки.
Мне известно одно лекарство: надо сделать #include <someclass.h> в модуле excel.cpp. Тогда компилятор будет знать об этом экспорте уже на этапе компиляции.
Но это плохой путь, поскольку класс SomeClass реально не используется в модуле excel.cpp. Неочевидно, что, всякий раз, когда используешь ArrayHolder<char> надо добавить не только хэдер ArrayHolder, но и хэдеры всех классов в DLL, которые используют ArrayHolder<char>.
Upd: Как проблема решилась.
Вариант 1:
В конец arrayholder.h добавить:
class IMPORTEXPORT ArrayHolder_char_dummy: public ArrayHolder<char>
{
};
- в результате компилятор всегда будет знать, что эта специализация есть в DLL-ке.
Вариант 2:
(только для Microsoft Studio)
В конец arrayholder.h добавить:
#if EXPORT
template class IMPORTEXPORT ArrayHolder<char>;
#else
extern template class IMPORTEXPORT ArrayHolder<char>;
#endif
- результат тот же, правда, сыпятся warning-и, что 'nonstandard extension used'...
error LNK2005: public: __thiscall ArrayHolder<char>::ArrayHolder<char>(void)" (??0?$ArrayHolder@D@@QAE@XZ) already defined in excel.obj
Причины я знаю (ниже), вопрос в том, как их культурно лечить.
1. Есть DLL и прога, которая ее юзает (то же самое, когда другая DLL, юзает первую DLL).
2. В одном из хэдеров DLL (arrayholder.h) определен некий темплейтный класс:
template <class T>
class ArrayHolder<T>
{
public:
ArrayHolder() {}
...
};
3. В одном из хэдеров DLL (someclass.h) определен некий класс, унаследованный от ArrayHolder<char> - не напрямую а через цепочку, но это неважно:
class EXPORTIMPORT SomeClass: public ArrayHolder<char>
{
...
};
У этого класса в someclass.cpp определена куча функций, которые используются как в DLL, так и в проге.
4. В проге, в некоем модуле excel.cpp создается и используется объект типа ArrayHolder<char>.
Проблема, похоже, в следующем.
Когда компилируется DLL, компилятор создает код конструктора ArrayHolder::ArrayHolder() внутри DLL, и его код экспортируется наружу вместе с конструктором SomeClass,
Когда компилируется excel.cpp, компилятор не знает о том, что ArrayHolder::ArrayHolder() уже есть в DLL-ке, и создает его еще раз. Ошибка обнаруживается на этапе линковки.
Мне известно одно лекарство: надо сделать #include <someclass.h> в модуле excel.cpp. Тогда компилятор будет знать об этом экспорте уже на этапе компиляции.
Но это плохой путь, поскольку класс SomeClass реально не используется в модуле excel.cpp. Неочевидно, что, всякий раз, когда используешь ArrayHolder<char> надо добавить не только хэдер ArrayHolder, но и хэдеры всех классов в DLL, которые используют ArrayHolder<char>.
Upd: Как проблема решилась.
Вариант 1:
В конец arrayholder.h добавить:
class IMPORTEXPORT ArrayHolder_char_dummy: public ArrayHolder<char>
{
};
- в результате компилятор всегда будет знать, что эта специализация есть в DLL-ке.
Вариант 2:
(только для Microsoft Studio)
В конец arrayholder.h добавить:
#if EXPORT
template class IMPORTEXPORT ArrayHolder<char>;
#else
extern template class IMPORTEXPORT ArrayHolder<char>;
#endif
- результат тот же, правда, сыпятся warning-и, что 'nonstandard extension used'...
no subject
Date: 2008-05-22 08:17 pm (UTC)no subject
Date: 2008-05-23 05:53 pm (UTC)(ц) онегдод про доктора и больного
no subject
Date: 2008-05-23 06:39 pm (UTC)no subject
Date: 2008-05-23 06:44 pm (UTC)— ну и как там у меня, доктор?
— хорошо, ай, как хорошо, просто отлично!
— что хорошо-то, доктор?
— хорошо, что у меня такого нет!
no subject
Date: 2008-05-23 06:51 pm (UTC)no subject
Date: 2008-05-23 06:54 pm (UTC)нехуй пихать классы в DLLдуховности и благодати нет.no subject
Date: 2008-05-28 01:56 pm (UTC)а хрен его знает. я никогда не был настолько помрачён сознанием, чтобы писать на цпп цто-либо сложнее софтины «fuck stroustrup!» какая, в общем-то, разница, как там реализовали очередной костыль…
no subject
Date: 2008-05-28 02:00 pm (UTC)