3

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.

Phil Miller
  • 36,389
  • 13
  • 67
  • 90
  • 1
    Possible duplicate of [Disambiguate overloaded member function pointer being passed as template parameter](https://stackoverflow.com/questions/17874489/disambiguate-overloaded-member-function-pointer-being-passed-as-template-paramet) – Jodocus Oct 24 '17 at 20:45
  • Ok, the linked question answers how a putative caller can resolve the ambiguity to which the compiler wouldn't deduce a solution. I'm trying to find *some* way to present this sort of API that the compiler *can* deduce, without callers needing to make the specification. The compiler quite readily knows what overload/instantiation of `method` it would call if it were applied to `args`. I'm looking for a way to convince it to abstract apply that exact same process of name resolution and argument type conversion, and give me the resulting pointer-to-member-function. – Phil Miller Oct 24 '17 at 21:02
  • Heck, if someone could answer for the non-member case, and just get the function pointer from an overload set or template instantiation, that would so. The fact that it's a class member is not really salient here. – Phil Miller Oct 24 '17 at 21:37
  • I'm going to edit to reflect that the member-function bit is a distraction here - the same exact problem arises for non-members, with syntactic burden to understand – Phil Miller Oct 24 '17 at 21:40
  • To be clear, I'm totally fine with the `remote_call` actually being implemented as a macro, or in some other way uses things like `decltype(args)` to get the right overload resolved. I just don't want the person writing the code that makes the call to have to write that out. – Phil Miller Oct 26 '17 at 17:44
  • Then just use macros `#define REMOTE_CALL(function, ...) function(__VA_ARGS__) #define REMOTE_MEMBER_CALL(function, obj, ...) (obj.*function)(__VA_ARGS__); #define REMOTE_MEMBER_PTR_CALL(function, ptr, ...) (ptr->*function)(__VA_ARGS__);` – Jodocus Oct 26 '17 at 18:59
  • The point is that the call can't just happen on the spot. If I were just calling the function, then the question would be pointless, because the compiler actually would resolve it. I need the compiler's resolution, so that I can refer to the selected implementation later/elsewhere. – Phil Miller Oct 26 '17 at 19:18
  • [P0127](http://wg21.link/p0127) helps here – Phil Miller Jul 31 '19 at 13:29

0 Answers0