4

There are a number of similar questions/answers, but I couldn't quite put those answers together to serve my purposes. I want a traits

template<typename Func, typename ReturnType, typename... Args>
struct returns_a { static const bool value; };

such that

returns_a<F,T,Args>::value

is true if F(Args) is well formed and returns a T. After some more research, I got it working as follows:

// value is true if Func(Args...) is well formed
template<typename Func, typename... Args>
class is_callable
{
  template <typename F>
  static decltype(std::declval<F>()(std::declval<Args>()...), void(), 0) test(int);
  template <typename>
  static void test(...);
public:
  static const bool value = !std::is_void<decltype(test<Func>(0))>::value;
};

// return_type<FunctionSignature>::type is the return type of the Function
template<typename>
struct return_type {};

template<typename ReturnType, typename... Args>
struct return_type<ReturnType(Args...)>
{ typedef ReturnType type; };

// helper class, required to use SFINAE together with variadic templates parameter
// generic case: Func(Args...) is not well-defined
template <typename Func, typename ReturnType, typename dummy, typename... Args>
struct returns_a_helper { static const bool value = false; };

// Func is a function signature
template <typename Func, typename ReturnType, typename... Args>
struct returns_a_helper<Func, ReturnType, typename
                        std::enable_if<std::is_function<Func>::value>::type, Args...>
{
  static const bool value =
    std::is_convertible<typename return_type<Func>::type,
                        ReturnType>::value;

};

// Func(Args...) not a function call, but well-defined 
template <typename Func, typename ReturnType, typename... Args>
struct returns_a_helper<Func,ReturnType,typename
                        std::enable_if<is_callable<Func>::value &&
                       !std::is_function<Func>::value
                                      >::type, Args...>
{
  static const bool value =
    std::is_convertible<typename std::result_of<Func(Args...)>::type,
                ReturnType>::value;
};

template <typename Func, typename ReturnType, typename... Args>
struct returns_a : returns_a_helper<Func, ReturnType, void, Args...> {};

which now works fine for functors and functions. Here is a simple test:

struct base { virtual bool member(int) const = 0; };
struct foo : base { bool member(int x) const { return x&2; } };
struct bar { foo operator()() { return foo(); } };
foo free_function() { return foo(); }

template<typename T, typename Func>
void test(Func const&func)
{
   std::cout << std::boolalpha << returns_a<Func,T>::value << std::endl;
}

int main()
{
   foo x;
   bar m;
   test<const base&>([&]() { return x; });
   test<const base&>(m);
   test<const base&>(free_function);
   return 0;
}

Well, this works, but it seems a bit cumbersome. Anybody has better/more elegant/shorter solutions?

