Although it doesn't directly answer your question, you may find that generated code is actually unnecessary for managing properties in C++. The following template code will allow you to declare and use properties conveniently:
// 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
Here is the code:
// 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
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 {}
private:
T _v;
};
check()
is a function that tests whether the value being assigned is valid. You can override it in a subclass:
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!";
}
}
};
There you have it -- all of the advantages of externally-generated getter/setter functions, with none of the mess! :)
You could choose to have check()
return a bool
indicating validity instead of throwing an exception. And you could in principle add a similar method, access()
, for catching read references to the property.
EDIT: As Mr. Fooz notes in the comments, the class author can later change the implementation without modifying the logical structure of the class (e.g. by replacing the property<int> x
member with a pair of x()
methods), although binary compatibility is lost so users will need to recompile their client code whenever such a change is made. This ability to painlessly incorporate future changes is actually the main reason people use getter/setter functions instead of public members in the first place.
Performance note: Because we are using the CRTP to achieve "compile-time polymorphism", there is no virtual-call overhead for providing your own check()
in a subclass, and you need not declare it virtual
.