Skip to content

Instantly share code, notes, and snippets.

@Arkanosis
Last active May 14, 2023 16:53
Show Gist options
  • Star 5 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save Arkanosis/632294 to your computer and use it in GitHub Desktop.
Save Arkanosis/632294 to your computer and use it in GitHub Desktop.
C# style properties in C++
/*
** C# style properties in C++
** (C) 2010 Arkanosis
** jroquet@arkanosis.net
**
** 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
}
@linklight2
Copy link

linklight2 commented Feb 28, 2020

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment