Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Type erasure and variadic templated member function

The example below is a minimal, maybe not so good example of a well known idiom.
It compiles and it is so ugly in order to be able to maintain it minimal, because the question is not about the idiom itself.

struct Foo {
    virtual void fn() = 0;
};

template<class T>
struct Bar: public Foo {
    void fn() override {
        T{}.fn();
    }
};

struct S {
    void fn() { }
};

int main() {
    Foo *foo = new Bar<S>{};
    foo->fn();
}

What I'm struggling with since an hour ago is how to change it (or even, if there exists an alternative idiom) to introduce a variadic template member method.
Obviously, I cannot modify the fn function of the Foo class, because it's a virtual one and virtual specifier doesn't goes along with templates. The same is valid for the fn specification of Bar, because it has to override somehow the one in the base class.

Note.

For I strongly suspect that this question could be one of the greatest XYProblem ever seen, I'd like also to give a brief description of the actual problem.

I have a class that exposes two templated member methods:

  • the first one accepts a template class T that is not used immediately, instead it should be stored somehow in order to be used later.

  • the second one accepts a variadic number of arguments (it is actually a variadic templated member function) and those arguments should be perfectly forwarded to a newly created instance of T.

Well, the problem is far more complex, but this is a good approximation of it and should give you an idea of what's the goal.

Edit

I guess that it is somehow similar to higher order functions.
I mean, what would solve the problem is indeed a templated function to which to bind the first argument, but as far as I know this is impossible as well as any other approach I've explored so far.
Any viable solution that expresses the same concept?

like image 932
skypjack Avatar asked Dec 06 '15 21:12

skypjack


1 Answers

What I mentioned in the comments is the following approach:

template<typename T> class Factory {

public:
    template<typename ...Args>
    auto construct(Args && ...args)
    {
        return T(std::forward<Args>(args)...);
    }
};

So now, your first exposed class method will be something like this:

template<typename T>
auto getFactory() {

    return Factory<T>();
}

So:

auto factory=object.getFactory<someClass>();

// Then later:

factory.construct(std::string("Foo"), bar()); // And so on...

Instead of construct() you could use operator() too, so the second part of this becomes, simply:

factory(std::string("Foo"), bar()); // And so on...

As I mentioned, this is not really type erasure. You can't use type erasure here.

Having given this a few minutes' more thought, the reason that type erasure cannot be used here is because a given instance of type erasure must be "self contained", or atomic, and what you need to do is to break atomic type erasure into two parts, or two class methods, in your case.

That won't work. Type erasure, by definition, takes a type and "erases" it. Once your first function type-erases its class method template parameter, what you end up with is an opaque, type-erased object of some kind. What was type-erased is no longer available, to the outside world. But you still haven't type-erased your constructor parameters, which occurs somewhere else.

You can type-erase the template class, and the constructor parameters together. You can't type-erase the template class, and the constructor parameters, separately and then somehow type-erase the result again.

The simple factory-based approach, like the one I've outlined, would be the closest you can get to results that are similar to type erasure, if both halfs of your desired type-erasure appear in the same scope, so you can actually avoid type-erasure, and instead rely on compiler-generated bloat.

like image 188
Sam Varshavchik Avatar answered Oct 11 '22 13:10

Sam Varshavchik