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

Сообщение Re[19]: как сократить количество vtbl от 05.05.2017 18:24

Изменено 05.05.2017 19:43 Erop

Re[19]: как сократить количество vtbl
Здравствуйте, qaz77, Вы писали:

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>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. Тут с "наследственной" схемой тоже паритет, но тут можно немного побороться.
Можно так реструктурировать это код, что в зависимости от ключа компиляции/макроса будет собираться обычная или оптимизированная версия, и все проверки делать в обычной.