Andrea Bocco
Andrea Bocco

Reputation: 932

Forward function calls to underlying data in a wrapper template

I'm writing a template wrapper, something like this

template<class T>
class Wrapper{
public:
    Wrapper(T data);
    ~Wrapper();

    void doSomeWrapperWork();

private:
    T data;
};

If I want to access the wrapped data, I can obviously add a getter function that returns a reference to the data. I was wondering however if there is anyway, with some template magic, to call directly on the wrapper functions of the underlying wrapped data. In other words, if I have a class TestObject with a public function void foo(), I would like to be able to call it in this way:

Wrapper<TestObject> myWrapper;
myWrapper.foo();

instead of:

myWrapper.getData().foo();

without knowing a priori that such a function exists. Is this possible?

Upvotes: 1

Views: 147

Answers (2)

andre
andre

Reputation: 155

Yes, try something like

#include <iostream>
struct TestObject{
    void foo() { std::cout << "TestObject" << std::endl;}
};

template <class T>
struct Wrapper : public T{
    Wrapper() {}
    void doSomeWrapperWork() {}
};

int main() {
    Wrapper<TestObject> wrapper;
    wrapper.foo();
}

This allows you to add more functionality as need while still allowing the wrapper to be used as a class T.

This is inspired by the decorator design pattern.

Upvotes: 1

AndyG
AndyG

Reputation: 41092

Not quite, until C++ gets reflection we can't easily write wrapper classes without a lot of boilerplate macros

but

You can add an implicit conversion of your wrapper class to T:

template<class T>
class Wrapper{
public:
    Wrapper(T data_) : data(data_) {}

    operator const T&() const
    {
        return data;
    }

private:
    T data;
};

And now your class is convertible to T, so we can call a method on T, with a little work:

usage:

struct Foo
{
    constexpr int bar() const
    {
        return 42;
    }
};

int main()
{
    constexpr Foo f;
    Wrapper<Foo> my_wrapper(f);
    static_assert(static_cast<const Foo&>(my_wrapper).bar() == 42);
}

Demo


If you want to avoid the casting yourself, you can write a free function that will call your function, so that implicit conversion may take over:

int call_bar(const Foo& f)
{
    return f.bar();
}

Usage:

Foo f;
Wrapper<Foo> my_wrapper(f);
std::cout << call_bar(my_wrapper); // 42

Demo2

I kind of like this approach as all it requires is that we add a free function for each wrapped function we want instead of having to manually do a bunch of casting.

Upvotes: 1

Related Questions