Determine the return type of a callable passed to a template

I have a simple wrapper template that allows free functions (e.g. open() close() etc) to passed as template parameters. The code is as follows:

template <auto fn, typename ReturnType=void>
struct func_wrapper {

    template<typename... Args>
    constexpr ReturnType operator()(Args&&... args) const {
        if constexpr( std::is_same<ReturnType, void>::value) {
            fn(std::forward<Args>(args)...);
        } else {
            return fn(std::forward<Args>(args)...);
        }
    }
};

This is used as follows:

    void CloseFunc2(int a);
    into OpenFunc2(const std::string&, int flags);

    using FileWrapper2 = DevFileWrapper<func_wrapper<OpenFunc2, int>,
                                        func_wrapper<CloseFunc2>>;

The code is working fine but I would like to remove the requirement to manually specify ReturnType when creating a func_wrapper.

I tried using std::result_of but that failed because fn is a non type template parameter, e.g.:

template<typename... Args>
constexpr auto operator()(Args&&... args) const 
    -> std::invoke_result<fn(std::forward<Args>(args)...)>::type {
    if constexpr( std::is_same<ReturnType, void>::value) {
        fn(std::forward<Args>(args)...);
    } else {
        return fn(std::forward<Args>(args)...);
    }
}

the error is:

template-parameter-callable.cpp:48:71: error: template argument for 
template type parameter must be a type
constexpr auto operator()(Args&&... args) const ->   
    std::invoke_result<fn(std::forward<Args>(args)...)>::type {                                                                           
                       ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/
usr/include/c++/v1/type_traits:4009:17: 
note: template parameter is declared here
template <class _Fn, class... _Args>
                ^

1 error generated.

How can I deduce the return type of calling fn at compile time?

>Solution :

template <auto fn>
struct func_wrapper {
    template<typename... Args>
    constexpr decltype(auto) operator()(Args&&... args) const {
      return fn(std::forward<Args>(args)...);
    }
};

have you tried this?

I think that works in . Definitely in .

The return type of a callable cannot be determined unless you also know the arguments you are calling it with.

I can extract the return type, but I don’t think you need it.

template <auto fn>
struct func_wrapper {
    template<typename... Args>
    constexpr decltype(auto) operator()(Args&&... args) const {
      using ReturnType = std::invoke_result_t<
        decltype(fn),
        Args&&...
      >;
      return fn(std::forward<Args>(args)...);
    }
};

but as you can see we don’t need it.

return f(x) when f returns void is (now) legal.

Leave a Reply