__declspec(property({get|put})) C++11 可移植方案

一、背景

        Microsoft c++ __declspec(property({get|put}))特殊属性 前面的文章我们已经介绍了基于Microsoft特殊属性,但是这个属性再linux gcc是编译不通过的,因为这个是依赖编译器的属性,C++语言层面并不支持该语法,所以要想实现跨平台,我们还需要另辟蹊径。

二、属性类

        Microsoft c++ __declspec(property({get|put})本身就是一个语法糖,实现成员函数的调用就像成员变量一样方便,作为左值时调用set接口,作为右值时调用get结构,只不过编译器在编译时对左右值访问做了转码特殊处理,我们要是想c++实现相似功能,我们可以从左右值分开考虑。

(1) 左值

        可以利用c++重载赋值运算符实现其他类型到对象的转换,当参数为对象相同类型时就是赋值构造函数。

(2) 右值

        可以利用c++重载类型运算符,实现对象类型转换为其他类型。如 operator int();

根据上面左值右值的想法我们可以实现一个属性类,利用std::function实现绑定函数调用。

#include <iostream>
#include <functional>

//定义一个属性类
template<typename ValueType>
class Property
{
public:
	using Getter = std::function<ValueType()>;
	using Setter = std::function<void(const ValueType&)>;

	Property(Getter&& get,Setter&& set)
		:m_getter(std::move(get)),m_setter(std::move(set))
	{

	}

	Property& operator=(const ValueType& src)
	{
		m_setter(src);
		return *this;
	}

	operator ValueType() const
	{
		return m_getter();
	}

private:
	Property(const Property&) = delete;
	Property& operator=(const Property&) = delete;
	Getter m_getter;
	Setter m_setter;
};
class Test
{
	int m_data;
public:
	void setData(const int& d)
	{
		if (d > 0 && d < 10000)
		{
			m_data = d;
		}
	}
	
	int getData()
	{
		return m_data;
	}

	Property<int> data{ std::bind(&Test::getData,this),std::bind(&Test::setData,this,std::placeholders::_1) };
};
int main()
{
	Test t;
	t.data = 100;
	std::cout << t.data << std::endl;
}

通过Propert构造函数绑定getter和setter方法,做类型转换时调用。这样实现了成员函数通过对象访问,但是我们这边定义的毕竟是类,可能会发生赋值和拷贝,Property又将赋值构造和拷贝构造指定为了privite,而且通过delete属性删除了这两个函数的实现,在实际应用中Property也是作为类的成员,所以我们只需要保证Test类可以赋值和拷贝。通过委托构造保证propert中getter和setter始终指向有效函数地址;

改造Test类

class Test
{
	int m_data = 0;
public:

	Test() :data(std::bind(&Test::getData, this), std::bind(&Test::setData, this, std::placeholders::_1))
	{
	}

	Test(const Test&) :Test()
	{

	}

	Test& operator=(const Test& src)
	{
		if (this == &src)
		{
			return *this;
		}
		m_data = src.m_data;
		return *this;
	}

	void setData(const int& d)
	{
		if (d > 0 && d < 10000)
		{
			m_data = d;
		}
	}

	int getData()
	{
		return m_data;
	}

	Property<int> data;
};
int main()
{
	Test t;
	t.data = 100;
	std::cout << t.data << std::endl;
	
	Test t2;
	t2 = t1;
	std::cout << t2.data << std::endl;
	return 0;
}

这边只是简单了模拟了赋值构造,如果Test作为函数参数时,会发生拷贝,这个时候拷贝委托给普通构造保证了property中getter和setter的有效性。

三、运算

int main()
{
	Test t;
	t.data = 100;
	std::cout << t.data << std::endl;
	
	Test t2;
	t2.data = t.data * 2 + 90;
	std::cout << t2.data << std::endl;
	return 0;
}

四、更多类型转换

         在实际应用中我们可能需要不同类型可以隐式转换,这样我们处理不同类型时会大大方便,而不要再一个一个的调用转换函数。下面我们将Property支持一个int 到std::string的转换 ,这边我们可以偏特化一个property类来专门处理这个转换 而其他类型不受影响

template<typename ValueType,typename Enable = void>
class Property
{
public:
	using Getter = std::function<ValueType()>;
	using Setter = std::function<void(const ValueType&)>;

	Property(Getter&& get, Setter&& set)
		:m_getter(std::move(get)), m_setter(std::move(set))
	{

	}

	Property& operator=(const ValueType& src)
	{
		m_setter(src);
		return *this;
	}

	operator ValueType() const
	{
		return m_getter();
	}

private:
	Property(const Property&) = delete;
	Property& operator=(const Property&) = delete;
	Getter m_getter;
	Setter m_setter;
};

template<typename ValueType>
class Property<ValueType,typename std::enable_if_t<std::is_integral_v<ValueType>>>
{
public:
	using Getter = std::function<ValueType()>;
	using Setter = std::function<void(const ValueType&)>;

	Property(Getter&& get, Setter&& set)
		:m_getter(std::move(get)), m_setter(std::move(set))
	{

	}

	Property& operator=(const ValueType& src)
	{
		m_setter(src);
		return *this;
	}

	operator ValueType() const
	{
		return m_getter();
	}

	Property& operator=(const std::string& src)
	{
		m_setter(std::atof(src.c_str()));
		return *this;
	}

	operator std::string() const
	{
		return std::to_string(m_getter());
	}

private:
	Property(const Property&) = delete;
	Property& operator=(const Property&) = delete;
	Getter m_getter;
	Setter m_setter;
};
int main()
{
	Test t;
	t.data = std::string("1000");
	std::cout << t.data << std::endl;
	Test t2;
	t2.data = t.data * 2 + 90;
	std::string result = t2.data ;
	std::cout << result << std::endl;
	return 0;
}

五、总结

        虽然我们通过C++实现了Microsoft c++ __declspec(property({get|put}))特殊属性,但是我们本质上还是一个对象成员 ,如果在其他模板中使用时,需要特化property<>处理或者显示转换到实例化类型 如 (int)t.data;

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值