简体   繁体   中英

Generically wrap member function of object to modify return type

I'm trying to create a type capable of accepting a callable object of any type and wrapping one of its member functions (in this case, operator() ) to take the same arguments, but modify (cast) the return type. An example follows:

template <typename Ret, typename Callable>
struct ReturnConverter : Callable
{
    ReturnConverter(Callable cb) : Callable(cb) { }

    Ret operator()(argsof(Callable::operator()) args)  // magic happens here
    {
        return static_cast<Ret>(Callable::operator()(std::forward<??>(args)); // how to forward?
    }
};

template <typename Ret, typename Callable>
auto make_converter(Callable cb)
{
    return ReturnConverter<Ret, Callable>(cb);
}

int main()
{
    auto callable = []() { return 1.0f; };
    auto converted = make_converter<int>(callable);

    auto x = converted(); // decltype(x) = int
}

ReturnConverter can take an object and override that object's operator() to cast whatever it returns to Ret .

The problem is expressing the argument types of the wrapping function - they should be exactly the same as those of Callable::operator() . Using a variadic template with std::forward does not satisfy this goal, as it would modify the signature of the function ( operator() now becomes a template where it wasn't before).

How can I express the argsof operator I've highlighted above?


Motivation : I'd like to modify the std::visit overload technique demonstrated in this article to be able to specify the desired return type from multiple lambda functors, so that I don't have to strictly match the return type in every lambda, for instance:

std::variant<int, float, void*> v = ...;
auto stringify = overload(
    [](int x) { return "int: " + std::to_string(x); },
    [](float x) { return "float: " + std::to_string(x); },
    [](auto v) { return "invalid type!"; }  // error! const char* != std::string
);
std::visit(stringify, v);

With the change above, I'd be able to write something like auto stringify = overload<std::string>(...);

I don't see a way to respond to your exact answer but... considering the "Motivation" of the question... I propose a wrapper for overload (a class that inherit from a class with one or more operator() , call the appropriate operator() from the base class and cast the return value to type Ret )

template <typename Ret, typename Wrpd>
struct wrp_overload : public Wrpd
{
  template <typename ... Args>
  Ret operator() (Args && ... as)
  { return Wrpd::operator()(std::forward<Args...>(as)...); }
};

and, given the Ret type isn'd deducible from the argument (the overload class) and that CTAD doesn't permit to explicit a template argumend, seems to me that a make_wrp_overload() function is required

template <typename Ret, typename ... Cs>
auto make_wrp_overload (Cs && ... cs)
{ return wrp_overload<Ret, overload<Cs...>>{{std::forward<Cs>(cs)...}}; }

so your std::visit() call become

std::visit(make_wrp_overload<std::string>(
           [](int x) { return "int: " + std::to_string(x); },
           [](float x) { return "float: " + std::to_string(x); },
           [](auto v) { return "invalid type!"; } 
), package);

The following is a full compiling C++17 example

#include <iostream>
#include <variant>

template <typename ... Ts>
struct overload : public Ts...
{ using Ts::operator()...; };

// not required anymore (also C++17)
//template <typename ... Ts> overload(Ts...) -> overload<Ts...>;

template <typename Ret, typename Wrpd>
struct wrp_overload : public Wrpd
{
  template <typename ... Args>
  Ret operator() (Args && ... as)
  { return Wrpd::operator()(std::forward<Args...>(as)...); }
};

template <typename Ret, typename ... Cs>
auto make_wrp_overload (Cs && ... cs)
{ return wrp_overload<Ret, overload<Cs...>>{{std::forward<Cs>(cs)...}}; }


int main() {
    std::variant<int, float, void*> package;

    std::visit(make_wrp_overload<std::string>(
               [](int x) { return "int: " + std::to_string(x); },
               [](float x) { return "float: " + std::to_string(x); },
               [](auto v) { return "(no more) invalid type"; } 
    ), package);
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM