C# Style Properties in C++

After reading this article: "http://www.cplusplus.com/forum/lounge/118961/", I decided to try my hand at creating a universal way of creating C# like properties in C++. It is better than the above solution in the following ways:

- With the usage of Macros, it contains much less boiler-plate code
- Same amount of memory overhead, only storing 1 pointer to the instance class
- The getters and setters can be made virtual, to support polymorphism
- The property can be implicitly converted to the stored type, allowing more natural access

Here is the implementation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
#include <functional>

namespace CSharpStyleProperties {
	template<class classT, class ReturnT, class PassT, ReturnT(classT:: * getFn)(), void(classT:: * setFn)(PassT)>
	class Property {
		classT* instanceThisPtr;

	public:

		Property(classT* thisPtr) : instanceThisPtr(thisPtr) {}

		ReturnT get() const {
			return std::invoke(getFn, instanceThisPtr);
		}

		void set(PassT value) {
			std::invoke(setFn, instanceThisPtr, value);
		}

		Property& operator=(const Property& rhs) = delete;

		Property& operator=(PassT value) {
			set(value);
			return *this;
		}

		ReturnT operator*() const {
			return get();
		}

		operator ReturnT() const {
			return get();
		}
	};
}

// Macros 

#define CUSTOM_PROPERTY(VarName, ClassType, ReturnType, GetFnCode, PassType, SetFnCode) \
ReturnType PROPERTY_GET_##VarName##() GetFnCode \
void PROPERTY_SET_##VarName##(##PassType value) SetFnCode \
CSharpStyleProperties::Property<ClassType, ReturnType, PassType, &##ClassType##::PROPERTY_GET_##VarName##, &##ClassType##::PROPERTY_SET_##VarName##> VarName = \
CSharpStyleProperties::Property<ClassType, ReturnType, PassType, &##ClassType##::PROPERTY_GET_##VarName##, &##ClassType##::PROPERTY_SET_##VarName##>(this)

#define READ_ONLY_PROPERTY(VarName, ClassType, ReturnType, GetFnCode) \
ReturnType PROPERTY_GET_##VarName##() GetFnCode \
CSharpStyleProperties::Property<ClassType, ReturnType, void*, &##ClassType##::PROPERTY_GET_##VarName, nullptr> VarName = \
CSharpStyleProperties::Property<ClassType, ReturnType, void*, &##ClassType##::PROPERTY_GET_##VarName, nullptr>(this)

#define POLYMORPHIC_PROPERTY(VarName, ClassType, ReturnType, GetFnCode, PassType, SetFnCode) \
virtual ReturnType PROPERTY_GET_##VarName##() GetFnCode \
virtual void PROPERTY_SET_##VarName##(##PassType value) SetFnCode \
CSharpStyleProperties::Property<##ClassType##, ReturnType##, PassType##, &##ClassType##::PROPERTY_GET_##VarName##, &##ClassType##::PROPERTY_SET_##VarName##> VarName; \
typedef ReturnType PROPERTY_RETURN_##VarName##_TYPE; \
typedef PassType PROPERTY_PASS_##VarName##_TYPE

#define READ_ONLY_POLYMORPHIC_PROPERTY(VarName, ClassType, ReturnType, GetFnCode) \
virtual ReturnType PROPERTY_GET_##VarName##() GetFnCode \
CSharpStyleProperties::Property<ClassType, ReturnType, void*, &##ClassType##::PROPERTY_GET_##VarName, nullptr> VarName; \
typedef ReturnType PROPERTY_RETURN_##VarName##_TYPE

#define GET_OVERRIDE(VarName) \
virtual PROPERTY_RETURN_##VarName##_TYPE PROPERTY_GET_##VarName##() override

#define SET_OVERRIDE(VarName) \
virtual void PROPERTY_SET_##VarName##(PROPERTY_PASS_##VarName##_TYPE value) override


Since each property stores a pointer to the instance of the class it was created in, there is an unresolved bug. When the above class is copied using a copy-constructor or the assignment operator, the copied class's properties still retain the original class's this pointer. Example below:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
#include <iostream>
#include <string>
#include "CSharpStyleProperties.h"

