Форум программистов «Весельчак У»
  *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

  • Рекомендуем проверить настройки временной зоны в вашем профиле (страница "Внешний вид форума", пункт "Часовой пояс:").
  • У нас больше нет рассылок. Если вам приходят письма от наших бывших рассылок mail.ru и subscribe.ru, то знайте, что это не мы рассылаем.
   Начало  
Наши сайты
Помощь Поиск Календарь Почта Войти Регистрация  
 
Страниц: [1] 2 3 4 ... 6   Вниз
  Печать  
Автор Тема: борьба с шаблонами  (Прочитано 124363 раз)
0 Пользователей и 1 Гость смотрят эту тему.
baldr
Команда клуба

cy
Offline Offline
Пол: Мужской
Дорогие россияне


WWW
« : 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
Большой босс

ru
Offline Offline
78


« Ответ #1 : 13-11-2004 12:14 » 

baldr, я не вижу инициализации статик указателя  
static Synchronizer<C> *_self;
в реализации надо бы добавить чтото вроде
Synchronizer *Synchronizer::_self = 0;
Записан

Насколько я опытен? Достаточно, чтобы понимать, что дураков нельзя заставить думать по–другому, но недостаточно, чтобы отказаться от попыток это сделать.
(с) Артур Джонс
baldr
Команда клуба

cy
Offline Offline
Пол: Мужской
Дорогие россияне


WWW
« Ответ #2 : 13-11-2004 13:58 » 

Да, сорри, это есть. Забыл указать. Но проблема не в этом.
Записан

Приличный компьютер всегда будет стоить дороже 1000 долларов, потому что 500 долларов - это не вполне прилично
Pu
Большой босс

ru
Offline Offline
78


« Ответ #3 : 13-11-2004 14:30 » 

baldr, гут , ща посмотрю ышо  Ага
Записан

Насколько я опытен? Достаточно, чтобы понимать, что дураков нельзя заставить думать по–другому, но недостаточно, чтобы отказаться от попыток это сделать.
(с) Артур Джонс
Serega
Гость
« Ответ #4 : 13-11-2004 15:45 » 

Где у тебя находится реализация ?
Записан
baldr
Команда клуба

cy
Offline Offline
Пол: Мужской
Дорогие россияне


WWW
« Ответ #5 : 15-11-2004 07:31 » 

Описание каждого класса находится в разных файлах. Реализация - тоже.
Проблема исчезает, если делать без шаблонов... :new_shot:
Записан

Приличный компьютер всегда будет стоить дороже 1000 долларов, потому что 500 долларов - это не вполне прилично
dedOK
Гость
« Ответ #6 : 15-11-2004 09:05 » new

Чтобы использовать шаблон функции или класса в некотором модуле, в этом модуле должно иметься определение функции или класса и всех его элементов.
Код:
// "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
Команда клуба

cy
Offline Offline
Пол: Мужской
Дорогие россияне


WWW
« Ответ #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
Команда клуба

cy
Offline Offline
Пол: Мужской
Дорогие россияне


WWW
« Ответ #9 : 17-11-2004 07:41 » 

dedOK, и в самом деле...  Я шокирован!  У меня MSVC все нормально взял если в одном файле.
А попробуй сделать описания классов в отдельных .h-файлах, реализацию - в отдельных .cpp-файлах?
Ведь линкер поэтому и орет, что не находит метод... У меня, вроде, все файлы включаются нормально...  :?
Записан

Приличный компьютер всегда будет стоить дороже 1000 долларов, потому что 500 долларов - это не вполне прилично
npak
Команда клуба

ru
Offline Offline
Пол: Мужской

« Ответ #10 : 17-11-2004 11:42 » 

baldr, методы для шаблонного класса должны быть реализованы в той же единице компиляции, где они используются.

Пусть ты вынес тела методов шаблонного класса в отдельный файл, скажем tmpl_impl.c
Тогда ты можешь использовать эти методы либо в файле tmpl_impl.c, либо надо явным образом включать tmpl_impl.c, то есть перед первым использованием шаблонного класса надо вставить
#include "tmpl_impl.c"

