xinnjie
xinnjie

Reputation: 732

c++ template parameter compiler can not deduce

here is function to register.

template <typename ReqT, typename RespT>
bool RegisterCmdHandler(int cmd_id, std::function<int(int, const ReqT&, RespT&)> sync_handler) {
    // ... do something with sync_handler and register it for later callback
    return true;
}

and a specific handler to register:

int SomeHandler(int id, const Foo& req, Bar& resp) {
    // ... specific logic
}

now I want to apply the Handler to Register Function, compiler complains

RegisterCmdHandler(1, SomeHandler); // ERROR, compiler can not deduce

while specificly write out the type is OK:

RegisterCmdHandler<Foo, Bar>(1, SomeHandler); // OK, compiler can deduce

but the latter has ugly API. How can I get the first on work?

Upvotes: 0

Views: 58

Answers (2)

max66
max66

Reputation: 66200

How can I get the first on work?

I see some ways.

(1) If you can modify the RegisterCmdHandler() function and you don't need to know, inside it, what types ReqT and RestT are, I suggest you to avoid at all std::function and accept sync_handler as a simple template type.

I mean

template <typename F>
bool RegisterCmdHandler (int cmd_id, F sync_handler) {
// ... do something with sync_handler
return true;
}

This is a very flexible solution because F can be a function, a function pointer, a std::function, a lambda (also a generic lambda, so this solution is more flexible than using std::function), another type of class/struct with an operator(), a value returned from a std::bind. In short: a generic callable.

(2) If you can modify the RegisterCmdHandler() function but you need to know (and use) the ReqT and RestT, you can follows the plain function pointer way (see Maxim Egorushkin's answer for the syntax). Unfortunately this works with function pointers only and doesn't works (by example) when sync_handler is a lambda.

(3) If you can't modify RegisterCmdHandler() but you can use C++17, you can use std::function deduction guides and call the function as follows

RegisterCmdHandler(1, std::function{SomeHandler}); 

or, maybe better if you have to call it in different places, call it through a converter

template <typename F>
auto CallRegisterCH (int cmd_if, F && func)
 { return RegisterCmdHandler(cmd_if, std::function{std::forward<F>(func)}); }

calling it as follows

CallRegisterCH(1, SomeHandler);

(4) if you can't modify RegisterCmdHandler() and you have to use C++11 or C++14... well... explicating the template types

RegisterCmdHandler<Foo, Bar>(1, SomeHandler); 

seems to me the better way.

Other ways you can explicit the std::function

std::function<int(int, Foo const &, Bar &)>  sh{ SomeHandler };

RegisterCmdHandler(1, sh);

but seems to me almost the same thing.

Upvotes: 1

Maxim Egorushkin
Maxim Egorushkin

Reputation: 136256

How can I get the first on work?

Add an overload for plain function pointers:

template <typename ReqT, typename RespT>
bool RegisterCmdHandler(int cmd_id, int(*sync_handler)(int, const ReqT&, RespT&)) {
    std::function<int(int, const ReqT&, RespT&)> sync_handler2(sync_handler);
    return RegisterCmdHandler(cmd_id, sync_handler2);
}

Upvotes: 1

Related Questions