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

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

本文关键字:属性 声明 clang 方案 declspec 是否 gcc 视觉 C++      更新时间:2023-10-16

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

虽然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

clang 有支持

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

尽管您必须使用-fdeclspec-fms-extensions将其打开

是的,

查看此链接

__declspec(property(get=..,put=..))完全由clang支持,这是gcc中对此Microsoft语言功能的支持的延续。

我一直在叮当声中使用它;它非常适合封装和重构。我帮助调试和推广了正确的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::...]]未定义的警告。有了这种模式,我的代码既为未来做好了准备,又可以干净、更一致地读取。

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

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

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

T::k.property ..

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

只是对Moshe Gottlieb的回答的一个警告。虽然这确实很好用,但它并不能涵盖所有情况。如果对象被序列化然后反序列化(特别是我尝试在传递给 Cuda 内核的对象上使用它(,引用显然停止工作,并且你会得到一个损坏的对象。所以它与"__declspec(属性(得到...",在这种情况下有效。