side virtual method implementation
От: c-smile Канада http://terrainformatica.com
Дата: 12.02.18 19:38
Оценка:
Есть вот такая иерархия классов

class AbstractFoo {
  virtual int do_something() = 0;
}

class Manipula {}

class Bar : public AbstractFoo, public Manipula {}
class Zap : public AbstractFoo, public Manipula {}


Понятно что все классы выше являются абстрактными.

Теперь нужно соорудить Concrete Bar и Zap добавив к ним имплементацию int do_something()

Наивный подход примерно такой:

#if WINDOWS 
class AbstractFooImpl {
  int do_something() { return 0; }
}
#elif MACOS
  ...
#endif

class ConcreteBar : public Bar, AbstractFooImpl {}  
class ConcreteZap : public Zap, AbstractFooImpl {}


но он не работает, т.к. AbstractFoo::do_something() и AbstractFooImpl::do_something() есть разные сущности.

Каким образом это сделать более кошерно ?

Пока два варианта видится : virtual inheritance и #define простихоспидя. Что еще можно придумать?
Re: side virtual method implementation
От: rg45 СССР  
Дата: 12.02.18 21:09
Оценка: 42 (2) +3
Здравствуйте, c-smile, Вы писали:

CS>Есть вот такая иерархия классов

CS>Понятно что все классы выше являются абстрактными.
CS>Теперь нужно соорудить Concrete Bar и Zap добавив к ним имплементацию int do_something()
CS>Наивный подход примерно такой:
CS>но он не работает, т.к. AbstractFoo::do_something() и AbstractFooImpl::do_something() есть разные сущности.
CS>Каким образом это сделать более кошерно ?

Я бы предложил переименовать как-нибудь AbstractFooImpl::do_something. А потом подмешивать AbstractFooImpl при помощи дополнительного шаблонного класса:


class AbstractFoo {
  virtual int do_something() = 0;
};

class Manipula {};

class Bar : public AbstractFoo, public Manipula {};
class Zap : public AbstractFoo, public Manipula {};

#if WINDOWS 
class AbstractFooImpl {
  int do_something_impl() { return 0; }
};
#elif MACOS
  ...
#endif

template <typename Base>
class AbstractFooImplMixer : public Base, public AbstractFooImpl {
  int do_something() override {
    return AbstractFooImpl::do_something_impl();
  }
};

class ConcreteBar : public AbstractFooImplMixer<Bar> { };  
class ConcreteZap : public AbstractFooImplMixer<Zap> { };
--
Не можешь достичь желаемого — пожелай достигнутого.
Отредактировано 12.02.2018 21:15 rg45 . Предыдущая версия . Еще …
Отредактировано 12.02.2018 21:14 rg45 . Предыдущая версия .
Отредактировано 12.02.2018 21:13 rg45 . Предыдущая версия .
Отредактировано 12.02.2018 21:10 rg45 . Предыдущая версия .
Re[2]: side virtual method implementation
От: c-smile Канада http://terrainformatica.com
Дата: 12.02.18 22:01
Оценка:
Здравствуйте, rg45, Вы писали:

R>Я бы предложил переименовать как-нибудь AbstractFooImpl::do_something. А потом подмешивать AbstractFooImpl при помощи дополнительного шаблонного класса:



А чем такой mixer лучше виртуального наследования:

class AbstractFoo {
  virtual int do_something() = 0;
}

class Manipula {}

class Bar : public virtual AbstractFoo, public Manipula {}
class Zap : public virtual AbstractFoo, public Manipula {}

class ConcreteFoo : public virtual AbstractFoo {
  int do_something() { return 0; }
}

class ConcreteBar : public Bar, ConcreteFoo {}  
class ConcreteZap : public Zap, ConcreteFoo {}


?
Отредактировано 12.02.2018 22:04 c-smile . Предыдущая версия .
Re[3]: side virtual method implementation
От: rg45 СССР  
Дата: 12.02.18 22:38
Оценка: 45 (3)
Здравствуйте, c-smile, Вы писали:

