Модераторы: Daevaorn

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> "Умная" работа с переменными-членами класса 
:(
    Опции темы
W4FhLF
Дата 13.3.2009, 19:04 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


found myself
****


Профиль
Группа: Участник Клуба
Сообщений: 2831
Регистрация: 2.12.2006

Репутация: 20
Всего: 121



Приветствую всех.

Есть такой, упрощённый, случай:

Код

class Body:
{
public:
     virtual ~Body();
};

class Sphere: public Body
{
     double radius;
public:
     virtual ~Sphere();
};

class Cone: public Body
{
     double radius;
     double height;
public:
     virtual ~Cone();
};

class Parallelepiped: public Body
{
     double width;
     double height;
     double length;
public:
     virtual ~Parallelepiped();
};

void setParams(Body* body)
{
     // Здесь необходимо задать параметры
}

int main(int argc, char* argv[])
{
     Body* b = new Cone();
     
     setParams(b);
}


Как создать наиболее рациональную реализацию setParams? 

Может есть паттерны или средства метапрограммирования(из того же boost'a)? Т.е. смысл в том, чтобы не завязываться на конкретном типе объекта наследника, а работать с данными-членами более гибко. Допустим по их именам(перечисление, задание, считывание...). 

Это сообщение отредактировал(а) W4FhLF - 13.3.2009, 19:06


--------------------
"Бог умер" © Ницше
"Ницше умер" © Бог
PM ICQ   Вверх
azesmcar
Дата 13.3.2009, 19:19 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


uploading...
****


Профиль
Группа: Участник Клуба
Сообщений: 6291
Регистрация: 12.11.2004
Где: Армения

Репутация: 81
Всего: 211



т.е. setParams должен понять какой ему тип передали и задать соответствующие параметры???

а если написать виртуальную функцию, Body сделать абстрактным и вызывать его? Или еще проще, устанавливать параметры в конструкторе конкретного обьекта.. Задача немного неясна, что именно должна делать фунцкяи setParams ??
PM   Вверх
andrew_121
Дата 13.3.2009, 19:22 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Кодофей
****


Профиль
Группа: Завсегдатай
Сообщений: 3448
Регистрация: 3.1.2008

Репутация: 6
Всего: 33



    Заюзай enum в Body.


--------------------
Удалил аккаунт. Прощайте!
PM MAIL   Вверх
W4FhLF
Дата 13.3.2009, 19:43 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


found myself
****


Профиль
Группа: Участник Клуба
Сообщений: 2831
Регистрация: 2.12.2006

Репутация: 20
Всего: 121



Цитата(azesmcar @  13.3.2009,  19:19 Найти цитируемый пост)
а если написать виртуальную функцию, Body сделать абстрактным и вызывать его? Или еще проще, устанавливать параметры в конструкторе конкретного обьекта.. 


Это всё немного не то. 

Представь, что у нас 15 наследников Body и у каждого свой набор параметров. Допустим теперь, что в функции setParams тебе необходимо получить от пользователя необходимый набор параметров для переданного объекта body(ну в случае сферы ты печатаешь в консоли Radius и ждёшь ввода, в случае Cone печатаешь Radius, потом, после ввода радиуса, печатаешь Height и т.д.). Каковы твои действия? 


andrew_121, не понял.

Это сообщение отредактировал(а) W4FhLF - 13.3.2009, 19:44


--------------------
"Бог умер" © Ницше
"Ницше умер" © Бог
PM ICQ   Вверх
mes
Дата 13.3.2009, 19:56 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


любитель
****


Профиль
Группа: Участник Клуба
Сообщений: 7954
Регистрация: 14.1.2006

Репутация: 144
Всего: 250



 если кол-во операций стабильно, но не стабильна иерархия (неизвестно конечное кол-во типов), то лучше просто использовать виртуальные функции
 если ж наоборот иерархия классов  постоянна(все конечные типы заранее известны), а  общий интерфейс требует легкого расширения то смотрите паттерн визитор/двойная диспетчеризация. 

Это сообщение отредактировал(а) mes - 13.3.2009, 20:02


--------------------
PM MAIL WWW   Вверх
mes
Дата 15.3.2009, 15:44 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


любитель
****


Профиль
Группа: Участник Клуба
Сообщений: 7954
Регистрация: 14.1.2006

Репутация: 144
Всего: 250



вот немного с форума об этом:
http://forum.vingrad.ru/index.php?showtopic=234230
http://forum.vingrad.ru/index.php?showtopic=235416

Это сообщение отредактировал(а) mes - 15.3.2009, 15:47


--------------------
PM MAIL WWW   Вверх
AnLun
Дата 16.3.2009, 18:51 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 59
Регистрация: 3.1.2008

Репутация: нет
Всего: нет



Цитата(azesmcar @ 13.3.2009,  17:19)
т.е. setParams должен понять какой ему тип передали и задать соответствующие параметры???

а если написать виртуальную функцию, Body сделать абстрактным и вызывать его? Или еще проще, устанавливать параметры в конструкторе конкретного обьекта.. Задача немного неясна, что именно должна делать фунцкяи setParams ??

W4FhLF, а чем так плохо?
PM MAIL   Вверх
W4FhLF
Дата 20.3.2009, 11:26 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


found myself
****


Профиль
Группа: Участник Клуба
Сообщений: 2831
Регистрация: 2.12.2006

Репутация: 20
Всего: 121



AnLun, что именно? 


--------------------
"Бог умер" © Ницше
"Ницше умер" © Бог
PM ICQ   Вверх
Lazin
Дата 20.3.2009, 11:51 (ссылка)  | (голосов:1) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Завсегдатай
Сообщений: 3820
Регистрация: 11.12.2006
Где: paranoid oil empi re

Репутация: 41
Всего: 154



Цитата(W4FhLF @  13.3.2009,  19:04 Найти цитируемый пост)
Как создать наиболее рациональную реализацию setParams? 

Может есть паттерны или средства метапрограммирования(из того же boost'a)? Т.е. смысл в том, чтобы не завязываться на конкретном типе объекта наследника, а работать с данными-членами более гибко. Допустим по их именам(перечисление, задание, считывание...). 

думаю что никак, так-как не понятно, что должна делать ф-я setParams, какие параметры(причем одни и те-же) передавать объектам разных типов...
если нужно работать с геометрией, то я бы подумал о том, что-бы сделать вирт. ф-ю отвечающую за преобразования координат, которая в качестве параметра принимала-бы матрицу, на этой основе можно сделать любые преобразования, повороты, смещения и т.д.

Код

class Body
{
Body* transform(const Matrix& m) const = 0;
void transform_inplace(const Matrix& m) = 0;
...
};

Body* Cone::transform(const Matrix& m) const
{
  Cone* c = new Cone(...параметры нового конуса...);
  return c;
}

void Cone::transform_inplace(const Matrix& m)
{
  ...вычисляем новые параметры объекта...
}


Это сообщение отредактировал(а) Lazin - 20.3.2009, 11:52
PM MAIL Skype GTalk   Вверх
GoldFinch
Дата 20.3.2009, 11:53 (ссылка)  | (голосов:1) Загрузка ... Загрузка ... Быстрая цитата Цитата



****


Профиль
Группа: Завсегдатай
Сообщений: 2141
Регистрация: 30.11.2008

Репутация: 15
Всего: 26



а откуда setParams(Body*) возьмет значения параметров если они ей не передаются?
PM MAIL ICQ   Вверх
mes
Дата 20.3.2009, 12:22 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


любитель
****


Профиль
Группа: Участник Клуба
Сообщений: 7954
Регистрация: 14.1.2006

Репутация: 144
Всего: 250



Цитата(GoldFinch @  20.3.2009,  10:53 Найти цитируемый пост)
а откуда setParams(Body*) возьмет значения параметров если они ей не передаются? 


Цитата(W4FhLF @  13.3.2009,  18:43 Найти цитируемый пост)
Представь, что у нас 15 наследников Body и у каждого свой набор параметров. Допустим теперь, что в функции setParams тебе необходимо получить от пользователя необходимый набор параметров для переданного объекта body(ну в случае сферы ты печатаешь в консоли Radius и ждёшь ввода, в случае Cone печатаешь Radius, потом, после ввода радиуса, печатаешь Height и т.д.). Каковы твои действия? 


вот набросал условный пример работы :
Код


class FigureVisitor;

class Figure
{
    public:
     virtual ~Figure() {}
     virtual  void  Accept (FigureVisitor&) =0;
};
class Line : public Figure
{
    public:
        virtual  void  Accept (FigureVisitor&);
        void SetParam (const Point& pt1, const Point& pt2) {}

};

class Circle : public Figure
{
   public:
       virtual  void  Accept (FigureVisitor&);
       void SetParam (const Point& pt1, int Radius) {}
};

class Quad : public Figure
{
   public:
       virtual  void  Accept (FigureVisitor&);
       void SetParam (const Point& pt1, const Point& pt2) {}
};


class SetParamFromUserOperation : public FigureVisitor
{
     public:
        void Visit (Figure& fig) { fig.Accept (*this); }
        void Visit (Line & obj ) { ...  } // аналогично Circle
        void Visit (Circle& obj) { std::cout << "enter center & radius of circle"; Point pt; int Radius; cin>>pt; cin>>Radius; obj.SetParam(pt, Radius); }
        void Visit (Quad& obj)   { ... } // аналогично Circle
};

Line::  Accept (FigureVisitor& v) { v.Visit(*this); }
Circle::Accept (FigureVisitor& v) { v.Visit(*this); }
Quad::  Accept (FigureVisitor& v) { v.Visit(*this); }


int main ()
{
   Figure * fig = new Circle ();
   SetParamFromUserOperation ().Visit(*fig);
//...
   delete fig;
}



Это сообщение отредактировал(а) mes - 20.3.2009, 12:44


--------------------
PM MAIL WWW   Вверх
GoldFinch
Дата 20.3.2009, 12:36 (ссылка)    | (голосов:1) Загрузка ... Загрузка ... Быстрая цитата Цитата



****


Профиль
Группа: Завсегдатай
Сообщений: 2141
Регистрация: 30.11.2008

Репутация: 15
Всего: 26



setParams(Body*) и Body::setParams() это одно и тоже, только 2е писать приятнее
PM MAIL ICQ   Вверх
Lazin
Дата 20.3.2009, 12:38 (ссылка) |    (голосов:1) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Завсегдатай
Сообщений: 3820
Регистрация: 11.12.2006
Где: paranoid oil empi re

Репутация: 41
Всего: 154



Цитата(W4FhLF @  13.3.2009,  19:43 Найти цитируемый пост)
Представь, что у нас 15 наследников Body и у каждого свой набор параметров. Допустим теперь, что в функции setParams тебе необходимо получить от пользователя необходимый набор параметров для переданного объекта body(ну в случае сферы ты печатаешь в консоли Radius и ждёшь ввода, в случае Cone печатаешь Radius, потом, после ввода радиуса, печатаешь Height и т.д.). Каковы твои действия? 

тогда, как сказал mes, можно использовать либо паттерн visitor, либо обычную виртуальную ф-ю, но есть один ньюанс

наиболее правильный ОО дизайн в этом случае, не строить огромную иерархию классов, состоящих их одних и тех-же элементов, а использовать композицию, то-есть выделить набор базовых примитивов (например point, angle, length) и создавать более сложные объекты на основе примитивных (например point, line(point, point), curve(point, point, point, point), rect(point, point, point, point), circle(point, length) etc) и создать механизм для поучения набора примитивных объектов из которых состоит более сложный объект
в этом случае, можно будет придумать generic версию ф-ии setParams
например, у нас есть набор объектов: Line, Circle, Rect
нам нужно полиморфно с ними работать, сначала мы получаем первый объект Body(Line), и получаем список его параметров { "Begin":point, "End":point } и выводим на экран просьбу ввести сначала Begin, а затем End. Потом берем следующий объект Body(Circle) - {"Radius":length, "Center Coord.":point } и так далее..
В общем случае, нужна возможность получить список параметров, причем не только самих параметров, но и их атрибутов, таких как название, значение по умолчанию, ограничения, все зависит от задачи.

Это сообщение отредактировал(а) Lazin - 20.3.2009, 12:40
PM MAIL Skype GTalk   Вверх
GoldFinch
Дата 20.3.2009, 12:43 (ссылка)   | (голосов:1) Загрузка ... Загрузка ... Быстрая цитата Цитата



****


Профиль
Группа: Завсегдатай
Сообщений: 2141
Регистрация: 30.11.2008

Репутация: 15
Всего: 26



"печатаешь в консоли Radius и ждёшь ввода"
значит надо вводить классы Radius, Height, Width, Lenght
Код

class Dimention {
public:
double value;
virtual void Input()=0;
}
class Radius: public Dimention {
public: virtual void Input() { std::cout<<"Radius="; ... 


PM MAIL ICQ   Вверх
mes
Дата 20.3.2009, 12:47 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


любитель
****


Профиль
Группа: Участник Клуба
Сообщений: 7954
Регистрация: 14.1.2006

Репутация: 144
Всего: 250



Цитата(GoldFinch @  20.3.2009,  11:43 Найти цитируемый пост)
}
class Radius: public Dimention {
public: virtual void Input() { std::cout<<"Radius="; ... 

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

Это сообщение отредактировал(а) mes - 20.3.2009, 13:05


--------------------
PM MAIL WWW   Вверх
GoldFinch
Дата 20.3.2009, 13:12 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата



****


Профиль
Группа: Завсегдатай
Сообщений: 2141
Регистрация: 30.11.2008

Репутация: 15
Всего: 26



mes, не, в этом случае например в объекте Cone будут не 2 поля с разными именами, а массив или вектор из 2х элементов типа Dimention, для ввода которых надо будет обойти этот массив итератором вызывая Input() 
PM MAIL ICQ   Вверх
mes
Дата 20.3.2009, 13:18 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


любитель
****


Профиль
Группа: Участник Клуба
Сообщений: 7954
Регистрация: 14.1.2006

Репутация: 144
Всего: 250



Цитата(GoldFinch @  20.3.2009,  12:12 Найти цитируемый пост)
mes, не, в этом случае например в объекте Cone будут не 2 поля с разными именами, а массив или вектор из 2х элементов типа Dimention, для ввода которых надо будет обойти этот массив итератором вызывая Input()  

Вы не про то.. я про Ваш субъект Radius. В вашей компоновке операция Input жестко привязана к нему. 
Разницы нет кто из чего состоит. Суть в том что к иерархии субъектов в таком случае нельзя добавить новую операцию, в отличии от подхода с визитором.
(см. пример  с кодом)


Это сообщение отредактировал(а) mes - 20.3.2009, 13:31


--------------------
PM MAIL WWW   Вверх
Lazin
Дата 20.3.2009, 14:33 (ссылка)  | (голосов:1) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Завсегдатай
Сообщений: 3820
Регистрация: 11.12.2006
Где: paranoid oil empi re

Репутация: 41
Всего: 154



накидал пример на скорую руку
Код

#include <iostream>
#include <exception>
#include <boost/fusion/sequence.hpp>
#include <boost/fusion/algorithm.hpp>
#include <boost/fusion/container/vector.hpp>


struct length
{
    double value;
    double& operator[] (int ix)
    {
        if (ix != 0) throw std::runtime_error("length operator[] error, index out of range");
        return value;
    }
    length(double x) : value(x) {}
};

struct point
{
    double xyz[3];
    double& operator [] (int ix) 
    {
        if(ix < 0||ix > 2) throw std::runtime_error("point operator[] error, index out of range");
        return xyz[ix]; 
    }  
    point(double x, double y, double z)
    {
        xyz[0] = x;
        xyz[1] = y;
        xyz[2] = z;
    }
};

struct metadata
{
    std::string class_name;
    size_t tag;
    metadata(const char* name, size_t tag) : class_name(name), tag(tag) {}
};

struct BodyAccessor 
{
    virtual void operator () (point& p) const = 0;
    virtual void operator () (length& p) const = 0;
    virtual void operator () (metadata& p) const = 0;
};

struct Body
{
    virtual ~Body() = 0;
    virtual void access(const BodyAccessor&) = 0;
};
Body::~Body() {}



template<class Sequence>
struct GenericBody : Body
{
    metadata traits;
    Sequence sequence;

    typedef Sequence param_type;

    virtual void access(const BodyAccessor& a)
    {
        a(traits);
        boost::fusion::for_each(sequence, a);
    }

    GenericBody( const Sequence& s, const metadata& m ) 
        : traits(m)
        , sequence(s)
    {
    }
};
typedef GenericBody< boost::fusion::vector<point, point> > Line;
typedef GenericBody< boost::fusion::vector<point, length>> Circle;

struct BodyFactory
{
    Body* create_line(const point& a, const point& b)
    {
        return new Line( Line::param_type(a, b), metadata("Line", 1) );
    }

    Body* create_circle(const point& center, const length& radius)
    {
        return new Circle( Circle::param_type(center, radius), metadata("Circle", 1) );
    }
};

struct InputAccessor : BodyAccessor
{
    void operator () (metadata& p) const
    {
        std::cout << "Input params for " << p.class_name << std::endl;
    }
    void operator () (point& p) const
    {
        std::cout << "enter point" << std::endl;
        std::cin >> p[0] >> p[1] >> p[2];
    }
    void operator () (length& p) const
    {
        std::cout << "enter length" << std::endl;
        std::cin >> p[0];
    }
};


struct OutputAccessor : BodyAccessor
{
    void operator () (metadata& p) const
    {
        std::cout << "Print params for " << p.class_name << std::endl;
    }
    void operator () (point& p) const
    {
        std::cout << "point(" << p[0] << ", " << p[1] << ", " << p[2] << ")" << std::endl;;
    }
    void operator () (length& p) const
    {
        std::cout << "length(" << p[0] << ")" << std::endl;
    }
};


int main()
{
    BodyFactory factory;
    Body* line = factory.create_line( point(0, 0, 0), point(0, 0, 0) );
    Body* circle = factory.create_circle( point(0, 0, 0), length(10) );
    InputAccessor in;
    OutputAccessor out;

    std::cout << " line " << std::endl; 
    line->access(out);
    line->access(in);
    line->access(out);
    
    std::cout << " circle " << std::endl; 
    circle->access(out);
    circle->access(in);
    circle->access(out);
    system("pause");
}

суть в том, чтобы вместо иерархии объектов, использовать композицию, в данном случае я использовал boost::fusion для хранения структуры объекта. Примитивные типы для данного примера это point, line и metadata. Объект класса metadata хранит информацию о классе объекта. В принципе, можно добавить метаданные для каждого параметра, каждого объекта, для этого нужно вместо boost::fusion::vector использовать что-то вроде boost::fusison::map, но мне лень это делать)

Добавлено через 3 минуты и 2 секунды
в принципе, объект класса metadata в каждом новом объекте GenericBody  это лишний оверхэд, достаточно создать один объект metadata на каждый "класс"(вариант инстанциирования GenericBody) и в GenericBody хранить только ссылку на такой глобальный объект

Добавлено через 6 минут и 40 секунд
примерно так:
Код

#include <iostream>
#include <exception>
#include <boost/fusion/sequence.hpp>
#include <boost/fusion/algorithm.hpp>
#include <boost/fusion/container/vector.hpp>


struct length
{
    double value;
    double& operator[] (int ix)
    {
        if (ix != 0) throw std::runtime_error("length operator[] error, index out of range");
        return value;
    }
    length(double x) : value(x) {}
};

struct point
{
    double xyz[3];
    double& operator [] (int ix) 
    {
        if(ix < 0||ix > 2) throw std::runtime_error("point operator[] error, index out of range");
        return xyz[ix]; 
    }  
    point(double x, double y, double z)
    {
        xyz[0] = x;
        xyz[1] = y;
        xyz[2] = z;
    }
};

struct metadata
{
    std::string class_name;
    size_t tag;
    metadata(const char* name, size_t tag) : class_name(name), tag(tag) {}
};

struct BodyAccessor 
{
    virtual void operator () (point& p) const = 0;
    virtual void operator () (length& p) const = 0;
    virtual void operator () (const metadata& p) const = 0;
};

struct Body
{
    virtual ~Body() = 0;
    virtual void access(const BodyAccessor&) = 0;
};
Body::~Body() {}



template<class Sequence>
struct GenericBody : Body
{
    const metadata &traits;
    Sequence sequence;

    typedef Sequence param_type;

    virtual void access(const BodyAccessor& a)
    {
        a(traits);
        boost::fusion::for_each(sequence, a);
    }

    GenericBody( const Sequence& s, const metadata& m ) 
        : traits(m)
        , sequence(s)
    {
    }
};
typedef GenericBody< boost::fusion::vector<point, point> > Line;
typedef GenericBody< boost::fusion::vector<point, length>> Circle;

struct BodyFactory
{
    metadata class_line, class_circle; 
    BodyFactory()
        : class_line("line", 1)
        , class_circle("circle", 2)
    {
    }
    Body* create_line(const point& a, const point& b)
    {
        return new Line( Line::param_type(a, b), class_line );
    }

    Body* create_circle(const point& center, const length& radius)
    {
        return new Circle( Circle::param_type(center, radius), class_circle );
    }
};

struct InputAccessor : BodyAccessor
{
    void operator () (const metadata& p) const
    {
        std::cout << "Input params for " << p.class_name << std::endl;
    }
    void operator () (point& p) const
    {
        std::cout << "enter point" << std::endl;
        std::cin >> p[0] >> p[1] >> p[2];
    }
    void operator () (length& p) const
    {
        std::cout << "enter length" << std::endl;
        std::cin >> p[0];
    }
};


struct OutputAccessor : BodyAccessor
{
    void operator () (const metadata& p) const
    {
        std::cout << "Print params for " << p.class_name << std::endl;
    }
    void operator () (point& p) const
    {
        std::cout << "point(" << p[0] << ", " << p[1] << ", " << p[2] << ")" << std::endl;;
    }
    void operator () (length& p) const
    {
        std::cout << "length(" << p[0] << ")" << std::endl;
    }
};


int main()
{
    BodyFactory factory;
    Body* line = factory.create_line( point(0, 0, 0), point(0, 0, 0) );
    Body* circle = factory.create_circle( point(0, 0, 0), length(10) );
    InputAccessor in;
    OutputAccessor out;

    std::cout << " line " << std::endl; 
    line->access(out);
    line->access(in);
    line->access(out);
    
    std::cout << " circle " << std::endl; 
    circle->access(out);
    circle->access(in);
    circle->access(out);
    system("pause");
}



PM MAIL Skype GTalk   Вверх
GoldFinch
Дата 20.3.2009, 14:44 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата



****


Профиль
Группа: Завсегдатай
Сообщений: 2141
Регистрация: 30.11.2008

Репутация: 15
Всего: 26



Lazin, выглядит страшно, а работает наверное еще страшнее,
с посетителем куда как лучше, и накладных расходов на память 0
к тому же то что у вас  - несколько не соответствует задаче ТС, нужно хранить размеры а не координаты
PM MAIL ICQ   Вверх
Lazin
Дата 20.3.2009, 14:54 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Завсегдатай
Сообщений: 3820
Регистрация: 11.12.2006
Где: paranoid oil empi re

Репутация: 41
Всего: 154



Цитата(GoldFinch @  20.3.2009,  14:44 Найти цитируемый пост)
с посетителем куда как лучше, и накладных расходов на память 0

а это и есть паттерн visitor

Цитата(GoldFinch @  20.3.2009,  14:44 Найти цитируемый пост)
нужно хранить размеры а не координаты

мой пример можно обобщит хоть на хранение килограммов и амперов, смысл в том, что у нас есть набор параметров разных типов(в данном случае это координаты точек и длины), и из них мы строим еще больше классов(линии и окружности), каждый такой класс(GenericBody) имеет ссылку на описатель класса и набор параметров, набор параметров хранится в boost::fusion::vector, что эквивалентно использованию обычной структуры, так-что утверждение
Цитата(GoldFinch @  20.3.2009,  14:44 Найти цитируемый пост)
выглядит страшно, а работает наверное еще страшнее

неверно smile

Добавлено через 5 минут и 19 секунд
по памяти это +4 байта на каждый объект GenericBody и еще +4 байта на указатель на таблицу вирт. функций, по сравнению с обычными структурами
по скорости, это один вызов виртуальной ф-ии(в случае если visitor получает указатель на весь класс сразу), против N вызовов(N - количество параметров класса), разница будет незаметна...
PM MAIL Skype GTalk   Вверх
vinter
Дата 20.3.2009, 15:04 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Explorer
****


Профиль
Группа: Завсегдатай
Сообщений: 2735
Регистрация: 1.4.2006
Где: Н.Новгород

Репутация: 13
Всего: 56



Цитата(Lazin @  20.3.2009,  15:33 Найти цитируемый пост)
накидал пример на скорую руку

 smile  страшно представить, что ты пишешь не на скорую  smile 


--------------------
Мой блог
PM MAIL WWW   Вверх
mes
Дата 20.3.2009, 15:15 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


любитель
****


Профиль
Группа: Участник Клуба
Сообщений: 7954
Регистрация: 14.1.2006

Репутация: 144
Всего: 250



Lazin, Ваш пример хорош, когда Body является просто контейнером параметров. Однако фигура это не просто набор параметров..
к примеру, попробуйте в Вашем примере, применить операцию Draw () smile 

Это сообщение отредактировал(а) mes - 20.3.2009, 15:30


--------------------
PM MAIL WWW   Вверх
Lazin
Дата 20.3.2009, 15:33 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Завсегдатай
Сообщений: 3820
Регистрация: 11.12.2006
Где: paranoid oil empi re

Репутация: 41
Всего: 154



мой пример хорош, когда код должен хорошо масштабировться в сторону добавления новых классов, так-как все "методы"(объекты клaсса BodyAccessor) не нужно реализовывать для каждого класса. В принципе можно получить любую функциональность, но, если таких классов мало, то проще использовать стандартный подход.
PM MAIL Skype GTalk   Вверх
mes
Дата 20.3.2009, 15:37 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


любитель
****


Профиль
Группа: Участник Клуба
Сообщений: 7954
Регистрация: 14.1.2006

Репутация: 144
Всего: 250



Цитата(Lazin @  20.3.2009,  14:33 Найти цитируемый пост)
мой пример хорош, когда код должен хорошо масштабировться в сторону добавления новых классов, так-как все "методы"(объекты клaсса BodyAccessor) не нужно реализовывать для каждого класса. В принципе можно получить любую функциональность, но, если таких классов мало, то проще использовать стандартный подход.

согласен, но сказал бы так:  его хорошо использовать наряду со стандартным подходом, в случае если предполагается также добавление новых классов (например посредством плагина).
т.е условная иерархия такая : Line, Cirlce, Quad, Generic. 

Это сообщение отредактировал(а) mes - 20.3.2009, 15:39


--------------------
PM MAIL WWW   Вверх
Lazin
Дата 20.3.2009, 16:05 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Завсегдатай
Сообщений: 3820
Регистрация: 11.12.2006
Где: paranoid oil empi re

Репутация: 41
Всего: 154



Цитата(mes @  20.3.2009,  15:37 Найти цитируемый пост)
т.е условная иерархия такая : Line, Cirlce, Quad, Generic.

я бы не сказал, тогда придется вручную реализовывать метод accept для всех вариантов: Line, Circle, Quad etc, в общем тогда проще использовать стандартный подход, и быдлокодить всю иерархию игнорируя тот факт, что объекты Line, Circle, Quad etc, состоят из небольшого набора элементов.
Конечно мой пример далек от совершенства, его еще можно сильно улучшить. Но с точки зрения архитектуры, он лучше чем иерархия классов.
PM MAIL Skype GTalk   Вверх
mes
Дата 20.3.2009, 16:21 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


любитель
****


Профиль
Группа: Участник Клуба
Сообщений: 7954
Регистрация: 14.1.2006

Репутация: 144
Всего: 250



Цитата(Lazin @  20.3.2009,  15:05 Найти цитируемый пост)
я бы не сказал, тогда придется вручную реализовывать метод accept для всех вариантов: Line, Circle, Quad etc, в общем тогда проще использовать стандартный подход, и быдлокодить всю иерархию игнорируя тот факт, что объекты Line, Circle, Quad etc, состоят из небольшого набора элементов.
Конечно мой пример далек от совершенства, его еще можно сильно улучшить. Но с точки зрения архитектуры, он лучше чем иерархия классов. 

покажите пример удобной реализации функции Draw() для Вашего примера, и если ее недостатки не перекроют достоинства Вашей модели, тогда я с Вами соглашусь.  smile 

Это сообщение отредактировал(а) mes - 20.3.2009, 16:22


--------------------
PM MAIL WWW   Вверх
Lazin
Дата 20.3.2009, 17:16 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Завсегдатай
Сообщений: 3820
Регистрация: 11.12.2006
Где: paranoid oil empi re

Репутация: 41
Всего: 154



Цитата(mes @  20.3.2009,  16:21 Найти цитируемый пост)
покажите пример удобной реализации функции Draw() для Вашего примера, и если ее недостатки не перекроют достоинства Вашей модели, тогда я с Вами соглашусь.

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

#include <iostream>
#include <exception>
#include <boost/fusion/sequence.hpp>
#include <boost/fusion/algorithm.hpp>
#include <boost/fusion/container/vector.hpp>


struct DrawContext
{
};

struct length
{
    double value;
    double& operator[] (int ix)
    {
        if (ix != 0) throw std::runtime_error("length operator[] error, index out of range");
        return value;
    }
    length(double x) : value(x) {}
};

struct point
{
    double xyz[3];
    double& operator [] (int ix) 
    {
        if(ix < 0||ix > 2) throw std::runtime_error("point operator[] error, index out of range");
        return xyz[ix]; 
    }  
    point(double x, double y, double z)
    {
        xyz[0] = x;
        xyz[1] = y;
        xyz[2] = z;
    }
};

struct metadata
{
    std::string class_name;
    size_t tag;
    metadata(const char* name, size_t tag) : class_name(name), tag(tag) {}
};

struct BodyAccessor 
{
    virtual void operator () (point& p) const = 0;
    virtual void operator () (length& p) const = 0;
    virtual void operator () (const metadata& p) const = 0;
};

struct Body
{
    virtual ~Body() = 0;
    virtual void access(const BodyAccessor&) = 0;
    virtual void draw(DrawContext& dc) = 0;
};
Body::~Body() {}



template<class Sequence>
struct GenericBody : Body
{
    const metadata &traits;
    Sequence sequence;

    typedef Sequence param_type;

    virtual void access(const BodyAccessor& a)
    {
        a(traits);
        boost::fusion::for_each(sequence, a);
    }

    GenericBody( const Sequence& s, const metadata& m ) 
        : traits(m)
        , sequence(s)
    {
    }
    virtual void draw(DrawContext& dc);
};

///line class
typedef GenericBody< boost::fusion::vector<point, point> > Line;
void Line::draw(DrawContext& dc)
{
    std::cout << "MoveTo(" 
              << boost::fusion::at_c<0>(sequence)[0] << ", " 
              << boost::fusion::at_c<0>(sequence)[1] << ");" << std::endl;
    std::cout << "LineTo(" 
              << boost::fusion::at_c<1>(sequence)[0] << ", " 
              << boost::fusion::at_c<1>(sequence)[1] << ");" << std::endl;
}


typedef GenericBody< boost::fusion::vector<point, length> > Circle;
void Circle::draw(DrawContext& dc)
{
    std::cout << "MoveTo(" 
              << boost::fusion::at_c<0>(sequence)[0] << ", " 
              << boost::fusion::at_c<0>(sequence)[1] << ");" << std::endl;
    std::cout << "DrawCircle(" 
              << boost::fusion::at_c<1>(sequence)[0] << ");" << std::endl;
}

struct BodyFactory
{
    metadata class_line, class_circle; 
    BodyFactory()
        : class_line("line", 1)
        , class_circle("circle", 2)
    {
    }
    Body* create_line(const point& a, const point& b)
    {
        return new Line( Line::param_type(a, b), class_line );
    }

    Body* create_circle(const point& center, const length& radius)
    {
        return new Circle( Circle::param_type(center, radius), class_circle );
    }
};

struct InputAccessor : BodyAccessor
{
    void operator () (const metadata& p) const
    {
        std::cout << "Input params for " << p.class_name << std::endl;
    }
    void operator () (point& p) const
    {
        std::cout << "enter point" << std::endl;
        std::cin >> p[0] >> p[1] >> p[2];
    }
    void operator () (length& p) const
    {
        std::cout << "enter length" << std::endl;
        std::cin >> p[0];
    }
};


struct OutputAccessor : BodyAccessor
{
    void operator () (const metadata& p) const
    {
        std::cout << "Print params for " << p.class_name << std::endl;
    }
    void operator () (point& p) const
    {
        std::cout << "point(" << p[0] << ", " << p[1] << ", " << p[2] << ")" << std::endl;;
    }
    void operator () (length& p) const
    {
        std::cout << "length(" << p[0] << ")" << std::endl;
    }
};


int main()
{
    BodyFactory factory;
    Body* line = factory.create_line( point(0, 0, 0), point(0, 0, 0) );
    Body* circle = factory.create_circle( point(0, 0, 0), length(10) );
    InputAccessor in;
    OutputAccessor out;

    std::cout << " line " << std::endl; 
    line->access(out);
    line->access(in);
    line->access(out);
    
    std::cout << " circle " << std::endl; 
    circle->access(out);
    circle->access(in);
    circle->access(out);

    DrawContext dc;
    circle->draw(dc);
    line->draw(dc);
    system("pause");
}


Добавлено через 41 секунду
но, можно это сделать и по другому)
PM MAIL Skype GTalk   Вверх
mes
Дата 20.3.2009, 17:40 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


любитель
****


Профиль
Группа: Участник Клуба
Сообщений: 7954
Регистрация: 14.1.2006

Репутация: 144
Всего: 250



Цитата(Lazin @  20.3.2009,  16:16 Найти цитируемый пост)
но, можно это сделать и по другому) 

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


--------------------
PM MAIL WWW   Вверх
Lazin
Дата 20.3.2009, 17:47 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Завсегдатай
Сообщений: 3820
Регистрация: 11.12.2006
Где: paranoid oil empi re

Репутация: 41
Всего: 154



я думал не специализировать для каждого GenericBody ф-ю draw, а просто реализовать ее в виде отдельного объекта и передавать ее в GenericBody в качестве параметра шаблона, сейчас покажу...

Добавлено через 10 минут и 35 секунд
примерно так...
Код

#include <iostream>
#include <exception>
#include <boost/fusion/sequence.hpp>
#include <boost/fusion/algorithm.hpp>
#include <boost/fusion/container/vector.hpp>



struct length
{
    double value;
    double& operator[] (int ix)
    {
        if (ix != 0) throw std::runtime_error("length operator[] error, index out of range");
        return value;
    }
    const double& operator[] (int ix) const
    {
        if (ix != 0) throw std::runtime_error("length operator[] error, index out of range");
        return value;
    }
    length(double x) : value(x) {}
};

struct point
{
    double xyz[3];
    double& operator [] (int ix)
    {
        if(ix < 0||ix > 2) throw std::runtime_error("point operator[] error, index out of range");
        return xyz[ix]; 
    }  
    const double& operator [] (int ix) const
    {
        if(ix < 0||ix > 2) throw std::runtime_error("point operator[] error, index out of range");
        return xyz[ix]; 
    }  
    point(double x, double y, double z)
    {
        xyz[0] = x;
        xyz[1] = y;
        xyz[2] = z;
    }
};

struct DrawContext
{
    void LineTo(const point& p)
    {
        std::cout << "LineTo(" << p[0] << ", " << p[1] << ");" << std::endl;
    }

    void MoveTo(const point& p)
    {
        std::cout << "MoveTo(" << p[0] << ", " << p[1] << ");" << std::endl;
    }

    void DrawCircle(const length& l)
    {
        std::cout << "DrawCircle(" << l[0] << ");" << std::endl;
    }
};

struct metadata
{
    std::string class_name;
    size_t tag;
    metadata(const char* name, size_t tag) : class_name(name), tag(tag) {}
};

struct BodyAccessor 
{
    virtual void operator () (point& p) const = 0;
    virtual void operator () (length& p) const = 0;
    virtual void operator () (const metadata& p) const = 0;
};

struct Body
{
    virtual ~Body() = 0;
    virtual void access(const BodyAccessor&) = 0;
    virtual void draw(DrawContext& dc) = 0;
};
Body::~Body() {}



template<class Sequence, class DrawFn>
struct GenericBody : Body
{
    const metadata &traits;
    Sequence sequence;

    typedef Sequence param_type;

    virtual void access(const BodyAccessor& a)
    {
        a(traits);
        boost::fusion::for_each(sequence, a);
    }

    GenericBody( const Sequence& s, const metadata& m ) 
        : traits(m)
        , sequence(s)
    {
    }
    virtual void draw(DrawContext& dc)
    {
        DrawFn draw_fn;
        draw_fn(dc, sequence);
    }
};

typedef boost::fusion::vector<point, point>  line_sequence_type;
typedef boost::fusion::vector<point, length> circle_sequence_type;

struct DrawLine
{
    void operator()(DrawContext& dc, const line_sequence_type& line)
    {
        dc.MoveTo(boost::fusion::at_c<0>(line));
        dc.LineTo(boost::fusion::at_c<1>(line));
    }
};

struct DrawCircle
{
    void operator()(DrawContext& dc, const circle_sequence_type& circle)
    {
        dc.MoveTo(boost::fusion::at_c<0>(circle));
        dc.DrawCircle(boost::fusion::at_c<1>(circle));
    }
};
///line class
typedef GenericBody< line_sequence_type, DrawLine > Line;


typedef GenericBody< circle_sequence_type, DrawCircle > Circle;

struct BodyFactory
{
    metadata class_line, class_circle; 
    BodyFactory()
        : class_line("line", 1)
        , class_circle("circle", 2)
    {
    }
    Body* create_line(const point& a, const point& b)
    {
        return new Line( Line::param_type(a, b), class_line );
    }

    Body* create_circle(const point& center, const length& radius)
    {
        return new Circle( Circle::param_type(center, radius), class_circle );
    }
};

struct InputAccessor : BodyAccessor
{
    void operator () (const metadata& p) const
    {
        std::cout << "Input params for " << p.class_name << std::endl;
    }
    void operator () (point& p) const
    {
        std::cout << "enter point" << std::endl;
        std::cin >> p[0] >> p[1] >> p[2];
    }
    void operator () (length& p) const
    {
        std::cout << "enter length" << std::endl;
        std::cin >> p[0];
    }
};


struct OutputAccessor : BodyAccessor
{
    void operator () (const metadata& p) const
    {
        std::cout << "Print params for " << p.class_name << std::endl;
    }
    void operator () (point& p) const
    {
        std::cout << "point(" << p[0] << ", " << p[1] << ", " << p[2] << ")" << std::endl;;
    }
    void operator () (length& p) const
    {
        std::cout << "length(" << p[0] << ")" << std::endl;
    }
};


int main()
{
    BodyFactory factory;
    Body* line = factory.create_line( point(0, 0, 0), point(0, 0, 0) );
    Body* circle = factory.create_circle( point(0, 0, 0), length(10) );
    InputAccessor in;
    OutputAccessor out;

    std::cout << " line " << std::endl; 
    line->access(out);
    line->access(in);
    line->access(out);
    
    std::cout << " circle " << std::endl; 
    circle->access(out);
    circle->access(in);
    circle->access(out);

    DrawContext dc;
    circle->draw(dc);
    line->draw(dc);
    system("pause");
}

теперь не обязательно реализовывать метод GenericBody::draw, функторы, занимающиеся отрисовкой фигур можно использовать повторно, либо написать обобщенную версию такого функтора smile 
PM MAIL Skype GTalk   Вверх
Lazin
Дата 20.3.2009, 18:03 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Завсегдатай
Сообщений: 3820
Регистрация: 11.12.2006
Где: paranoid oil empi re

Репутация: 41
Всего: 154



в принципе, так можно добавить любую операцию, зависящую от всех данных сразу, например преобразование координат, в то-же время, можно писать обобщенные версии ф-ий, которые работают не со всем объектом целиком, а с его частями, например сериализация, интроспекция. Что-бы написать, к примеру, ф-ю считающую количество вершин геом. фигуры, нужно написать всего один функтор.
PM MAIL Skype GTalk   Вверх
mes
Дата 20.3.2009, 19:25 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


любитель
****


Профиль
Группа: Участник Клуба
Сообщений: 7954
Регистрация: 14.1.2006

Репутация: 144
Всего: 250



Цитата(Lazin @  20.3.2009,  17:03 Найти цитируемый пост)
в принципе, так можно добавить любую операцию, зависящую от всех данных сразу, например преобразование координат, в то-же время, можно писать обобщенные версии ф-ий,


Ради одних преимуществ, Вы абсолютно потеряли другие, а ведь ничто не мешало пользоваться и тем и другим.

Начнем с того что Ваш_метод :
1. открывает приватные переменные и разрешает кому угодно что угодно с ними делать.
2. ведет к потери индивидуальности каждого класса
3. и при всем этом не предоставляет разделение операций от субъектов.

Цитата(Lazin @  20.3.2009,  16:47 Найти цитируемый пост)
теперь не обязательно реализовывать метод GenericBody::draw, функторы, занимающиеся отрисовкой фигур можно использовать повторно, либо написать обобщенную версию такого функтора smile  

да использование внешних функций/функторов как прообраз операции это хорошо. Однако Ваш_метод затрудняет написание и поощряет опечатки.. 






--------------------
PM MAIL WWW   Вверх
Lazin
Дата 20.3.2009, 20:09 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Завсегдатай
Сообщений: 3820
Регистрация: 11.12.2006
Где: paranoid oil empi re

Репутация: 41
Всего: 154



Цитата(mes @  20.3.2009,  19:25 Найти цитируемый пост)
1. открывает приватные переменные и разрешает кому угодно что угодно с ними делать.
Согласен, но, я писал этот код наспех, и использовал кругом структуры, тупо ради того, что-бы не писать private, protected, public в самих классах и при наследовании. Ну и поскольку это просто набросок, я не думал о том, какие переменные должны быть закрытыми, а какие нет, я просто сделал все открытым, что-бы мне было проще это все писать.

Цитата(mes @  20.3.2009,  19:25 Найти цитируемый пост)
2. ведет к потери индивидуальности каждого класса

Что под этим имеется ввиду? Да, все классы будут сгенерированы компилятором,  то как они бдут работать можно настроить. А можно просто отнаследовать свой класс от Body, и сделать их него настоящую индивидуальность smile 

Цитата(mes @  20.3.2009,  19:25 Найти цитируемый пост)
3. и при всем этом не предоставляет разделение операций от субъектов

Совсем не сложно сделать метод draw - функцией, или реализовать тот-же самый паттерн visitor, но только для всего объекта, а не для его частей.

Цитата(mes @  20.3.2009,  19:25 Найти цитируемый пост)
Ради одних преимуществ, Вы абсолютно потеряли другие, а ведь ничто не мешало пользоваться и тем и другим

привиди пример, как бы ты это сделал, может я чего-то не понимаю...?
PM MAIL Skype GTalk   Вверх
mes
Дата 20.3.2009, 21:32 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


любитель
****


Профиль
Группа: Участник Клуба
Сообщений: 7954
Регистрация: 14.1.2006

Репутация: 144
Всего: 250



Цитата(Lazin @  20.3.2009,  19:09 Найти цитируемый пост)
но, я писал этот код наспех, и использовал кругом структуры, тупо ради того, что-бы не писать private, protected, public в самих классах и при наследовании. Ну и поскольку это просто набросок, я не думал о том, какие переменные должны быть закрытыми, а какие нет, я просто сделал все открытым, что-бы мне было проще это все писать.

нее... Я не о том, что все через public написано.. это понятно что демонстрационное, а о том что все функции оперируют внутренним состоянием фигуры


Цитата(Lazin @  20.3.2009,  19:09 Найти цитируемый пост)
привиди пример, как бы ты это сделал, может я чего-то не понимаю...? 

вобщем возмущения 
 из за typedef :

Цитата(Lazin @  20.3.2009,  16:16 Найти цитируемый пост)
typedef GenericBody< boost::fusion::vector<point, point> > Line;


из за пользования приватными знаниями :
Цитата(Lazin @  20.3.2009,  16:47 Найти цитируемый пост)
struct DrawLine
{
    void operator()(DrawContext& dc, const line_sequence_type& line)
    {
        dc.MoveTo(boost::fusion::at_c<0>(line));


опять из за typedef, что приведет к затруднению перегрузки:
Цитата(Lazin @  20.3.2009,  16:47 Найти цитируемый пост)
typedef boost::fusion::vector<point, point>  line_sequence_type;


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

Цитата(Lazin @  20.3.2009,  13:33 Найти цитируемый пост)
struct InputAccessor : BodyAccessor
{
    void operator () (const metadata& p) const
    {
        std::cout << "Input params for " << p.class_name << std::endl;
    }



Цитата(Lazin @  20.3.2009,  19:09 Найти цитируемый пост)
привиди пример, как бы ты это сделал, может я чего-то не понимаю...? 


оставить возможности Body только для сериализации и ей подобной.
получать типы фигур не typedef, a наследованием.
тайпдефить fusion::vector внутри класса фигуры, которой он принадлежит
и разгружать ее данные через функции.

т.е так 
Код

class FCircle : public Figure
{
        typedef  boost::fusion::vector<point, length> data_t;
       data_t   m_data;
  public:
      int Radius () { return ..at_c<1>(m_data); }
      int Center () { return ..at_c<0>(m_data); }
      void Draw (.. dc) { dc.DrawCircle (Center(), Radius()); }
};


ну а Inputer может быть привязан не к самим фигурам , а к их составляющем; но без допуска к внутренностям объекта.
например так:
Код

struct Inputer
{
   virtual void Аcquaint (metadata&) =0;
   virtual int GetRadus () =0;
   virtual int GetLength()=0;
   virtual int GetCenter()=0;
};

struct ConsoleInputer : Inputer
{
    virtual void Аcquaint (metadata& data)  { cout<< "Enter data for " << data.name()<<endl; }
   virtual int GetRadus ()  { int rad: cout <<"Radius : " ; cin >>rad; return rad; } ;
   virtual int GetLength()  {..};
  ..
}



Это сообщение отредактировал(а) mes - 20.3.2009, 21:37


--------------------
PM MAIL WWW   Вверх
Lazin
Дата 22.3.2009, 00:19 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Завсегдатай
Сообщений: 3820
Регистрация: 11.12.2006
Где: paranoid oil empi re

Репутация: 41
Всего: 154



я немного поковырял этот код, улучшил его, но не так как говорил mes smile 
в общем, я вынес всю шаблонную магию в отдельный класс, от которого можно наследовать
ф-ии DrawCircle, и DrawLine, теперь не получают ссылку на sequence, вместо этого они принимают обычный набор параметров, пользователь теперь может писать такие функции:
Код

struct DrawLine
{
    void operator()(const DrawContext& dc, const point& begin, const point& end)
    {
        std::cout << "DrawLine" << std::endl;
        dc.MoveTo(begin);
        dc.LineTo(end);
    }
};

получать переменные класса можно так-же как и у boost::fusion::vector, с помощью at_c
класс GenericBody теперь выглядит намного проще
Код

template<class Sequence, class DrawFn>
struct GenericShape : Shape, composite_object<Sequence>
{
    const metadata &traits;
    typedef Sequence param_type;

    GenericShape( const Sequence& s, const metadata& m )
        : composite_object<Sequence>(s)
        , traits(m)
    {
    }

    virtual void access(const BodyAccessor& a)
    {
        a(traits);
        this->for_each(a);
    }

    virtual void draw(DrawContext& dc)
    {
        DrawFn fn;
        this->apply(fn, dc);
    }
};

весь код я решил не выкладывать, вместо этого, код можно получить из моего репозитория на bitbucket - composite_object

Добавлено через 3 минуты и 17 секунд
ps
еще можно сделать так, что-бы конструктор composite_object-a принимал не ForwardSequence, а обычный набор параметров, а потом запаковывал его в последовательность
в идеале, пользователь класса не дожен вообще уметь пользоваться boost::fusion
PM MAIL Skype GTalk   Вверх
mes
Дата 22.3.2009, 00:52 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


любитель
****


Профиль
Группа: Участник Клуба
Сообщений: 7954
Регистрация: 14.1.2006

Репутация: 144
Всего: 250



Цитата(Lazin @  21.3.2009,  23:19 Найти цитируемый пост)
ф-ии DrawCircle, и DrawLine, теперь не получают ссылку на sequence, вместо этого они принимают обычный набор параметров, пользователь теперь может писать такие функции:

ага , к самим структурам претензий нет, однако  порядок парамeтров все равно жестко прописан на sequence. 
т.е если вдруг функции нужен только один параметр, она все равно должна иметь в определении всю  очередность.


Цитата(Lazin @  21.3.2009,  23:19 Найти цитируемый пост)
вынес всю шаблонную магию в отдельный класс, 

в шаблонной магии тоже ничего не режет глаз, однако main.cc все так же и остался полон зависимостей и ограничений.
(не буду повторяться какие, потому что основные замечания описаны в предыдущем посте) smile

Это сообщение отредактировал(а) mes - 22.3.2009, 00:55


--------------------
PM MAIL WWW   Вверх
Lazin
Дата 22.3.2009, 14:21 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Завсегдатай
Сообщений: 3820
Регистрация: 11.12.2006
Где: paranoid oil empi re

Репутация: 41
Всего: 154



Цитата(mes @  22.3.2009,  00:52 Найти цитируемый пост)
main.cc все так же и остался полон зависимостей и ограничений.

эти ограничения не на пустом месте появились, за все нужно платить
в принципе, можно порождать новые классы с помощью наследования, а не typedef 
Код

struct Rect : Shape, composite_object< boost::fusion::vector< point, point > >
{
    const metadata& traits;
    Rect(const point& p1, const point& p2, const metadata& m) 
        : composite_object<boost::fusion::vector<point, point> >( boost::fusion::make_vector(p1, p2) )
        , traits(m)
    {
    }

    point& first()
    {
        return at_c<0>(*this);
    }

    point& second()
    {
        return at_c<1>(*this);
    }

    virtual void access(const BodyAccessor& a)
    {
        a(traits);
        this->for_each(a);
    }
    virtual void draw(DrawContext& dc)
    {
        dc.DrawRect(first(), second());
    }
};

но писанины больше...

Цитата(mes @  20.3.2009,  21:32 Найти цитируемый пост)
получать типы фигур не typedef, a наследованием

я не вижу каких-либо проблемм с typedef smile 
Цитата(mes @  22.3.2009,  00:52 Найти цитируемый пост)
однако  порядок парамeтров все равно жестко прописан на sequence. 
т.е если вдруг функции нужен только один параметр, она все равно должна иметь в определении всю  очередность.

можно поступить так, как в предидущем примере, но степень обобщенности в этом случае ниже... придется многие ф-ии писать ручками, т.е один класс, одна ф-я, и нельзя их комбинировать... 
в случае, если потомков Shape/Body очень много, это может быть не так удобно.. с другой стороны передача нескольких лишних параметров по ссылке, вряд-ли вызовает проблеммы с производительностью, так-что, я думаю что все ок...

Добавлено через 33 секунды
зы
обновил код в репозитории, хотя изменений там мало
PM MAIL Skype GTalk   Вверх
mes
Дата 22.3.2009, 14:44 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


любитель
****


Профиль
Группа: Участник Клуба
Сообщений: 7954
Регистрация: 14.1.2006

Репутация: 144
Всего: 250



Цитата(Lazin @  22.3.2009,  13:21 Найти цитируемый пост)
с другой стороны передача нескольких лишних параметров по ссылке, вряд-ли вызовает проблеммы с производительностью, так-что, я думаю что все ок...

о производительности тут речи не шло smile шло лишь об удобстве пользователя.


Цитата(Lazin @  22.3.2009,  13:21 Найти цитируемый пост)
ожно поступить так, как в предидущем примере, но степень обобщенности в этом случае ниже... придется многие ф-ии писать ручками, т.е один класс, одна ф-я, и нельзя их комбинировать...

Наш спор в основном вызван тем, что Ваш_метод мы рассматриваем применительно к иерархии фигур. 
Дело в том что у примитивов фигур очень мало действительно общего, и обобщение на уровне их состояния (приватных данных) только вызывает путаницу.

Цитата(Lazin @  22.3.2009,  13:21 Найти цитируемый пост)
придется многие ф-ии писать ручками, т.е один класс, одна ф-я, и нельзя их комбинировать...

да каркас класса придется писать ручками, но лично я считаю это, применительно к выше-обсуждаемой задаче, преимуществом.
Очень неудобно работать с одним и тем же интерфейсом с Линией, Углом, Трапецией и Кубом. 
Но это не мешает нам комбинировать их функции. Для этого достаточно разбить на двa условных уровня :
 функции/функторы и классы их использующее (как в принципе у Вас и обстоит дела с рисованием)


Цитата(Lazin @  22.3.2009,  13:21 Найти цитируемый пост)

я не вижу каких-либо проблемм с typedef smile 

проблема не с typedef,  а с абсолютной унификацией (потерей индивидуальности) интерфейса (опять же оговорюсж, что лишь применительно к нашей задаче)

Цитата(Lazin @  22.3.2009,  13:21 Найти цитируемый пост)

эти ограничения не на пустом месте появились, за все нужно платить

ну а как иначе.. просто из двух зол выбирают меньшее smile

и еще раз замечу, что основное замечание, было к InputAccessor`у, ради которого в принципе и была применена технология комбинирования.

Добавлено через 5 минут и 8 секунд
Цитата(Lazin @  20.3.2009,  17:03 Найти цитируемый пост)
Что-бы написать, к примеру, ф-ю считающую количество вершин геом. фигуры, нужно написать всего один функтор. 

Как понял с помощью BodyAccessora ? ну и что вернет Ваш функтор применительно к окружности ?! ;)


--------------------
PM MAIL WWW   Вверх
Lazin
Дата 22.3.2009, 15:54 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Завсегдатай
Сообщений: 3820
Регистрация: 11.12.2006
Где: paranoid oil empi re

Репутация: 41
Всего: 154



Цитата(mes @  22.3.2009,  14:44 Найти цитируемый пост)
Как понял с помощью BodyAccessora ? ну и что вернет Ваш функтор применительно к окружности ?! ;) 

это просто не удачный пример, вот и все
Цитата(mes @  22.3.2009,  14:44 Найти цитируемый пост)
и еще раз замечу, что основное замечание, было к InputAccessor`у, ради которого в принципе и была применена технология комбинирования.

ну, смотря для чего его использовать, если для сериализации, и подобных сериализации задач, то проблемм нет, для более интелектуальных задач это использовать уже сложнее
можно организовать что-то вроде двойной диспетчеризации, передавать в BodyAccessor, не только параметры, но и ссылку на сам объект, в этом случае можно будет перегрузить операции для разных объектов
PM MAIL Skype GTalk   Вверх
mes
Дата 22.3.2009, 16:50 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


любитель
****


Профиль
Группа: Участник Клуба
Сообщений: 7954
Регистрация: 14.1.2006

Репутация: 144
Всего: 250



Цитата(Lazin @  22.3.2009,  14:54 Найти цитируемый пост)
ну, смотря для чего его использовать, если для сериализации, и подобных сериализации задач, то проблемм нет,

Цитата(mes @  20.3.2009,  20:32 Найти цитируемый пост)
оставить возможности Body только для сериализации и ей подобной.

smile

Цитата(Lazin @  22.3.2009,  14:54 Найти цитируемый пост)
для более интелектуальных задач это использовать уже сложнее
можно организовать что-то вроде двойной диспетчеризации, передавать в BodyAccessor, не только параметры, но и ссылку на сам объект, в этом случае можно будет перегрузить операции для разных объектов 

зачем усложнять, когда стандартным подходом это решается нагляднее и проще. smile

ну а  в общем мне кажется, что мы уже в принципе пришли к "общему знаменателю" по этой ветке темы
smile


Это сообщение отредактировал(а) mes - 22.3.2009, 16:51


--------------------
PM MAIL WWW   Вверх
Страницы: (3) [Все] 1 2 3 
Ответ в темуСоздание новой темы Создание опроса
Правила форума "С++:Общие вопросы"
Earnest Daevaorn

Добро пожаловать!

  • Черновик стандарта C++ (за октябрь 2005) можно скачать с этого сайта. Прямая ссылка на файл черновика(4.4мб).
  • Черновик стандарта C (за сентябрь 2005) можно скачать с этого сайта. Прямая ссылка на файл черновика (3.4мб).
  • Прежде чем задать вопрос, прочтите это и/или это!
  • Здесь хранится весь мировой запас ссылок на документы, связанные с C++ :)
  • Не брезгуйте пользоваться тегами [code=cpp][/code].
  • Пожалуйста, не просите написать за вас программы в этом разделе - для этого существует "Центр Помощи".
  • C++ FAQ

Если Вам понравилась атмосфера форума, заходите к нам чаще! С уважением, Earnest Daevaorn

 
0 Пользователей читают эту тему (0 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | C/C++: Общие вопросы | Следующая тема »


 




[ Время генерации скрипта: 0.1068 ]   [ Использовано запросов: 22 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.