Дело в том, что методы шаблонного класса не являются функциями.  При инстанциации шаблона компилятор подставляет аргументы шаблона и генерирует фактические методы.  Поэтому компилятору надо знать тела шаблонных методов, чтобы из них сгенерировать фактические методы.  

Линкер тут ни причём.  До него дело даже не доходит.
Записан

UniTesK -- индустриальная технология надежного тестирования.

http://www.unitesk.com/ru/
baldr
Команда клуба

cy
Offline Offline
Пол: Мужской
Дорогие россияне


WWW
« Ответ #11 : 17-11-2004 13:55 » 

npak, компилятор все проходит нормально. А кричит как раз линкер.
И про то, что ты говоришь я первый раз слышу. Собственно, какая компилятору разница - описание класса есть и реализация есть? В реализации есть include для описания.
Записан

Приличный компьютер всегда будет стоить дороже 1000 долларов, потому что 500 долларов - это не вполне прилично
baldr
Команда клуба

cy
Offline Offline
Пол: Мужской
Дорогие россияне


WWW
« Ответ #12 : 17-11-2004 16:01 » 

Как это ни странно, но npak прав. Правда, я не понял почему.  :oops:
Соответственно, npak получает +1 и выходит в следующий тур.  Отлично
Записан

Приличный компьютер всегда будет стоить дороже 1000 долларов, потому что 500 долларов - это не вполне прилично
npak
Команда клуба

ru
Offline Offline
Пол: Мужской

« Ответ #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++ » Записан

UniTesK -- индустриальная технология надежного тестирования.

http://www.unitesk.com/ru/
dedOK
Гость
« Ответ #14 : 17-11-2004 21:28 » 

Цитата: npak

Поэтому компилятору надо знать тела шаблонных методов, чтобы из них сгенерировать фактические методы.

Цитата: baldr

Как это ни странно, но npak прав. Правда, я не понял почему

Меня, наверно не поняли.  :? Именно об этом я и писал первый раз. Постараюсь пояснить. При разработке шаблонов используются три модели:
(1) модель включения
(2) модель явного инстанционирования
(3) модель разделения (или экспорта)

(1) - создается "заголовочный файл", содержащий все объявления и определения классов и их функций. В том числе и невстариваемых (чего не делают для обычных функций). Далее, этот заголовок включается в тот модуль, где шаблон используется. В качестве примера можно посмотреть любой STL-заголовок.
(2) Все делается как с обычными модулями. Но в модуль, где присутствуют все определения, вносят команды явного инстанционирования тех экземпляров шаблона, которые используются во всем проекте.
(3) Все как с обычными модулями. При этом все определения классов и невстраеваемых функций помечаются ключевым словом export. Это есть в стандарте. Однако export сейчас большинство компиляторов не поддерживают.
Записан
baldr
Команда клуба

cy
Offline Offline
Пол: Мужской
Дорогие россияне


WWW
« Ответ #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
Команда клуба

cy
Offline Offline
Пол: Мужской
Дорогие россияне


WWW
« Ответ #17 : 26-11-2004 06:27 » 

dedOK, гранмерси!!!
Етить ее растак, но она слинковалась!
Записан

Приличный компьютер всегда будет стоить дороже 1000 долларов, потому что 500 долларов - это не вполне прилично
baldr
Команда клуба

cy
Offline Offline
Пол: Мужской
Дорогие россияне


WWW
« Ответ #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
Команда клуба

ru
Offline Offline
Пол: Мужской

« Ответ #19 : 26-11-2004 08:51 » 

baldr, reportFunc возвращает указатель на метод.  Для вызова необходимо подставить объект класса C

(не проверял)

Код:
C objC;
int   outVal=objC.*reportFunc(counter, wData);
« Последнее редактирование: 02-12-2007 17:51 от Алексей1153++ » Записан

UniTesK -- индустриальная технология надежного тестирования.

http://www.unitesk.com/ru/
baldr
Команда клуба

cy
Offline Offline
Пол: Мужской
Дорогие россияне


WWW
« Ответ #20 : 26-11-2004 10:18 » 

Делаю так:
Код:
outVal=Parent->*reportFunc(counter, wData);
Ничего. Та же ошибка линкера.
Parent - Это C* Parent;
« Последнее редактирование: 02-12-2007 17:52 от Алексей1153++ » Записан