CS>А чем такой mixer лучше виртуального наследования:


Просто как альтернативный вариант. Ромбовидное и/или виртуальное наследование имеет свои издержки и некоторые разработчики всеми способами избегают их использования. Как пример, можно почитать здесь.
--
Не можешь достичь желаемого — пожелай достигнутого.
Отредактировано 12.02.2018 22:40 rg45 . Предыдущая версия .
Re: side virtual method implementation
От: Engler Беларусь  
Дата: 13.02.18 12:23
Оценка:
А если вот так?

// Original
// ---------------------------------
class AbstractFoo 
{
    virtual int do_something() = 0;
};

class Manipula {};

class Bar : public AbstractFoo, public Manipula {};
class Zap : public AbstractFoo, public Manipula {};

// ---------------------------------

#if 1

class WinFooImplZap : Zap
{
    virtual int do_something() override { return 0; }
};

#endif

class ConcreteZap : public WinFooImplZap
{
    // we inherit impl here from WinFooImplZap
};
Re[2]: side virtual method implementation
От: c-smile Канада http://terrainformatica.com
Дата: 13.02.18 15:44
Оценка:
Здравствуйте, Engler, Вы писали:

E>А если вот так?


E>
E>// Original
E>// ---------------------------------
E>class AbstractFoo 
E>{
E>    virtual int do_something() = 0;
E>};

E>class Manipula {};

E>class Bar : public AbstractFoo, public Manipula {};
E>class Zap : public AbstractFoo, public Manipula {};

E>// ---------------------------------

E>#if 1

E>class WinFooImplZap : Zap
E>{
E>    virtual int do_something() override { return 0; }
E>};

E>#endif

E>class ConcreteZap : public WinFooImplZap
E>{
E>    // we inherit impl here from WinFooImplZap
E>};
E>


С одним классом понятно, но в моем примере их два (а в реале больше).
Смысл в том чтобы иметь одну имплементацию ConcreteFoo и добавлять её (mixin) к нужным классам.
Re[3]: side virtual method implementation
От: Аноним Россия  
Дата: 13.02.18 16:12
Оценка: 44 (1)
Здравствуйте, c-smile, Вы писали:

CS>С одним классом понятно, но в моем примере их два (а в реале больше).

CS>Смысл в том чтобы иметь одну имплементацию ConcreteFoo и добавлять её (mixin) к нужным классам.

так а сделай этот один класс шаблонным параметром и указывай явно при описании Concrete?
#include <iostream>
using namespace std;


class AbstractFoo {
public:
  virtual int do_something() = 0;
};

class Manipula {};

class Bar : public AbstractFoo, public Manipula {};
class Zap : public AbstractFoo, public Manipula {};

#if 1
template <class Base>
class AbstractFooImpl : public Base {
  int do_something() { return 0; }
};
#endif

class ConcreteBar : public AbstractFooImpl<Bar> {};
class ConcreteZap : public AbstractFooImpl<Zap> {};

int main() {

  AbstractFoo *af;
  af = new ConcreteBar;
  af->do_something();
  
  af = new ConcreteZap;
  af->do_something();
  
  return 0;
}
Re[4]: side virtual method implementation
От: c-smile Канада http://terrainformatica.com
Дата: 13.02.18 20:07
Оценка:
Здравствуйте, Аноним, Вы писали:

А>так а сделай этот один класс шаблонным параметром и указывай явно при описании Concrete?


Ситуация реально там несколько более закрученная.

Это вот не компилируется (есс-но):

class iwindow {
public:
  int n;
  iwindow() : n(42) {}
  virtual bool foo() = 0;
};

template<typename Base> 
class iwindow_impl: public Base {
public:
  virtual bool foo() override { return true; }
};

class a : public iwindow {
};

