иерархия классов с шаблонами.
От: Ilias  
Дата: 09.10.07 16:44
Оценка:
Похоже, я как-то совсем сильно торможу вечером, но не понимаю как (и можно ли) сделать такое.

Есть базовый класс-шаблон. Что-то типа

template<class _Type> class Base
{
public:

    static int id;
    _Type val;
  _Type getVal();
    void setVal(_Type& val);
    ...
}


В этом базовом классе нельзя определить все функции с _Type. Потому наследуемся от него пару раз примерно так:

class StringItem : public Base<std::string>
{
 void setVal(std::string& val);
 
}

class IntItem : public Base<int>
{
    void setVal(int& val);
}


Это, тсскзать, ядро. Дальше, в работе надо иметь возможность создавать что-то типа
class MyBlaBlaBlaStringItem : public StringItem
и
class My666IntItem : public IntItem

которые по большому счету от StringItem и IntItem отличаются только значением некоторых полей, например id.

И, под конец, всё это хотелось бы хранить в каком-то контейнере указателей на базовый тип. Как-то типа:

std::vector<Base*> items;
items.push_back(new MyBlaBlaBlaStringItem());
items.push_back(new My666IntItem());


Подскажите, пожалуйста, возможно ли реализовать что-то похожее?
... << RSDN@Home 1.1.4 stable SR1 rev. 568>>
Re: иерархия классов с шаблонами.
От: Ilias  
Дата: 09.10.07 17:35
Оценка:
Здравствуйте, Ilias, Вы писали:

I>Похоже, я как-то совсем сильно торможу вечером, но не понимаю как (и можно ли) сделать такое.


Реально торможу.

I>И, под конец, всё это хотелось бы хранить в каком-то контейнере указателей на базовый тип. Как-то типа:


I>
I>std::vector<Base*> items;
I>items.push_back(new MyBlaBlaBlaStringItem());
I>items.push_back(new My666IntItem());
I>


Про это сам понял что туплю. А остальное, всё же, кажется, что как-то возможно реализовать.
... << RSDN@Home 1.1.4 stable SR1 rev. 568>>
Re: иерархия классов с шаблонами.
От: Аноним  
Дата: 09.10.07 17:36
Оценка:
Здравствуйте, Ilias, Вы писали:

I>И, под конец, всё это хотелось бы хранить в каком-то контейнере указателей на базовый тип. Как-то типа:


I>
I>std::vector<Base*> items;
I>items.push_back(new MyBlaBlaBlaStringItem());
I>items.push_back(new My666IntItem());
I>


I>Подскажите, пожалуйста, возможно ли реализовать что-то похожее?


Почему нет — можно. главное — не пытаться потом кастить элементы вектора к иным типам. Во-первых, это "прощай ООП", а во-вторых — огрести от компилера/линкера можно по самое не балуйся.
И, конечно, не забудь виртуальные деструкторы
Re[2]: иерархия классов с шаблонами.
От: Ilias  
Дата: 09.10.07 17:38
Оценка:
Здравствуйте, <Аноним>, Вы писали:

А>Здравствуйте, Ilias, Вы писали:


I>>И, под конец, всё это хотелось бы хранить в каком-то контейнере указателей на базовый тип. Как-то типа:


I>>
I>>std::vector<Base*> items;
I>>items.push_back(new MyBlaBlaBlaStringItem());
I>>items.push_back(new My666IntItem());
I>>


I>>Подскажите, пожалуйста, возможно ли реализовать что-то похожее?


А>Почему нет — можно. главное — не пытаться потом кастить элементы вектора к иным типам. Во-первых, это "прощай ООП", а во-вторых — огрести от компилера/линкера можно по самое не балуйся.

А>И, конечно, не забудь виртуальные деструкторы

Насколько я понял, таки нельзя, т.к. Base — шаблонный тип и не понятно какие типы будут возвращать его наследники.
... << RSDN@Home 1.1.4 stable SR1 rev. 568>>
Re: иерархия классов с шаблонами.
От: Erop Россия  
Дата: 09.10.07 17:56
Оценка:
Здравствуйте, Ilias, Вы писали:

I>Подскажите, пожалуйста, возможно ли реализовать что-то похожее?

1) Нехорошо использовать идентификаторы, начинающиеся с подчерка и большой буквы. Можно и нарваться, например на макросы какеие-нибудь

2) Если прототипы неопределённых функций тебе известны уже в шаблоне, то можно их определить путём частичной/полной специализации методов, например так:
template<typename T> class MyClass {
    void Foo( T& ); // при использовании надо определить!
};
template<> void MyClass<int>::Foo( int& i ) { i++; }
template<> void MyClass<double>::Foo( int& i ) { i -= 0.5; }

