12

I have some function templates, for example

template <typename T>
void foo(T);

template <typename T>
void bar(T);

// others

and I need to pass each one to an algorithm that will call it with various types, e.g.

template <typename F>
void some_algorithm(F f)
{
    // call f with argument of type int
    // call f with argument of type SomeClass
    // etc.
}

I can't pass in my function template uninstantiated, but I can't instantiate it with any specific type either because some_algorithm will need to call it with arguments of several different types.

I could adapt my function templates to be polymorphic function objects, e.g.

struct foo_polymorphic
{
    template <typename T>
    void operator()(T t)
    {
        foo(t);
    }
};

and then pass it as some_algorithm(foo_polymorphic()). But this requires writing a separate adapter for each of my function templates.

Is there a generic way of adapting a function template to be a polymorphic function object, i.e. some mechanism that I can re-use for each of the function templates I need to adapt, without having to declare something separately for each one?

HighCommander4
  • 50,428
  • 24
  • 122
  • 194
  • @Seth: Because that doesn't make sense to the language. – GManNickG Aug 11 '11 at 22:37
  • Wait, does the foo_polymorphic() trick actually work? It looks like it would be impossible... – Owen Aug 11 '11 at 22:39
  • Oh wow it actually does. I'm impressed with C++. – Owen Aug 11 '11 at 22:42
  • I don't think so, because the type F is a type `void (*foo)(T)` (or something), and f is only known at runtime, so can't be instantiated with template types. Somehow you'll have to pass a template function pointer as a template parameter, which I'm not sure can be done. I think it would vaguely look like `template – Mooing Duck Aug 11 '11 at 22:46
  • 1
    `Non-type template parameters must be of integral, enumeration, pointer, reference, or pointer to member type, and must be constant at compile time.` You can't pass an uninstantated function name, basically because you can't get a pointer to it. You'll have to use some other trick. – Mooing Duck Aug 11 '11 at 22:53
  • 1
    foo_polymorphic can be generated with a macro to simplify the task. – Mooing Duck Aug 11 '11 at 22:55
  • 4
    I'm going to say this cannot be done without macros. In C++0x *had there been polymorphic lambdas*, you'd just do: `some_algorithm([](auto x){ foo(x);});`. From this point, then, I see your question no differently from other "how can I emulate lambdas" question, just in your case your want more enhanced lambdas. As was always the case for emulating lambdas: by hand. :/ – GManNickG Aug 11 '11 at 23:01
  • 1
    Want to mention that the above struct may have issues since it isn't perfect forwarding t into foo. – Polymer Jan 07 '14 at 01:49
  • @Polymer: Yeah, good point. It was just a quick example to illustrate the technique. A proper version would use C++11 perfect forwarding. – HighCommander4 Jan 07 '14 at 05:19
  • Yeah, a problem I have requires something like the above. I was so frustrated I contemplated writing all my current and future algorithms as polymorphic structs... – Polymer Jan 09 '14 at 03:47

2 Answers2

2

The short version of the problem is given an overloaded name f, how to concisely write an object ff such that ff(a0, a1, a2, ...) ultimately calls f(a0, a1, a2, ...).

A polymorphic functor, how you point out yourself, is the usual solution. But it must be defined out of line (since it has a template member), so I'll consder that not concise enough for the purposes of my answer.

Currently lambda expressions yield a monomorphic functor, so they're close but not quite there.

// set of functions overloaded on int and double
void f(int);
void f(double);

auto ff = [](int i) { return f(i); };

As GMan pointed out in the comments polymorphic lambdas would (should?) be the solution to concisely write polymorphic functors inline.

In the meantime, it is possible to write a make_overload helper that combines multiple functors into one, such that

auto ff = make_overload(
    [](int arg0) { return f(arg0); }
    , [](double arg0) { return f(arg0); } );

would 'capture' the whole overload set. Perhaps a Boost.Preprocessor macro could help here, so that auto ff = POLYMORPHIC_LAMBDA( 1, (int)(double), { return f(arg0); } ); be used inline. I suspect there are arity restrictions however (hence the first macro argument), unlike the usual out-of-line hand-written polymorphic functor solution; so this wouldn't help with e.g. variadic function templates.

Luc Danton
  • 34,649
  • 6
  • 70
  • 114
  • I was hoping that the slightly less general case of a single function template, might be easier than the general case of an overloaded name. I guess not... – HighCommander4 Aug 11 '11 at 23:31
  • Interesting! If we can somehow get a hold of, say, an `mpl::vector` of the types that the function needs to be called with, could we write a macro of the form `POLYMORPHIC_LAMBDA(f, mplvec)`? – HighCommander4 Aug 11 '11 at 23:34
  • @HighCommander4 They share similarities: the name `foo` (from your question) cannot be passed as function parameter (to a function template) or as a non-type template parameter without losing polymorphism. Which is the root of the problem. – Luc Danton Aug 11 '11 at 23:37
  • @HighCommander4 An interesting idea. The macro, however, cannot manipulate a Boost.MPL sequence; if it could, then `from_sequence::make_overload()(f, f, f, f, f);` (assuming 5 overloads here) would solve the problem. So that's why is used a Boost.PP sequence in my example: the macro has to insert the name (which is the only thing that behaves polymorphically) as many times as there are overloads. It's still more elaborated than my proposition that inserts a whole body, and it handles the arity to boot! (Note that this kind of extended discussions are welcome in the C++ chat.) – Luc Danton Aug 11 '11 at 23:49
  • (@Luc: I disagree, even though the site owners think otherwise. Your discussion perfectly belongs here, right in the context of where it began. That way readers later don't have to go searching for some chat room only to find it's been deleted.) – GManNickG Aug 11 '11 at 23:52
  • @Gman I have no position myself on extended discussions in the comments (well admittedly it's not terribly convenient). But I *am* already using the chat ;). Honestly my macro was intended as an illustration, a working solution is not really part of my answer proper. – Luc Danton Aug 11 '11 at 23:56
  • @Luc: What if we can get a Boost.MPL sequence, *and* its size as a preprocessor token? The preprocessor can then generate typenames of the form `mpl::at::type`, ..., `mpl::at::type`. Would that be sufficient? – HighCommander4 Aug 11 '11 at 23:58
1

Why couldn't you use template template parameters? You said you can't pass your template uninstantiated, but I'm not sure if you've heard of this before, tell me if you have and it won't work.

I don't know what your code structure looks like, but can you do something like

I know this works, don't know if it's what yo uwant though:

template<typename T>
T some_algorithm(T data) { return T(); } // just returning nothing for example

template<typename T, T(*Something)(T)>
class FuncClass {
public:
    T run(T data) { return Something(data); }
};

template<typename T, typename Functor>
void apply_algorithm(T data) {
    Functor F;
    F.run(data);
}

int main() {
    int mydata = 4;
    apply_algorithm<int, FuncClass<int, some_algorithm<int> > >(mydata);

    cin.get();
}
Seth Carnegie
  • 73,875
  • 22
  • 181
  • 249
  • 3
    That's fine if `Algorithm` is a class type. A function is not. – GManNickG Aug 11 '11 at 22:44
  • @GMan can you take a look at it again and see if it makes sense at all? – Seth Carnegie Aug 11 '11 at 22:50
  • @GMan: This is interesting - so can a function template bind to anything at all? – Kerrek SB Aug 11 '11 at 22:57
  • 3
    @Seth: All you've done is specify which versions of `myalgo` are instantiated (in this case, `int`). Note that his manual version doesn't need to know anything but the function name, as deduction handles the rest. Needing to specify the template arguments defeats the purpose. – GManNickG Aug 11 '11 at 22:59