Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there an alternative for visual C++ __declspec (property declaration attribute) in clang and gcc?

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.

like image 247
Iter Ator Avatar asked Sep 21 '19 12:09

Iter Ator


3 Answers

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

like image 87
Moshe Gottlieb Avatar answered Nov 14 '22 21:11

Moshe Gottlieb


clang has support

See my example at: https://godbolt.org/z/PobB_3

Although you must turn it on with either -fdeclspec or -fms-extensions

like image 35
darune Avatar answered Nov 14 '22 23:11

darune


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 ..
like image 26
smallscript Avatar answered Nov 14 '22 23:11

smallscript