在clang和gcc中,是否有替代Visual C++ __declspec(属性声明属性)的方法?

kdfy810k  于 2022-11-13  发布在  其他
关注(0)|答案(4)|浏览(446)

有一个特定于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;
}

有没有办法用clang或gcc来定义属性声明属性?如果我搜索__declspec,我找到的只是__declspec(dllexport),但我并不是在寻找它。

pnwntuvh

pnwntuvh1#

虽然C不支持智能可重写运算符(也没有gcc扩展),但该语言允许您使用其现有特性来实现它。
下面的示例(并不假定涵盖所有情况!)显示了使用原生C
11或更高版本的可能解决方案。
我们可以使用虚拟覆盖来覆盖属性,但这不是敕勒智能属性在其他语言(如swift、C#等)中的工作方式,因此,我使用lambda来为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;
}

编译如下内容:
c++ -std=c++11 test.cpp -o test
运行时间:
./test

nimxete2

nimxete22#

clang具有支持

请参见我的示例:https://godbolt.org/z/PobB_3
尽管您必须使用-fdeclspec-fms-extensions打开它

zlhcx6iw

zlhcx6iw3#

是的,
查看此链接
__declspec(property(get=..,put=..))完全受 clang 支持,这是 gcc 对此Microsoft语言功能的支持的延续。
我用它所有的时间在 clang;它对于封装和重构是极好的。我帮助调试和促进了正确的 clang 实现。
它在优化数组访问器属性方面做得很好。

foo[expr0][expr1] = expr2;

其中,foo

__declspec(property(put=foo_set)) foo_t foo[];
foo_t foo_set(T0 expr0, T1 expr1, foo_t expr2) {..}

它还可以很好地处理模板化函数,使其成为高效重载和前向引用的理想选择。

template<typename T0, typename T1, typename foo_ta = foo_t>
foo_ta foo_set(T0 expr0, T1 expr1, foo_ta expr2) {..}

唯一 * 令人沮丧 * 的是,您不能使用现代c++自定义属性的简写:

[[msvc::property(put = foo_set)]] foo_t foo[];

因此,我使用以下 * 模式 *:

[[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) {..}

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
}
  • 您不需要使用任何模板用法或数组访问器用法。我提供这些用法只是为了说明以上和超出可以使用属性和使用重载函数的内容。
  • 我使用-Wattributes控制有关[[msvc::...]]未定义的警告。使用该模式,我的代码既为将来做好了准备,而且读起来更干净、更一致。*

给定的属性只在instances上有效。将它们放在类型上的技术是在类型上使用空的singleton:

struct T {
  static inline struct K {
    ..declare properties on `k` here..
  } k;
  .. whatever you are doing with this `T` type.
};
  • 现在,您可以访问该类/静态属性,如下所示:*
T::k.property ..
gmxoilav

gmxoilav4#

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 {
        static_assert(!WriteOnly, "Cannot access write-only property.");
        // Call override getter if we have it
        if (getter) return getter();
        return get();
    }
    const T& operator = (const T& other) {
        static_assert(!ReadOnly, "Cannot set read-only property.");
        // 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;
};

相关问题