class b : public a, public iwindow_impl<b> {
public:
  b() { n = 44; }
};

int main()
{
  b* binst = new b();
  printf("b %d %d\n", binst->foo(), sizeof(b) );
  return 0;
}


Нужно доп. прослойка:

class iwindow {
public:
  int n;
  iwindow() : n(42) {}
  virtual bool foo() = 0;
};

template<typename Base> 
class iwindow_impl: public Base {
public:
  virtual bool foo() override { return true; }
};

class a : public iwindow {
};

class a_bis : public a {
public:
  a_bis() { n = 44; }
};

typedef iwindow_impl<a_bis> b;

int main()
{
  b* binst = new b();
  printf("b %d %d\n", binst->foo(), sizeof(b) );
  return 0;
}
Re: side virtual method implementation
От: rean  
Дата: 13.02.18 21:52
Оценка:
deleted
Отредактировано 22.04.2019 9:24 deleted2 . Предыдущая версия . Еще …
Отредактировано 13.02.2018 22:22 deleted2 . Предыдущая версия .
Re: side virtual method implementation
От: reversecode google
Дата: 13.02.18 22:01
Оценка:
проблема в дизайне
так никто не делает, если подразумевается много платформенная оконная система

либо поизучайте тот же QT / WxWidget
Re[2]: side virtual method implementation
От: c-smile Канада http://terrainformatica.com
Дата: 14.02.18 02:11
Оценка:
Здравствуйте, rean, Вы писали:

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


Ага, спасибо.

R>Предлагаю сместить абстракцию ближе к клиентскому коду.

