6

我正在尝试设置类似于 c# 属性概念的语法糖。

我读过这篇文章:C#-like properties in native c++? . 这很有帮助,但缺乏我想要的设计。我也恭敬地不同意那里提出的几个断言,即属性概念是不好的 oo 形式,因为我看不出一组名为 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使用。

4

2 回答 2

2

所以基本上,我需要一个允许 someClass 成为虚拟接口的设计

如果我对您的理解正确,则此意图与您提出的解决方案相矛盾。虚拟继承仅涉及虚拟成员函数。而且您无法动态继承嵌套结构,因为它是 C++ 不支持的词法元编程的主题。

我的建议是仔细考虑是否可以使对象不可变。然后,对 Builder/Factory 设计模式的一些调整将允许您消除对属性的使用。

您也可以考虑编写代码生成器。如果您选择了一个好的标记关键字,这可能很容易。

更新 我添加了一些代码来澄清我的建议。

首先我们准备一些辅助对象。它们应该放在库和客户端都可以访问的头文件中。这些对象永远不会被修改。

GetSetProp <>----> IGetSetProp <----- 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;
于 2012-02-22T10:07:17.353 回答
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;
}

// ------------------------------------------------------------------
于 2012-02-23T11:43:11.657 回答