Но гибче сделать как-то так:
namespace MyClassImpl {
    // определи для своего типа
    // void foo( T& );!!!
}   // namespace MyClassImpl
template<typename T> class CMyClass {
    void foo( T& i ) { MyClassImpl::foo( i ); }
};
namespace MyClassImpl {
    void foo( int& i ) { i++; }
    void foo( double& f ) { f -= 0.5; }
}   // namespace MyClassImpl


3) Наверное тебе нужно какие-то функции сделать виртуальными, а ещё в базу можно внедрить счётчик, чтобы можно было использовать интрузивные умные указатели

4) Вся конструкция какая-то очень сложная, если до утра не отпустит, объясни по-человесески, что тебе на самом деле-то нужно

5) Советую сделать 15-минутный перерыв, например пойти прогуляться или попить чаю

С уважением, и пожелаением идти отдохнуть
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[2]: иерархия классов с шаблонами.
От: Ilias  
Дата: 09.10.07 18:15
Оценка:
Здравствуйте, Erop, Вы писали:

E>Здравствуйте, Ilias, Вы писали:


I>>Подскажите, пожалуйста, возможно ли реализовать что-то похожее?

E>1) Нехорошо использовать идентификаторы, начинающиеся с подчерка и большой буквы. Можно и нарваться, например на макросы какеие-нибудь

Ну это я как в стл написал, не особо задумываясь. Понятно что это только пример — поправить не сложно.

E>2) Если прототипы неопределённых функций тебе известны уже в шаблоне, то можно их определить путём частичной/полной специализации методов, например так:
template<typename T> class MyClass {
E>    void Foo( T& ); // при использовании надо определить!
E>};
E>template<> void MyClass<int>::Foo( int& i ) { i++; }
E>template<> void MyClass<double>::Foo( int& i ) { i -= 0.5; }
E>


А, т.е. можно определять как-то отдельные функции у шаблонов? Тогда второй уровень (StringItem, IntItem ) вообще не нужны наверное.

E>Но гибче сделать как-то так:
namespace MyClassImpl {
E>    // определи для своего типа
E>    // void foo( T& );!!!
E>}   // namespace MyClassImpl
E>template<typename T> class CMyClass {
E>    void foo( T& i ) { MyClassImpl::foo( i ); }
E>};
E>namespace MyClassImpl {
E>    void foo( int& i ) { i++; }
E>    void foo( double& f ) { f -= 0.5; }
E>}   // namespace MyClassImpl


Вот это не совсем понял. Зачем два раза namespace MyClassImpl и почему оно гибче, чем первый вариант?

E>3) Наверное тебе нужно какие-то функции сделать виртуальными, а ещё в базу можно внедрить счётчик, чтобы можно было использовать интрузивные умные указатели


Для чего счетчик и указатели? Где их использовать?

E>4) Вся конструкция какая-то очень сложная, если до утра не отпустит, объясни по-человесески, что тебе на самом деле-то нужно


Попробую еще раз. Есть некий объект, с некими действиями, которыми с ним можно производить.
Объекты эти могут быть нескольких разных типов (строка, число и т.д.)
В одной и той же функции для объекта типа строка и объекта типа число может быть (и будет!) разный код.
Т.е. например в setVal(string& val) надо проверять, чтобы в строке не было знаков '-', а в setVal(int& val) надо проверять, чтобы val нацело делилось на 7.
Дальше, после того как определены типы объектов, надо завести несколько конкретных объектов заданного типа. Например объект типа строка с начальным значением этой строки "blablabla", или объект типа число с начальным значением 16.
Причем желательно чтобы конкретный объект типа число с начальным значением 16 и то же самое с начальным значением 17 были разными типами.

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


E>5) Советую сделать 15-минутный перерыв, например пойти прогуляться или попить чаю


E>С уважением, и пожелаением идти отдохнуть


Так всю жизнь проотдыхать можно. Фигарить надо, к сожалению
... << RSDN@Home 1.1.4 stable SR1 rev. 568>>
Re: иерархия классов с шаблонами.
От: Sashaka Россия  
Дата: 09.10.07 18:19
Оценка:
мда, бугога =)
(VC 6)

struct Base
{
    virtual void RunSet() = 0;
    virtual void RunGet() = 0;
};

template <class T>
struct Callback
{
    virtual void OnSet(T& param) = 0;
    virtual void OnGet(T param) = 0;
};

template <class T>
struct Holder : public Base
{
    Holder( Callback<T>* p)
        : m_p(p), m_val(T())
    { }

    void RunSet()
    {
        m_p->OnSet(m_val);
    }
    void RunGet()
    {
        m_p->OnGet(m_val);
    }

