Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / C++

CSharpish Properties in Modern C++

5.00/5 (10 votes)
25 Oct 2020CPOL3 min read 13.6K  
Simple trick to implement csharpish properties in modern C++
C# has properties that behave like class member variables and capture read and write operations. We can implement similar behavior in modern C++ in only 15 lines of code.

Introduction

Traditionally, variables have been used as a space for storing and reading values. With evolution of programming, it was discovered that being able to do something when we store a value into a variable or read from it is quite practical. Today, most modern languages support this via properties.

In C#, a property has two pseudo functions that implement reading and writing. They are called a getter and a setter. Inside the setter, you can use a special keyword called value. It holds the desired value that we want to assign to the property.

This is how your average C# property looks like:

C#
public class Person {
    private int _age;
    public int Age
    {
        get { return _age; }
        set { _age = value; }
    }
}

In this example, the getter simply returns the value of persons' age, stored in the private member variable _age. And the setter assigns the previously mentioned value to a private variable _age.

We would like to re-create this in Modern C++.

The Solution

Mimic Native Type

An int property class should behave like an int. Hence, our first task is to make our property class behave like its type. This can be done with a combination of a template, an implicit cast and an assignment operator.

So let's define the template that roughly behaves like a native type.

C++
template<typename T>
class property {
    operator T() const { return /* return property value */ };
    property<T>& operator= (const T& value) { /* set your value */ }
}

We can now use our C++ property like this:

C++
property<int> i;
i=10;     // Calls the assignment operator=.
int j=i;  // Calls the T() implicit cast.

Pass Setter and Getter

In the previous chapter, we discovered how we can capture get and set operations by implementing an implicit cast and an assignment operator. Now let us enable passing a custom setter and the getter functions to our properties.

This is a job for lambda expressions. To be able to define properties directly in C++ header files, let's pass getter and setter functions to a property via the constructor:

C++
#include <functional>

template<typename T>
class property {
public:
    property(
        std::function<void(T)> setter,
        std::function<T()> getter) :
        setter_(setter),getter_(getter) { }
    operator T() const { return getter_(); }
    property<T>& operator= (const T &value) { setter_(value); return *this; }
private:
    std::function<void(T)> setter_;
    std::function<T()> getter_;
};

With this addition, we're almost there. In fact, if we only need to capture storing and reading values from the property, we are already there.

To fully mimic the C# property, we still need to implement that special value member though.

The Value

Let's add a private member variable of template type T to the property class and call it value_. To access this value, let's use a really dirty trick that will raise some eyebrows. Let's create a function value() that simply returns a reference to private member value_. Why? Because this way, we can assign to that property member variable directly - bypassing getters and setters, just like we can assign to a private class member variable in C#.

This is the final property class after adding a private variable value_ of type T and a function T& value() that returns a reference to it.

C++
#include <functional>

template<typename T>
class property {
public:
    property(
        std::function<void(T)> setter,
        std::function<T()> getter) :
        setter_(setter),getter_(getter) { }
    operator T() const { return getter_(); }
    property<T>& operator= (const T &value) { setter_(value); return *this; }
    T& value() { return value_; }
private:
    std::function<void(T)> setter_;
    std::function<T()> getter_;
    T value_;
};

And we're done!

Using Properties

We can define our sample property in C++ like this:

C++
class person {
public:
property<int> age {
    [this] (int x) { this->age.value()=x; },        // Setter ... yes you can do this!
    [this] () -> int {  return this->age.value(); } // Getter.
    };
};

And use it as an integer in the rest of our code:

C++
person p;
p.age=10;         // Call setter.
std::cout<<p.age; // Call getter.

Known Issues

To completely mimic underlying type, our property class could override and pass other operators like ++,--,+=. In addition, the class could be separated to base class without the value_ called property, and a derived class with a value_ member called value_property.

History

  • 25th October, 2020: Initial version

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)