/*<- Copyright Barrett Adair 2016 Distributed under the Boost Software License, Version 1.0. (See accompanying file LICENSE.md or copy at http ://boost.org/LICENSE_1_0.txt) ->*/ #include #ifdef CALLABLE_TRAITS_DISABLE_ABOMINABLE_FUNCTIONS int main(){ return 0; } #else //[ intro //` This short program showcases some, but not all, of the features available in [libname]. #include #include #include #include namespace ct = callable_traits; // foo is an example of a function object struct foo { void operator()(int, int&&, const int&, void* = nullptr) const {} }; int main() { // indexed argument types using second_arg = ct::arg_at<1, foo>; static_assert(std::is_same::value, ""); // arg types are packaged into std::tuple, which serves as the default // type list in ``[libname]`` (runtime capabilities are not used). using args = ct::args; using expected_args = std::tuple; static_assert(std::is_same::value, ""); // ``[namespace_scoped]``function_type "decays" a callable type to a plain // function type, which is structured in terms of INVOKE. using function_type = ct::function_type; using expected_function_type = void(int, int&&, const int&, void*); static_assert(std::is_same::value, ""); // By design, the ``[libname]`` interface uses constexpr functions accepting // objects and returning std::integral_constants (whenever sensible). However, // for those times where you don't have an object at hand, you can also pass // the type of that object only: static_assert(ct::arity(foo{}) == 4, ""); // with object static_assert(ct::arity() == 4, ""); // with type // Attentive readers might notice that the type of the foo{} // expression above is foo&&, rather than foo. Indeed, // ``[libname]`` is designed to also allow both ref-qualified // and cv-qualified arguments across the board: static_assert(ct::arity() == 4, ""); // Now, if foo had an operator() overload with a && qualifier, taking // a different number of arguments, the above static assert would fail. // For consistency, we'll avoid the value-style overloads // for the remainder of this example (whenever possible). static_assert(ct::max_arity() == 4, ""); static_assert(ct::min_arity() == 3, ""); // a quick way to check for a void return type static_assert(ct::has_void_return(), ""); // C-style variadics detection (e.g. an ellipses in a signature) static_assert(!ct::has_varargs(), ""); int i = 0; // ``[namespace_scoped]``is_invokable allows us to preview whether // std::invoke would compile with the given arguments. static_assert(ct::is_invokable(foo{}, 0, 0, i), ""); // no error: std::invoke(foo{}, 0, 0, i); // This call returns std::false_type, because it's an illegal call. static_assert(!ct::is_invokable(foo{}, nullptr), ""); // error: std::invoke(foo{}, nullptr); // For function objects, the following checks are determined by the // function qualifiers on operator(), rather than the qualifiers on // of the type passed. This is done for consistency with member function // pointers, where the checks below would look at the function qualifiers // (rather than qualifiers on the pointer itself). static_assert(ct::is_const_member(), ""); static_assert(!ct::is_volatile_member(), ""); static_assert(!ct::is_reference_member(), ""); static_assert(!ct::is_lvalue_reference_member(), ""); static_assert(!ct::is_rvalue_reference_member(), ""); // is_constexpr would return std::true_type if foo's operator() were constexpr. static_assert(!ct::is_constexpr(), ""); // The same check can be performed using std::integral_constant // in conjunction with function addresses: using pmf = decltype(&foo::operator()); using pmf_constant = std::integral_constant; static_assert(!ct::is_constexpr(), ""); // So that you don't have to scroll to the top to check, // here's the type of pmf for reference. using with_const = void (foo::*)(int, int&&, const int&, void*) const; static_assert(std::is_same::value, ""); // If you find yourself in the unfortunate-and-probably-avoidable // situation of needing to transform member function pointer // types, ``[libname]`` has all the tools you need to prolong // your sanity. // ``[libname]`` lets you manipulate qualifiers on PMF types. // To remove const: using mutable_pmf = ct::remove_member_const; using without_const = void (foo::*)(int, int&&, const int&, void*) /*no const!*/; static_assert(std::is_same::value, ""); // To add an rvalue qualifier: using rvalue_pmf = ct::add_member_rvalue_reference; using with_rvalue = void (foo::*)(int, int&&, const int&, void*) const &&; static_assert(std::is_same::value, ""); // Just like std::add_rvalue_reference, ``[namespace_scoped]``add_member_rvalue_reference // follows C++11 reference collapsing rules. While remove_member_const // and add_member_rvalue_reference are somewhat clumsy names, they are the best // the best the author could provide while still allowing both terseness // and grep-ability against std::remove_const, etc. in . // Naturally, ``[libname]`` provides similar tools for the other C++ // function qualifiers. Head to the reference section of this documentation // for more examples. // To remove a member pointer: using fn = ct::remove_member_pointer; using expected_fn = void (int, int&&, const int&, void*) const; static_assert(std::is_same::value, ""); // We just created an abominable function type - notice the const // qualifier. ``[namespace_scoped]``remove_member_const accepts abominable // types too (and so does any feature where it is legal to do so): using not_abominable = ct::remove_member_const; using expected_fn2 = void (int, int&&, const int&, void*); static_assert(std::is_same::value, ""); } //] #endif