R>Например, что-то типа:
R>[ccode]
R>class UniversalFeatures {
R>#ifdef _WIN32
R> WindowsFeatures
R>#else
R> MacOSFeatures
R>#endif
R> features;
R> ...

Не получится. Разные файлы .mm (MacOS Objective-C++) и .cpp для остальных.

R>Только не virtual. Это не решит задачу разницу между платформами.


Там не только между платформами. Например для Linux поддерживать три типа window backend: GTK/Gnome, KDE/Qt, XWindow... и все это в одном binary.

R>PS. Множественное наследование — зло. Лучше юзеру его не давать, внутри себя можно, но в классах, какие никогда не будут видны пользователю.


Чего это? А как же COM ?
Re[3]: side virtual method implementation
От: rean  
Дата: 14.02.18 13:28
Оценка:
deleted
Отредактировано 22.04.2019 9:23 deleted2 . Предыдущая версия .
Re: side virtual method implementation
От: andrey.desman  
Дата: 14.02.18 14:57
Оценка:
Здравствуйте, c-smile, Вы писали:

CS>Пока два варианта видится : virtual inheritance и #define простихоспидя. Что еще можно придумать?


Можно через CRTP протащить тип на почти самый верх и там звать переименованный FooImpl. (Это немного отличается от уже предложенных шаблонных вариантов).

#include <cstdio>

class AbstractFoo {
  virtual int do_something() = 0;
};

template<typename T>
class AbstractFooFwd: public AbstractFoo
{
  public:
  virtual int do_something()
  {
    return static_cast<T*>( this )->do_something_impl();
  }
};


class Manipula {};

template<typename T>
class Bar : public AbstractFooFwd<T>, public Manipula {};
template<typename T>
class Zap : public AbstractFooFwd<T>, public Manipula {};

class AbstractFooImpl {
public:
  int do_something_impl() { return 42; }
};


class ConcreteBar : public Bar<ConcreteBar>, public AbstractFooImpl {};

class ConcreteZap : public Zap<ConcreteZap>, public AbstractFooImpl {};

int main(int argc, char* argv[])
{
  ConcreteBar a;
  ConcreteZap b;
  printf("%d %d\n", a.do_something(), b.do_something());
  return 0;
}


Еще вариант — увеличить размер класса и хранить явный указатель на impl через еще один интерфейс, но там все равно CRTP и в итоге получается даже хуже.
В общем, если не пересматривать структуру наследования, то виртуально наследование — самый простой вариант.
Re[4]: side virtual method implementation
От: c-smile Канада http://terrainformatica.com
Дата: 14.02.18 17:10
Оценка:
Здравствуйте, rean, Вы писали:

CS>>Там не только между платформами. Например для Linux поддерживать три типа window backend: GTK/Gnome, KDE/Qt, XWindow... и все это в одном binary.


R>А как собираетесь решать проблему отсутствия требумых dll? Оно же не запустится, если не будет чего-то на машине.


Ну дык dlopen / LoadLibrary же ж... Т.е. lazy load.
Что есть не том и работаем ...

Ну вот Sciter например работает от WinXP до Win10 — binaries одни и те же.
При этом на Win10 используются некоторые функции которые только там и есть.
Re[5]: side virtual method implementation
От: vopl Россия  
Дата: 16.02.18 10:37
Оценка:
Здравствуйте, c-smile, Вы писали:

CS>Ситуация реально там несколько более закрученная.


CS>Это вот не компилируется (есс-но):


CS>
CS>class iwindow {
CS>public:
CS>  int n;
CS>  iwindow() : n(42) {}
CS>  virtual bool foo() = 0;
CS>};

CS>template<typename Base> 
CS>class iwindow_impl: public Base {
CS>public:
CS>  virtual bool foo() override { return true; }
CS>};

CS>class a : public iwindow {
CS>};

CS>class b : public a, public iwindow_impl<b> {
CS>public:
CS>  b() { n = 44; }
CS>};

CS>int main()
CS>{
CS>  b* binst = new b();
CS>  printf("b %d %d\n", binst->foo(), sizeof(b) );
CS>  return 0;
CS>}
CS>


эмм.. ты пытаешься унаследоваться от самого объявляемого класса b, конечно так не получится, это цикл по наследованию

CS>Нужно доп. прослойка:


можно без прослоек

#include <cstdio>

class iwindow {
public:
  int n;
  iwindow() : n(42) {}
  virtual bool foo() = 0;
};

template<typename Base>
class iwindow_impl: public Base {
public:
  virtual bool foo() override { return true; }
};

class a : public iwindow {
};

//class a_bis : public a {
//public:
//  a_bis() { n = 44; }
//};

//typedef iwindow_impl<a_bis> b;

class b : public iwindow_impl<a> {// 'a' унаследуется внутри iwindow_impl
public:
  b() { n = 44; }
};

int main()
{
  b* binst = new b();
  printf("b %d %zd\n", (int)binst->foo(), sizeof(b) );
  return 0;
}


либо я не понял закрученность ситуации
Re: side virtual method implementation
От: Caracrist https://1pwd.org/
Дата: 07.04.18 19:39
Оценка:
Здравствуйте, c-smile, Вы писали:

CS>Каким образом это сделать более кошерно ?


CS>Пока два варианта видится : virtual inheritance и #define простихоспидя. Что еще можно придумать?


однажды поднял этот вопрос, нормольно не решается.
Самое чистое решение вот такое:

class A
{
public:
 virtual ~A(){}
 template<class... ParamsT>
 inline auto foo(ParamsT&&...params)
 {
  return foo_A_impl(std::forward<ParamsT>(params)...);
 }
protected:
 virtual void foo_A_impl(int) = 0;
};
class B
{
public:
 virtual ~B(){}
 template<class... ParamsT>
 inline auto foo(ParamsT&&...params)
 {
  return foo_B_impl(std::forward<ParamsT>(params)...);
 }
protected:
 virtual void foo_B_impl(int) = 0;
};
class C : public A, public B
{
protected:
 virtual void foo_A_impl(int) override 
 {
  //do something as A
 }
 virtual void foo_B_impl(int) override 
 {
  //do something as B
 }
};
~~~~~
~lol~~
~~~ Single Password Solution
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.