Home
Code-Blog
Projects
Twitter
Blog
Links / Books
About
|
Websites |
C++ Properties using C++ 11
A property is a "virtual field".
The idea behind this implementation is to detect
Sampleauto result = x.prop;
x.prop = something;
will be equivalent of: auto result = x.get_prop();
x.set_prop(something);
Tested with:
properties.h// // Copyright (C) 2012, Thiago R. Adams // http://www.thradams.com // Permission to copy, use, modify, sell and distribute this software // is granted provided this copyright notice appears in all copies. // This software is provided "as is" without express or implied // warranty, and with no claim as to its suitability for any purpose. // #pragma once #include <type_traits> #include <ostream> struct Property {}; template<class T> struct is_property : public std::is_base_of<Property, T> {}; template <class T> auto eval(const T& v) -> typename std::enable_if < is_property<T>::value, decltype(v.get())>::type { return v.get(); } template <class T> auto eval(const T& v) -> typename std::enable_if < !is_property<T>::value, decltype(v) >::type { return v; } #define READ_ONLY_PROPERTY(CLASSTYPE, GETFUNC, GETTYPE, name)\ struct CLASSTYPE_##name : public Property\ {\ template<class T> operator T()\ {\ return get();\ }\ GETTYPE operator ()() const \ {\ return get();\ }\ operator GETTYPE()\ {\ return get();\ }\ GETTYPE get() const \ {\ return reinterpret_cast<const CLASSTYPE*>(this - offsetof(CLASSTYPE, name))->GETFUNC();\ }\ } name #define PROPERTY(CLASSTYPE, GETFUNC, SETFUNC,GETTYPE, name)\ struct CLASSTYPE_##name : public Property\ {\ typedef CLASSTYPE_##name MyType;\ template<class T> operator T()\ {\ return get();\ }\ GETTYPE operator ()() const \ {\ return get();\ }\ operator GETTYPE()\ {\ return get();\ }\ GETTYPE get() const \ {\ return reinterpret_cast<const CLASSTYPE*>(this - offsetof(CLASSTYPE, name))->GETFUNC();\ }\ template <class T> typename std::enable_if<is_property<T>::value, MyType&>::type\ operator =(T&& value)\ {\ reinterpret_cast<CLASSTYPE*>(this - offsetof(CLASSTYPE, name))->SETFUNC(std::forward<T>(value.get()));\ return *this;\ }\ template <class T> typename std::enable_if<!is_property<T>::value, MyType&>::type\ operator =(T&& value)\ {\ reinterpret_cast<CLASSTYPE*>(this - offsetof(CLASSTYPE, name))->SETFUNC(std::forward<T>(value));\ return *this;\ }\ } name /// === Arithmetic operators === //Addition template <class TA, class TB> auto operator +(const TA& a, const TB& b) -> typename std::enable_if < is_property<TA>::value || is_property<TB>::value, decltype(eval(a) + eval(b)) >::type { return eval(a) + eval(b); } //Subtraction template <class TA, class TB> auto operator -(const TA& a, const TB& b) -> typename std::enable_if < is_property<TA>::value || is_property<TB>::value, decltype(eval(a) - eval(b)) >::type { return eval(a) - eval(b); } //Unary plus (integer promotion) //TODO //Unary minus (additive inverse) //TODO //Multiplication template <class TA, class TB> auto operator *(const TA& a, const TB& b) -> typename std::enable_if < is_property<TA>::value || is_property<TB>::value, decltype(eval(a) * eval(b)) >::type { return eval(a) * eval(b); } //Division template <class TA, class TB> auto operator /(const TA& a, const TB& b) -> typename std::enable_if < is_property<TA>::value || is_property<TB>::value, decltype(eval(a) / eval(b)) >::type { return eval(a) / eval(b); } //Modulo (remainder) template <class TA, class TB> auto operator %(const TA& a, const TB& b) -> typename std::enable_if < is_property<TA>::value || is_property<TB>::value, decltype(a.get() % b) >::type { return eval(a) % eval(b); } //Increment Prefix template <class TA> auto operator ++(TA& a) -> typename std::enable_if < is_property<TA>::value, decltype(a.get()) >::type { a = (a.get() + 1); return a.get(); } //Increment Suffix template <class TA> auto operator ++(TA& a, int) -> typename std::enable_if < is_property<TA>::value, decltype(a.get()) >::type { auto temp = a.get(); a = (a.get() + 1); return temp; } //Decrement Prefix template <class TA> auto operator --(TA& a) -> typename std::enable_if < is_property<TA>::value , decltype(a.get()) >::type { a = (a.get() - 1); return a.get(); } //Decrement Suffix template <class TA> auto operator --(TA& a, int) -> typename std::enable_if < is_property<TA>::value , decltype(a.get()) >::type { auto temp = a.get(); a = (a.get() - 1); return temp; } // ===Comparison operators/relational operators=== //Equal to template <class TA, class TB> auto operator ==(const TA& a, const TB& b) -> typename std::enable_if < is_property<TA>::value || is_property<TB>::value, decltype(eval(a) == eval(b)) >::type { return eval(a) == eval(b); } //Not equal to template <class TA, class TB> auto operator !=(const TA& a, const TB& b) -> typename std::enable_if < is_property<TA>::value || is_property<TB>::value, decltype(eval(a) != eval(b)) >::type { return eval(a) != eval(b); } //Greater than //bug clang? decltype(eval(a) > eval(b)), bool works template <class TA, class TB> auto operator > (const TA& a, const TB& b) -> typename std::enable_if < is_property<TA>::value || is_property<TB>::value, bool >::type { return eval(a) > eval(b); } //Less than template <class TA, class TB> auto operator <(const TA& a, const TB& b) -> typename std::enable_if < is_property<TA>::value || is_property<TB>::value, decltype(eval(a) < eval(b)) >::type { return eval(a) < eval(b); } //Greater than or equal to template <class TA, class TB> auto operator >=(const TA& a, const TB& b) -> typename std::enable_if < is_property<TA>::value || is_property<TB>::value, decltype(eval(a) >= eval(b)) >::type { return eval(a) >= eval(b); } //Less than or equal to template <class TA, class TB> auto operator <=(const TA& a, const TB& b) -> typename std::enable_if < is_property<TA>::value || is_property<TB>::value, decltype(eval(a) <= eval(b)) >::type { return eval(a) <= eval(b); } // === Logical operators === //Logical negation (NOT) template <class TA> auto operator !(const TA& a) -> typename std::enable_if < is_property<TA>::value , decltype(!eval(a)) >::type { return !a.get(); } //Logical AND //Logical OR // ===Bitwise operators=== //Bitwise NOT template <class TA> auto operator ~(const TA& a) -> typename std::enable_if < is_property<TA>::value , decltype(~a.get()) >::type { return ~a.get(); } //Bitwise AND //Bitwise XOR template<class Elem, class Traits, class TB> inline typename std::enable_if < is_property<TB>::value, std::basic_ostream<Elem, Traits>& >::type operator << (std::basic_ostream<Elem, Traits>& os, const TB& b) { return os << b.get(); } //Bitwise right shift //TODO //===Compound assignment operators=== //Addition assignment template <class TA, class TB> auto operator += (TA& a, const TB& b) -> typename std::enable_if < is_property<TA>::value, TA& >::type { a = (a.get() + b); return a; } //Subtraction assignment template <class TA, class TB> auto operator -= (TA& a, const TB& b) -> typename std::enable_if < is_property<TA>::value, TA& >::type { a = (a.get() - b); return a; } //Multiplication assignment template <class TA, class TB> auto operator *= (TA& a, const TB& b) -> typename std::enable_if < is_property<TA>::value, TA& >::type { a = (a.get() * b); return a; } //Division assignment template <class TA, class TB> auto operator /= (TA& a, const TB& b) -> typename std::enable_if < is_property<TA>::value, TA& >::type { a = (a.get() / b); return a; } //Modulo assignment template <class TA, class TB> auto operator %= (TA& a, const TB& b) -> typename std::enable_if < is_property<TA>::value, TA&>::type { a = (a.get() % b); return a; } //Bitwise AND assignment template <class TA, class TB> auto operator &= (TA& a, const TB& b) -> typename std::enable_if < is_property<TA>::value, decltype(a.get() &= b) >::type { return a = (a.get() & b); } //Bitwise OR assignment template <class TA, class TB> auto operator |= (TA& a, const TB& b) -> typename std::enable_if < is_property<TA>::value, decltype(a.get() |= b) >::type { return a = (a.get() | b); } //Bitwise XOR assignment template <class TA, class TB> auto operator ^= (TA& a, const TB& b) -> typename std::enable_if < is_property<TA>::value , decltype(a.get() ^= b) >::type { return a = (a.get() ^ b); } //Bitwise left shift assignment template <class TA, class TB> auto operator <<= (TA& a, const TB& b) -> typename std::enable_if < is_property<TA>::value, decltype(a.get() <<= b) >::type { return a.get() <<= b; } //Bitwise right shift assignment template <class TA, class TB> auto operator >>= (TA& a, const TB& b) -> typename std::enable_if < is_property<TA>::value, decltype(a.get() >>= b) >::type { return a.get() >>= b; } //==Member and pointer operators== //==Other operators==
Testing#include <cassert> #include <string> #include <iostream> #include "properties.h" struct X { std::string m_text; void set_text(std::string value) { m_text = value; } std::string get_text() const { return m_text; } PROPERTY(X, get_text,set_text, std::string, text); int m_width; void set_width(int value) { m_width = value; } int get_width() const { return m_width; } PROPERTY(X, get_width, set_width, int, width); bool m_b; void set_b(bool value) { m_b = value; } bool get_b() const { return m_b; } PROPERTY(X, get_b, set_b, bool, b); }; struct X2 { std::string m_text2; void set_text2(const char* value) { m_text2 = value; } void set_text2(std::string value) { m_text2 = value; } const std::string& get_text2() const { return m_text2; } PROPERTY(X2, get_text2, set_text2, const std::string&, text2); int m_width; void set_width(int value) { m_width = value; } int get_width() const { return m_width; } PROPERTY(X, get_width, set_width,int, width); bool m_b; bool get_b() const { return m_b; } READ_ONLY_PROPERTY(X, get_b, bool, b); }; void f(int) {} void f1(const int) {} void f2(int&) {} void f3(double) {} void f4(const std::string&) {} template <typename R, typename C> R get_return_type(R (C::*)(void)); int main() { X x; X2 x2; bool b = x.text <= x2.text2; b = x2.text2 <= x.text; x.text = x2.text2; x.text == x2.text2; x.text != x2.text2; x.text != "aa"; x.text = "aa"; x.text = x.text + "b"; x.text = "a" + x.text; x.text += "c"; x.text = x.text + x2.text2; x2.text2="text2"; b = (x.text == "aa"); b = x.text > x2.text2; b = x.text < x2.text2; b = x.text < "a"; //x2.b = true; //b is read only f4(x.text); b = x.text <= x2.text2; x.width = !x.width; x.width = 1; x.width += 1; x.width -= 1; x.width *= 1; x.width /= 1; x.width = x.width == 2 || x.width == 1; x.width = x.width && x.width ; b = x2.b; x.b = false; x.b = !x.b; x.b == x2.b; x.width++; x.width--; x.b = x.b || 1; x.b = x.b || x2.b; x.width = x.width && 1; x.width = x.width && x.width; x.width += x.width; x.width -= 1; x2.width = 2; x.width == x2.width; x.width++; x.width >= 1; x.width >= x2.width; x.width <= x2.width; b = x.width < x2.width; b = x.width > x2.width; f(x.width); f1(x.width); ++x.width; x.width = x2.width + 1; x.width = x.width + x2.width; x.width = x.width - x2.width; x.width = 5; assert(x.width % 2 == 1); x.width = 1; x2.width = 2; assert(x2.width > x.width); assert(x.width < x2.width); x.width <= x2.width; x.width >= x2.width; x.width != x2.width; if (x.text().empty()) { } x.text().clear(); //ok //x2.text2().clear(); // error std::cout << x.width; std::cout << x.text; } History
Limitations of this implementation
The workaround is to use operator () bool b = x.prop().empty(); See also
Compiling using Visual C++ 11 PreviewUsing VC++ 11 preview or Clang is possible to find out the "get" return type. #include <type_traits> #include <ostream> struct Property {}; template<class T> struct is_property : public std::is_base_of<Property, T> {}; template <class T> auto eval(const T& v) -> typename std::enable_if < is_property<T>::value, decltype(v.get())>::type { return v.get(); } template <class T> auto eval(const T& v) -> typename std::enable_if < !is_property<T>::value, decltype(v) >::type { return v; } template <typename R, typename O> R ReturnType(R (O::*)(void) const) { return R(); } #define READ_ONLY_PROPERTY(CLASSTYPE, GETFUNC, name)\ struct CLASSTYPE_##name : public Property\ {\ typedef decltype(ReturnType(&CLASSTYPE::GETFUNC)) GETTYPE;\ template<class T> operator T()\ {\ return get();\ }\ GETTYPE operator ()() const \ {\ return get();\ }\ operator GETTYPE()\ {\ return get();\ }\ GETTYPE get() const \ {\ return reinterpret_cast<const CLASSTYPE*>(this - offsetof(CLASSTYPE, name))->GETFUNC();\ }\ } name #define PROPERTY(CLASSTYPE, GETFUNC, SETFUNC, name)\ struct CLASSTYPE_##name : public Property\ {\ typedef CLASSTYPE_##name MyType;\ typedef decltype(ReturnType(&CLASSTYPE::GETFUNC)) GETTYPE;\ template<class T> operator T()\ {\ return get();\ }\ GETTYPE operator ()() const \ {\ return get();\ }\ operator GETTYPE()\ {\ return get();\ }\ GETTYPE get() const \ {\ return reinterpret_cast<const CLASSTYPE*>(this - offsetof(CLASSTYPE, name))->GETFUNC();\ }\ template <class T> typename std::enable_if<is_property<T>::value, MyType&>::type\ operator =(T&& value)\ {\ reinterpret_cast<CLASSTYPE*>(this - offsetof(CLASSTYPE, name))->SETFUNC(std::forward<T>(value.get()));\ return *this;\ }\ template <class T> typename std::enable_if<!is_property<T>::value, MyType&>::type\ operator =(T&& value)\ {\ reinterpret_cast<CLASSTYPE*>(this - offsetof(CLASSTYPE, name))->SETFUNC(std::forward<T>(value));\ return *this;\ }\ } name ... Sample of use: ...
PROPERTY(X, get_width, set_width, width);
...
READ_ONLY_PROPERTY(X, get_b, b);
...
|