Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > C/C++: Общие вопросы > Связь класса с определённой структурой данных


Автор: eXtremal7 3.9.2009, 10:44
Предположим, есть набор объектов типа:
Код

class base {...};
class A : public base {...};
class B : public base {...};
class C : public base {,,,};


Задача состоит в том, чтобы была возможность доступа к определённой структуре данных(уникальной для A, B и C) во время выполнения. Для этого в базовый класс base можно добавить поле типа int index, создать массив структур, и получать нужную структуру по индексу. Однако при создании объектов A, B и C надо же как-то этот индекс определять то. Вот для этого и можно использовать список типов из библиотеки loki:

Код

typedef MakeTypelist<A, B, C>::Result AllTypes;


Тогда можно преобразовывать имя типа в индекс, но тогда как объявить массив структур, о котором говорилось выше? Разве что только так:

Код

//_objectDescription - статическая структура, которая должна быть отдельно
// описана для A, B и C
StructType AllStructures[Length<AllTypes>::value] = {
  TypeAt<AllTypes, 0>::Result::_objectDescription, 
  TypeAt<AllTypes, 1>::Result::_objectDescription,
  TypeAt<AllTypes, 2>::Result::_objectDescription
};


А конструкторы объектов будут выглядеть как-то так:

Код

A() : base(IndexOf<AllTypes, A>::value) {}


Решение вполне рабочее, но два момента совсем не нравится:
    Ручное указание всех элементов в структуре(TypeAt<AllTypes, 0>, TypeAt<AllTypes, 1> и.т.д.)
    Явное указание типа объекта в конструкторе(indexOf<AllTypes, A>)
Вот это хотелось бы как-то обойти, т.е. сделать так, чтобы список типов раскрывался автоматом, когда массив AllStructures объявляешь, а также чтоб в indexOf не надо было явно передавать тип - может как-нибудь можно использовать "this", ведь компилятору известен его тип..

Автор: mes 3.9.2009, 11:01
посмотрите паттерн визитор

Автор: Lazin 3.9.2009, 11:18
можно использовать специализацию классов вместо списков типов:

Код

template<class T>
class myTraits;


temlate<>
class myTraits<A>
{
public:
static StructType value;
};

template<>
StructType myTraits<A>::value = {...};


но, стоит задаться вопросом, зачем? ведь это:
Цитата(eXtremal7 @  3.9.2009,  10:44 Найти цитируемый пост)
Задача состоит в том, чтобы была возможность доступа к определённой структуре данных(уникальной для A, B и C) во время выполнения.
можно сделать с помощью статического члена класса A, B и С.  smile 

Автор: eXtremal7 3.9.2009, 11:42
Цитата(Lazin @  3.9.2009,  11:18 Найти цитируемый пост)
можно сделать с помощью статического члена класса A, B и С.  smile  

И как я смогу по указателю базового класса к ним добраться интересно? smile Разве что объявлять виртуальную функцию в A, B и C. Проверить, является ли base* X указателем на A, B или C можно будет только dynamic_cast, а это всё неэффективно.

Была идея хранить непосредственно указатель на структуру в базовом классе вместо индекса.. тогда список типов не нужен:

Код

class base {
private:
  StructType *value;
}


но появляется ограничение - весь код с этими классами должен собираться как shared library(.dll/.so), в противном случае в каждом исполняемом модуле будет своя копия структур.. может быть так что объекты будут одинаковые, а значения value - разные, поэтому проверить принадлежность указателя base к производным классам простым сравнением value нельзя.


Цитата(mes @  3.9.2009,  11:01 Найти цитируемый пост)
посмотрите паттерн визитор 

А это что, можно подробнее ?

Автор: zim22 3.9.2009, 11:57
Цитата(eXtremal7 @  3.9.2009,  11:42 Найти цитируемый пост)
А это что, можно подробнее ?

http://lmgtfy.com/?q=%D0%BF%D0%B0%D1%82%D1%82%D0%B5%D1%80%D0%BD+%D0%B2%D0%B8%D0%B7%D0%B8%D1%82%D0%BE%D1%80

Автор: Lazin 3.9.2009, 12:43
поскольку StructType один и тот-же для A, B и С, то можно добавить виртуальный метод(защищенный), который будет возвращать указатель на эту структуру и вызываться он будет из базового класса, это очевидный вариант

можно еще ввести класс - посредник, между классом А..С и базовым классом - 
Код

class Base
{
protected:
    virtual StructType& getStruct() = 0;
public:
    void foo()
    {
        StructType& s = getStruct();
    }
};

template<class Derived>
class BaseProxy : public Base
{
protected:
    StructType& getStruct()
    {
        return static_cast<Derived*>(this)->metadata;
    }
};

class A : public BaseProxy<A>// вместо Base
{
    template<class D> 
    friend class BaseProxy;

    static StructType metadata;
public:
};

StructType A::metadata = {...};

тут, шаблон BaseProxy, знает тип своего потомка, и может извлечь какие-либо специфичные для A данные, благодаря этому, не нужно вручную определять метод getStruct для каждого потомка

Цитата(eXtremal7 @  3.9.2009,  11:42 Найти цитируемый пост)
И как я смогу по указателю базового класса к ним добраться интересно?

а как-бы тебе в этом помогли списки типов?

Добавлено @ 12:44
Цитата(eXtremal7 @  3.9.2009,  11:42 Найти цитируемый пост)
Разве что объявлять виртуальную функцию в A, B и C.
кстати, твой первоначальный пример, как раз и является кривой эмуляцией вызова вирт. ф-ии, по сути, не по форме

