Информация об изменениях

Сообщение Re: side virtual method implementation от 13.02.2018 21:52

Изменено 22.04.2019 9:24 deleted2

Re: side virtual method implementation
ИМХО, суть проблемы вот в этом:

CS>class AbstractFoo {

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

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


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

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


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

Например, что-то типа:
#ifdef _WIN32
  class WindowsFeatures { };
#else
  class MacOSFeatures { };
#endif

class UniversalFeatures {
#ifdef _WIN32
  WindowsFeatures
#else
  MacOSFeatures
#endif
  features;
  ...

void someCode() {
#ifdef MACOS
  ...
#endif
}

};


или, если полагаться на возможность компилятора выкинуть лишний if(false):

void someCode() {
  if (features.isMacOS) {}
}


Чуть чище выходит, но ИМХО, пусть будут макросы.

Лучше без наследования, поскольку UniversalFeatures должен разруливать разницу между платформами. WindowsFeatures и MacOSFeatures не обязательно делать максимально похожими. Их задача — максимально хорошо делать задачу в своей платформе. А задача UniversalFeatures разруливать разницу.

А уже от UniversalFeatures наследуйте свои платформенно-независимые вещи.

Другой вариант, если не хочется писать #ifdef, можно обернуть все в шаблоны. Тогда C++ будет сам выкидывать ненужное, и не нужны будут даже виртуальные функции:

template <class Platform>
class UniversalFeatures {
  Platform features;

};


или даже:


#ifdef _WIN32
template <class Client>
class WindowsFeatures {
};
#else
template <class Client>
class MacOSFeatures {
};
#endif


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

void WindowsFeatures::someFunc() {
  thisFunctionIsCallableFromPlatformFeatures();  
}

class Client: public MacOSFeatures<Client> {
  void thisFunctionIsCallableFromPlatformFeatures() {}
}


Не обязательно шаблонные кишки отдавать клиенту. Можно Client сделать абстрактной базой последующей иерархии, а шаблонные кишки будут скрыты в имплементации. Дальше уже будет классическое ООП.

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

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


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

PS. Множественное наследование — зло. Лучше юзеру его не давать, внутри себя можно, но в классах, какие никогда не будут видны пользователю.
Re: side virtual method implementation
deleted