从不透明的C结构

时间:2018-03-08 12:17:07

标签: c++ c++11

我正在使用现有的C库(我无法修改),其中某些结构具有必须通过特定的setter和getter访问的不透明字段,如下面的粗略示例(想象x是私人的,即使它是用C)写的。

struct CObject {
    int x;
};

void setCObjectX(CObject* o, int x) {
    o->x = x;
}

int getCObjectX(CObject* o) {
    return o->x;
}

我正在编写私有拥有这些类型结构的类,有点像包装器,虽然更复杂。我想以方便的方式公开相关领域。起初,我只是在必要时编写setter和getter。但是,我想到了其他的东西,我想知道这个方法是否有任何缺点。它使用函数指针(std::function)来存储C setter-getter对,并将它们呈现为直接访问字段而不是函数。

这是我写的通用类,用于帮助定义这样的"假的"字段:

template<typename T>
struct IndirectField {
    void operator=(const T& value) {
        setter(value);
    }
    auto operator()() const -> T {
        return *this;
    }
    operator T() const {
        return getter();
    }

    std::function<void(const T&)> setter;
    std::function<T()> getter;
};

通过在C ++类中定义实例并使用相应的C函数设置settergetter来使用它:

IndirectField<int> x;
// ...
x.setter = [=](int x) {
    setCObjectX(innerObject.get(), x);
};
x.getter = [=]() {
    return getCObjectX(innerObject.get());
};

Here is a complete, working code for testing.

使用这种方法有什么缺点吗?它会导致最终的危险行为吗?

3 个答案:

答案 0 :(得分:2)

我在您的解决方案中遇到的最大问题是std::function个对象占用了IndirectFieldCPPObject的每个实例内的空间,即使CObject类型相同。 / p>

您可以通过将函数指针设置为模板参数来解决此问题:

template<typename T,typename R,void setter(R*,T),T getter(R*)>
struct IndirectField {
    IndirectField(R *obj) : obj(obj) {
    }
    void operator=(const T& value) {
        setter(obj, value);
    }
    auto operator()() const -> T {
        return *this;
    }
    operator T() const {
        return getter(obj);
    }
private:
    R *obj;
};

以下是如何使用此实现:

class CPPObject {
    std::unique_ptr<CObject,decltype(&freeCObject)> obj;
public:
    CPPObject()
    :   obj(createCObject(), freeCObject)
    ,   x(obj.get())
    ,   y(obj.get()) {
    }
    IndirectField<int,CObject,setCObjectX,getCObjectX> x;
    IndirectField<double,CObject,setCObjectY,getCObjectY> y;
};

此方法为每std::functionCObject*指针交换两个IndirectField个对象。不幸的是,存储此指针是必需的,因为您无法从模板内的上下文中获取它。

Your modified demo.

答案 1 :(得分:1)

  

使用这种方法有什么缺点吗?

您的代码中需要强调一些事项:

  1. 你的吸气者&amp; setters,不属于同类,打破封装。 (你真的想把自己永久地绑在这个图书馆吗?)
  2. 您的示例显示正在进行大量复制;这将比它需要的慢。 (auto operator()()operator T()仅限2)。
  3. 它占用了比你需要的更多的内存,并且增加了比传递Cobject更多的复杂性。如果你不想让事情知道它是一个CObject,那么创建一个抽象类并传递该抽象类(例如见下文)。
  4.   

    它会导致最终的危险行为吗?

    封装的破坏将导致x从任意数量的路线改变;并强迫其他事物知道它是如何存储在对象中的。这很糟糕。

    创建IndirectField意味着每个对象都必须以这种方式拥有getter和setter;这将是一场维护噩梦。

    我觉得你真正想要的是:

    struct xProvider {
        virtual int getX() const = 0;
        virtual void setX() = 0;
    };
    struct MyCObject : xProvider {
    private:
        CObject obj;
    public:
        int getX() const override {return obj.x;}
        CObject& getRawObj() {return obj;}
        // etc ...
    }
    

    然后你只需将参考/指针传递给xProvider。 这将消除对此外部C库的依赖;如果你认为合适,允许你用你自己的测试结构或一个全新的库替换它;无需使用它重写所有代码

答案 2 :(得分:0)

默认情况下,public中的

(发布时)所有字段均为protected,因此客户端软件可以访问这些字段。我想让它们可以被派生类访问(如果您知道字段合同并且想要以明确定义的方式访问它,则不需要重新实现任何内容)它们是private。如果您希望无人访问它们,请将它们标记为private

如果此类软件的作者不希望您触及这些字段,他会将其标记为set_myField(),然后您将无所事事,但要适应这种行为。不这样做会给你带来不良后果。

假设您创建了一个使用<select>方法修改的字段,该方法可以在您进行更改时随时调用侦听器列表。如果绕过方法访问功能,将绕过所有侦听器(其中许多来源不明),并且不会收到有关字段更改的通知。这在对象编程中非常常见,因此您必须遵守作者对您施加的规则。