1

I need to convert asynchronous function calls into synchronous function calls. There are many such functions, so I want to do the conversion using a template. Here is how a simplified program looks like:

#include <functional>
#include <future>
#include <iostream>

using namespace std;

using CallBack = function<void (int)>;

class C {
    template<typename... Ts>
    int InvokeAsync(void (C::*async)(Ts..., CallBack), Ts... args) {
        promise<int> p;
        auto cb = [&p](int r) { p.set_value(r); };
        async(args..., cb);
        auto f = p.get_future();
        f.wait();
        return f.get();
    }
public:
    void Async(int x, int y, CallBack cb) {
        cb(x + y);
    }
    int Sync(int x, int y) {
        return InvokeAsync(&C::Async, x, y);
    }
    void Async(int x, CallBack cb) {
        cb(x);
    }
    int Sync(int x) {
        return InvokeAsync(&C::Async, x);
    }
};

This program does not compile. Here are the errors produced by gcc and clang, respectively:

gcc

$ g++ --version
g++ (GCC) 5.4.0
Copyright (C) 2015 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
$ g++ -c -std=c++14 -o x.o x.cc
x.cc: In member function ‘int C::Sync(int, int)’:
x.cc:24:43: error: no matching function for call to ‘C::InvokeAsync(<unresolved overloaded function type>, int&, int&)’
         return InvokeAsync(&C::Async, x, y);
                                           ^
x.cc:11:9: note: candidate: template<class ... Ts> int C::InvokeAsync(void (C::*)(Ts ..., CallBack), Ts ...)
     int InvokeAsync(void (C::*async)(Ts..., CallBack), Ts... args) {
         ^
x.cc:11:9: note:   template argument deduction/substitution failed:
x.cc:24:43: note:   mismatched types ‘CallBack {aka std::function<void(int)>}’ and ‘int’
         return InvokeAsync(&C::Async, x, y);
                                           ^
x.cc:24:43: note:   mismatched types ‘CallBack {aka std::function<void(int)>}’ and ‘int’
x.cc:24:43: note:   could not resolve address from overloaded function ‘&((C*)this)->*C::Async’
x.cc: In member function ‘int C::Sync(int)’:
x.cc:30:40: error: no matching function for call to ‘C::InvokeAsync(<unresolved overloaded function type>, int&)’
         return InvokeAsync(&C::Async, x);
                                        ^
x.cc:11:9: note: candidate: template<class ... Ts> int C::InvokeAsync(void (C::*)(Ts ..., CallBack), Ts ...)
     int InvokeAsync(void (C::*async)(Ts..., CallBack), Ts... args) {
         ^
x.cc:11:9: note:   template argument deduction/substitution failed:
x.cc:30:40: note:   mismatched types ‘CallBack {aka std::function<void(int)>}’ and ‘int’
         return InvokeAsync(&C::Async, x);
                                        ^
x.cc:30:40: note:   mismatched types ‘CallBack {aka std::function<void(int)>}’ and ‘int’
x.cc:30:40: note:   could not resolve address from overloaded function ‘&((C*)this)->*C::Async’

clang

$ clang++ --version
clang version 3.7.1 (tags/RELEASE_371/final)
Target: x86_64-apple-darwin16.6.0
Thread model: posix
$ clang++ -c -std=c++14 -o x.o x.cc
x.cc:24:16: error: no matching member function for call to 'InvokeAsync'
        return InvokeAsync(&C::Async, x, y);
               ^~~~~~~~~~~
x.cc:11:9: note: candidate template ignored: substitution failure [with Ts = <int, int>]
    int InvokeAsync(void (C::*async)(Ts..., CallBack), Ts... args) {
        ^
x.cc:14:9: error: called object type 'void (C::*)(int, CallBack)' is not a function or function pointer
        async(args..., cb);
        ^~~~~
x.cc:30:16: note: in instantiation of function template specialization 'C::InvokeAsync<int>' requested here
        return InvokeAsync(&C::Async, x);
               ^
2 errors generated.

What do I do wrong?

0

2 Answers 2

3

Firstly, you should make the template parameter pack parameter the last one in parameter list, i.e.

template<typename... Ts>
int InvokeSync(void (C::*async)(CallBack, Ts...), Ts... args) {

then

void Async(CallBack cb, int x, int y)
void Async(CallBack cb, int x)

Secondly, when call on async you should specify the instance to be called on, i.e.

(this->*async)(cb, args...);

LIVE

Sign up to request clarification or add additional context in comments.

Comments

0

You are overconstraining the function type. A simpler version will do:

template<typename F typename... Ts>
int InvokeAsync(F C::*async, Ts... args) {
    promise<int> p;
    auto cb = [&p](int r) { p.set_value(r); };
    (this->*async)(args..., cb);
    auto f = p.get_future();
    f.wait();
    return f.get();
}

2 Comments

Have you tried it? How did overload resolution manage to choose between the two versions of Async?
@n.m. Fair point, I did not. This would require explicit cast at call site which is no better the passing the callback first.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.