    Callback<T>* m_p;
    T m_val;
};

class xxx  : 
    public Callback<int>,
    public Callback<char>,
    public Callback<double>
{
public:
    xxx();
    virtual ~xxx();
    void Zzz()
    {
        Base* p[] = { new Holder<int>(this), new Holder<char>(this), new Holder<double>(this) } ;
        
        for (int i=0; i<3; ++i)
            p[i]->RunSet();

        for ( i=0; i<3; ++i)
            p[i]->RunGet();
    }
    void OnSet(int& val)
    {
        val = 5;
    }
    void OnSet(char& val)
    {
        val = 'a';
    }
    void OnSet(double& val)
    {
        val = 0.2;
    }
    
    void OnGet(int val)
    {
        TRACE("int val = %d\n", val);
    }
    void OnGet(char val)
    {
        TRACE("char val = %c\n",val);
    }
    void OnGet(double val)
    {
        TRACE("double val = %.2f\n",val);
    }
};

/*
.........
*/

xxx x;
x.Zzz();
Re[2]: иерархия классов с шаблонами.
От: Ilias  
Дата: 09.10.07 18:39
Оценка:
Здравствуйте, Sashaka, Вы писали:


S>мда, бугога =)

S>(VC 6)
S>
S>/*
S>.........
S>*/

S>


Да, спасибо, тоже вариант
... << RSDN@Home 1.1.4 stable SR1 rev. 568>>
Re[3]: иерархия классов с шаблонами.
От: Erop Россия  
Дата: 09.10.07 19:04
Оценка:
Здравствуйте, Ilias, Вы писали:

I>Ну это я как в стл написал, не особо задумываясь. Понятно что это только пример — поправить не сложно.

Ну форум разные люди читают...

I>Вот это не совсем понял. Зачем два раза namespace MyClassImpl и почему оно гибче, чем первый вариант?

1) Оно не два раза нужно, а столько сколько понадобится, потому, что мало ли какие ещё специализации где пригодятся... Если, вдруг, кто не в курсе, пространство имён можно пополнять сколько хочешь раз.
2) Так гибче потому, что можно использовать шаблоны функций для разных параметров, например так:
namespace MyClassImpl {
    void foo( int i ) { std::cout << "0x" << itohex( i ); }
    void foo( double d ) { std::cout << d; }
    void foo( const char* s ) { assert( s != 0 ); std::cout << "\"" << s << "\""; }
}   // namespace MyClassImpl


И теперь будут работать и MyClass<short> и MyClass<int>, и MyClass<const char*>, и MyClass<CString>...
правда нужна ли тебе такая гибкость --

I>Для чего счетчик и указатели? Где их использовать?

Чтобы было удобно владеть элементами вектора по указетелю.

I>Попробую еще раз. Есть некий объект, с некими действиями, которыми с ним можно производить.

I>Объекты эти могут быть нескольких разных типов (строка, число и т.д.)
I>В одной и той же функции для объекта типа строка и объекта типа число может быть (и будет!) разный код.
I>Т.е. например в setVal(string& val) надо проверять, чтобы в строке не было знаков '-', а в setVal(int& val) надо проверять, чтобы val нацело делилось на 7.
I>Дальше, после того как определены типы объектов, надо завести несколько конкретных объектов заданного типа. Например объект типа строка с начальным значением этой строки "blablabla", или объект типа число с начальным значением 16.
I>Причем желательно чтобы конкретный объект типа число с начальным значением 16 и то же самое с начальным значением 17 были разными типами.

А как ты планируешь звать SetValue у базы, еслине знаешь тип аргкмента? Как ты себе мыслишь клиентский код?

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


// этот класс лучше взятьиз библиотеки, но, так как сейчас поздно, напишу какой-нибудь, а библиотеку найдёшь потом сам.
template<typename T> class MyPtr { 
public:
    MyPtr() : ptr( 0 ) {}
    MyPtr( T* p ) : ptr( p ) { p->AddRef(); }
    ~MyPtr() { if( ptr != 0 ) ptr->Release(); }

    void operator = ( MyPtr other ) { Swap( other ); }
    void Swap( MyPtr& other ) { srd::swap( ptr, other.ptr ); }
    operator T* () const { return ptr; }

private:
    T* ptr;
};
namespace str { template<typename T> swap( MyPtr<T>& a1, MyPtr<T>& a2 ) { a1.Swap( a2 ); }
template<typename T> T& SafeRef( T* p ) { assert( p != 0 ); return *p; }

// Ну а теперь само изделие...
template<typename T> class MyValue;
class MyAnstractValue {
public:
    int AddRef() { return ++count; }
    int Release() { assert( count > 0 ); if( --count == 0 ) delete this; }
 
