Сообщение Re[19]: как сократить количество vtbl от 05.05.2017 18:24
Изменено 05.05.2017 19:43 Erop
Re[19]: как сократить количество vtbl
Здравствуйте, qaz77, Вы писали:
Q>Все касты неявные и во время компиляции.
Ну, тогда, можно операторы преобразования сделать защищёнными и открывать их, вместо того, что бы реализовывать.
Ну, то есть, если IBar не играет в оптимизацию, то пишешь так:
Q>
а если играет, то так:
и БОЛЬШЕ НИЧЕГО НЕ МЕНЯЕШЬ
Q>
Q>Все касты неявные и во время компиляции.
Ну, тогда, можно операторы преобразования сделать защищёнными и открывать их, вместо того, что бы реализовывать.
Ну, то есть, если IBar не играет в оптимизацию, то пишешь так:
Q>
Q>struct IBar: ISerializable,
Q> IClonable,
Q> ...
Q>{
Q>};
а если играет, то так:
struct IBar : protected IGentlemansSet {
using IGentlSet::operator ISerializable&;
using IGentlSet::operator IClonable&;
...
};
и БОЛЬШЕ НИЧЕГО НЕ МЕНЯЕШЬ
Q>
Q>struct IBaz: IClonable,
Q> IComparable,
Q> ...
Q>{
Q>};
Q>IBar& getRandomBar();
Q>IBaz& getRandomBaz();
Q>void saveSomethingToDisk(const char* filename, ISerializable& s);
Q>int main()
Q>{
Q> IBar& bar = getRandomBar();
Q> saveSomethingToDisk("bar.dat", bar);
Q> IBaz& baz = getRandomBaz();
Q> saveSomethingToDisk("baz.dat", baz); // << compile time error, IBaz не поддерживает ISerializable
Q> return 0;
Q>}
Q>
Re[19]: как сократить количество vtbl
Здравствуйте, qaz77, Вы писали:
Q>Все касты неявные и во время компиляции.
Ну, тогда, можно операторы преобразования сделать защищёнными и открывать их, вместо того, что бы реализовывать.
Ну, то есть, если IBar не играет в оптимизацию, то пишешь так:
Q>
а если играет, то так:
и БОЛЬШЕ НИЧЕГО НЕ МЕНЯЕШЬ
Q>
p.s.
Мало того, в самом IGentlemansSet тоже минимальный объём кода и никаких RT накладных расходов:
template<typename T>
T* IGentlemansSet::to<T>() { return static_cast<T*>( this ); }
template<typename T>
const T* IGentlemansSet::to<T>() const { return static_cast<T*>( this ); }
[/c] тут, правда есть подводный камень, который состоит в том, что определения типов аргументов и результатов методов из всех интерфейсов джентльменского набора придётся включить или предопределить, хотя бы. Но может это и не создаст много зависимостей.
В любом случае это не дороже наследования левых интерфейсов друг от друга.
И да, напоминаю, что типичный хедер оптимизированного интерфейса будет выглядеть теперь так:
С точки зрения пользователя библы ничего вообще не поменяется.
С точки зрения того, кто реализует неоптимизированные интерфейсы, очень мало
И только в тех классах, которые играют в оптимизацию, что-то поменяется. Но они все твои, как я понял, и их немного?
Я вижу три проблемы:
1) Нелегальный down cast. IMHO эта проблема носит чисто формальный характер. В этом смысле такое решение чуть хуже решения с выведением всех интерфейсов IGentlemansSet друг из друга.
2) В IGentlemansSet появляется лишняя связность кода. Но тут с вариантом с выведением паритет, или, даже, чуть лучше, так как типы нужные в ISerializable, в GentlemansSet.h можно только объявить, а давать им определение только в Serializable.h
3) Нет CT проверки, что в MDT реализованы все нужные методы, так как мы не можем теперь методы IGentlemansSet сделать pure virtual. Тут с "наследственной" схемой тоже паритет, но тут можно немного побороться.
Можно так реструктурировать это код, что в зависимости от ключа компиляции/макроса будет собираться обычная или оптимизированная версия, и все проверки делать в обычной.
Q>Все касты неявные и во время компиляции.
Ну, тогда, можно операторы преобразования сделать защищёнными и открывать их, вместо того, что бы реализовывать.
Ну, то есть, если IBar не играет в оптимизацию, то пишешь так:
Q>
Q>struct IBar: ISerializable,
Q> IClonable,
Q> ...
Q>{
Q>};
а если играет, то так:
struct IBar : protected IGentlemansSet {
using IGentlemansSet::operator ISerializable&;
using IGentlemansSet::operator IClonable&;
...
};
и БОЛЬШЕ НИЧЕГО НЕ МЕНЯЕШЬ
Q>
Q>struct IBaz: IClonable,
Q> IComparable,
Q> ...
Q>{
Q>};
Q>IBar& getRandomBar();
Q>IBaz& getRandomBaz();
Q>void saveSomethingToDisk(const char* filename, ISerializable& s);
Q>int main()
Q>{
Q> IBar& bar = getRandomBar();
Q> saveSomethingToDisk("bar.dat", bar);
Q> IBaz& baz = getRandomBaz();
Q> saveSomethingToDisk("baz.dat", baz); // << compile time error, IBaz не поддерживает ISerializable
Q> return 0;
Q>}
Q>
p.s.
Мало того, в самом IGentlemansSet тоже минимальный объём кода и никаких RT накладных расходов:
// Предъобявления интиерфейсов
struct ISerializable;
struct IClonable
// ...
struct IGentlemansSet {
protected:
virtual ~IGentlemansSet() {}
template<typename T> T* to();
template<typename T> const T* to() const; // если надо
operator ISerializable& () { return *to<ISerializable>(); }
operator IClonable&() { return *to<IClonable>(); }
// ...
// virtual методы ISerializable
virtual void save( TSaveDst& ) const { assert( false ); }
virtual bool load( TLoadSrc& ) { assert( false ); return false; }
// virtual методы IClonable
// ...
// ...
};
template<typename T>
T* IGentlemansSet::to<T>() { return static_cast<T*>( this ); }
template<typename T>
const T* IGentlemansSet::to<T>() const { return static_cast<T*>( this ); }
[/c] тут, правда есть подводный камень, который состоит в том, что определения типов аргументов и результатов методов из всех интерфейсов джентльменского набора придётся включить или предопределить, хотя бы. Но может это и не создаст много зависимостей.
В любом случае это не дороже наследования левых интерфейсов друг от друга.
И да, напоминаю, что типичный хедер оптимизированного интерфейса будет выглядеть теперь так:
// было
struct ISerializable {
virtual void save( TSaveDst& ) const = 0;
virtual bool load( TLoadSrc& ) = 0;
};
// стало
struct ISerializable : protected IGentlemansSet {
using IGentlemansSet::load;
using IGentlemansSet::save;
}
С точки зрения пользователя библы ничего вообще не поменяется.
С точки зрения того, кто реализует неоптимизированные интерфейсы, очень мало
И только в тех классах, которые играют в оптимизацию, что-то поменяется. Но они все твои, как я понял, и их немного?
Я вижу три проблемы:
1) Нелегальный down cast. IMHO эта проблема носит чисто формальный характер. В этом смысле такое решение чуть хуже решения с выведением всех интерфейсов IGentlemansSet друг из друга.
2) В IGentlemansSet появляется лишняя связность кода. Но тут с вариантом с выведением паритет, или, даже, чуть лучше, так как типы нужные в ISerializable, в GentlemansSet.h можно только объявить, а давать им определение только в Serializable.h
3) Нет CT проверки, что в MDT реализованы все нужные методы, так как мы не можем теперь методы IGentlemansSet сделать pure virtual. Тут с "наследственной" схемой тоже паритет, но тут можно немного побороться.
Можно так реструктурировать это код, что в зависимости от ключа компиляции/макроса будет собираться обычная или оптимизированная версия, и все проверки делать в обычной.