I realize it's this question is probably too old to add another answer but to expand on Moo-Juice's answer, I've come up with a pretty neat and simple solution:
/// Utility for functions get, set & ptr.
template<typename TVal>
using GetFn = std::function<const TVal& (void)>;
template<typename TVal>
using SetFn = std::function<void(const TVal&)>;
template<typename TVal>
using PtrFn = std::function<TVal* (void)>;
/// The property class and each specialization utility.
template<typename TVal, bool Delegate, bool ReadOnly>
class Property;
template<typename TVal>
using PropertyGetSet = Property<TVal, false, false>;
template<typename TVal>
using PropertyDelGetSet = Property<TVal, true, false>;
template<typename TVal>
using PropertyGet = Property<TVal, false, true>;
template<typename TVal>
using PropertyDelGet = Property<TVal, true, true>;
/// <summary>
/// Property get-set.
/// </summary>
/// <typeparam name="TVal">Value type.</typeparam>
template<typename TVal>
class Property<TVal, false, false>
{
public:
typedef TVal Value;
Property(const TVal& val)
: m_value(val)
{}
inline const TVal& Get() const { return m_value; }
inline void Set(const TVal& val) { m_value = val; }
inline TVal* Ptr() { return &m_value; }
private:
TVal m_value;
};
/// <summary>
/// Property delegate get-set.
/// </summary>
/// <typeparam name="TVal">Value type.</typeparam>
template<typename TVal>
class Property<TVal, true, false>
{
public:
typedef TVal Value;
Property(GetFn<TVal> getFn, SetFn<TVal> setFn, PtrFn<TVal> ptrFn)
: m_getFn(getFn)
, m_setFn(setFn)
, m_ptrFn(ptrFn)
{}
inline const TVal& Get() const { return m_getFn(); }
inline void Set(const TVal& val) { m_setFn(val); }
inline TVal* Ptr() { return m_ptrFn(); }
private:
GetFn<TVal> m_getFn;
SetFn<TVal> m_setFn;
PtrFn<TVal> m_ptrFn;
};
/// <summary>
/// Property get.
/// </summary>
/// <typeparam name="TVal">Value type.</typeparam>
template<typename TVal>
class Property<TVal, false, true>
{
public:
typedef TVal Value;
Property(const TVal& val)
: m_value(val)
{}
inline const TVal& Get() const { return m_value; }
inline TVal* Ptr() { return &m_value; }
private:
TVal m_value;
};
/// <summary>
/// Property delegate get.
/// </summary>
/// <typeparam name="TVal">Value type.</typeparam>
template<typename TVal>
class Property<TVal, true, true>
{
public:
typedef TVal Value;
Property(GetFn<TVal> getFn, PtrFn<TVal> ptrFn)
: m_getFn(getFn)
, m_ptrFn(ptrFn)
{}
inline const TVal& Get() const { return m_getFn(); }
inline TVal* Ptr() { return m_ptrFn(); }
private:
GetFn<TVal> m_getFn;
PtrFn<TVal> m_ptrFn;
};
And then to use it:
PropertyGetSet<std::string> strGetSet = PropertyGetSet<std::string>("GetSet");
std::string m_strGetSet = "DelGetSet";
PropertyDelGetSet<std::string> strDelGetSet =
PropertyDelGetSet<std::string>(
[&]() -> const std::string& { return m_strGetSet; },
[&](const std::string& val) { m_strGetSet = val; },
[&]() { return &m_strGetSet; /* throw? */ });
// The get (read-only) version is the same but without the set function
Some caveats:
The get function returns a const& so you are not able to use it for changing the value, this is by design as it would allow people to use the reference to set the value instead of the explicit Set which gives the advantage of knowing when the value is set.
There is no syntactic sugar for the get-set-ptr functions, personally, I didn't like using operators cause it made the underlying system more obtuse, so using explicit functions lets the user know that it's a property and not something else. But if you may, you could sprinkle some operator overloads.
All specializations have a Ptr function which will be the pointer of the data. However, when using the delegate version, you can choose to throw so anyone trying to use it will have to work around it. The reason it's there is that in the worst-case scenario you may try to use the pointer for a very particular situation, I would highly advise not to use this tho, so feel free to remove it or make an extra specialization for it.
Lastly, it's a bit verbose, you could wrap the usage in a macro to make the syntax a bit shorter, but personally, I like it the way it is as it's more explicit that way.
EDIT:
You may run into the same issue I had with this design, have a look at the following link for the issue and the solution I've come up with: https://mcmap.net/q/16727/-how-to-correctly-copy-a-lambda-with-a-reference-capture
someThing.text
be a public member if you're going to treat it like one anyway? – Making