【问题标题】:Is there an alternative for visual C++ __declspec (property declaration attribute) in clang and gcc?在 clang 和 gcc 中是否有 Visual C++ __declspec(属性声明属性)的替代方法?
【发布时间】:2020-01-22 05:20:53
【问题描述】:

有一个 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),但我不是在寻找那个。

【问题讨论】:

  • 此扩展适用于 COM。

标签: c++ gcc visual-c++ clang


【解决方案1】:

是的,

this link

__declspec(property(get=..,put=..)) 得到 clang 的完全支持,这是 gcc 对 Microsoft 语言功能的支持的继承。

我一直在clang中使用它;它非常适合封装和重构。我帮助调试和推广了正确的 clang 实现。

使用array accessor properties 进行优化非常出色。

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++ custom-attribute简写:

[[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
}

您不需要使用我上面展示的任何 template 用法或 array accessor 用法。我只是为了说明超越可以用属性做什么以及利用重载函数

我使用-Wattributes 控制有关[[msvc::...]] 未定义的警告。使用这种模式,我的代码既可以为未来做好准备,又可以更清晰、更一致地阅读。

给定属性仅适用于实例。将它们放在类型上的技术是在类型上使用空单例:

struct T {
  static inline struct K {
    ..declare properties on `k` here..
  } k;
  .. whatever you are doing with this `T` type.
};

现在您可以通过以下方式访问该类/静态属性:

T::k.property ..

【讨论】:

    【解决方案2】:

    虽然 C++ 不提供对智能可覆盖运算符的支持(并且没有 gcc 扩展),但该语言允许您使用其现有功能来实现它。
    以下示例(假定未涵盖所有情况!)显示了使用本机 C++ 11 或更高版本的可能解决方案。
    我们可以使用虚拟覆盖来覆盖属性,但这不是现代智能属性在其他语言(如 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;
    }
    

    用类似的东西编译:
    c++ -std=c++11 test.cpp -o test
    运行:
    ./test

    【讨论】:

    • 感谢@MaximEgorushkin,它适用于clang,修复了它也适用于gcc,并且还使自定义代码成为一种清晰的方法。
    • 谢谢,重载复制和引用运算符是个好主意。但是如果我想在 getter 方法中构造一个对象并返回那个对象呢?通过重载运算符 & 我需要返回一个引用,但我不能引用一个临时的。
    • @IterAtor 你可以创建另一个Property 模板并让它使用值而不是引用,也许称它为ValueProperty,然后执行如下操作:T get() constoperator T() const。如果按价值计算是你想要的,你就可以做到。
    • 我试过operator T() const,但由于它是一个类型转换运算符,它在所有情况下都不能按预期工作:cpp.sh/2uad2
    • @IterAtor operator T() 不会自动工作,您需要显式或隐式转换它。在此处查看示例:cpp.sh/2dajb
    【解决方案3】:

    有支持

    请参阅我的示例:https://godbolt.org/z/PobB_3

    虽然您必须使用-fdeclspec-fms-extensions 将其打开

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-05-17
      • 1970-01-01
      • 2017-04-06
      • 1970-01-01
      • 2011-10-24
      • 1970-01-01
      • 2023-03-08
      • 2018-04-26
      相关资源
      最近更新 更多