简体   繁体   中英

Select object for call function at run time, without base class and templates

I have two classes with same interface methods:

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

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

And class Wrapper:

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);
}

Is it possible to write some C++ construction (class or any other, but not macros) instead getImpl() for resolving current implementation object and call corresponding method? like this:

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

Note: Only changes to ImplCustom could be applied (add base class or make template or something else), ImplGenerated is auto-generated by external project therefore couldn't be changed ( add base class is impossible ). Wrapper could not be template, because is interface class.

Update: It is impossible to derive ImplCustom from ImplGenerated.

The solution I see here is to create a wrapper

The goal of this solution is to generate an interface for your two unrelated classes.

Let's start by making a Base classe:

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

Now you can create wrapper for each Impl you got:

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;
};

Now you can use these wrapper like this:

ImplInterface* wrapper = new GeneratedWrapper(implGenerated);

This method could be much shorter using templates, let's make One wrapper for your new interface:

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;
};

Now you can use it like this:

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

If you can't modify the existing classes, you can still add a facade to provide post-hoc polymorphism. That is:

Wrapper could not be template, because is interface class

is only partly true. You can have a non-templated interface (ABC) and a templated concrete subclass.

// 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;
}

Ideally you should be using unique_ptr for the new member in real code.

If you can use the c++11 Standard you can use std::function to accomplish this:

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
};

I believe what you're wanting is to use inheritance / interface:

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);
    .....
};

now your getImpl() will return a ImplInterface*

Or if you can't add a base class, then in your Wrapper class instead of getImpl() do this:

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!";
}

I know it's a lot of work, but hey you've no base class to work with.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM