简体   繁体   English

clang 和 gcc 中的视觉 C++ __declspec(属性声明属性)是否有替代方案?

[英]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:有一个 Microsoft 特定的扩展,可以像这样定义属性 getter 和 setter:

// 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?有没有办法用 clang 或 gcc 定义属性声明属性? If I search for __declspec , all I find is __declspec(dllexport) , but I am not looking for that.如果我搜索__declspec ,我发现的只是__declspec(dllexport) ,但我不是在寻找那个。

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.虽然 C++ 不提供对智能可覆盖运算符的支持(并且没有 gcc 扩展),但该语言允许您使用其现有功能来实现它。
The following example (which does not assume to cover all cases.) shows a possible solution using native C++ 11 or higher.以下示例(假定未涵盖所有情况。)显示了使用本机 C++ 11 或更高版本的可能解决方案。
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.我们可以使用虚拟覆盖来覆盖属性,但这不是现代智能属性在其他语言(例如 swift、C# 等)中的工作方式,所以相反 - 我使用 lambdas 为 setter 和 getter 注入覆盖代码。

// 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

has support 有支持

See my example at: https://godbolt.org/z/PobB_3请参阅我的示例: https://godbolt.org/z/PobB_3

Although you must turn it on with either -fdeclspec or -fms-extensions尽管您必须使用-fdeclspec-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. __declspec(property(get=..,put=..))完全受clang支持,这是gcc中支持此 Microsoft 语言功能的结转。

I use it all the time in clang ;我一直在clang中使用它; it is fabulous for encapsulation and refactoring.它非常适合封装和重构。 I helped debug and promote the correct clang implementation.我帮助调试和推广了正确的clang实现。

It does a great job optimizing with array accessor properties .它对数组访问器属性进行了很好的优化。

foo[expr0][expr1] = expr2;

Where foo is foo在哪里

__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:唯一的遗憾是您不能使用现代c++自定义属性简写:

[[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 .我使用-Wattributes控制有关[[msvc::...]]未定义的警告。 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:将它们放在类型上的技术是在类型上使用空的 singleton:

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 ..

Moshe Gottlieb's excellent answer can be improved to allow for read-only or write-only properties at compile time with the following code.可以使用以下代码改进 Moshe Gottlieb 的出色答案,以在编译时允许只读或只写属性。

// Simulate Microsoft-specific extension:
//    __declspec(property(get = getprop, put = putprop)) type propname

template<typename T, bool ReadOnly = false, bool WriteOnly = false>
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 {
        static_assert(!WriteOnly, "Cannot access write-only property.");
        return t;
    }
    // Use this to always set without overrides, useful for use with overriding implementations
    const T& set(const T& other) {
        static_assert(!ReadOnly, "Cannot set read-only property.");
        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;
};

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM