mercredi 30 janvier 2019

Trouble deducing return type for wrapper of member functions

I have a collection of methods with varying signatures which all need the same prefix and postfix code, so I'd like to wrap each of them up neatly. I'm trying to formulate a c++11 variadic template for a generic wrapper to my member functions, and I hit trouble when deducing the return type. Auto works, but I need the return type explicitly to provide a valid return value even if I catch an exception inside the wrapper when executing the wrappee.

So far I managed to deduce the return type for a plain wrapper function using std::result_of and I got correct behaviour for non static member functions using auto. I tried for two days now to make the std::result_of approach work for member functions, also tried a lot of variations of decltype and declval, but with no luck so far. I am running out of ideas on how to deduce the return type.

This is my working example

#include <iostream>

int foo(int a, int b) { return a + b; }
int bar(char a, char b, char c) { return a + b * c; }


template<typename Fn, typename... Args>
typename std::result_of<Fn&(Args... )>::type 
wrapFunc(Fn f, Args... args) {
  //Do some prefix ops
  typename std::result_of<Fn&(Args... )>::type ret = f(std::forward<Args>(args)...);
  //Do some suffix ops
  return ret;
}

class MemberWithAuto {
  private:
  public:
    MemberWithAuto() {};
    int foo(int i, int j) { return i + j;}

    template<typename Fn, typename... Args>
    auto wrapper(Fn f, Args... args) {
      //Do some prefix ops
      auto ret = (*this.*f)(std::forward<Args>(args)...);
      //Do some suffix ops
      return ret;
    } 
};



int main() {

  std::cout << "FuncWrapper for foo with 1 + 2 returns "         << wrapFunc<decltype(foo)>(foo, 1, 2)      << std::endl;
  std::cout << "FuncWrapper for bar with 'a' + 'b' * 1  returns "  << wrapFunc<decltype(bar)>(bar, 'a','b', 1)  << std::endl;

  MemberWithAuto meau = MemberWithAuto();
  std::cout << "MemberFunction with Auto with 6 + 1 returns  "  << meau.wrapper(&MemberWithAuto::foo, 6, 1)  << std::endl;

  return 0;
}

Both of these work well, but the wrapper method using auto doesn't get me the return type for later use. I tried lots of variations with std::result:of and decltype with the following code, but I can't get it to compile correctly

#include <iostream>

int foo(int a, int b) { return a + b; }
int bar(char a, char b, char c) { return a + b * c; }

class MemberWithDecl {
  private:
  public:
    MemberWithDecl() {};
    int foo(int i, int j) { return i + j;}

    template<typename Fn, typename... Args>
    typename std::result_of<Fn&(Args... )>::type wrapper(Fn f, Args... args) {
      //Do some prefix ops
      typename std::result_of<Fn&(Args... )>::type ret = (*this.*f)(std::forward<Args>(args)...);
      //Do some suffix ops
      return ret;
    } 
};



int main() {
  MemberWithDecl medcl = MemberWithDecl();
  std::cout << "MemberFunction with declaration also works "  << medcl.wrapper(&MemberWithDecl::foo, 6, 1)  << std::endl;
  return 0;
}

I was expecting to find a solution where the signature of Fn with Args... is correctly recognized, because auto also successfully deduces the types. My type declaration does not seems to find a matching template though, no matter the variations I tried, I get

error: no matching function for call to ‘MemberWithDecl::wrapper(int (MemberWithDecl::*)(int, int), int, int)’

If I leave the wrapper's return type to be auto and just try my declaration for the variable ret within, I get

error: no type named ‘type’ in ‘class std::result_of<int (MemberWithDecl::*&(int, int))(int, int)>’
       typename std::result_of<Fn&(Args... )>::type ret = (*this.*f)(std::forward<Args>(args)...);

After reading the standard, I think this means that result_of does not regard Fn&(Args... ) to be well-formed, but I don't know how the correct form should look like.

Any help would be much appreciated

Aucun commentaire:

Enregistrer un commentaire