Автор: Lazin 3.9.2009, 13:01
Цитата(Lazin @  3.9.2009,  12:43 Найти цитируемый пост)
тут, шаблон BaseProxy, знает тип своего потомка, и может извлечь какие-либо специфичные для A данные, благодаря этому, не нужно вручную определять метод getStruct для каждого потомка

в принципе, это можно сделать еще проще, и более обобщенно:
Код

class Base
{
protected:
    virtual StructType& getStruct() = 0;
public:
    void foo()
    {
        StructType& s = getStruct();
    }
};


struct proxy_all_access
{
    template<class T>
    static StructType& getMetadata(T&)
    {
        return T::metadata;
    }
};

template<class Derived>
class BaseProxy : public Base
{
protected:
    StructType& getStruct()
    {
        return proxy_all_access::getMetadata(static_cast<Derived&>(*this));
    }
};

class A : public BaseProxy<A>// вместо Base
{
    friend proxy_all_access;

    static StructType metadata;
public:
};

StructType A::metadata = {...};

с помощью структуры proxy_all_access, можно не только ссылку на статический член класса - потомка возвращать, а вообще, делать с потомком все что душе угодно, для этого нужно добавить соотв. методы в структуру.
еще один плюс, тому, кто реализует класс - потомок, достаточно написать friend proxy_all_access, вместо template<class T> friend ...; это лучше читается

Автор: eXtremal7 3.9.2009, 13:16
Цитата(Lazin @  3.9.2009,  12:43 Найти цитируемый пост)
кстати, твой первоначальный пример, как раз и является кривой эмуляцией вызова вирт. ф-ии, по сути, не по форме

Почему же кривой то, разве прочитать индекс из объекта будет не быстрее чем вызов виртуальной функции ? А есть сравнить производительность dynamic_cast со сравнением индекса: 
Код

if (base->ID == IndexOf<AllTypes, A>) {...}

Списки типов нужны чтобы сопоставить индекс каждому типу, это удобнее чем enum отдельный объявлять для этого.

Цитата(Lazin @  3.9.2009,  12:43 Найти цитируемый пост)
поскольку StructType один и тот-же для A, B и С, то можно добавить виртуальный метод(защищенный), который будет возвращать указатель на эту структуру и вызываться он будет из базового класса, это очевидный вариант

Если предполагается расширяемость, то это единственный путь, а вот если набор объектов жестко зафиксирован, почему бы и не оптимизировать доступ к структуре и проверку типов ?

Автор: Lazin 3.9.2009, 13:31
Цитата(eXtremal7 @  3.9.2009,  13:16 Найти цитируемый пост)
Почему же кривой то, разве прочитать индекс из объекта будет не быстрее чем вызов виртуальной функции ? А есть сравнить производительность dynamic_cast со сравнением индекса
прочитать индекс из объекта не намного быстрее, чем получить адрес вирт. ф-ии из VMT объекта, разница в один уровень косвенности при обращении к данным
в любом случае, это преждевременная оптимизация, к тому-же очень сомнительная smile

Добавлено через 4 минуты и 21 секунду
Цитата(eXtremal7 @  3.9.2009,  13:16 Найти цитируемый пост)
А есть сравнить производительность dynamic_cast со сравнением индекса: 

Код

if (base->ID == IndexOf<AllTypes, A>) {...}

каждый программист, рано или поздно пытается написать свою "быструю" замену dynamic_cast? smile
если правильно спроектировать программу,то dynamic_cast не нужен

Автор: eXtremal7 4.9.2009, 11:20
Цитата(Lazin @  3.9.2009,  13:31 Найти цитируемый пост)
если правильно спроектировать программу,то dynamic_cast не нужен

Далеко не всегда известен тип данных во время компиляции.. но если известен, то конечно dynamic_cast не нужен.
А там где нужен обычно и делают свои ускоренные реализации.

Автор: mes 4.9.2009, 12:28
Цитата(eXtremal7 @  4.9.2009,  10:20 Найти цитируемый пост)
Далеко не всегда известен тип данных во время компиляции.. но если известен, то конечно dynamic_cast не нужен.
А там где нужен обычно и делают свои ускоренные реализации. 

Lazin имел в виду, что dynamic_cast не используется не потому, что известен реальный тип объекта, а потому что есть другие способы реализации нужного взаимодействия с объектом,
и они не есть ускоренные реализации dynamic_casta.

Автор: eXtremal7 7.9.2009, 13:39
Цитата(mes @  4.9.2009,  12:28 Найти цитируемый пост)
Lazin имел в виду, что dynamic_cast не используется не потому, что известен реальный тип объекта, а потому что есть другие способы реализации нужного взаимодействия с объектом,и они не есть ускоренные реализации dynamic_casta.

А я говорю, что не всегда можно так организовать взаимодействие с объектом, что dynamic_cast будет не нужен, могу привести пару примеров: llvm.org и их набор шаблонов cast<>, dyn_cast<>, isa<>, ну а также Qt и их qobject_cast<> - всё это является ускоренными аналогами dynamic_cast-а и активно используется в этих проектах.

Автор: hente 7.9.2009, 14:30
гы....задача таже самая тока под Linux. Кропотливое но элементарное решение как было сказано выше с помощью паттерна  визитор.
Реализовав раз достаточно легко переносится на  другой проект...Где то год назад(даже не зная слова паттерн) сам придумывал мучился кое-как сделал, щас на это дело ушло пару часов, за одно и старую библиотеку переделалsmile smile 
Вообще хочу сказать что паттерны милое дело если грамотно делать то переносимость 100%я (Win<->Linux, без Qt), а времени скока экономит так ваще молчу....

К стате советую книгу "Приемы объектно орентированного проектирования" Э.Гамма,Р.Хелм,Р.Джонсон,Д.Влиссилес. smile 

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)