在运行时为调用函数选择对象,没有基类和模板

时间:2015-10-28 13:35:19

标签: c++

我有两个具有相同接口方法的类:

struct ImplGenerated {
    int foo(int x, int y);
    void bar(double x);
    ....
};

struct ImplCustom {
    int foo(int x, int y);
    void bar(double x);
    .....
};

和课程包装:

struct Wrapper {
    Wrapper(ImplGenerated * i): m_generated(i), m_custom(0) {}
    Wrapper(ImplCustom * i): m_generated(0), m_custom(i) {}
    int foo(int x, int y);
    void bar(double x);
    ....
private:
    ??? getImpl();
    ImplGenerated * m_generated;
    ImplCustom * m_custom;
};

int Wrapper::foo(int x, int y) {
    return getImpl()->foo(x, y);
}
void Wrapper::bar(double x) {
    getImpl()->bar(x);
}

是否可以编写一些C ++构造(类或任何其他但不是宏)而不是 getImpl()来解析当前的实现对象并调用相应的方法? 像这样:

???? getImpl() {
    return m_custom ? m_custom : m_generated;
}

注意: 只能应用对ImplCustom的更改(添加基类或制作模板或其他内容),ImplGenerated由外部项目自动生成,因此无法更改(添加基类是不可能的)。 包装器不能是模板,因为是接口类。

更新: 从ImplGenerated派生ImplCustom是不可能的。

4 个答案:

答案 0 :(得分:4)

我在这里看到的解决方案是创建一个包装器

此解决方案的目标是为两个不相关的类生成一个接口。

让我们从制作Base classe开始:

struct ImplInterface {
    virtual int foo(int x, int y) = 0;
    virtual void bar(double x) = 0;
    // ...
};

现在你可以为你得到的每个Impl创建包装器:

struct GeneratedWrapper : ImplInterface {
    virtual int foo(int x, int y) {
        _impl.foo(x, y);
    }

    virtual void bar(double x) {
        _impl.bar(x);
    }

private:
    ImplGenerated _impl;
};

struct CustomWrapper : ImplInterface {
    virtual int foo(int x, int y) {
        _impl.foo(x, y);
    }

    virtual void bar(double x) {
        _impl.bar(x);
    }

private:
    ImplCustom _impl;
};

现在您可以像这样使用这些包装器:

ImplInterface* wrapper = new GeneratedWrapper(implGenerated);

使用模板可以缩短这个方法,让我们为你的新界面制作一个包装器:

template<typename T>
struct EveryImplWrapper : ImplInterface {
    virtual int foo(int x, int y) {
        _impl.foo(x, y);
    }

    virtual void bar(double x) {
        _impl.bar(x);
    }

private:
    T _impl;
};

现在您可以像这样使用它:

ImplInterface* = new EveryImplWrapper<ImplCustom>(implCustom);

答案 1 :(得分:2)

如果无法修改现有类,仍可以添加外观以提供事后多态。那就是:

  

Wrapper不能是模板,因为是接口类

只是部分正确。你可以有一个非模板化的接口(ABC)和一个模板化的具体子类。

// publically visible parts
struct ImplInterface {
    virtual ~ImplInterface() {}
    virtual int foo(int x, int y) = 0;
    virtual void bar(double x) = 0;
    ....
};

struct Wrapper {
    // ...
    ImplInterface *Wrapper::getImpl();
    // ... do you want to keep the same impl selection across calls?
    ImplInterface *m_impl;
};

// implementation details can be hidden in a cpp file

template <typename RealImpl>
struct ImplFacade: ImplInterface {
    RealImpl pimpl_;

    explicit ImplFacade(RealImpl *impl) : pimpl_(impl) {}

    int foo(int x, int y) override { return pimpl_->foo(x,y); }
    void bar(double x)    override { pimpl_->bar(x); }
};

ImplInterface *Wrapper::getImpl() {
    if (!m_impl) {
        if (m_custom)
            m_impl = new ImplFacade<ImplCustom>(m_custom);
        else
            m_impl = new ImplFacade<ImplGenerated>(m_generated);
    }
    return m_impl;
}

理想情况下,您应该在实际代码中使用unique_ptr作为新成员。

答案 2 :(得分:2)

如果您可以使用c ++ 11 Standard,则可以使用std::function来完成此操作:

struct Wrapper {
    Wrapper(ImplGenerated * i):
        foo(std::bind(&ImplGenerated::foo, i)),
        bar(std::bind(&ImplGenerated::bar, i)) {}

    Wrapper(ImplCustom * i):
        foo(std::bind(&ImplCustom ::foo, i)),
        bar(std::bind(&ImplCustom ::bar, i)) {}

    //Now the functions are member variables but it works the same way
    std::function<int(int x, int y)> foo;
    std::function<void(double x)> bar;
    ....

//If you don't Need to destruct the impl-objects then you don't even Need to store them
};

答案 3 :(得分:-1)

我相信你想要的是使用继承/接口:

struct ImplInterface {
    virtual int foo(int x, int y) = 0;
    virtual void bar(double x) = 0;
    ....
};

struct ImplGenerated : public ImplInterface {
    virtual int foo(int x, int y);
    virtual void bar(double x);
    ....
};

struct ImplCustom : public ImplInterface {
    virtual int foo(int x, int y);
    virtual void bar(double x);
    .....
};

现在,您的getImpl()将返回ImplInterface*

或者,如果您无法添加基类,则在Wrapper班级而不是getImpl()执行此操作:

int foo(int x, int y)
{
    if (m_generated != nullptr)
    {
         return m_generated->foo(x,y);
    }
    else if (m_custom != nullptr)
    {
         return m_custom->foo(x, y);
    }
    throw "hey dude!";
}

我知道它做了很多工作,但嘿,你没有基础可以合作。