I have a system that provides remote procedure calls through code-generated proxy objects Proxy<T>
:
struct Foo {
void bar(int);
void bar(double);
};
#include "generated_code_for_proxy_foo.h"
Proxy<Foo> p;
p.bar(7);
p.bar(6.5);
I'd like to move away from code generation, to using C++-native facilities directly. This would save my users having to learn and apply the code generator, and would save the development team having to maintain this code generator that ends up having to parse more and more of C++.
I want to be able to implement a template method along the lines of
remote_call(p, &Foo::bar, 7);
That can take the proxy object and the name of the member I want called, and do the right thing.
If I define remote_call
as follows, things work OK as long as Foo::bar
is not an overload or template, or the argument types are an exact match for one of the members in the overload set:
template <typename T, typename ...Args>
void remote_call(Proxy<T> p, void (T::*method)(Args...), Args ... args);
However, if I have a target type like
struct Baz {
void qux(int);
void qux(std::string);
};
Then a call
remote_call(p, &Baz::qux, "string literal");
fails to compile, because &Baz::qux
isn't resolved to the overload that would actually be applied in an expression like
Baz b;
// Converts "string literal" to std::string, calls Baz::qux(std::string)
b.qux("string literal");
Here's what I have right now, simplifying out even the member-function complexity to just the bare function overload case:
#include <type_traits>
using std::enable_if_t;
using std::is_convertible_v;
template <
typename Params,
typename Args,
typename Method = void (*)(Params),
typename = enable_if_t<is_convertible_v<Args, Params>>
>
void remote_call(Method method, Args args)
{
using R = decltype((*method)(args));
};
#include <string>
void bar(int);
void bar(std::string);
void test()
{
remote_call(&bar, "string literal");
}
If someone has a technique to make that work for the single argument case, I should be able to generalize from there. I want to avoid putting the burden of overload resolution, template instantiation, argument conversion, and the explicit specification of the results of the preceding on my callers. The compiler knows what it would do if a call expression f.bar("string literal")
appeared in the source code. I want to find some way to make it apply that knowledge in a situation where it is not immediately generating the code to make that call.