我正在尝试设置类似于c#属性概念的语法糖。
我读过这篇文章:C#-like properties in native c++?。这很有帮助,但缺乏我想要的设计。我还恭敬地不同意那里的几个断言,即属性概念是不好的形式,因为我没有看到一组标题为get_id()和set_id()的方法与暴露相同概念的运算符重载之间的区别,允许代码在使用类时更干净,并且通常不鼓励公共访问私有字段,也将公共实现与私有设计分离。
然而,我提出的代码(受到该链接的启发)真的非常难以维护。我想知道是否有人有任何建议来清理它,或者更重要的是,知道更好的方法来做到这一点。
class someClass
{
public:
struct someProperty_struct{
virtual someProperty_struct& operator=(const int&){return *this;}
virtual operator int(){return 0;}
}someProperty;
};
class derivedClass : someClass
{
int i;
public:
struct intProperty: someClass::someProperty_struct
{
protected:
friend class derivedClass;
derivedClass *outer;
public:
virtual someProperty_struct& operator=(const int& value){
outer->i = value;
return *this;}
virtual operator int(){return outer->i;}
};
derivedClass()
{
intProperty p = intProperty();
p.outer = this;
someProperty = p;
}
};
class consumerClass
{
public:
someClass* data;
consumerClass()
{
data = (someClass*)(new derivedClass());
}
void doSomething()
{
data->someProperty = 7;
int x = data->someProperty;
}
};
编辑1: 在我看来,我没有透露任何有关我的意图。它将用于调度程序,我们将对类中的所有数据进行大量比较和分配。 'someClass'将有效地成为数据的接口(需要的方法很少,大量数据应该相对透明)。它将在可执行文件将链接的静态库中定义。 'derivedClass'实际上是一个外部实现,在动态加载的dll中实现。这样做的原因是启用dll的“热交换”与另一个实现不同文件后端的dll。我们计划使用插件系统来实现xml,sqlite和mysql存储后端来加载它们。
基本上,我需要一种设计,允许someClass是一个由derivedClass继承的虚拟接口,它由工厂方法加载,通过插件系统传递,最后由consumerClass使用。
答案 0 :(得分:1)
基本上,我需要一种允许someClass成为虚拟的设计 接口
如果我理解正确,这个意图与你提出的解决方案相矛盾。虚拟继承仅涉及虚拟成员函数。并且你无法动态继承嵌套结构,因为它是词汇元编程的主题,C ++不支持。
我的建议是仔细考虑是否可以使对象不可变。然后,一些Builder / Factory设计模式的改编将允许您消除属性的使用。
您也可以考虑编写代码生成器。如果您选择了一个好的标记关键字,可能很容易。
<强>更新强> 我添加了一些代码来澄清我的建议。
首先,我们准备一些辅助对象。它们应放在可供库和客户端访问的头文件中。永远不会修改这些对象。
GetSetProp&lt;&gt; ----&gt; IGetSetProp&lt; ----- PropFunctionAdapter
template<class _Ty>
struct __declspec(novtable) IGetSetProp
{
typedef std::tr1::shared_ptr<IGetSetProp<_Ty>> ptr_t;
virtual void set_Prop(_Ty const& val);
virtual _Ty get_Prop() const;
};
template<typename _Ty>
class PropFunctionAdapter : public IGetSetProp<_Ty>
{
std::function<_Ty(void)> getter;
std::function<void(_Ty const&)> setter;
public:
PropFunctionAdapter(std::function<_Ty(void)> _getter, std::function<void(_Ty const&)> _setter)
: getter(_getter)
, setter(_setter)
{
// One may want to verify that getter and setter are not empty
}
virtual ~PropFunctionAdapter() throw() {}
inline static std::tr1::shared_ptr<typename PropFunctionAdapter<_Ty>> Create(std::function<_Ty(void)> _getter, std::function<void(_Ty const&)> _setter)
{
return std::make_shared<typename PropFunctionAdapter<_Ty>>(_getter, _setter);
}
public:
void set_Prop(_Ty const& val)
{
setter(val);
}
_Ty get_Prop() const
{
return getter();
}
};
template<class _Owner, class _Ty>
typename IGetSetProp<_Ty>::ptr_t CreateAdapter(_Owner& source, _Ty(_Owner::*getter)() const, void(_Owner::*setter)(_Ty const&))
{
return PropFunctionAdapter<_Ty>::Create(
std::tr1::bind(std::mem_fn(getter), &source),
std::tr1::bind(std::mem_fn(setter), &source, std::tr1::placeholders::_1));
}
template<class _Ty>
class GetSetProp
{
typename IGetSetProp<_Ty>::ptr_t prop;
public:
GetSetProp(typename IGetSetProp<_Ty>::ptr_t _prop)
: prop(_prop)
{
}
GetSetProp<_Ty>& operator= (_Ty const& val)
{
prop->set_Prop(val);
return *this;
}
operator _Ty() const
{
return prop->get_Prop();
}
};
同样,您可以定义GetProperty和SetProperty。
假设您有一个包含两个字段的数据协定:Pressure:int和Description:string。然后定义数据合同:
class BaseClass
{
public:
GetSetProp<int> Pressure;
GetSetProp<std::string> Description;
protected:
BaseClass(IGetSetProp<int>::ptr_t fieldA, IGetSetProp<std::string>::ptr_t fieldB)
: Pressure(fieldA)
, Description(fieldB)
{
}
virtual ~BaseClass() throw() {}
};
它在图书馆的实施:
class DerivedClass : public BaseClass
{
public:
// Here you initialize fields assigning them correspondent setters and getters
// You may define an ATL-like mapping in order to hide implementation details
DerivedClass()
: BaseClass(
CreateAdapter(*this, &DerivedClass::get_Pressure, &DerivedClass::set_Pressure)
, CreateAdapter(*this, &DerivedClass::get_Description, &DerivedClass::set_Description))
{
}
virtual ~DerivedClass() throw() {}
private:
void set_Pressure(int const& value)
{
val = value;
}
int get_Pressure() const
{
return val;
}
void set_Description(std::string const& description)
{
this->description = description;
}
std::string get_Description() const
{
return description;
}
private:
int val;
std::string description;
};
// The client-side code
DerivedClass d;
BaseClass& b = d;
b.Description = "Hello";
std::string descr = b.Description;
b.Pressure = 2;
int value = b.Pressure;
答案 1 :(得分:1)
创建一个必须作为要添加属性的类的成员实例化的属性助手类是一个坏主意,因为您将为此类的所有实例承担属性成员的成本。
提供与其他语言相近的“属性”机制的解决方案可以使用我稍后描述的内容在C ++中提供。
唯一的限制是您使用
访问该属性instance.property(); //获得
和
instance.property()= 42; //设置
#include <iostream>
#include <string>
using namespace std;
// ------------------------------------------------------------------
#define PROPERTY_GET_SET(CLASS, NAME, TYPE) GetSetProperty<CLASS, TYPE> NAME() { return GetSetProperty<CLASS, TYPE>(this, &CLASS::get_##NAME, &CLASS::set_##NAME); }
#define PROPERTY_GET(CLASS, NAME, TYPE) GetProperty<CLASS, TYPE> NAME() { return GetProperty<CLASS, TYPE>(this, &CLASS::get_##NAME); }
#define PROPERTY_SET(CLASS, NAME, TYPE) SetProperty<CLASS, TYPE> NAME() { return SetProperty<CLASS, TYPE>(this, &CLASS::set_##NAME); }
template <typename CLASS, typename TYPE>
struct GetSetProperty {
typedef TYPE (CLASS::*Getter_t)() const;
typedef void (CLASS::*Setter_t)(TYPE);
GetSetProperty(CLASS* instance, Getter_t getter, Setter_t setter) : m_instance(instance), m_getter(getter), m_setter(setter) {}
operator TYPE() const { return (this->m_instance->*this->m_getter)(); }
GetSetProperty<CLASS, TYPE>& operator=(TYPE value) { (this->m_instance->*this->m_setter)(value); return *this; }
CLASS* const m_instance;
const Getter_t m_getter;
const Setter_t m_setter;
};
template <typename CLASS, typename TYPE>
struct GetProperty {
typedef TYPE (CLASS::*Getter_t)() const;
GetProperty(CLASS* instance, Getter_t getter) : m_instance(instance), m_getter(getter) {}
operator TYPE() const { return (this->m_instance->*this->m_getter)(); }
CLASS* const m_instance;
const Getter_t m_getter;
};
template <typename CLASS, typename TYPE>
struct SetProperty {
typedef void (CLASS::*Setter_t)(TYPE);
SetProperty(CLASS* instance, Setter_t setter) : m_instance(instance), m_setter(setter) {}
SetProperty<CLASS, TYPE>& operator=(TYPE value) { (this->m_instance->*this->m_setter)(value); return *this; }
CLASS* const m_instance;
const Setter_t m_setter;
};
template <typename CLASS, typename TYPE>
ostream& operator<<(ostream& ostr, const GetSetProperty<CLASS, TYPE>& p) { ostr << (p.m_instance->*p.m_getter)(); return ostr; }
template <typename CLASS, typename TYPE>
ostream& operator<<(ostream& ostr, const GetProperty<CLASS, TYPE>& p) { ostr << (p.m_instance->*p.m_getter)(); return ostr; }
// ------------------------------------------------------------------
class Dummy
{
public:
Dummy() : m_value1(42) {}
PROPERTY_GET_SET(Dummy, Value1, int);
PROPERTY_GET_SET(Dummy, Value2, const string&);
protected:
virtual int get_Value1() const { return this->m_value1; }
virtual void set_Value1(int value) { this->m_value1 = value; }
virtual const string& get_Value2() const { return this->m_value2; }
virtual void set_Value2(const string& value) { this->m_value2 = value; }
private:
int m_value1;
string m_value2;
};
int main(int argc, char* argv[]) {
Dummy d;
cout << d.Value1() << endl;
d.Value1() = 3;
cout << d.Value1() << endl;
cout << d.Value2() << endl;
d.Value2() = "test";
cout << d.Value2() << endl;
return 0;
}
// ------------------------------------------------------------------