Свойства (properties) для C++

Но зачем?

Иногда программисту, который помимо С++ работает с другими языками, очень не хватает свойств объектов.

Казалось бы, языки вроде Java и C++ обходятся без свойств объектов, таких например как в Ruby, Python, JavaScript или Delphi.

Однако, предпринимаются попытки перенести свойства в C++ (например как это делает Qt), в связи с их преимуществами:

1) Изменение реализации без изменения интерфейса — просто меняем или убираем методы доступа (аксессоры)
2) Лаконичность и понятность: circle.radius = 3; вместо circle.setRadius(3);
3) Возможность «утиной типизации»

Кажется, идея заманчивая. И я решил попробовать.

Почему велосипед?

Проблема фреймворков с поддержкой свойств вроде Builder с его __property и QT c его Q_PROPERTY в их размерах или привязанности к компилятору.

Отдельно небольшой минус использования Qt в том, что все properties используют QVariant, что напрочь убивает статическую типизацию и привязывает программиста к системе конверсии вариантов (привет Q_DECLARE_METATYPE ;) )

Чего хотим от своей реализации?

1) Наши свойства должны хранить значения переменной
2) Свойства должны поддерживать функию-getter. При ее отсутствии отдавать значение как переменную. Должно работать как value = object.property; (Де-факто, получилось value = object.property.get() из-за ограничений языка)
3) Свойства должны поддерживать функию-setter. При ее отсутствии устанавливать значение как переменную.
Должно работать как object.property = value;
4) Свойства можно делать read-only (должна вылетать runtime-ошибка при присвоении)
5) Минимум зависимостей
6) Сохранить статическую типизацию

На мой взгляд, для простой реализации этого списка должно хватить с головой.

К интерфейсам!

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

class Test {
properties
public:
property<std::string, Test> st;
property<int, Test> st1;
property<bool, Test> st2;
property<bool, Test> st3;
property<std::string, Test> st4;
property<std::string, Test> st5;

// Установим аксессоры прямо в конструкторе, но можно и позже (последний bool устанавливает Read Only)
Test(): st5(property<std::string, Test>(«Значение по умолчанию», this, &Test;::testGetter, &Test;::testSetter, true)) {
expose_property(st1);
expose_property(st);
expose_property(st2);


Более подробный пример работы со свойствами можно посмотреть здесь: github.com/Cabalbl4/cpp-properties/blob/master/main.cpp

Но как?

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

Макрос properties скрывает инициализацию карты свойств _properties, которая появляется у объекта. Свойства добавляются в эту карту уже в конструкторе макросом expose_property(имя свойства);

Теперь свойства можно перечислять, но в карте хранятся указатели на безтипового предка класса property, base_property. Следует отметить, что использование этих макросов опционально.

Небольшой сложностью является то, что base_property отдает только тип хранимой переменной, но не ее саму. Но это можно обойти за счёт приведения к потомку, т.к. его тип известен, например так: property<int, Test>* castedProperty = dynamic_cast<property<int,Test>*>(someBasicIntProperty);

Что потребуется для работы?

Требуется С++ 2011 + RTTI.
Также потребуется property.h с моего репозитория github github.com/Cabalbl4/cpp-properties

Еще примеры и описания

Можно найти еще примеры в описании к репозиторию и файле main.cpp

Надеюсь, кто-то найдёт мои простейшие попытки создать property полезными! :)


0 комментариев

Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.