There is a Microsoft specific extension, which makes it possible to define property getters and setters like this:
// declspec_property.cpp
struct S {
int i;
void putprop(int j) {
i = j;
}
int getprop() {
return i;
}
__declspec(property(get = getprop, put = putprop)) int the_prop;
};
int main() {
S s;
s.the_prop = 5;
return s.the_prop;
}
Is there any way to define property declaration attribute with clang or gcc?
If I search for __declspec
, all I find is __declspec(dllexport)
, but I am not looking for that.
While C++ does not offer support for smart overridable operators (and there are no gcc extensions for that), the language allows you to implement it using it's existing features.
The following example (which does not assume to cover all cases!) shows a possible solution using native C++ 11 or higher.
We could use virtual overrides to override the properties, but that's not how modern smart properties work in other languages such as swift, C# etc, so instead - I'm using lambdas to inject overriding code for setters and getters.
// The following is by no means a FULL solution!
#include <functional>
#include <iostream>
#include <cassert>
template<typename T>
class Property {
public:
Property(){}
operator const T& () const {
// Call override getter if we have it
if (getter) return getter();
return get();
}
const T& operator = (const T& other) {
// Call override setter if we have it
if (setter) return setter(other);
return set(other);
}
bool operator == (const T& other) const {
// Static cast makes sure our getter operator is called, so we could use overrides if those are in place
return static_cast<const T&>(*this) == other;
}
// Use this to always get without overrides, useful for use with overriding implementations
const T& get() const {
return t;
}
// Use this to always set without overrides, useful for use with overriding implementations
const T& set(const T& other) {
return t = other;
}
// Assign getter and setter to these properties
std::function<const T&()> getter;
std::function<const T&(const T&)> setter;
private:
T t;
};
// Basic usage, no override
struct Test {
Property<int> prop;
};
// Override getter and setter
struct TestWithOverride {
TestWithOverride(){
prop.setter = [&](const int& other){
std::cout << "Custom setter called" << std::endl;
return prop.set(other);
};
prop.setter = std::bind(&TestWithOverride::setProp,this,std::placeholders::_1);
prop.getter = std::bind(&TestWithOverride::getProp,this);
}
Property<int> prop;
private:
const int& getProp() const {
std::cout << "Custom getter called" << std::endl;
return prop.get();
}
const int& setProp(const int& other){
std::cout << "Custom setter called" << std::endl;
return prop.set(other);
}
};
int main(int,char**){
Test t;
TestWithOverride t1;
t.prop = 1;
assert(t.prop == 1);
t1.prop = 1;
assert(t1.prop == 1);
/*
Expected output:
1. No aborts on assertions
2. Text:
Custom setter called
Custom getter called
*/
return 0;
}
Compile with something like:c++ -std=c++11 test.cpp -o test
Run:./test
See my example at: https://godbolt.org/z/PobB_3
Although you must turn it on with either -fdeclspec
or -fms-extensions
Yes,
See this link
__declspec(property(get=..,put=..))
is fully supported by clang and that is a carryover from support in gcc for this Microsoft language feature.
I use it all the time in clang; it is fabulous for encapsulation and refactoring. I helped debug and promote the correct clang implementation.
It does a great job optimizing with array accessor properties.
foo[expr0][expr1] = expr2;
Where foo
is
__declspec(property(put=foo_set)) foo_t foo[];
foo_t foo_set(T0 expr0, T1 expr1, foo_t expr2) {..}
It also works excellently with templated functions, making it ideal for efficient overloading and forward referencing.
template<typename T0, typename T1, typename foo_ta = foo_t>
foo_ta foo_set(T0 expr0, T1 expr1, foo_ta expr2) {..}
The only bummer is that you can't use a modern c++ custom-attribute shorthand of:
[[msvc::property(put = foo_set)]] foo_t foo[];
So I use this pattern:
[[msvc::property(put = foo_set)]] __declspec(property(put = foo_set))
foo_t foo[];
template<typename T0, typename T1, typename foo_ta = foo_t>
foo_ta foo_set(T0 expr0, T1 expr1, foo_ta expr2) {..}
OR
template<bool fFwd=true>
bar_t bar_get() {
// reference any types declared later in your code
// template mechanics mean they will not be resolved until
// first **property** use
}
You do not need to use any of the template usage or array accessor usage I showed above. I only provided that to illustrate above and beyond what can be done with properties and making use of overloaded functions.
I control warnings about [[msvc::...]]
being undefined using -Wattributes
. With that pattern my code is both ready for the future, and reads cleanly and more consistently.
Given properties only work on instances. The technique to place them on types is to use an empty singleton on a type:
struct T {
static inline struct K {
..declare properties on `k` here..
} k;
.. whatever you are doing with this `T` type.
};
Now you can access that class/static properties as:
T::k.property ..
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With