Хотя это не дает прямого ответа на ваш вопрос, вы можете обнаружить, что сгенерированный код фактически не нужен для управления свойствами на C++. Следующий код шаблона позволит вам объявлять и свойство удобно использовать:
// Declare your class containing a few properties
class my_class {
public:
property<int> x;
property<string> y;
...
};
...
my_class obj;
cout << obj.x(); // Get
obj.y("Hello, world!"); // Set
Вот код:
// Utility template to choose the 2nd type if the 1st is void
template <typename T, typename U>
struct replace_void {
typedef T type;
};
template <typename T>
struct replace_void<void, T> {
typedef T type;
};
// Getter/setter template
template <typename T, typename D = void>
class property {
typedef typename replace_void<D, property>::type derived_type;
derived_type& derived() { return static_cast<derived_type&>(*this); }
public:
property() {} // May be safer to omit the default ctor
explicit property(T const& v) : _v(v) {}
property(property const& p) : _v(p._v) {}
property& operator=(property const& p) { _v = p._v; return *this; }
T operator()() const { return _v; } // Getter
void operator()(T const& v) { derived().check(v); _v = v; } // Setter
protected:
// Default no-op check (derive to override)
void check(T const& v) const { (void)v; //avoid unused variable warning}
private:
T _v;
};
check()
функция, которая проверяет, является ли значение быть назначено. Вы можете переопределить его в подклассе:
class nonnegative_int : public property<int, nonnegative_int> {
public:
// Have to redeclare all relevant ctors unfortunately :(
nonnegative_int(int v) : property<int, nonnegative_int>(v) {}
void check(int const& v) const {
if (v < 0) {
throw "Yikes! A negative integer!";
}
}
};
Там у Вас есть это - все преимущества внешне сгенерированных функций геттер/сеттер, ни с кем из беспорядка! :)
Вы можете выбрать check()
return a bool
с указанием действительности, а не для исключения. И вы могли бы в принципе добавить аналогичный метод, access()
, для ловли прочитанных ссылок на свойство.
EDIT: Как г-н Fooz отмечает в комментариях, класс автор может впоследствии изменить реализацию без изменения логической структуры класса (например, путем замены property<int> x
элемента с парой x()
методов), хотя бинарный совместимость теряется, поэтому пользователям необходимо будет перекомпилировать свой клиентский код всякий раз, когда это делается. Эта способность безболезненно включать будущие изменения на самом деле является основной причиной, по которой люди используют функции getter/setter вместо публичных членов.
Производительность Примечание: Поскольку мы используем CRTP для достижения «время компиляции полиморфизм», нет никакого виртуального вызова накладных расходов для обеспечения вашей собственной check()
в подклассе, и вам не нужно объявить его virtual
.
Будет ли это работать с частными членами данных? – nmuntz
Ничего себе! Я на самом деле думал об использовании шаблонов для эмуляции свойств и вернулся, чтобы отредактировать мой ответ ... только для того, чтобы найти это решение уже здесь, и оно намного более полно, чем простой хак, который я собирался публиковать! Спасибо, что поделился. –
Возможно, стоит отметить, что автор класса может изменить реализацию без изменения логической структуры класса (например, путем прямого создания пары методов x() или изменения реализации D), но двоичная совместимость теряется, поэтому пользователям необходимо будет перекомпилируйте свой клиентский код всякий раз, когда это делается. –