Первое, самое очевидное – никак не проверяется вызов init в конструкторе. То есть если программист забыл проинициализировать свойства, то компилятор ничего не скажет, а падение в рантаме в коде класса Property – весьма непонятная для дебага ошибка.
Выходом из данной ситуации может быть простейшая вещь – добавление модификатора const. Константные поля программист обязан инициализировать в конструкторе, поэтому компилятор не даст забыть про инициализацию свойств. Константность даже логична, ведь сам объект свойства не должен изменяться за время существования объекта, которому он принадлежит. Разумеется, добавление const к существующей реализации не решит проблемы – такой код даже не соберётся. Требуется написать методы для работы с константными свойствами. Так же для удобства использования определим макрос, который вместо Property будет подставлять const Poperty.
Второй существенный недостаток – отсутствие конструктора копирования свойства. Для того чтобы их реализовать надо понять, что должно копироваться: объект свойства или значение, которое можно получить через getter? Объект свойства целиком копировать нельзя как минимум из-за того, что нужно изменить указатель на владельца. Да и выше вроде как было решено сделать свойства константными. Геттер вообще может не существовать, как и сеттер. Из всего выше сказанного делаю вывод: конструктор копирования не нужен вообще. Копировать просто нечего и некуда: они константны, ничего измениться не сможет. Таким образом константность решает проблему описанную тут.
В итоге получаем код:
enum PropertyAccess { ReadOnly, WriteOnly, ReadWrite }; template <typename Type, typename Owner, PropertyAccess Access> class MyProperty { }; template<typename Type, typename Owner> class MyProperty<typename Type, typename Owner, ReadWrite> { protected: typedef Type (Owner::*getter)(); typedef void (Owner::*setter)(Type); Owner * m_owner; getter m_getter; setter m_setter; public: // Оператор приведения типа. Реализует геттер. operator Type() const { return (m_owner->*m_getter)(); } // Оператор присваивания. Реализует сеттер. void operator =(Type data) const { (m_owner->*m_setter)(data); } MyProperty(Owner * const owner, getter getmethod, setter setmethod) : m_owner(owner), m_getter(getmethod), m_setter(setmethod) {} }; template<typename Type, typename Owner> class MyProperty<typename Type, typename Owner, ReadOnly> { protected: typedef Type (Owner::*getter)(); Owner * m_owner; getter m_getter; public: // Оператор приведения типа. Реализует геттер. operator Type() const { return (m_owner->*m_getter)(); } MyProperty(Owner * const owner, getter getmethod) : m_owner(owner), m_getter(getmethod) { } }; template<typename Type, typename Owner> class MyProperty<typename Type, typename Owner, WriteOnly> { protected: typedef void (Owner::*setter)(Type); Owner * m_owner; setter m_setter; public: // Оператор присваивания. Реализует сеттер. void operator =(Type data) const { (m_owner->*m_setter)(data); } MyProperty(Owner * const owner, setter setmethod) : m_owner(owner), m_setter(setmethod) { } }; #define Property (const MyProperty)Вот так всё просто: проблемы было две, решать начинал по-разному, а решение оказалось одно универсальное, при этом ещё и весьма логичное.
Новые исходники.
Проект Visual Studio 2008.
Комментариев нет:
Отправить комментарий