using std::string;
using std::cout;

class Animal {
protected:
	int _age = 0;
	string _name;
	string _species;
	string _animalSound;

public:
	// Polymorphic properties must be constructed with the class, otherwise there is a complicated
	// template error. 
	Animal(const string& name) : _name(name), animalSound(this), species(this) {}

	// Custom Property(VarName, ClassType, ReturnType (of get function), Get Function Code,
	// PassType (for 'value' in the set function), Set Function Code);
	CUSTOM_PROPERTY(age, Animal, int, { return _age; }, int, { _age = value; });
	// 'value' is the name of the parameter passed to the Set Function

	// ReadOnly Property(VarName, ClassType, ReturnType (of get function), Get Function Code);
	READ_ONLY_PROPERTY(name, Animal, const string&, { return _name; });

	// Same thing as Custom Property, but it can be used polymorphically
	POLYMORPHIC_PROPERTY(animalSound, Animal, string, { return "I say " + _animalSound + "?"; }, const string&, { _animalSound = value; });

	// Same thing as ReadOnly property, but it can be used polymorphically
	READ_ONLY_POLYMORPHIC_PROPERTY(species, Animal, string, { return "A Generic Animal"; });
};

class Dog : public Animal {
public:
	Dog(const string& name) : Animal(name) {}

	// Get Override(VarName (that you used for the base class)) { getter }
	GET_OVERRIDE(animalSound) {
		return "I say " + _animalSound + "!";
	}
	// Set Override(VarName (that you used for the base class)) { setter }
	SET_OVERRIDE(animalSound) {
		_animalSound = value + "!!";
	}
	// Get Override(VarName (that you used for the base class)) { getter }
	GET_OVERRIDE(species) {
		return "Dog";
	}
};

int main() {
	Animal* Froggy = new Animal("Fred");
	Animal* Doggy = new Dog("Gary");

	// This presents a big problem with my solution. Because of the way C++ implicitly 
	// copies objects, the property's 'this' pointer incorrectly points to the other object's 'this'
	Animal problem = *Doggy;

	char ch;

	Froggy->animalSound = "Ribbit"; // animalSound returns "I say Ribbit?"
	Froggy->age = 3; // age returns '3'
	//Froggy->species = "Dog"; // Causes error, read-only properties cannot be assigned to
	//Froggy->name = "Gary"; // Causes error, read-only properties cannot be assigned to

	problem.animalSound = "Moo"; // animalSound will be overriden by Doggy->animalSound = "Woof"
	problem.age = 10; // age will be overriden by Doggy->age = 4

	Doggy->animalSound = "Woof"; // animalSound returns "I say Woof!!!"
	Doggy->age = 4; // age returns 4;

	// The * operator can easily be used to retrieve the content's of the get function 
	// if it is not automatically converting. 
	cout << "(Froggy) My name is " << *Froggy->name << ", I am " << Froggy->age
		<< " years old, my species is \"" << *Froggy->species << "\", and "
		<< *Froggy->animalSound << "\n";

	cout << "(Problem) My name is " << *problem.name << ", I am " << problem.age // 'age' should be 10
		<< " years old, my species is \"" << *problem.species << "\", and " // 'species' should be "A Generic Animal"
		<< *problem.animalSound << "\n"; // 'animalSound' should be "I say Moo?"

	cout << "(Doggy) My name is " << *Doggy->name << ", I am " << Doggy->age
		<< " years old, my species is \"" << *Doggy->species << "\", and "
		<< *Doggy->animalSound << "\n";

	std::cin >> ch; // Used to wait till the user presses a key before console exit

	return 0;
}



(Froggy) My name is Fred, I am 3 years old, my species is "A Generic Animal", and I say Ribbit?
(Problem) My name is Gary, I am 4 years old, my species is "Dog", and I say Woof!!!
(Doggy) My name is Gary, I am 4 years old, my species is "Dog", and I say Woof!!!


If any of you can think of any solutions to this issue other than custom defining constructors and assignment operators for every class one would use a c# style property in, that would be much appreciated.
Last edited on
closed account (Ey80oG1T)
Nice work man!
Topic archived. No new replies allowed.