Version restriction: C++17.
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 moreoperator()
, call the appropriateoperator()
from the base class and cast the return value to typeRet
)and, given the
Ret
type isn't deducible from the argument (theoverload
class) and that CTAD doesn't permit to explicit a template argumend, seems to me that amake_wrp_overload()
function is requiredso your
std::visit()
call becomeThe following is a full compiling C++17 example