    template<typename T> MyValue<T>& GetValue() { return SafeRef( dynamic_cast<MyValue<T>*>( this ) ); }
    template<typename T> const MyValue<T>& GetValue() const 
        { return SafeRef( dynamic_cast<const MyValue<T>*>( this ) ); }
protected:
    virtual ~MyAnstractValue() {}
};

template<typename T> class MyValue : public: MyAbstractValue {
public:
    MyValue( const T& t ) { SetValue( t ); }
    void SetValue( const T& ); // Должно определить в специализации
    const T& GetValue() const { return value; }
private:
    T value;
};


Ну и теперь использование:
std::vector< MyPtr<MyAbstractValue> > values;
values.push_back( new MyValue<std::string>( "мама" ) );
values.push_back( new MyValue<int>( 7 ) );

I>Так всю жизнь проотдыхать можно. Фигарить надо, к сожалению
Ну-ну...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[4]: Поправочка!
От: Erop Россия  
Дата: 09.10.07 19:52
Оценка:
// этот класс лучше взятьиз библиотеки, но, так как сейчас поздно, напишу какой-нибудь, а библиотеку найдёшь потом сам.
template<typename T> class MyPtr { 
public:
    MyPtr() : ptr( 0 ) {}
    MyPtr( const MyPtr& other ) : ptr( other.ptr ) { if( ptr != 0 ) ptr->AddRef(); }
    MyPtr( T* p ) : ptr( p ) { assert( p != 0 ); ptr->AddRef(); }
    ~MyPtr() { if( ptr != 0 ) ptr->Release(); }

    // Внимание!!! Тип аргумента оператора присванивания такой не случайно!!!
    void operator = ( MyPtr other ) { Swap( other ); }
    void Swap( MyPtr& other ) { srd::swap( ptr, other.ptr ); }
    operator T* () const { return ptr; }

private:
    T* ptr;
};
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[4]: иерархия классов с шаблонами.
От: Ilias  
Дата: 10.10.07 05:11
Оценка:
Здравствуйте, Erop, Вы писали:

E>Здравствуйте, Ilias, Вы писали:



I>>Попробую еще раз. Есть некий объект, с некими действиями, которыми с ним можно производить.

I>>Объекты эти могут быть нескольких разных типов (строка, число и т.д.)
I>>В одной и той же функции для объекта типа строка и объекта типа число может быть (и будет!) разный код.
I>>Т.е. например в setVal(string& val) надо проверять, чтобы в строке не было знаков '-', а в setVal(int& val) надо проверять, чтобы val нацело делилось на 7.
I>>Дальше, после того как определены типы объектов, надо завести несколько конкретных объектов заданного типа. Например объект типа строка с начальным значением этой строки "blablabla", или объект типа число с начальным значением 16.
I>>Причем желательно чтобы конкретный объект типа число с начальным значением 16 и то же самое с начальным значением 17 были разными типами.

E>А как ты планируешь звать SetValue у базы, еслине знаешь тип аргкмента? Как ты себе мыслишь клиентский код?


Ну про это я уже повыше написал, что ступил и такого не надо. В смысле не надо чтобы они хранились где-нибудь в векторе указателей на базовый класс.
Тут, похоже, последний момент остался не освещенным. Вот этот

I>>Причем желательно чтобы конкретный объект типа число с начальным значением 16 и то же самое с начальным значением 17 были разными типами.


Так вообще можно сделать?
... << RSDN@Home 1.1.4 stable SR1 rev. 568>>
Re[5]: иерархия классов с шаблонами.
От: Erop Россия  
Дата: 10.10.07 06:38
Оценка:
Здравствуйте, Ilias, Вы писали:

I>Так вообще можно сделать?

Конечно можно.

1) Если речь идёт о числах, то можно так:
template<int I> struct UniqueType { enum { Val = I }; };


2) Если речь идёт о более сложных объектах, то сделать так, чтобы отслеживалась разность значений не получится, зато получится сделать так, чтобы каждому глобальному экземпляру твоих данных сопоставлялся в отдельный тип.
 struct MyDataBase {};
template<typename T> struct MyData : MyDataBase { T Data; };
template<const MyDataBase* P> struct UniqueType { const MyDataBase* const Data; };
template<const MyDataBase* P> const MyDataBase* const UniqueType<P>::Data = P;

//  Ну и используешь, теперь:
extern MyData<int> int17 = { 17 };  // Внешнее связывание принципиально!
extern MyData<const char*> strHiAll = { "Hi All!!!" };

UniqueType<&int17> XXX;
UniqueType<&strHiAll> YYY;
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.