Reputation: 27
I want to implement a C++ equivalent of C#'s properties that does not use C Macros at all. I created a template, that basically sets or retrieves an internal value in a fancier way that get()
and set()
. In the constructor there are also two parameters of type std::function
where I can override the default getter and setter methods.
template<typename T>
class Property
{
public:
Property(T initialValue, std::function<void(T&)> setter, std::function<T&()> getter)
{
Value = initialValue;
Setter = setter;
Getter = getter;
}
~Property()
{
}
void operator =(T value)
{
if(Setter == nullptr)
{
Set(value);
}
else
{
Setter(value);
}
}
T& operator ()()
{
if(Getter == nullptr)
{
return Get();
}
else
{
return Getter();
}
}
void Set(T& value)
{
Value = value;
}
T& Get()
{
return Value;
}
private:
T Value;
std::function<void(T&)> Setter = nullptr;
std::function<T&()> Getter = nullptr;
}
The following code snippet shows the definition of a Property
object of type int
. (Getter and setter are overridden by lambda expressions.)
Property<int> prop = Property<int>(0,
[&](int& val)
{
// some code inside the overridden setter
prop.Set(val);
},
[&]() -> int&
{
// some code inside the overridden getter
return prop.Get();
});
Getter and setter are called in the following way:
int foo = prop();
prop = foo;
I thought everything will work fine and it did. It did until I tried to pass a class as template type, that did not contain a standard constructor. I know, that the declaration T Value;
causes the problem but I can't figure out any alternatives or workarounds.
Maybe, you have an idea how to make the concept work for primitive types, classes and classes without a standard constructor.
Upvotes: 1
Views: 2018
Reputation: 96524
IMO, this class is a bad idea.
All those std::function
s will probably add a reasonable overhead (in both performance and memory).
That said, here's the answer:
Not specifying Value
in the member initializer list of the constructor of Property
causes Value
to be default-initialized (and then assigned a value).
The solutuion is to initialize it in the member initializer list, instead of assigning to it in the constructor body:
Property(T initialValue, std::function<void(T&)> setter, std::function<T&()> getter)
: Value(initialValue)
{
Setter = setter;
Getter = getter;
}
It's a good idea to do the same thing for Setter
and Getter
too.
Also, Get
and operator()
must not return non-const
references (return by value or by a const
reference instead). Returning a non-const reference here allows user to bypass the specified setter and change the value directly. Additionally, you should make Get
and operator()
const
.
operator=
, Set
, and std::function<...> Setter
should take the new value by a const
reference.
You should pass a reference to T Value
to Setter
and a const
reference to it to Getter
as a parameter, rather than capturing references to the property in the lambdas. Capturing the reference makes your class break if it's moved.
Getter
should return by value (to allow it to return a modified Value
), or return void
(if its sole purpose is to call a function when the value is returned, without being able to modify what's returned).
Upvotes: 1