BraunBerry
BraunBerry

Reputation: 27

C++ property template for classes without standard constructor

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

Answers (1)

HolyBlackCat
HolyBlackCat

Reputation: 96524

IMO, this class is a bad idea.

All those std::functions 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

Related Questions