Приличный компьютер всегда будет стоить дороже 1000 долларов, потому что 500 долларов - это не вполне прилично
npak
Команда клуба

ru
Offline Offline
Пол: Мужской

« Ответ #21 : 26-11-2004 12:59 » 

baldr, это ошибка генерится компилятором

Дело в том, что приоритет оператора "вызов функции" выше, чем оператора "Pointer to member", поэтому вызов функции по указателю надо оборачивать в скобки.

Код:
outVal=(Parent->*reportFunc)(counter, wData);
« Последнее редактирование: 02-12-2007 17:53 от Алексей1153++ » Записан

UniTesK -- индустриальная технология надежного тестирования.

http://www.unitesk.com/ru/
baldr
Команда клуба

cy
Offline Offline
Пол: Мужской
Дорогие россияне


WWW
« Ответ #22 : 26-11-2004 13:10 » 

npak, отлично. Все работает.  Отлично
Записан

Приличный компьютер всегда будет стоить дороже 1000 долларов, потому что 500 долларов - это не вполне прилично
Алексей++
глобальный и пушистый
Глобальный модератор

ru
Offline 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
Деятель
Команда клуба

ru
Offline Offline
Пол: Мужской

« Ответ #24 : 30-01-2009 13:24 » 

Цитата: Алексей1153++
видим, что код детёв одинаков

Что-то типа:
Код: (C++)
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> {};
 
(Не проверял на компилябельность.)
Записан

Программировать - значит понимать (К. Нюгард)
Невывернутое лучше, чем вправленное (М. Аврелий)
Многие готовы скорее умереть, чем подумать (Б. Рассел)
Алексей++
глобальный и пушистый
Глобальный модератор

ru
Offline Offline
Сообщений: 13


« Ответ #25 : 31-01-2009 08:01 » 

dimka, спасибо, вот наконец то нашлось для меня первое серьёзное применение для шаблонов Улыбаюсь

только насчёт идентификатора DELETE - в студии он, оказывается, уже зарезервирован, надо другой, но это мелочи.

ещё, зачем
dynamic_cast<T *>(this)

если можно
(T*)this

?
Записан

Вад
Модератор

ru
Offline Offline
Пол: Мужской

« Ответ #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
Деятель
Команда клуба

ru
Offline Offline
Пол: Мужской

« Ответ #27 : 31-01-2009 08:47 » 

Вад, я думаю, случая, когда dynamic_cast в этом примере вернёт NULL, не будет. Поскольку new T() должен привестись к Parent *. Если T - не потомок Parent, то компилятор на это обидится. Но в целом в данном примере dynamic_cast, конечно, надёжнее static_cast.

P.S. В С# можно явно указать, что параметр шаблона должен быть потомком какого-то класса. В C++, к сожалению, нельзя.
Записан

Программировать - значит понимать (К. Нюгард)
Невывернутое лучше, чем вправленное (М. Аврелий)
Многие готовы скорее умереть, чем подумать (Б. Рассел)
Вад
Модератор

ru
Offline Offline
Пол: Мужской

« Ответ #28 : 31-01-2009 09:03 » 

Вот ещё конструкция, с которой пока не пойму, что делать:
Код:
class Child4: public Child<Child2> {};
Если конструкторы приватные, и в качестве объекта Class4 будет на самом деле существовать объект Class2 - то, вроде, ничего страшного. Но поведение всё равно получается запутанное. А уж если можно явно инстанцировать Class4...

Правильно я понимаю, что из-за срезки нельзя по this установить, какой конкретно тип является спецификацией шаблона?
« Последнее редактирование: 31-01-2009 09:08 от Вад » Записан
Алексей++
глобальный и пушистый
Глобальный модератор

ru
Offline Offline
Сообщений: 13


« Ответ #29 : 31-01-2009 14:04 » 

думаю, с кастом тут вы оба преувеличили ) Правило использзование класса я напишу, а кто захочет экспериментировать - флаг ему в шаловливые ручки ) Ну ладно, static_cast оставлю
Записан

Страниц: [1] 2 3 4 ... 6   Вверх
  Печать  
 

Powered by SMF 1.1.21 | SMF © 2015, Simple Machines