Last active
May 14, 2023 16:53
-
-
Save Arkanosis/632294 to your computer and use it in GitHub Desktop.
C# style properties in C++
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
** C# style properties in C++ | |
** (C) 2010 Arkanosis | |
** [email protected] | |
** | |
** Example code released under the MIT license | |
** http://www.opensource.org/licenses/mit-license.php | |
** | |
** This is an original portable version of | |
** http://www.codeproject.com/KB/cpp/properties.aspx | |
** which only worked under MSVC | |
*/ | |
/* | |
** This is the C#-like version, for the Python-like version, see: | |
** http://gist.github.com/632240 | |
*/ | |
#include <iostream> | |
/************************************************************ | |
** This is where the magic goes | |
*************************************************************/ | |
template <typename ClassT, typename TypeT, TypeT (ClassT::*Getter)() const> | |
class _Get | |
{ | |
public: | |
explicit _Get(ClassT const* self) | |
: _this(self) { | |
} | |
inline operator TypeT() const | |
{ | |
return (_this->*Getter)(); | |
} | |
private: | |
TypeT operator=(TypeT value) const; | |
private: | |
ClassT const* _this; | |
}; | |
template <typename ClassT, typename TypeT, void (ClassT::*Setter)(TypeT)> | |
class _Set | |
{ | |
public: | |
explicit _Set(ClassT* self) | |
: _this(self) { | |
} | |
inline TypeT operator=(TypeT value) const | |
{ | |
(_this->*Setter)(value); | |
return value; | |
} | |
private: | |
operator TypeT() const; | |
private: | |
ClassT* _this; | |
}; | |
template <typename ClassT, typename TypeT, TypeT (ClassT::*Getter)() const, void (ClassT::*Setter)(TypeT)> | |
class _GetSet | |
{ | |
public: | |
explicit _GetSet(ClassT* self) | |
: _this(self) { | |
} | |
inline operator TypeT() const | |
{ | |
return (_this->*Getter)(); | |
} | |
inline TypeT operator=(TypeT value) const | |
{ | |
(_this->*Setter)(value); | |
return value; | |
} | |
private: | |
ClassT* _this; | |
}; | |
#define GET(CLASS, TYPE, NAME, CODE) \ | |
inline TYPE get##NAME() const CODE \ | |
_Get<CLASS, TYPE, &CLASS::get##NAME> NAME; | |
#define SET(CLASS, TYPE, NAME, CODE) \ | |
inline void set##NAME(TYPE value) CODE \ | |
_Set<CLASS, TYPE, &CLASS::set##NAME> NAME; | |
#define GETSET(CLASS, TYPE, NAME, GET, SET) \ | |
inline TYPE get##NAME() const GET \ | |
inline void set##NAME(TYPE value) SET \ | |
_GetSet<CLASS, TYPE, &CLASS::get##NAME, &CLASS::set##NAME> NAME; | |
/************************************************************ | |
** End of magic, now demo! | |
*************************************************************/ | |
class MyClass | |
{ | |
private: | |
int _attribute; | |
public: | |
GET(MyClass, int, readOnlyProperty, { | |
std::clog << "Getting attribute equals to " << _attribute << std::endl; | |
return _attribute; | |
}) | |
SET(MyClass, int, writeOnlyProperty, { | |
std::clog << "Setting attribute to " << value << std::endl; | |
_attribute = value; | |
}) | |
GETSET(MyClass, int, readWriteProperty, { | |
std::clog << "Getting attribute equals to " << _attribute << std::endl; | |
return _attribute; | |
},{ | |
std::clog << "Setting attribute to " << value << std::endl; | |
_attribute = value; | |
}) | |
MyClass() | |
: _attribute(0), | |
readOnlyProperty(this), | |
writeOnlyProperty(this), | |
readWriteProperty(this) { | |
} | |
}; | |
int main() | |
{ | |
MyClass myVar; | |
// Read only | |
std::cout << myVar.readOnlyProperty << std::endl; // OK | |
//myVar.readOnlyProperty = 42; // Will fail | |
// Write only | |
//std::cout << myVar.writeOnlyProperty << std::endl; // Will fail | |
myVar.writeOnlyProperty = 42; // OK | |
// Read write | |
std::cout << myVar.readWriteProperty << std::endl; // OK | |
myVar.readWriteProperty = 42; // OK | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Interesting solution. I like that with your solution, only a this pointer is stored, while the getter and setter functions are templated. I tried to solve this one myself, and my solution ended up storing function pointers to the functions. I do however, see a problem that you may not have forseen when writing this code. Since each property stores a 'this' pointer to the instance of the class, when the class is copy-constructed, the internal 'this' pointer should be copied as well. Meaning the usage of all your copied class functions point to the original object and not the newly constructed one. Additionally if the constructor is an R-value constructor, the copied this pointer now points to a deleted object. Hence when you call the function you either corrupt the heap or violate your program's memory access.
I ran into this problem myself, and the only solution I had was to create custom constructors and assignment operators for every class I used the C# style properties in. I'm not sure how to really fix this issue, but if you use this code please keep that in mind.