Walter
  • 44,150
  • 20
  • 113
  • 196
  • `std::is_function` is a wrong and unnecessary test here - it only checks if its argument is a function signature and for the free function case (where `Func == foo()`, i.e. a signature), the test is `std::is_function`, aka a function returning a function - [which is an error](https://ideone.com/yQn6Vk). That causes SFINAE and the primary template to be selected... – Xeo Jun 06 '13 at 12:42
  • @Xeo okay, I now avoid that, but still cannot get it working – Walter Jun 06 '13 at 13:05

3 Answers3

1
#include <tuple>
#include <utility>

template<typename Func, typename R, typename Args, typename=void>
struct will_return_helper: std::false_type {};

template<typename Func, typename R, typename... Args>
struct will_return_helper<
  Func, R, std::tuple<Args...>,
  typename std::enable_if<
    std::is_same<
      R,
      decltype( std::declval<Func&>()( std::declval<Args>()... ) )
    >::value
  >::type
> : std::true_type {};

template<typename Func, typename R, typename... Args>
struct will_return:
  will_return_helper< typename std::decay<Func>::type, R, std::tuple<Args...> >
{};

#include <iostream>
struct Foo {
  int operator()(double) {return 0;}
};
int main()
{
  std::cout << "1 = "<< will_return< int(), int >::value << "\n";
  std::cout << "1 = "<< will_return< int(*)(), int >::value << "\n";
  std::cout << "0 = "<< will_return< int(*)(), double >::value << "\n";
  std::cout << "1 = "<< will_return< Foo, int, double >::value << "\n";
  std::cout << "1 = "<< will_return< Foo, int, int >::value << "\n";
  std::cout << "0 = "<< will_return< Foo, double, int >::value << "\n";
}

Live example.

A better signature for will_return, in my opinion, would be:

template<typename Func, typename Sig>
struct will_return;

template<typename Func, typename R, typename... Args>
struct will_return<Func, R(Args...)>:
  will_return_helper< typename std::decay<Func>::type, R, std::tuple<Args...> >
{};

which gives you:

  std::cout << "1 = "<< will_return< int(), int() >::value << "\n";

and

  std::cout << "1 = "<< will_return< Foo, int(double) >::value << "\n";

which I think looks prettier.

If you prefer "can be converted" rather than "is the same type", you can change is_same above to is_convertible.

Yakk - Adam Nevraumont
  • 262,606
  • 27
  • 330
  • 524
  • this looks like it's not going to compile. Did you test it? – Walter Jun 06 '13 at 13:44
  • @Walter sorry, missed a `>` when I changed it from "can be called" to "will return". Formatting included, and test code, now. Basically, I exploited `std::decay` to get rid of a bunch of your work, and packaged the args in `std::tuple`. – Yakk - Adam Nevraumont Jun 06 '13 at 13:50
1

I think this will do:-

(Corrected including aschepler's test case)

#include <type_traits>

template<typename Func, typename Ret, typename... Args>
struct returns_a
{   
    template<typename F, typename ...As>
    static constexpr bool test(
        decltype(std::declval<F>()(std::declval<As>()...)) * prt) {
        return std::is_same<Ret *,decltype(prt)>::value;
    }

    template <typename F, typename ...As>
    static constexpr bool test(...) {
        return false; 
    }

    static const bool value = test<Func,Args...>(static_cast<Ret *>(0)); 
};

// Testing...

#include <iostream>

void fn0();
int fn1(int);
int fn2(int,int);
struct cls{};
struct fntor
{
    int operator()(int i) { return 1; }
};
auto lamb0 = [](int i) -> int { return i; };
struct B {}; 
struct D : public B {};
auto lamb1 = []{ return B{}; }; 

int main()
{
    std::cout << returns_a<decltype(fn0),void>::value << std::endl; // 1
    std::cout << returns_a<decltype(fn1),int,int>::value << std::endl; // 1
    std::cout << returns_a<decltype(fn1),int,double>::value << std::endl; // 1
    std::cout << returns_a<decltype(fn1),double,int>::value << std::endl; // 0
    std::cout << returns_a<decltype(fn1),char,int>::value << std::endl; // 0
    std::cout << returns_a<decltype(fn1),unsigned,int>::value << std::endl; // 0
    std::cout << returns_a<decltype(fn2),int,int,int>::value << std::endl; // 1
    std::cout << returns_a<decltype(fn2),int,char,float>::value << std::endl; // 1
    std::cout << returns_a<cls,int,int>::value << std::endl; // 0
    std::cout << returns_a<fntor,int,int>::value << std::endl; // 1
    std::cout << returns_a<decltype(lamb0),int,int>::value << std::endl; // 1
    std::cout << returns_a<double,int,int>::value << std::endl; // 0
    std::cout << returns_a<decltype(lamb1), D>::value << std::endl; //0
    return 0;
}

(Built with clang 3.2, gcc 4.7.2, gcc 4.8.1)

Mike Kinghan
  • 55,740
  • 12
  • 153
  • 182
  • Given `struct B {}; struct D : public B {};`, `returns_a::value` is true. – aschepler Jun 07 '13 at 00:19
  • Forgetting about downcasting is my thing of the week. Corrected. – Mike Kinghan Jun 07 '13 at 08:31
  • @Walter Quite so. `fn1(char)` *is* well-formed, as you require, and it returns `int`; so my solution says that `returns_a::value == true`, as you require. See also my test case for `returns_a`,where `double` is implicitly converted to `int`. Yakk's solution has the same property. Did you mean to ask for a compiletime test for whether a given function *has a specific signature*? – Mike Kinghan Jun 10 '13 at 16:06
0

Well, this is embarrassing: I found a rather simple (hence elegant) way:

template <typename Func, typename ReturnType, typename... Args>
using returns_a = std::is_convertible<Func, std::function<ReturnType(Args...)>>;

when all the hard work is done in std::function<>.

Walter
  • 44,150
  • 20
  • 113
  • 196
  • 1
    This doesn't quite do what you asked for, as it works if the return type of `Func` when passed `Args...` is convertible to `ReturnType`. [Live example](https://ideone.com/CSakmj) – Yakk - Adam Nevraumont Jun 06 '13 at 13:41
  • 2
    And here comes the twist: According to standard, a `std::function` is constructible from *anything*. – Xeo Jun 06 '13 at 14:30
  • @Xeo which is something they really should fix! – Yakk - Adam Nevraumont Jun 06 '13 at 20:07
  • @Yakk What else do you think I've asked for? Are you being pedantic about the `is_convertible` as opposed to *is*? In my actual problem, this is exactly what I want, when `is_convertible` is used to allow base-derived type conversion ("is a" relation). How else would you implement `is_a` ? – Walter Jun 10 '13 at 13:34
  • 1
    `is_convertible` is not "is a" relation, it is a "is convertible to" relation. A `double` is *not* an `int`, but a `double` "is convertible to" an `int`. It is true that things that "is a" are convertible: but `A->B` is not the same as `B->A`. `is_a` isn't a trait I'd create, as I suspect it would be used in ways where the reader of the code wouldn't be clear what it would do. If someone held a gun to my head, `can_losslessly_be_considered_to_be` I might name `is_a`. @Xeo's objection is more important, however. – Yakk - Adam Nevraumont Jun 10 '13 at 13:54