How to pass the specific callback to a template function?

I have the following code:

#include <iostream>
using namespace std;
template <class T>

int get_num(int k) {
    return k + 3;
}

float get_num(float k) {
    return k + 3;
}


template <class T1, class T2>
void function(T1 (*callback)(T2), T2 arg) {
    callback(arg);
}

int main() {
    // your code goes here
    function(get_num, 3);
    return 0;
}

I need to call the get_num() function with an int argument. But compiler gets this error:

prog.cpp: In function ‘int main()’: prog.cpp:21:21: error: no matching
function for call to ‘function(<unresolved overloaded function type>,
int)’   function(get_num, 3);
                 ^ prog.cpp:15:6: note: candidate: template<class T1, class T2> void function(T1 (*)(T2), T2)  void function(T1
(*callback)(T2), T2 arg) {
      ^~~~~~~~ prog.cpp:15:6: note:   template argument deduction/substitution failed: prog.cpp:21:21: note:   couldn't deduce
template parameter ‘T1’   function(get_num, 3);

How can it be done ?

Upvotes: 3

Views: 1791

Answers (8)

Darklighter
Darklighter

Reputation: 2192

After removing template <class T> from int get_num(int) to get a normal overload set, you can use Some programmer dude’s answer.

In this answer I want to elaborate how you can still use a function pointer based parameter.

If you switch the arguments to function at least gcc is able to deduce it:

template <typename T, typename U>
void function2(T arg, U(*callback)(T)) {
    callback(arg);
}

clang doesn’t like it when you use U there, so if your return types will always be the same as your arguments, you can use T twice:

template <typename T>
void function2(T arg, T(*callback)(T)) {
    callback(arg);
}

To resolve disambiguities like the one in your error message in general, you can also do the overload resolution manually with static_cast:

function(static_cast<float(*)(float)>(&get_num), 3.0f);
function(static_cast<int(*)(int)>(&get_num), 3);

Upvotes: 2

jswl
jswl

Reputation: 82

Here's the one using the C++ functors.

#include <iostream>
using namespace std;

template<class T>
struct get_num : public std::unary_function<T,T>
{
  T operator()(const T& k) {
    return k+3;
  }
};

template< class T1, class T2 >
void function( T1 fun, T2 arg)
{
    fun(arg);
    cout << fun(arg) << endl;
}

int main()
{
    function(get_num<int>(), 3);
    return 0;
}

Upvotes: 2

463035818_is_not_an_ai
463035818_is_not_an_ai

Reputation: 122458

You have a template <class T> in front of your int get_num(int k). Lets assume for a moment it isnt there, then this works:

Sometimes you cannot change the function into a template, but need to work with function pointers to a function that has several overloads. The way to choose the right overload is to specify the type of the function pointer (because for different overloads the function pointers are of different type).

typedef int (* int_get_num_t)(int);
int main() {
    int_get_num_t correct_overload = get_num;
    function(correct_overload, 3);
    return 0;
}

In case the int get_num(int k) is really supposed to be a template (then why the float one isnt?) then you simply have to pick the template version:

int_get_num_t correct_overload = get_num<int>;

where actually you could pass any type instead of int as your template get_num always takes an int and returns an int irrespective of the template parameter.

And finally... you actually dont need the second overload for get_num but you need only one template. And in that case you still need to pick the right template to get the function pointer:

template <typename T>    
T get_num(T k) { return k + 3; }

template <class T1, class T2>
void function(T1 (*callback)(T2), T2 arg) {
    callback(arg);
}

int main() {
    int_get_num_t correct_overload = get_num<int>;
    function(correct_overload, 3);
    return 0;
}

Upvotes: 2

Klaus
Klaus

Reputation: 25613

I want to provide an solution which differs a bit. I explain it inside the code to make it hopefully more comfortable to read and understand:

// create a helper class, 
// which collects all callable classes to build one callable object later
template<class... Ts> struct funcs : Ts... { using Ts::operator()...; };
template<class... Ts> funcs(Ts...) -> funcs<Ts...>;

// instead of free functions, build objects with methods
// and use operator() instead of function names.
// this makes it easier to "understand" that this will be an callable object
struct Func1
{
    int operator()(int k) {
        return k + 3;
    }
};

struct Func2
{
    float operator()(float k) {
        return k + 3;
    }
};

// adapt your code to this:
template <class T1, class T2>
auto function(T1 callback, T2 arg) {
    return callback(arg);
}

// and finaly you can use it this way, also with return types 
// the central hack is:
// funcs{ Func1(), Func2() }
// this will generate a callable object with all the overloads
// from the inserted callable objects
int main() {
    // your code goes here
    std::cout << function(funcs{ Func1(), Func2() }, 3) << std::endl;
    std::cout << function(funcs{ Func1(), Func2() }, (float)7.999) << std::endl;
    return 0;
}

Upvotes: 0

Smit Ycyken
Smit Ycyken

Reputation: 1181

You have to specify the type of function

#include <iostream> 
#include <string>
int get_num(int k) {
    return k + 3;
}

float get_num(float k) {
    return k + 3;
}

std::string get_num (double a)
{
    return "this is a string " + std::to_string(a);
}

template <class T1, class T2>
using callback = T1(*)(T2);


template <class T1, class T2>
void function(callback<T1, T2> function, T2 arg) {
    std:: cout << function(arg) << std::endl;
}

int main() {
    // your code goes here
    function<int, int>(get_num, 3);

    function<std::string, double>(get_num, 3);

    system("pause");
    return 0;
}

Why 2 different template arguments? -The OP's question is not about optimization, it is about

How to pass the specific callback to a template function?

So, this is one of many implementations, solving the specific error.

Upvotes: 1

Some programmer dude
Some programmer dude

Reputation: 409176

One problem is that you have different types for return-type and argument-type for function, but in reality both are the same.

That means you could do something like

template<typename T, typename F = T(T)>
void function(F callback, T arg)
{
    callback(arg);
}

The template argument F is just to simplify the callback argument declaration.

Upvotes: 2

tianshilei1992
tianshilei1992

Reputation: 65

The following code will work:

#include <iostream>

using namespace std;

template<typename T>
int get_num(int k) {
    return k + 3;
}

float get_num(float k) {
    return k + 3;
}


template<typename T1, typename T2> // Maybe here you want the `typename`, not the `class`
void f(T1 (*callback)(T2), T2 arg) {
    callback(arg);
}

int main() {
    // your code goes here
    f(get_num<int>, 3); // The key point is here!
    return 0;
}

The reason you get the compiling error is the compiler could not deduce the type T if you just use get_num, because all the arguments are nothing with the type T.

Upvotes: 1

bartop
bartop

Reputation: 10315

I allowed myself to simplify a bit Your code. This should work fine:

#include <iostream>
using namespace std;

template <class T>
T get_num(T k) {
    return k + 3;
}

template <class T1, class T2>
void function(T1 callback, T2 arg) {
    callback(arg);
}

int main() {
    function(get_num<int>, 3);
    return 0;
}

Upvotes: 0

Related Questions