Mixin в С++ или как получить доступ к безымянным предкам
От: gencoder  
Дата: 26.12.16 13:11
Оценка:
Вопрос по архитектуре.

Требуется предусмотреть расширение функциональности некоторого класса GLScene (не обязательно весь функциональный набор будет использован в финальной сборке).
Функциональность связана с рисованием некоторых сцен (тестовых).

Для этого создаем классы примеси Scene1Mix, Scene2Mix. Каждый из классов хранит указатель w на объект типа окно QGLWidget на котором будет выполняться рисование.

Вопрос: как правильно передать указателю w указатель на безымянный объект типа QGLWidget (т.е. на GLScene)?
Или подскажите более подходящее архитектурное решение.

class GLScene: public QGLWidget {
   ...
};

class Scene1Mixin {
   QGLWidget * w;
public:
   Scene1Mix( QGLWidget * _w ) { w = _w ; }
   draw1(){ w.glBegin(/* ...*/;}
}
class Scene2Mixin {
   QGLWidget * w;
public:
   Scene2Mix( QGLWidget * _w ) { w = _w ; }
   draw2(){ w.glBegin(/* ...*/;}
}

class Scene1: public GLScene, public Scene1Mix {
       Scene1( /*...*/ ) { /* w = this???; ...*/ draw1(); }
}


поправил заголовок, чтобы не вводить во смущение — Кодт
Отредактировано 27.12.2016 12:07 Кодт . Предыдущая версия . Еще …
Отредактировано 26.12.2016 13:26 gencoder . Предыдущая версия .
множественное наследование mixin
Re: Minix в С++ или как получить доступ к безымянным предкам
От: kov_serg Россия  
Дата: 26.12.16 14:00
Оценка:
Здравствуйте, gencoder, Вы писали:

G>Вопрос по архитектуре.


G>Требуется предусмотреть расширение функциональности некоторого класса GLScene (не обязательно весь функциональный набор будет использован в финальной сборке).

G>Функциональность связана с рисованием некоторых сцен (тестовых).

G>Для этого создаем классы примеси Scene1Mix, Scene2Mix. Каждый из классов хранит указатель w на объект типа окно QGLWidget на котором будет выполняться рисование.


G>Вопрос: как правильно передать указателю w указатель на безымянный объект типа QGLWidget (т.е. на GLScene)?

G>Или подскажите более подходящее архитектурное решение.

Уберите всё из конструктора и замените private на protected:
struct Scene1Mixin {
  Scene1Mix() { w=0; }
  virtual ~Scene1Mix() {}
  void setWidget(QGLWidget *w) { this->w=w; }
  void draw1(){ w->glBegin(/* ...*/;}
protected:
  QGLWidget * w;
};

struct Scene1 : Scene1Mixin {
  void draw2() { draw1(); w->...  }
};

Scene1* createScene1(QGLWidget * w) {
  Scene1 *s=new Scene1();
  s->setWidget(w);
  //s->someOtherMagic();
  return s;
}

И создание переложи на отдельный клас
Отредактировано 26.12.2016 14:02 kov_serg . Предыдущая версия .
Re[2]: Minix в С++ или как получить доступ к безымянным пред
От: gencoder  
Дата: 26.12.16 14:23
Оценка:
Хотелось бы сделать несколько иное в итоге: перед сборкой проекта просто сконструировать новый класс путем наследования базового класса и некоторой примеси (или нескольких примесей)
class FinalScene: public Scene, public Scene25Mixin, public Scene125Mixin 
{ 
public: 
    FinalScene(QWidget * p): Scene(p) { magicDrawFirst(); /*from 25*/ magicDrawSecond(); /*from 125*/ } 
};


P.S.1 просто "закинуть" в класс несколько методов (использование порождающих паттернов здесь не нужно, т.к. до сборки проекта будет набираться функционал из классов примесей)
P.S.2 без шаблонов похоже никак ?

Здравствуйте, kov_serg, Вы писали:
_
_>Уберите всё из конструктора и замените private на protected:
_>
_>struct Scene1Mixin {
_>  Scene1Mix() { w=0; }
_>  virtual ~Scene1Mix() {}
_>  void setWidget(QGLWidget *w) { this->w=w; }
_>  void draw1(){ w->glBegin(/* ...*/;}
_>protected:
_>  QGLWidget * w;
_>};

_>struct Scene1 : Scene1Mixin {
_>  void draw2() { draw1(); w->...  }
_>};

_>Scene1* createScene1(QGLWidget * w) {
_>  Scene1 *s=new Scene1();
  s->>setWidget(w);
_>  //s->someOtherMagic();
_>  return s;
_>}
_>

_>И создание переложи на отдельный клас
Отредактировано 26.12.2016 14:38 gencoder . Предыдущая версия .
Re[3]: Minix в С++ или как получить доступ к безымянным пред
От: kov_serg Россия  
Дата: 26.12.16 15:28
Оценка: 1 (1)
Здравствуйте, gencoder, Вы писали:

G>Хотелось бы сделать несколько иное в итоге: перед сборкой проекта просто сконструировать новый класс путем наследования базового класса и некоторой примеси (или нескольких примесей)

G>
G>class FinalScene: public Scene, public Scene25Mixin, public Scene125Mixin 
G>{ 
G>public: 
G>    FinalScene(QWidget * p): Scene(p) { magicDrawFirst(); /*from 25*/ magicDrawSecond(); /*from 125*/ } 
G>};
G>


G>P.S.1 просто "закинуть" в класс несколько методов (использование порождающих паттернов здесь не нужно, т.к. до сборки проекта будет набираться функционал из классов примесей)

G>P.S.2 без шаблонов похоже никак ?

Вот то что вы хотите, но лично бы я так не делал
#include <stdio.h>

struct Base {
    int handle;
    Base(int handle=0) : handle(handle) {}
};

struct Mix : virtual Base {};

struct Mix1 : Mix {
  void do1() { printf("1-%d\n",handle); }
};

struct Mix2 : Mix {
  void do2() { printf("2-%d\n",handle); }
};

struct Res : virtual Base,Mix1,Mix2 {
    Res(int handle) : Base(handle) { do1(); do2(); }
};

int main(int argc,char** argv) {
    Res res(123);
    return 0;
}
Отредактировано 26.12.2016 15:36 kov_serg . Предыдущая версия . Еще …
Отредактировано 26.12.2016 15:34 kov_serg . Предыдущая версия .
Отредактировано 26.12.2016 15:34 kov_serg . Предыдущая версия .
Re[4]: Minix в С++ или как получить доступ к безымянным пред
От: gencoder  
Дата: 26.12.16 15:59
Оценка:
Здравствуйте, kov_serg, Вы писали:

_>Вот то что вы хотите, но лично бы я так не делал

_>

_>struct Mix : virtual Base {};
_>


почему вы против mixin class (в смысле классов предоставляющих некоторый дополнительный интерфейс или функционал, и не предполагающий непосредственного инстанцирования)?
Re[5]: Minix в С++ или как получить доступ к безымянным пред
От: kov_serg Россия  
Дата: 26.12.16 18:35
Оценка:
Здравствуйте, gencoder, Вы писали:

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


_>>Вот то что вы хотите, но лично бы я так не делал

_>>

_>>struct Mix : virtual Base {};
_>>


G>почему вы против mixin class (в смысле классов предоставляющих некоторый дополнительный интерфейс или функционал, и не предполагающий непосредственного инстанцирования)?


Я не mixin-ов как в D, идея хорошая, но лично я против того треша и угара который генерирует компилятор C++ в случаях виртуального наследования.
Re: Minix в С++ или как получить доступ к безымянным предкам
От: Marty Пират https://www.youtube.com/channel/UChp5PpQ6T4-93HbNF-8vSYg
Дата: 26.12.16 19:16
Оценка:
Здравствуйте, gencoder, Вы писали:

Казалось бы, причем тут Minix? Даже заинтересовался, зашел глянуть, кто тут под неё пишет. А тут про mixin'ы оказывается речь идет
Маньяк Робокряк колесит по городу
Re: Minix в С++ или как получить доступ к безымянным предкам
От: antropolog  
Дата: 26.12.16 23:24
Оценка:
Здравствуйте, gencoder, Вы писали:

во-первых миксин лучше наследовать приватно. Во-вторых, никто не мешает передать this в конструктор предка.
class Scene1: public GLScene, private Scene1Mix {
       Scene1( /*...*/ ) : Scene1Mix(this) { /*...*/ draw1(); }
}
Re: Minix в С++ или как получить доступ к безымянным предкам
От: so5team https://stiffstream.com
Дата: 27.12.16 06:29
Оценка:
Здравствуйте, gencoder

Mixin-ы имеет смысл делать разве что в случае, когда методы mixin-а расширяют интерфейс целевого класса:
class Scene1Mixin {
public :
  void draw() {...}
  void clear() {...}
  ...
};

class Scene1 : public GLScene, public Scene1Mixin {
...
};

Scene1 sc1;
sc1.draw(); // Это Scene1Mixin::draw.


Если вам нужно именно это, и в mixin-классах нужно иметь ссылку на GLScene (а шаблонами вы пользоваться не хотите), то можно использовать виртуальные методы:

class Scene1Mixin {
protected :
  virtual GLScene & scene() = 0;
public :
  void draw() { GLScene & s = scene(); ...}
  void clear() { GLScene & s = scene(); ...}
  ...
};

class Scene1 : public GLScene, public Scene1Mixin {
protected :
  virtual GLScene & scene() { return *this; }
...
};


Если внутренности mixin-класса не должны становиться частью интерфейса целевого класса, то можно вообще обойтись без mixin-ов:
namespace scene1_impl {
void draw(GLScene & scene) { ... }
void clear(GLScene & scene) { ... }
...
}

class Scene1 : public GLScene {
public :
  Scene1() { using namespace scene1_impl; draw(*this); ... }
  ...
};
Re: Minix в С++ или как получить доступ к безымянным предкам
От: Кодт Россия  
Дата: 27.12.16 10:07
Оценка:
Здравствуйте, gencoder, Вы писали:

Есть ещё одна волшебная техника, называется CRTP
template<class Final> struct MixinBase {
  // downcast
  Final*       self()       { return static_cast<Final*>      (this); }
  Final const* self() const { return static_cast<Final const*>(this); }
};

template<class Final> struct GlMixinBase : MixinBase<Final> {
  // upcast не требует явного static_cast
  QGLWidget*       widget()       { return this->self(); }
  QGLWidget const* widget() const { return this->self(); }
};

.....

template<class Final> struct Mixin1 : GLMixinBase<Final> {
  void draw1() { this->widget()->glBegin(); ..... }
};
template<class Final> struct Mixin2 : GLMixinBase<Final> {
  void draw2() { this->widget()->glBegin(); ..... }
};

.....

class MyScene : public Mixin1<MyScene>, Mixin2<MyScene> ..... {
  ...
  void draw() { draw1(); draw2(); ...... }
  ...
};


Минусы — шаблоны, программирование мясом наружу.
Плюсы — никаких лишних косвенностей, — ни виртуальных функций, ни указателей.
Перекуём баги на фичи!
Re[2]: Minix в С++ или как получить доступ к безымянным предкам
От: kov_serg Россия  
Дата: 27.12.16 10:28
Оценка:
Здравствуйте, Кодт, Вы писали:

К>Есть ещё одна волшебная техника, называется CRTP

...
К>Минусы — шаблоны, программирование мясом наружу.
К>Плюсы — никаких лишних косвенностей, — ни виртуальных функций, ни указателей.
Вобщем как обычно, оптимальное решение выглядит не эстетично.
А эстетичное, не оптимально. Так и живём.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.