baldr
|
|
« : 13-11-2004 08:59 » |
|
Есть у меня два класса: template <class C> class Synchronizer { private: Synchronizer(); // Hidden constructor to realize Singleton pattern static Synchronizer<C> *_self; public: static Synchronizer<C> *Instance(); };
class BehaviourController { public: BehaviourController(); private: Synchronizer <BehaviourController>*Sync; }; И, соответственно, реализация: template <class C> Synchronizer<C>::Synchronizer() { // stuff }
template <class C> Synchronizer<C> *Synchronizer<C>::Instance() { if(!_self) { _self = new Synchronizer(); } return _self; } Тут все нормально, вроде? А если делаем вот так: BehaviourController::BehaviourController() { Sync=Synchronizer<BehaviourController>::Instance(); } То компилятор верещит вот так: BehaviourController.obj : error LNK2001: unresolved external symbol "public: static class Synchronizer<class BehaviourController> * __cdecl Synchronizer<class BehaviourController>::Instance(void)" (?Instance@?$Synchronizer@VBehaviourController@@@@SAPAV1@XZ) Вопрос: Что я делаю не так? Я хотел реализовать класс с шаблоном (дальше с ним идет работа) на основе паттерна Singleton. Пока не было шаблона, все было нормально. :?
|
|
« Последнее редактирование: 29-09-2009 07:13 от Алексей1153++ »
|
Записан
|
Приличный компьютер всегда будет стоить дороже 1000 долларов, потому что 500 долларов - это не вполне прилично
|
|
|
Pu
Большой босс
Offline
78
|
|
« Ответ #1 : 13-11-2004 12:14 » |
|
baldr, я не вижу инициализации статик указателя static Synchronizer<C> *_self; в реализации надо бы добавить чтото вроде Synchronizer *Synchronizer::_self = 0;
|
|
|
Записан
|
Насколько я опытен? Достаточно, чтобы понимать, что дураков нельзя заставить думать по–другому, но недостаточно, чтобы отказаться от попыток это сделать. (с) Артур Джонс
|
|
|
baldr
|
|
« Ответ #2 : 13-11-2004 13:58 » |
|
Да, сорри, это есть. Забыл указать. Но проблема не в этом.
|
|
|
Записан
|
Приличный компьютер всегда будет стоить дороже 1000 долларов, потому что 500 долларов - это не вполне прилично
|
|
|
Pu
Большой босс
Offline
78
|
|
« Ответ #3 : 13-11-2004 14:30 » |
|
baldr, гут , ща посмотрю ышо
|
|
|
Записан
|
Насколько я опытен? Достаточно, чтобы понимать, что дураков нельзя заставить думать по–другому, но недостаточно, чтобы отказаться от попыток это сделать. (с) Артур Джонс
|
|
|
Serega
Гость
|
|
« Ответ #4 : 13-11-2004 15:45 » |
|
Где у тебя находится реализация ?
|
|
|
Записан
|
|
|
|
baldr
|
|
« Ответ #5 : 15-11-2004 07:31 » |
|
Описание каждого класса находится в разных файлах. Реализация - тоже. Проблема исчезает, если делать без шаблонов... :new_shot:
|
|
|
Записан
|
Приличный компьютер всегда будет стоить дороже 1000 долларов, потому что 500 долларов - это не вполне прилично
|
|
|
dedOK
Гость
|
|
« Ответ #6 : 15-11-2004 09:05 » |
|
Чтобы использовать шаблон функции или класса в некотором модуле, в этом модуле должно иметься определение функции или класса и всех его элементов. // "C.h" template <typename T> class C { void f(); ... }; template <typename T> void C<T>::f() { ... }
#include "C.h" C<int> a; ...
Второй вариант - явное инстанционирование используемых экземпляров шаблонов в модуле с определениями. // "C.h" template <typename T> class C { void f(); ... };
// "C.cpp" #include "C.h" template <typename T> void C<T>::f() { ... }
template<> class C<int>;
#include "C.h" C<int> a; ...
|
|
« Последнее редактирование: 02-12-2007 17:34 от Алексей1153++ »
|
Записан
|
|
|
|
baldr
|
|
« Ответ #7 : 15-11-2004 14:38 » |
|
dedOK, по-моему, меня не поняли. :new_mpr: Я знаю что такое шаблон. По-моему, у тебя написано то же самое, что и у меня. :l_smile:
|
|
|
Записан
|
Приличный компьютер всегда будет стоить дороже 1000 долларов, потому что 500 долларов - это не вполне прилично
|
|
|
dedOK
Гость
|
|
« Ответ #8 : 16-11-2004 18:23 » |
|
Не знаю каким ты компилятором пользуешься, но у меня в билдере все скомпилировалось и запустилось без проблем: template <class C> class Synchronizer { private: Synchronizer(); static Synchronizer<C> *_self; public: static Synchronizer<C> *Instance(); };
template <class C> Synchronizer<C>* Synchronizer<C>::_self;
template <class C> Synchronizer<C>::Synchronizer() { /* stuff */ }
template <class C> Synchronizer<C> *Synchronizer<C>::Instance() { if(!_self) { _self = new Synchronizer(); } return _self; }
class BehaviourController { public: BehaviourController(); private: Synchronizer <BehaviourController>*Sync; };
BehaviourController::BehaviourController() { Sync=Synchronizer<BehaviourController>::Instance(); }
int main(int argc, char* argv[]) { BehaviourController a, b; return 0; }
|
|
« Последнее редактирование: 02-12-2007 17:38 от Алексей1153++ »
|
Записан
|
|
|
|
baldr
|
|
« Ответ #9 : 17-11-2004 07:41 » |
|
dedOK, и в самом деле... У меня MSVC все нормально взял если в одном файле. А попробуй сделать описания классов в отдельных .h-файлах, реализацию - в отдельных .cpp-файлах? Ведь линкер поэтому и орет, что не находит метод... У меня, вроде, все файлы включаются нормально... :?
|
|
|
Записан
|
Приличный компьютер всегда будет стоить дороже 1000 долларов, потому что 500 долларов - это не вполне прилично
|
|
|
npak
|
|
« Ответ #10 : 17-11-2004 11:42 » |
|
baldr, методы для шаблонного класса должны быть реализованы в той же единице компиляции, где они используются.
Пусть ты вынес тела методов шаблонного класса в отдельный файл, скажем tmpl_impl.c Тогда ты можешь использовать эти методы либо в файле tmpl_impl.c, либо надо явным образом включать tmpl_impl.c, то есть перед первым использованием шаблонного класса надо вставить #include "tmpl_impl.c"
Дело в том, что методы шаблонного класса не являются функциями. При инстанциации шаблона компилятор подставляет аргументы шаблона и генерирует фактические методы. Поэтому компилятору надо знать тела шаблонных методов, чтобы из них сгенерировать фактические методы.
Линкер тут ни причём. До него дело даже не доходит.
|
|
|
Записан
|
|
|
|
baldr
|
|
« Ответ #11 : 17-11-2004 13:55 » |
|
npak, компилятор все проходит нормально. А кричит как раз линкер. И про то, что ты говоришь я первый раз слышу. Собственно, какая компилятору разница - описание класса есть и реализация есть? В реализации есть include для описания.
|
|
|
Записан
|
Приличный компьютер всегда будет стоить дороже 1000 долларов, потому что 500 долларов - это не вполне прилично
|
|
|
baldr
|
|
« Ответ #12 : 17-11-2004 16:01 » |
|
Как это ни странно, но npak прав. Правда, я не понял почему. :oops: Соответственно, npak получает +1 и выходит в следующий тур.
|
|
|
Записан
|
Приличный компьютер всегда будет стоить дороже 1000 долларов, потому что 500 долларов - это не вполне прилично
|
|
|
npak
|
|
« Ответ #13 : 17-11-2004 16:14 » |
|
baldr, сорри, немного отстал от жизни про инстанциацию шаблонов. Ругается действительно линкер, но причина именна та, про которую я писал -- компилятор не может сгененрировать метод для инстанса. Когда компилятор доходит до строки Sync=Synchronizer<BehaviourController>::Instance(); он пытается сгенерировать функцию с именем "?Instance@?$Synchronizer@VBehaviourController@@@@SAPAV1@XZ". Но ему это не удаётся (почему -- см. далее). Компилятор вставляет ссылку на функцию с этим именем и продолжает обработку файла. При сборке приложения линкер ругается, так как ни в одном объектнике функция с таким именем не обнаружена В объектнике с "реализацией" шаблонных методов нет такой функции, так как в соответствующем исходнике не используется Synchronizer<BehaviourController>::Instance(). В объектнике с реализацией конструктора для BehaviorController нет функции, так как компилятор не смог создать тело метода. как происходит инстанциация метода (условно говоря) -- компилятор берёт тело шаблонного метода (текст!) и заменяет вхождения параметра шаблона на аргумент шаблона. То есть должен строиться метод (BehaviorController_Synchronizer_instance -- внутреннее сгенерированное имя класса-инстанса): BehaviorController_Synchronizer_instance *BehaviorController_Synchronizer_instance::Instance() { if(!_self) { _self = new BehaviorController_Synchronizer_instance(); } return _self; } Почему не может создать тело метода? Потому что не из чего. При компиляции файла с BehaviorController нет доступа к тексту файла с телами шаблонных методов, поэтому нет синтаксического материала для сборки тела метода инстанса.
|
|
« Последнее редактирование: 02-12-2007 17:43 от Алексей1153++ »
|
Записан
|
|
|
|
dedOK
Гость
|
|
« Ответ #14 : 17-11-2004 21:28 » |
|
Поэтому компилятору надо знать тела шаблонных методов, чтобы из них сгенерировать фактические методы.
Как это ни странно, но npak прав. Правда, я не понял почему
Меня, наверно не поняли. :? Именно об этом я и писал первый раз. Постараюсь пояснить. При разработке шаблонов используются три модели: (1) модель включения (2) модель явного инстанционирования (3) модель разделения (или экспорта) (1) - создается "заголовочный файл", содержащий все объявления и определения классов и их функций. В том числе и невстариваемых (чего не делают для обычных функций). Далее, этот заголовок включается в тот модуль, где шаблон используется. В качестве примера можно посмотреть любой STL-заголовок. (2) Все делается как с обычными модулями. Но в модуль, где присутствуют все определения, вносят команды явного инстанционирования тех экземпляров шаблона, которые используются во всем проекте. (3) Все как с обычными модулями. При этом все определения классов и невстраеваемых функций помечаются ключевым словом export. Это есть в стандарте. Однако export сейчас большинство компиляторов не поддерживают.
|
|
|
Записан
|
|
|
|
baldr
|
|
« Ответ #15 : 25-11-2004 16:45 » |
|
Опять линкеру что-то не нравится. Достал он меня... Вот что есть: //file Synchronizer.h template <class C> class Synchronizer { private: Synchronizer(); // Hiddec constructor to realize Singleton pattern static Synchronizer<C> *_self; public: static Synchronizer<C> *Instance() { if(!_self) _self = new Synchronizer(); return _self; } BOOL SetReportFunc(int (C::*RF)(long tick, SHORT* inputValArr)); private: static int (C::*reportFunc)(long tick, SHORT* inputValArr); }; #include "Synchronizer.cpp" //file Synchronizer.cpp template <class C> Synchronizer<C>::Synchronizer() { /*stuff*/ }
template <class C> BOOL Synchronizer<C>::SetReportFunc(int (C::*RF)(long tick, SHORT* inputValArr)) { if (RF=NULL) return FALSE; reportFunc=RF; return TRUE; }
template <class C> Synchronizer<C> *Synchronizer<C>::_self=NULL; template <class C> int Synchronizer<C>::*reportFunc(long tick, SHORT* inputValArr)=NULL; //file BehaviourController.h #include "Synchronizer.h" class BehaviourController { public: BehaviourController(); private: Synchronizer<BehaviourController> *Sync; }; //file BehaviourController.cpp #include "stdafx.h" #include "BehaviourController.h" BehaviourController::BehaviourController() { Sync=Synchronizer<BehaviourController>::Instance(); Sync->SetReportFunc(GetReport); }
Линкер кричит: BehaviourController.obj : error LNK2001: unresolved external symbol "private: static int (__thiscall BehaviourController::* Synchronizer<class BehaviourController>::reportFunc)(long,short *)" (?reportFunc@?$Synchronizer@VBehaviourController@@@@0P8BehaviourController@@AEHJPAF@ZQ2@) Сорри за большой кусок кода - зато все объяснил. Так вот. Линкеру не нравится описанная переменная-указатель на функцию reportFunc. Вопрос: правильно ли я ее описал в последней строке файла Synchronizer.cpp ? Попытку вставить все в один файл тут можно уже не предлагать - пробовал. Не работает.
|
|
« Последнее редактирование: 02-12-2007 17:46 от Алексей1153++ »
|
Записан
|
Приличный компьютер всегда будет стоить дороже 1000 долларов, потому что 500 долларов - это не вполне прилично
|
|
|
dedOK
Гость
|
|
« Ответ #16 : 26-11-2004 05:27 » |
|
По-моему дело в том, что template <class C> int Synchronizer<C>::*reportFunc( long tick, SHORT* inputValArr)=NULL; выглядит как указатель на функцию-элемент Synchronizer<C>. А как указать что это указатель на функцию-элемент C и при этом статический элемент Synchronizer<C>? У меня слинковался такой код: template <class C> class Synchronizer { ... public: typedef int (C::*ReportFunc)(long tick, SHORT* inputValArr); ... BOOL SetReportFunc(ReportFunc rF); private: static ReportFunc reportFunc; }; template <class C> Synchronizer<C>::ReportFunc Synchronizer<C>::reportFunc = NULL;
|
|
« Последнее редактирование: 02-12-2007 17:48 от Алексей1153++ »
|
Записан
|
|
|
|
baldr
|
|
« Ответ #17 : 26-11-2004 06:27 » |
|
dedOK, гранмерси!!! Етить ее растак, но она слинковалась!
|
|
|
Записан
|
Приличный компьютер всегда будет стоить дороже 1000 долларов, потому что 500 долларов - это не вполне прилично
|
|
|
baldr
|
|
« Ответ #18 : 26-11-2004 08:06 » |
|
Блин. Но как тогда ее вызвать?? int outVal=reportFunc(counter, wData); Так не прокатывает. error C2064: term does not evaluate to a function
|
|
« Последнее редактирование: 02-12-2007 17:50 от Алексей1153++ »
|
Записан
|
Приличный компьютер всегда будет стоить дороже 1000 долларов, потому что 500 долларов - это не вполне прилично
|
|
|
npak
|
|
« Ответ #19 : 26-11-2004 08:51 » |
|
baldr, reportFunc возвращает указатель на метод. Для вызова необходимо подставить объект класса C (не проверял) C objC; int outVal=objC.*reportFunc(counter, wData);
|
|
« Последнее редактирование: 02-12-2007 17:51 от Алексей1153++ »
|
Записан
|
|
|
|
baldr
|
|
« Ответ #20 : 26-11-2004 10:18 » |
|
Делаю так: outVal=Parent->*reportFunc(counter, wData); Ничего. Та же ошибка линкера. Parent - Это C* Parent;
|
|
« Последнее редактирование: 02-12-2007 17:52 от Алексей1153++ »
|
Записан
|
Приличный компьютер всегда будет стоить дороже 1000 долларов, потому что 500 долларов - это не вполне прилично
|
|
|
npak
|
|
« Ответ #21 : 26-11-2004 12:59 » |
|
baldr, это ошибка генерится компилятором Дело в том, что приоритет оператора "вызов функции" выше, чем оператора "Pointer to member", поэтому вызов функции по указателю надо оборачивать в скобки. outVal=(Parent->*reportFunc)(counter, wData);
|
|
« Последнее редактирование: 02-12-2007 17:53 от Алексей1153++ »
|
Записан
|
|
|
|
baldr
|
|
« Ответ #22 : 26-11-2004 13:10 » |
|
npak, отлично. Все работает.
|
|
|
Записан
|
Приличный компьютер всегда будет стоить дороже 1000 долларов, потому что 500 долларов - это не вполне прилично
|
|
|
Алексей++
глобальный и пушистый
Глобальный модератор
Offline
Сообщений: 13
|
|
« Ответ #23 : 29-01-2009 19:07 » |
|
а вот такой вопрос скажем, в итоге что хочется получить (неважно, зачем, важно - удобно): class Parent { virtual Parent* NEW()=0; virtual void DELETE()=0; void F2() { Parent* pX=0; pX=pX->NEW(); ... pX->=DELETE(); }; };
class Child1:Parent { virtual Parent* NEW() { return new Child1; }
virtual void DELETE() { delete (Child1*)this; } };
class Child2:Parent { virtual Parent* NEW() { return new Child2; }
virtual void DELETE() { delete (Child2*)this; } };
видим, что код детёв одинаков, за исключением имени типа дитя. Допустим, делаем через макрос class Parent { virtual Parent* NEW()=0; virtual void DELETE()=0; void F2() { Parent* pX=NEW(); ... pX->=DELETE(); }; };
#define Parent_new_delete(class_type) \ virtual Parent* NEW()\ {\ return new (class_type);\ }\ \ virtual void DELETE()\ {\ delete ((class_type)*)this;\ }
class Child1:Parent { Parent_new_delete(Child1) };
class Child2:Parent { Parent_new_delete(Child2) };
//пример вызова
Child2 A; A.F2();
уже красивее выглядит. Но макрос не хотелось бы так оставлять. И вот вопрос: можно ли вместо данного макроса применить некий шаблон функции ? Насколько я понял, для этого потребуется шаблон класса, но мне его тут нафиг не надо... Как лучше поступить ?
|
|
« Последнее редактирование: 29-01-2009 19:21 от Алексей1153++ »
|
Записан
|
|
|
|
Dimka
Деятель
Команда клуба
Offline
Пол:
|
|
« Ответ #24 : 30-01-2009 13:24 » |
|
видим, что код детёв одинаков Что-то типа: template<class T> class Child: public Parent { public: virtual Parent *NEW() { return new T(); } virtual void DELETE() { delete dynamic_cast<T *>(this); } };
class Child1: public Child<Child1> {}; class Child2: public Child<Child2> {}; (Не проверял на компилябельность.)
|
|
|
Записан
|
Программировать - значит понимать (К. Нюгард) Невывернутое лучше, чем вправленное (М. Аврелий) Многие готовы скорее умереть, чем подумать (Б. Рассел)
|
|
|
Алексей++
глобальный и пушистый
Глобальный модератор
Offline
Сообщений: 13
|
|
« Ответ #25 : 31-01-2009 08:01 » |
|
dimka, спасибо, вот наконец то нашлось для меня первое серьёзное применение для шаблонов только насчёт идентификатора DELETE - в студии он, оказывается, уже зарезервирован, надо другой, но это мелочи. ещё, зачем dynamic_cast<T *>(this) если можно (T*)this ?
|
|
|
Записан
|
|
|
|
Вад
|
|
« Ответ #26 : 31-01-2009 08:28 » |
|
Алексей1153++, потому что если dynamic_cast не сможет привести тип, он вернёт null, а delete для null вполне безобиден. В общем случае приведение в C-style опаснее: скажем, если какой-нибудь нехороший человек чем-то не тем специфицирует шаблон. Например, class Child3: public Child<AnotherClass> {};
что тогда будет? Понятно, что в данном контексте такое определение не вполне корректно Но мало ли чем ещё можно специфицировать шаблон, и приводить всё это к типу, не будучи уверенным, что приведётся... Ошибки во время выполнения будут весьма странные. Я бы, кстати, использовал static_cast - он в этом случае отлично проверяет, чтобы кто чего не натворил, минуя наследование Child: class Parent {}; class AnotherClass : public Parent {};
template<class T> class Child : public Parent { public: virtual Parent *Create() { return new T(); } virtual void Release() { delete static_cast<T *>(this); } };
class Child1: public Child<Child1> {}; class Child2: public Child<Child2> {}; class Child3: public Child<AnotherClass>{};
Child<AnotherClass> компилятор не пропустит, всё остальное - ок.
|
|
« Последнее редактирование: 31-01-2009 08:47 от Вад »
|
Записан
|
|
|
|
Dimka
Деятель
Команда клуба
Offline
Пол:
|
|
« Ответ #27 : 31-01-2009 08:47 » |
|
Вад, я думаю, случая, когда dynamic_cast в этом примере вернёт NULL, не будет. Поскольку new T() должен привестись к Parent *. Если T - не потомок Parent, то компилятор на это обидится. Но в целом в данном примере dynamic_cast, конечно, надёжнее static_cast.
P.S. В С# можно явно указать, что параметр шаблона должен быть потомком какого-то класса. В C++, к сожалению, нельзя.
|
|
|
Записан
|
Программировать - значит понимать (К. Нюгард) Невывернутое лучше, чем вправленное (М. Аврелий) Многие готовы скорее умереть, чем подумать (Б. Рассел)
|
|
|
Вад
|
|
« Ответ #28 : 31-01-2009 09:03 » |
|
Вот ещё конструкция, с которой пока не пойму, что делать: class Child4: public Child<Child2> {}; Если конструкторы приватные, и в качестве объекта Class4 будет на самом деле существовать объект Class2 - то, вроде, ничего страшного. Но поведение всё равно получается запутанное. А уж если можно явно инстанцировать Class4... Правильно я понимаю, что из-за срезки нельзя по this установить, какой конкретно тип является спецификацией шаблона?
|
|
« Последнее редактирование: 31-01-2009 09:08 от Вад »
|
Записан
|
|
|
|
Алексей++
глобальный и пушистый
Глобальный модератор
Offline
Сообщений: 13
|
|
« Ответ #29 : 31-01-2009 14:04 » |
|
думаю, с кастом тут вы оба преувеличили ) Правило использзование класса я напишу, а кто захочет экспериментировать - флаг ему в шаловливые ручки ) Ну ладно, static_cast оставлю
|
|
|
Записан
|
|
|
|
|