diff --git a/doc/callable_traits.qbk b/doc/callable_traits.qbk index 4b15a09..e524f7f 100644 --- a/doc/callable_traits.qbk +++ b/doc/callable_traits.qbk @@ -13,74 +13,178 @@ [lang en] ] -[template library_name[][^CallableTraits]] +[/ Boost developer: you should enable word wrap before you read further] + +[template libname[][^CallableTraits]] +[template libns[][^callable_traits::]] [template header_include_prefix[]callable_traits/] +[template detail_open_include[] `#include<`[^[header_include_prefix]]] +[template detail_close_include[]`.hpp>`] +[template include_header[name][detail_open_include][^[name]][detail_close_include][br]] +[template invoke[][@http://en.cppreference.com/w/cpp/utility/functional/invoke INVOKE]] + +[/ *** concepts *** ] + +[template fn_obj[][role green FunctionObject]] +[template overloaded_fn_obj[][role green OverloadedFunctionObject]] +[template simple_fn_obj[][role green SimpleFunctionObject]] +[template pmf[][role green MemberFunctionPtr]] +[template pmd[][role green MemberDataPtr]] +[template member_ptr[][role green MemberPtr]] +[template fn[][role green Function]] +[template abominable[][role green AbominableFunction]] +[template fn_ptr[][role green FunctionPtr]] +[template fn_ref[][role green FunctionReference]] +[template simple_callable[][role green SimpleCallable]] +[template simple_invokable[][role green SimpleInvokable]] +[template callable[][role green Callable]] +[template invokable[][role green Invokable]] +[template constexpr_constructible[][role green ConstexprDefaultConstructible]] + + [section:introduction Introduction] -[heading Quick Example] + +[libname] provides a comprehensive, uniform, and modern type-level interface for the inspection, decomposition, and synthesis of C++ callable types. This documentation will be most beneficial to readers with a basic understanding of the syntax and usage of the following C++ features: + +* [@http://en.cppreference.com/w/cpp/language/partial_specialization template specializations] +* [@http://en.cppreference.com/w/cpp/language/sfinae SFINAE] +* [invoke] rules +* function types +* [@http://en.cppreference.com/w/cpp/language/pointer#Pointers_to_functions function pointers] +* [@http://stackoverflow.com/questions/480248/function-references function references] +* [@http://en.cppreference.com/w/cpp/language/pointer#Pointers_to_member_functions pointers to member functions] +* [@http://en.cppreference.com/w/cpp/language/pointer#Pointers_to_data_members pointers to data members] +* [@http://en.cppreference.com/w/cpp/language/operators#Function_call_operator the function call operator, [^operator()]] +* [@https://isocpp.org/blog/2012/11/universal-references-in-c11-scott-meyers universal references] and [@http://stackoverflow.com/questions/13725747/concise-explanation-of-reference-collapsing-rules-requested-1-a-a-2 reference collapsing rules] +* [@http://en.cppreference.com/w/cpp/language/member_functions#const-.2C_volatile-.2C_and_ref-qualified_member_functions cv-qualified and ref-qualified member functions] +* [@http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/p0172r0.html "abominable" function types] +* [@http://en.cppreference.com/w/cpp/language/overloaded_address taking the address of an overloaded function] +* [@http://en.cppreference.com/w/c/language/variadic C-style variadics], a.k.a. `varargs` +* [@https://en.wikipedia.org/wiki/X86_calling_conventions calling conventions] + +[section Quick Example] [import ../example/intro.cpp] [intro] +[endsect] [section:motivation Motivation] -The complexity of callable types in C++ is extensive: +Several library solutions exist to manipulate functions or callable types. However, none of these are comprehensive, and many of them have not aged well with the advent of modern C++. [libname] fills the gaps found in existing solutions, and improves upon them wherever possible. -*function types -*function pointers -*function references -*objects with `operator()` -*objects with function pointer conversions -*[@http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/p0172r0.html "abominable"] function types -*pointers to member data -*pointers to member functions -*qualified overloads of member functions: `const`, `volatile`, `&`, `&&` -*C-style varargs (`...`) -*calling conventions/attributes (`__cdecl`, `__stdcall`, `__fastcall`, `pascal`, etc.) -*[@http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/p0012r1.html [^noexcept]] +Consider for a moment this rather hideous template specialization: -[library_name] provides a comprehensive, uniform, and modern type-level interface for the manipulation and inspection of callable types in C++. By filling the gaps where existing library solutions fall short, [library_name] aims to provide such an exhaustive interface for the features listed above that library writers will *never again* need to specialize templates for callable types. [library_name] eliminates the need for horrific template specializations like these: - template - struct foo{ - //... - }; +[import ./hideous_template.snippet.cpp] +[hideous_template] -Several library solutions exist to manipulate these types, or to abstract away their complexities. However, these solutions are occasionally inflexible or lacking in features, especially regarding function objects/lambdas. In [library_name], function pointers, function references, function types, abominable function types, member function pointers, member data pointers, function objects/lambdas, and references/pointers/smart pointers thereof are generally interchangeable. +Potential use cases for such obscure specializations are vitually nonexistent in run-of-the-mill C++ application codebases. Even in library code, these are exceedingly rare. However, there are a handful of specific problems in C++ that cannot be solved by any other means. While rare, a correct and generic implementation of these templates is tedious and time consuming. [libname] premises that these rare cases necessitate a library solution. One goal of [libname] is to [*completely and decisively eliminate the need for function signature template specializations]. To the knowledge of the author, [libname] is the first and only library solution to attempt this. -The use cases for [library_name] are closely related to those of [@http://www.boost.org/doc/libs/1_60_0/libs/type_traits/doc/html/boost_typetraits/reference/function_traits.html function_traits] and [@http://www.boost.org/doc/libs/1_60_0/libs/function_types/doc/html/index.html FunctionTypes]. +The use cases for [libname] are closely related to those of [@http://www.boost.org/doc/libs/1_60_0/libs/type_traits/doc/html/boost_typetraits/reference/function_traits.html function_traits] and [@http://www.boost.org/doc/libs/1_60_0/libs/function_types/doc/html/index.html FunctionTypes]. -[important The upcoming C++17 ISO standard brings a language change that adds `noexcept` to signatures. Currently, this is not handled in [library_name], but will be in the future for platforms that support it.] +[important The upcoming C++17 ISO standard includes a [@http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/p0012r1.html change to the core language] which adds [^noexcept] to function signatures. Currently, this is not handled in [libname]. New features to account for this change are planned in [libname], which the author intends to add, with regard for backwards compatibility, as soon as the language change is implemented in a major compiler. ] [endsect] -[section:ten_reasons 10 reasons to use [library_name]] +[section:basic_concepts Concept Definitions] -# [library_name]' template aliases and `constexpr` `std::integral_constant` functions are preferable to `typename foo::type` and `foo::value` +[heading [fn_ptr]] +* Any function pointer type +* May be cv-qualified and/or ref-qualified +* e.g. `void(*)(int, int)` -# [library_name] offers a familar, consistent interface for the synthesis and decomposition of callable types - * e.g. `remove_function_const` parallels `std::remove_const` +[heading [fn_ref]] +* Any function reference type +* e.g. `void(&)(int, int)` -# universal references are accepted everywhere, so you don't need to worry about using `std::remove_reference` first +[heading [fn]] +* Any unqualified function type +* e.g. `void(int, int)` -# function object pointers/ pointers are accepted (when `INVOKE` is not an immediate concern) +[heading [abominable]] +* Any qualified (a.k.a. [@http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/p0172r0.html "abominable"]) function type +* Not technically "callable", but still falls under the [libname] umbrella +* Mutually exclusive with [fn] +* e.g. `void(int, int) const` -# `arg_at<2, Callable>` is better than `typename function_traits::arg2_type` +[heading [pmf]] +* Any pointer to member function type +* The pointer type may be cv-qualified and/or ref-qualified +* e.g. `void (foo::*)(int, int)` -# function objects are first-class citizens in [library_name] - * e.g. `result_of` can accept a function object +[heading [pmd]] +* Any pointer to data member type +* The pointer type may be cv-qualified and/or ref-qualified +* e.g. `int foo::*` -# `is_constexpr` lets you check whether a function, member function, or function object is `constexpr` +[heading [simple_fn_obj]] +* Any class/struct with a ['non-templated, non-overloaded] function call operator +* Includes non-generic lambda types +* May be cv-qualified and/or ref-qualified. +* Mutually exclusive with [overloaded_fn_obj] +* e.g. the type of this lambda: `[](int x, int y) { return x + y; }` -# `can_invoke` lets you test `INVOKE`-ability at compile-time using value semantics +[heading [overloaded_fn_obj]] +* Any class/struct with a ['templated or overloaded] function call operator, with the following limitation for those with templated function call operators (which includes generic lambdas): + * If templated , all instantiations must be [@http://stackoverflow.com/questions/35033306/what-does-it-mean-when-one-says-something-is-sfinae-friendly SFINAE-friendly], and must not use [@http://en.cppreference.com/w/cpp/language/dependent_name dependent names], except where the dependent "names" are C++ operators. +* May be cv-qualified and/or ref-qualified +* e.g. the type of this generic lambda: `[](auto x, auto y) { return x + y; }` -# `can_invoke_constexpr` adds a `constexpr`-ness check to `can_invoke` +[warning Generic lambdas or classes with templated function objects that are either not SFINAE-friendly or rely on dependent names for template instantiations are generally incompatible with [libname].] -# `min_arity` can be used to detect default arguments of a function object +[heading [fn_obj]] +* Any type that is either an [overloaded_fn_obj] or a [simple_fn_obj] + +[heading [callable]] +* The superset of the following: + * [fn_ptr] + * [fn_ref] + * [fn] + * [abominable] + * [pmf] + * [pmd] + * [fn_obj] + +[heading [simple_callable]] +* Includes all [callable] types, [*except for [overloaded_fn_obj]] + +[heading [simple_invokable]] +* Includes all [callable] types, [*except for [overloaded_fn_obj], [fn], and [abominable]] + +[heading [invokable]] +* Includes all [callable] types, [*except for [fn] and [abominable]] + +[heading [constexpr_constructble]] +* Any [@http://en.cppreference.com/w/cpp/concept/LiteralType LiteralType] that is also default-constructible + +[endsect] + +[section:reasons Why use [libname]?] + +[*1.] [libname] offers template aliases such as `remove_function_const` for manipulating function qualifiers, designed to parallel the `` aliases such as `std::remove_const_t`. + +[*2.] [libname] is designed to accept cv-qualified and ref-qualified types, which eliminates the need to prepare template argument types with metafunctions such as `std::remove_reference` and `std::decay`. This is especially useful when dealing with perfectly-forwarded parameter types in function templates. + +[*3.] [libname] is designed in terms of the [invoke] rules, with one unobtrusive and beneficial deviation: [fn] types and [abominable] types are compatible with all operations that do not specifically require an invocation. + +[*4.] [libns]`arg_at<2, Callable>` is both more flexible and more readable than `typename boost::function_traits::arg3_type`. + +[*5.] [libns]`can_invoke` can be used to test `INVOKE`-ability at compile-time, with value semantics. For the craziest metaprogrammers, [libns]`can_invoke_constexpr` does the same as `can_invoke` for [constexpr_constructible] types, with an added check for `constexpr`-ness (Note: for `can_invoke_constexpr`, argument types must also be [constexpr_constructible]) + +[*6.] [libns]`is_constexpr` can be used to check whether a [constexpr_constructible] type is a [callable] type that yields a `constexpr` result -- no arguments necessary. + +[*7.] [libns]`bind` can be used by library writers to easily accept `std::placeholder` expressions anywhere, without requiring the user to `std::bind` them first. Library writers can then decide to use `std::bind`, `boost::bind`, or whatever implementation suits their needs. Either way, it allows for the creation of more flexible APIs, where a function template taking a "callable type" can be overloaded to also accept placeholder expressions. It's perhaps a small improvement from requiring `std::bind` first, but in API design, even the smallest things count. + +[*8.] [libns]`min_arity` can be used to detect the presence of default arguments on a [simple_fn_obj] + +[*9.] The [libname] interface mainly consists of template aliases and `constexpr std::integral_constant` functions, which are arguably preferable to `typename foo::type` and `foo::value`. While the `std::integral_constant` functions in [libname] may be a deviation from traditional type trait designs, they facilitate a metaprogramming style that uses value-semantics (a la [@https://boostorg.github.io/hana/ [^Boost.Hana]]). The functions can generally be used with either types or values, which eliminates unnecessary `decltype` usage. + +[*10.] [libname] includes optional features for the manipulation and inspection of calling conventions. These features are currently deemed experimental, because they greatly increase the test surface of [libname], are platform-specific, and are not yet fully tested on any platform. [endsect] [section:function_sugar Use case: `std::function` sugar] -At the time of this writing, there are 260 [@http://stackoverflow.com/search?q=convert+std%3A%3Abind+to+std%3A%3Afunction search results] on Stack Overflow concerning the conversion from `std::bind` to `std::function`. There are 336 [@http://stackoverflow.com/search?q=convert+lambda+to+std%3A%3Afunction search results] concerning the conversion of lambdas to `std::function`. With this example, we'll kill both birds with the same stone. The `make_function` function defined below will leverage [library_name] to... +At the time of this writing, there are around 260 [@http://stackoverflow.com/search?q=convert+std%3A%3Abind+to+std%3A%3Afunction search results] on Stack Overflow concerning the conversion from `std::bind` to `std::function`. There are roughly 340 [@http://stackoverflow.com/search?q=convert+lambda+to+std%3A%3Afunction search results] concerning the conversion of lambdas to `std::function`. In the example below, we'll kill both birds with the same stone. The `make_function` function will leverage [libname] to... # Create an `std::function` where T is not explicitly supplied by the user # Create an `std::function` where T is the deduced "signature" of an `std::placeholders` expression. @@ -94,21 +198,13 @@ Without real-world context, `make_function` may seem rather silly to those who k [endsect] - - -[template ct_open_include[] `#include<`[^[header_include_prefix]]] -[template ct_close_include[]`.hpp>`] -[template include_header[name][ct_open_include][^[name]][ct_close_include][br]] - - - [section:headers Headers] -The simplest way to use [library_name] is to include the main header file: +The simplest way to use [libname] is to include the main header file: [include_header callable_traits] -[library_name] interface is also broken down by trait into individual header files. To use only the traits you need, include one or more of the following headers, listed alphabetically: +[libname] interface is also broken down by trait into individual header files. To use only the traits you need, include one or more of the following headers, listed alphabetically: [include_header [link callable_traits.ref_add_calling_convention add_calling_convention]] [include_header [link callable_traits.ref_add_function_const add_function_const]] @@ -153,12 +249,30 @@ The simplest way to use [library_name] is to include the main header file: [section:ref_add_calling_convention add_calling_convention] -TODO +`add_calling_convention*/ +//[ hideous_template +template +struct foo { + //... +}; +//] diff --git a/doc/html/callable_traits/ref_add_calling_convention.html b/doc/html/callable_traits/ref_add_calling_convention.html index d366fce..e02eec6 100644 --- a/doc/html/callable_traits/ref_add_calling_convention.html +++ b/doc/html/callable_traits/ref_add_calling_convention.html @@ -27,8 +27,71 @@ add_calling_convention

- TODO + add_calling_convention<T, CcTag + will add a calling conventions to a type, where T + is a FunctionPtr or MemberFunctionPtr, + and CcTag is one of the following + types:

+
    +
  • + callable_traits::cdecl_tag +
  • +
  • + callable_traits::stdcall_tag +
  • +
  • + callable_traits::fastcall_tag +
  • +
  • + callable_traits::pascal_tag +
  • +
+

+ For add_calling_convention + to actually work, you must: +

+
    +
  1. + Be programming on a supported platform +
  2. +
  3. + Add the calling convention to a pointer type where the platform allows + you to do so +
  4. +
  5. + Before including any CallableTraits headers, you must + first define the appropriate macro(s) to enable the desired calling convention(s): +
      +
    • + CALLABLE_TRAITS_ENABLE_CDECL + for __cdecl +
    • +
    • + CALLABLE_TRAITS_ENABLE_STDCALL + for __stdcall +
    • +
    • + CALLABLE_TRAITS_ENABLE_FASTCALL + for __fastcall +
    • +
    • + CALLABLE_TRAITS_ENABLE_PASCAL + for pascal +
    • +
    +
  6. +
+
+ + + + + +
[Warning]Warning

+ This feature is currently considered experimental. Your feedback and advice + is much appreciated! +

Example @@ -68,28 +131,9 @@ Example - __cdecl

-
#define CALLABLE_TRAITS_ENABLE_CDECL
-
-#include <type_traits>
-#include <callable_traits/has_calling_convention.hpp>
-#include <callable_traits/add_calling_convention.hpp>
-
-namespace ct = callable_traits;
-
-struct foo {};
-
-int main() {
-
-    //depending on your platform, pmf may alrady have an implicit __cdecl
-    using pmf = void(foo::*)();
-    using expect = void(__cdecl foo::*)();
-    using test = ct::add_calling_convention<pmf, ct::cdecl_tag>;
-
-    static_assert(std::is_same<test, expect>::value, "");
-    static_assert(ct::has_calling_convention<expect, ct::cdecl_tag>(), "");
-    static_assert(ct::has_calling_convention<test, ct::cdecl_tag>(), "");
-}
-
+

+ [calling_convention_cdecl] +

diff --git a/doc/html/callable_traits/ref_args.html b/doc/html/callable_traits/ref_args.html index ac53bd7..4e000e9 100644 --- a/doc/html/callable_traits/ref_args.html +++ b/doc/html/callable_traits/ref_args.html @@ -56,7 +56,6 @@ autolamda=[](int,float&,constchar*){};usinglam=decltype(lamda);test<lam>(); - test<lam*>();test<lam&>();test<lam&&>();test<lamconst&>(); diff --git a/doc/html/callable_traits/ref_function_type.html b/doc/html/callable_traits/ref_function_type.html index 29f799a..6c9177b 100644 --- a/doc/html/callable_traits/ref_function_type.html +++ b/doc/html/callable_traits/ref_function_type.html @@ -53,7 +53,6 @@ autolamda=[](int,float&,constchar*){};usinglam=decltype(lamda);test<lam>(); - test<lam*>();test<lam&>();test<lam&&>();test<lamconst&>(); diff --git a/doc/html/index.html b/doc/html/index.html index 25dab44..15429f4 100644 --- a/doc/html/index.html +++ b/doc/html/index.html @@ -38,9 +38,11 @@
Introduction
+
Quick Example
Motivation
-
10 reasons - to use CallableTraits
+
Concept + Definitions
+
Why use CallableTraits?
Use case: std::function sugar
@@ -89,29 +91,101 @@ Introduction -

- - Quick - Example -

+

+ CallableTraits provides a comprehensive, uniform, and modern + type-level interface for the inspection, decomposition, and synthesis of C++ + callable types. This documentation will be most beneficial to readers with + a basic understanding of the syntax and usage of the following C++ features: +

+ +
+ +

+ This short program showcases some, but not all, of the features available + in CallableTraits. +

#include <type_traits>
 #include <functional>
 #include <tuple>
 #include <callable_traits/callable_traits.hpp>
 
+namespace ct = callable_traits;
+
+// foo is an example of a function object
 struct foo {
     void operator()(int, int&&, const int&, void* = nullptr) const {}
 };
 
-namespace ct = callable_traits;
-using namespace std::placeholders;
-
 int main() {
 
     // indexed argument types
@@ -119,173 +193,154 @@
     static_assert(std::is_same<second_arg, int&&>::value, "");
 
     // arg types are packaged into std::tuple, which serves as the default
-    // type list in CallableTraits (runtime capabilities are not used).
+    // type list in CallableTraits (runtime capabilities are not used).
     using args = ct::args<foo>;
     using expected_args = std::tuple<int, int&&, const int&, void*>;
     static_assert(std::is_same<args, expected_args>::value, "");
 
-    //callable_traits::result_of is a bit friendlier than std::result_of
-    using return_type = ct::result_of<foo>;
-    static_assert(std::is_same<return_type, void>::value, "");
-
-    //has_void_return is a quicker way to perform the check above
-    static_assert(ct::has_void_return(foo{}), "");
-
-    // callable_traits::function_type decays a callable type to
-    // a plain function type, which is structured in terms of INVOKE
+    // callable_traits::function_type "decays" a callable type to a plain
+    // function type, which is structured in terms of INVOKE.
     using function_type = ct::function_type<foo>;
     using expected_function_type = void(int, int&&, const int&, void*);
     static_assert(std::is_same<function_type, expected_function_type>::value, "");
 
-    // when trait information can be conveyed in an std::integral_constant,
-    // callable_traits opts for constexpr functions instead of template aliases.
-    // This is done to encourage value semantics, and to simplify usage inside
-    // of forwarding functions.
-    static_assert(ct::arity(foo{}) == 4, "");
-    static_assert(ct::max_arity(foo{}) == 4, "");
-    static_assert(ct::min_arity(foo{}) == 3, "");
-
-    // CallableTraits provides constexpr functions so that the user doesn't
-    // need to worry about reference collapsing or decltype when dealing with
-    // universal references to callables. Still, you don't NEED an instance,
-    // because CallableTraits provides non-deduced function templates for
-    // all constexpr functions (except for can_invoke/can_invoke_constexpr and bind,
-    // which model std::invoke and std::bind, respectively -- more on these below).
-    // Here's an example of the non-deduced version of arity, which take an
-    // explicit type argument. We'll ignore these non-deduced overloads for the
-    // rest of this example.
+    // By design, the CallableTraits interface uses constexpr
+    // std::integral_constant functions (whenever sensible).
+    // By also defining the appropriate overloads, this gives
+    // users the option of using either type arguments or a value
+    // arguments, which often eliminates the need for decltype:
     static_assert(ct::arity<foo>() == 4, "");
+    static_assert(ct::arity(foo{}) == 4, "");
 
-    // C-style variadics detection (ellipses in a signature)
-    static_assert(!ct::has_varargs(foo{}), "");
+    // 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<foo&&>() == 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<foo>() == 4, "");
+    static_assert(ct::min_arity<foo>() == 3, "");
+
+    // a quick way to check for a void return type
+    static_assert(ct::has_void_return<foo>(), "");
+
+    // C-style variadics detection (e.g. an ellipses in a signature)
+    static_assert(!ct::has_varargs<foo>(), "");
 
     int i = 0;
 
-    // callable_traits::can_invoke allows us to preview whether std::invoke will
-    // compile with the given arguments.
+    // callable_traits::can_invoke allows us to preview whether
+    // std::invoke would compile with the given arguments.
     static_assert(ct::can_invoke(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::can_invoke(foo{}, nullptr), "");
     // error:         std::invoke(foo{}, nullptr);
 
+    // Note that since can_invoke models std::invoke,
+    // only a value-style function is defined.
+
     // For function objects, the following checks are determined by the
-    // qualifiers on operator(), rather than the category of the passed value.
-    // For member function pointers and abominable function types, the
-    // qualifiers on the function type are used.
-    static_assert(ct::is_const_qualified(foo{}), "");
-    static_assert(!ct::is_volatile_qualified(foo{}), "");
-    static_assert(!ct::is_reference_qualified(foo{}), "");
-    static_assert(!ct::is_lvalue_qualified(foo{}), "");
-    static_assert(!ct::is_rvalue_qualified(foo{}), "");
-
-
-    // If you find yourself in the unfortunate situation of needing
-    // to manipulate member function pointer types, CallableTraits
-    // has all the tools you need to maintain your sanity.
+    // 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_qualified<foo>(), "");
+    static_assert(!ct::is_volatile_qualified<foo>(), "");
+    static_assert(!ct::is_reference_qualified<foo>(), "");
+    static_assert(!ct::is_lvalue_qualified<foo>(), "");
+    static_assert(!ct::is_rvalue_qualified<foo>(), "");
 
+    // is_constexpr would return std::true_type if foo's operator() were constexpr.
+    static_assert(!ct::is_constexpr<foo>(), "");
 
+    // 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<pmf, &foo::operator()>;
+    static_assert(!ct::is_constexpr<pmf_constant>(), "");
 
-    // So that you don't have to scroll back up to see, here's the type of pmf:
+    // 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<pmf, with_const>::value, "");
 
-    // Let's remove the const qualifier:
+    // If you find yourself in the unfortunate-and-probably-avoidable
+    // situation of needing to transform member function pointer
+    // types, CallableTraits has all the tools you need to prolong
+    // your sanity.
+
+    // CallableTraits lets you manipulate qualifiers on PMF types.
+    // To remove const:
     using mutable_pmf = ct::remove_function_const<pmf>;
     using without_const = void (foo::*)(int, int&&, const int&, void*) /*no const!*/;
     static_assert(std::is_same<mutable_pmf, without_const>::value, "");
 
-    // Now let's add an rvalue qualifier (&&):
+    // To add an rvalue qualifier:
     using rvalue_pmf = ct::add_function_rvalue<pmf>;
     using with_rvalue = void (foo::*)(int, int&&, const int&, void*) const &&;
     static_assert(std::is_same<rvalue_pmf, with_rvalue>::value, "");
 
-    // You get the picture. CallableTraits lets you add and remove all PMF
-    // qualifiers (const, volatile, &, &&, and any combination thereof).
-    // These type operations can generally be performed on abominable function
-    // types as well.
+    // Just like std::add_rvalue_reference, callable_traits::add_function_rvalue
+    // follows C++11 reference collapsing rules. While remove_function_const
+    // and add_function_rvalue 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 <type_traits>.
+    // Naturally, CallableTraits provides similar tools for the other C++
+    // function qualifiers. Head to the reference section of this documentation
+    // for more examples.
 
-    // is_constexpr would return std::true_type if foo's operator() were constexpr.
-    static_assert(!ct::is_constexpr(foo{}), "");
+    // To remove a member pointer:
+    using fn = ct::remove_member_pointer<pmf>;
+    using expected_fn = void (int, int&&, const int&, void*) const;
+    static_assert(std::is_same<fn, expected_fn>::value, "");
 
-    // to check constexprness of a function or member function, you must use an
-    // std::integral_constant, like this:
-    using pmf_constant = std::integral_constant<pmf, &foo::operator()>;
-    static_assert(!ct::is_constexpr(pmf_constant{}), "");
+    // We just created an abominable function type - notice the const
+    // qualifier! libnsremove_function_const accepts abominable
+    // types too (and so does any feature where it is legal to do so):
+    using not_abominable = ct::remove_function_const<fn>;
+    using expected_fn2 = void (int, int&&, const int&, void*);
+    static_assert(std::is_same<not_abominable, expected_fn2>::value, "");
 }
 
+

- The complexity of callable types in C++ is extensive: + Several library solutions exist to manipulate functions or callable types. + However, none of these are comprehensive, and many of them have not aged + well with the advent of modern C++. CallableTraits fills + the gaps found in existing solutions, and improves upon them wherever possible.

-
    -
  • - function types -
  • -
  • - function pointers -
  • -
  • - function references -
  • -
  • - objects with operator() -
  • -
  • - objects with function pointer conversions -
  • -
  • - "abominable" - function types -
  • -
  • - pointers to member data -
  • -
  • - pointers to member functions -
  • -
  • - qualified overloads of member functions: const, - volatile, &, && -
  • -
  • - C-style varargs (...) -
  • -
  • - calling conventions/attributes (__cdecl, - __stdcall, __fastcall, pascal, - etc.) -
  • -
  • - noexcept -
  • -

- CallableTraits provides a comprehensive, uniform, and - modern type-level interface for the manipulation and inspection of callable - types in C++. By filling the gaps where existing library solutions fall short, - CallableTraits aims to provide such an exhaustive interface - for the features listed above that library writers will never - again need to specialize templates for callable types. CallableTraits - eliminates the need for horrific template specializations like these: + Consider for a moment this rather hideous template specialization:

-
template<typename Ret, typename T, typename... Args>
-struct foo<Ret(T::*)(Args..., ...) const volatile &&>{
-                //...
+
template<typename Return, typename T, typename... Args>
+struct foo<Return(T::*)(Args...) const volatile &&> {
+    //...
 };
 

- Several library solutions exist to manipulate these types, or to abstract - away their complexities. However, these solutions are occasionally inflexible - or lacking in features, especially regarding function objects/lambdas. In - CallableTraits, function pointers, function references, - function types, abominable function types, member function pointers, member - data pointers, function objects/lambdas, and references/pointers/smart pointers - thereof are generally interchangeable. + Potential use cases for such obscure specializations are vitually nonexistent + in run-of-the-mill C++ application codebases. Even in library code, these + are exceedingly rare. However, there are a handful of specific problems in + C++ that cannot be solved by any other means. While rare, a correct and generic + implementation of these templates is tedious and time consuming. CallableTraits + premises that these rare cases necessitate a library solution. One goal of + CallableTraits is to completely + and decisively eliminate the need for function signature template specializations. + To the knowledge of the author, CallableTraits is the + first and only library solution to attempt this.

The use cases for CallableTraits are closely related to @@ -298,69 +353,310 @@

Important

- The upcoming C++17 ISO standard brings a language change that adds noexcept to signatures. Currently, this - is not handled in CallableTraits, but will be in the - future for platforms that support it. + The upcoming C++17 ISO standard includes a change + to the core language which adds noexcept to + function signatures. Currently, this is not handled in CallableTraits. + New features to account for this change are planned in CallableTraits, + which the author intends to add, with regard for backwards compatibility, + as soon as the language change is implemented in a major compiler.

-
    +
    + + FunctionPtr +
    +
    • - CallableTraits' template aliases and constexpr std::integral_constant - functions are preferable to typename - foo::type and foo::value + Any function pointer type
    • - CallableTraits offers a familar, consistent interface - for the synthesis and decomposition of callable types -
      • - e.g. remove_function_const - parallels std::remove_const + May be cv-qualified and/or ref-qualified +
      • +
      • + e.g. void(*)(int, int) +
      • +
      +
      + + FunctionReference +
      +
        +
      • + Any function reference type +
      • +
      • + e.g. void(&)(int, int) +
      • +
      +
      + + Function +
      +
        +
      • + Any unqualified function type +
      • +
      • + e.g. void(int, int) +
      • +
      +
      + + AbominableFunction +
      +
        +
      • + Any qualified (a.k.a. "abominable") + function type +
      • +
      • + Not technically "callable", but still falls under the CallableTraits + umbrella +
      • +
      • + Mutually exclusive with Function +
      • +
      • + e.g. void(int, int) const +
      • +
      +
      + + MemberFunctionPtr +
      +
        +
      • + Any pointer to member function type +
      • +
      • + The pointer type may be cv-qualified and/or ref-qualified +
      • +
      • + e.g. void (foo::*)(int, int) +
      • +
      +
      + + MemberDataPtr +
      +
        +
      • + Any pointer to data member type +
      • +
      • + The pointer type may be cv-qualified and/or ref-qualified +
      • +
      • + e.g. int foo::* +
      • +
      +
      + + SimpleFunctionObject +
      +
        +
      • + Any class/struct with a non-templated, non-overloaded + function call operator +
      • +
      • + Includes non-generic lambda types +
      • +
      • + May be cv-qualified and/or ref-qualified. +
      • +
      • + Mutually exclusive with OverloadedFunctionObject +
      • +
      • + e.g. the type of this lambda: [](int x, int y) { return x + y; } +
      • +
      +
      + + OverloadedFunctionObject +
      +
        +
      • + Any class/struct with a templated or overloaded + function call operator, with the following limitation for those with + templated function call operators (which includes generic lambdas): +
      • - universal references are accepted everywhere, so you don't need to worry - about using std::remove_reference first + May be cv-qualified and/or ref-qualified
      • - function object pointers/ pointers are accepted (when INVOKE - is not an immediate concern) + e.g. the type of this generic lambda: [](auto x, auto y) { return x + y; }
      • +
      +
      + + + + + +
      [Warning]Warning

      + Generic lambdas or classes with templated function objects that are either + not SFINAE-friendly or rely on dependent names for template instantiations + are generally incompatible with CallableTraits. +

      +
      + + FunctionObject +
      +
      • + Any type that is either an OverloadedFunctionObject + or a SimpleFunctionObject +
      +
      + + Callable +
      +
      • + The superset of the following: +
        • - arg_at<2, Callable> is better than typename - function_traits<Callable>::arg2_type -
        • + FunctionPtr +
        • - function objects are first-class citizens in CallableTraits -
          • - e.g. result_of - can accept a function object -
          -
        • + FunctionReference +
        • - is_constexpr lets you - check whether a function, member function, or function object is constexpr -
        • + Function +
        • - can_invoke lets you test - INVOKE-ability at compile-time - using value semantics -
        • + AbominableFunction +
        • - can_invoke_constexpr - adds a constexpr-ness check - to can_invoke -
        • + MemberFunctionPtr +
        • - min_arity can be used - to detect default arguments of a function object -
        • -
+ MemberDataPtr + +
  • + FunctionObject +
  • +
    + +
    + + SimpleCallable +
    +
    • + Includes all Callable types, except for OverloadedFunctionObject +
    +
    + + SimpleInvokable +
    +
    • + Includes all Callable types, except for OverloadedFunctionObject, + Function, and AbominableFunction +
    +
    + + Invokable +
    +
    • + Includes all Callable types, except for Function and AbominableFunction +
    +
    + + [constexpr_constructble] +
    +
    • + Any LiteralType + that is also default-constructible +
    + +
    + +

    + 1. CallableTraits offers + template aliases such as remove_function_const + for manipulating function qualifiers, designed to parallel the <type_traits> aliases such as std::remove_const_t. +

    +

    + 2. CallableTraits is + designed to accept cv-qualified and ref-qualified types, which eliminates + the need to prepare template argument types with metafunctions such as std::remove_reference + and std::decay. This is especially useful when dealing + with perfectly-forwarded parameter types in function templates. +

    +

    + 3. CallableTraits is + designed in terms of the INVOKE + rules, with one unobtrusive and beneficial deviation: Function + types and AbominableFunction types are compatible + with all operations that do not specifically require an invocation. +

    +

    + 4. callable_traits::arg_at<2, Callable> is both more flexible and more readable + than typename boost::function_traits<Callable>::arg3_type. +

    +

    + 5. callable_traits::can_invoke can be used to test INVOKE-ability at compile-time, with value + semantics. For the craziest metaprogrammers, callable_traits::can_invoke_constexpr does the same as + can_invoke for ConstexprDefaultConstructible + types, with an added check for constexpr-ness + (Note: for can_invoke_constexpr, + argument types must also be ConstexprDefaultConstructible) +

    +

    + 6. callable_traits::is_constexpr can be used to check whether + a ConstexprDefaultConstructible type is a + Callable type that yields a constexpr + result -- no arguments necessary. +

    +

    + 7. callable_traits::bind can be used by library writers to + easily accept std::placeholder expressions anywhere, without + requiring the user to std::bind + them first. Library writers can then decide to use std::bind, + boost::bind, or whatever implementation suits + their needs. Either way, it allows for the creation of more flexible APIs, + where a function template taking a "callable type" can be overloaded + to also accept placeholder expressions. It's perhaps a small improvement + from requiring std::bind first, but in API design, even the + smallest things count. +

    +

    + 8. callable_traits::min_arity can be used to detect the presence + of default arguments on a SimpleFunctionObject +

    +

    + 9. The CallableTraits + interface mainly consists of template aliases and constexpr + std::integral_constant functions, which are + arguably preferable to typename foo::type + and foo::value. While the std::integral_constant + functions in CallableTraits may be a deviation from traditional + type trait designs, they facilitate a metaprogramming style that uses value-semantics + (a la Boost.Hana). + The functions can generally be used with either types or values, which eliminates + unnecessary decltype usage. +

    +

    + 10. CallableTraits includes + optional features for the manipulation and inspection of calling conventions. + These features are currently deemed experimental, because they greatly increase + the test surface of CallableTraits, are platform-specific, + and are not yet fully tested on any platform. +

    @@ -368,12 +664,12 @@ std::function sugar

    - At the time of this writing, there are 260 search + At the time of this writing, there are around 260 search results on Stack Overflow concerning the conversion from std::bind - to std::function. There are 336 search + to std::function. There are roughly 340 search results concerning the conversion of lambdas to std::function. - With this example, we'll kill both birds with the same stone. The make_function function defined below will - leverage CallableTraits to... + In the example below, we'll kill both birds with the same stone. The make_function function will leverage CallableTraits + to...

    1. @@ -400,7 +696,7 @@ namespace ct = callable_traits; - //make_function turns a non-overloaded callable into a type-erased std::function object + // make_function turns a non-overloaded callable into a type-erased std::function object template<typename T> inline decltype(auto) make_function(T&& t) { @@ -412,11 +708,11 @@ return result_type{ std::forward<T>(t) }; } - //this make_function overload turns a bind expression into a type-erased std::function object + // this make_function overload turns a bind expression into a type-erased std::function object template<typename T, typename First, typename... Others> inline decltype(auto) make_function(T&& t, First&& first, Others&&... others) { - // callable_traits::bind is essentially a compile-time parser of placeholders + // callable_traits::bind is essentially a compile-time parser of placeholder // expressions, for the purpose of retaining more type information than // std::bind normally allows - specifically, callable_traits::bind is used to // determine the de-facto signature of the std::bind return type, with special diff --git a/example/CMakeLists.txt b/example/CMakeLists.txt index bec89a8..543cdf2 100644 --- a/example/CMakeLists.txt +++ b/example/CMakeLists.txt @@ -9,8 +9,11 @@ add_dependencies(check examples) include_directories(${callable_traits_SOURCE_DIR}/include) file(GLOB_RECURSE EXAMPLES "*.cpp") -file(GLOB_RECURSE EXCLUDED_EXAMPLES ${EXCLUDED_EXAMPLES}) -list(REMOVE_ITEM EXAMPLES "" ${EXCLUDED_EXAMPLES}) + + +if (NOT CALLABLE_TRAITS_BUILD_EXPERIMENTAL) + list(REMOVE_ITEM EXAMPLES "experimental*.cpp") +endif() foreach(_file IN LISTS EXAMPLES) callable_traits_target_name_for(_target "${_file}") diff --git a/example/args.cpp b/example/args.cpp index c529d80..1e73118 100644 --- a/example/args.cpp +++ b/example/args.cpp @@ -29,7 +29,6 @@ int main() { auto lamda = [](int, float&, const char*){}; using lam = decltype(lamda); test(); - test(); test(); test(); test(); diff --git a/example/calling_convention_cdecl.cpp b/example/experimental_calling_convention_cdecl.cpp similarity index 95% rename from example/calling_convention_cdecl.cpp rename to example/experimental_calling_convention_cdecl.cpp index 35acacb..7c66053 100644 --- a/example/calling_convention_cdecl.cpp +++ b/example/experimental_calling_convention_cdecl.cpp @@ -5,7 +5,7 @@ Distributed under the Boost Software License, Version 1.0. (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt) ->*/ -//[ calling_convention_cdecl +//[ experimental_calling_convention_cdecl #define CALLABLE_TRAITS_ENABLE_CDECL diff --git a/example/changing_calling_conventions.cpp b/example/experimental_changing_calling_conventions.cpp similarity index 96% rename from example/changing_calling_conventions.cpp rename to example/experimental_changing_calling_conventions.cpp index 19b3b34..a2958ad 100644 --- a/example/changing_calling_conventions.cpp +++ b/example/experimental_changing_calling_conventions.cpp @@ -5,7 +5,7 @@ Distributed under the Boost Software License, Version 1.0. (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt) ->*/ -//[ changing_calling_conventions +//[ experimental_changing_calling_conventions #define CALLABLE_TRAITS_ENABLE_STDCALL #define CALLABLE_TRAITS_ENABLE_FASTCALL diff --git a/example/function_type.cpp b/example/function_type.cpp index 156e66d..a0ac14d 100644 --- a/example/function_type.cpp +++ b/example/function_type.cpp @@ -26,7 +26,6 @@ int main() { auto lamda = [](int, float&, const char*){}; using lam = decltype(lamda); test(); - test(); test(); test(); test(); diff --git a/example/intro.cpp b/example/intro.cpp index 1865061..816b5c1 100644 --- a/example/intro.cpp +++ b/example/intro.cpp @@ -5,18 +5,20 @@ Distributed under the Boost Software License, Version 1.0. ->*/ //[ 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 {} }; -namespace ct = callable_traits; -using namespace std::placeholders; - int main() { // indexed argument types @@ -24,99 +26,121 @@ int main() { static_assert(std::is_same::value, ""); // arg types are packaged into std::tuple, which serves as the default - // type list in CallableTraits (runtime capabilities are not used). + // type list in ``[libname]`` (runtime capabilities are not used). using args = ct::args; using expected_args = std::tuple; static_assert(std::is_same::value, ""); - //callable_traits::result_of is a bit friendlier than std::result_of - using return_type = ct::result_of; - static_assert(std::is_same::value, ""); - - //has_void_return is a quicker way to perform the check above - static_assert(ct::has_void_return(foo{}), ""); - - // callable_traits::function_type decays a callable type to - // a plain function type, which is structured in terms of INVOKE + // ``[libns]``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, ""); - // when trait information can be conveyed in an std::integral_constant, - // callable_traits opts for constexpr functions instead of template aliases. - // This is done to encourage value semantics, and to simplify usage inside - // of forwarding functions. - static_assert(ct::arity(foo{}) == 4, ""); - static_assert(ct::max_arity(foo{}) == 4, ""); - static_assert(ct::min_arity(foo{}) == 3, ""); - - // CallableTraits provides constexpr functions so that the user doesn't - // need to worry about reference collapsing or decltype when dealing with - // universal references to callables. Still, you don't NEED an instance, - // because CallableTraits provides non-deduced function templates for - // all constexpr functions (except for can_invoke/can_invoke_constexpr and bind, - // which model std::invoke and std::bind, respectively -- more on these below). - // Here's an example of the non-deduced version of arity, which take an - // explicit type argument. We'll ignore these non-deduced overloads for the - // rest of this example. + // By design, the ``[libname]`` interface uses constexpr + // std::integral_constant functions (whenever sensible). + // By also defining the appropriate overloads, this gives + // users the option of using either type arguments or a value + // arguments, which often eliminates the need for decltype: static_assert(ct::arity() == 4, ""); + static_assert(ct::arity(foo{}) == 4, ""); - // C-style variadics detection (ellipses in a signature) - static_assert(!ct::has_varargs(foo{}), ""); + // 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; - // callable_traits::can_invoke allows us to preview whether std::invoke will - // compile with the given arguments. + // ``[libns]``can_invoke allows us to preview whether + // std::invoke would compile with the given arguments. static_assert(ct::can_invoke(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::can_invoke(foo{}, nullptr), ""); // error: std::invoke(foo{}, nullptr); + // Note that since can_invoke models std::invoke, + // only a value-style function is defined. + // For function objects, the following checks are determined by the - // qualifiers on operator(), rather than the category of the passed value. - // For member function pointers and abominable function types, the - // qualifiers on the function type are used. - static_assert(ct::is_const_qualified(foo{}), ""); - static_assert(!ct::is_volatile_qualified(foo{}), ""); - static_assert(!ct::is_reference_qualified(foo{}), ""); - static_assert(!ct::is_lvalue_qualified(foo{}), ""); - static_assert(!ct::is_rvalue_qualified(foo{}), ""); - - - // If you find yourself in the unfortunate situation of needing - // to manipulate member function pointer types, CallableTraits - // has all the tools you need to maintain your sanity. + // 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_qualified(), ""); + static_assert(!ct::is_volatile_qualified(), ""); + static_assert(!ct::is_reference_qualified(), ""); + static_assert(!ct::is_lvalue_qualified(), ""); + static_assert(!ct::is_rvalue_qualified(), ""); + // 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 back up to see, here's the type of pmf: + // 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, ""); - // Let's remove the const qualifier: + // 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_function_const; using without_const = void (foo::*)(int, int&&, const int&, void*) /*no const!*/; static_assert(std::is_same::value, ""); - // Now let's add an rvalue qualifier (&&): + // To add an rvalue qualifier: using rvalue_pmf = ct::add_function_rvalue; using with_rvalue = void (foo::*)(int, int&&, const int&, void*) const &&; static_assert(std::is_same::value, ""); - // You get the picture. CallableTraits lets you add and remove all PMF - // qualifiers (const, volatile, &, &&, and any combination thereof). - // These type operations can generally be performed on abominable function - // types as well. + // Just like std::add_rvalue_reference, ``[libns]``add_function_rvalue + // follows C++11 reference collapsing rules. While remove_function_const + // and add_function_rvalue 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. - // is_constexpr would return std::true_type if foo's operator() were constexpr. - static_assert(!ct::is_constexpr(foo{}), ""); + // 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, ""); - // to check constexprness of a function or member function, you must use an - // std::integral_constant, like this: - using pmf_constant = std::integral_constant; - static_assert(!ct::is_constexpr(pmf_constant{}), ""); + // We just created an abominable function type - notice the const + // qualifier! ``libns``remove_function_const accepts abominable + // types too (and so does any feature where it is legal to do so): + using not_abominable = ct::remove_function_const; + using expected_fn2 = void (int, int&&, const int&, void*); + static_assert(std::is_same::value, ""); } //] diff --git a/example/make_function.cpp b/example/make_function.cpp index f3eebd9..f07d4f4 100644 --- a/example/make_function.cpp +++ b/example/make_function.cpp @@ -13,7 +13,7 @@ namespace example_library { namespace ct = callable_traits; - //make_function turns a non-overloaded callable into a type-erased std::function object + // make_function turns a non-overloaded callable into a type-erased std::function object template inline decltype(auto) make_function(T&& t) { @@ -25,11 +25,11 @@ namespace example_library { return result_type{ std::forward(t) }; } - //this make_function overload turns a bind expression into a type-erased std::function object + // this make_function overload turns a bind expression into a type-erased std::function object template inline decltype(auto) make_function(T&& t, First&& first, Others&&... others) { - // callable_traits::bind is essentially a compile-time parser of placeholders + // callable_traits::bind is essentially a compile-time parser of placeholder // expressions, for the purpose of retaining more type information than // std::bind normally allows - specifically, callable_traits::bind is used to // determine the de-facto signature of the std::bind return type, with special diff --git a/include/callable_traits/detail/function.hpp b/include/callable_traits/detail/function.hpp index 78e6e2c..1966f6b 100644 --- a/include/callable_traits/detail/function.hpp +++ b/include/callable_traits/detail/function.hpp @@ -14,7 +14,6 @@ Distributed under the Boost Software License, Version 1.0. #include #include #include -#include #include diff --git a/include/callable_traits/detail/function_object.hpp b/include/callable_traits/detail/function_object.hpp index bb167a7..0d4b746 100644 --- a/include/callable_traits/detail/function_object.hpp +++ b/include/callable_traits/detail/function_object.hpp @@ -10,9 +10,9 @@ Distributed under the Boost Software License, Version 1.0. #ifndef CALLABLE_TRAITS_DETAIL_FUNCTION_OBJECT_HPP #define CALLABLE_TRAITS_DETAIL_FUNCTION_OBJECT_HPP -#include #include #include +#include #include #include @@ -21,60 +21,22 @@ namespace callable_traits { namespace detail { - template - struct has_normal_call_operator - { - template struct check { check(std::nullptr_t) {} }; + template + struct function_object : Base { - template - static std::int8_t test(check); - - template - static std::int16_t test(...); - - static constexpr bool value = sizeof(test(nullptr)) == sizeof(std::int8_t); - - }; - - struct callable_dummy { - void operator()() {} - }; - - template - using default_normal_callable = typename std::conditional< - has_normal_call_operator::value, - T, - callable_dummy - >::type; - - template - struct function_object - : std::conditional< - has_normal_call_operator::value, - pmf::operator())>, - default_callable_traits - >::type { - - using base = typename std::conditional< - has_normal_call_operator::value, - pmf::operator())>, - default_callable_traits - >::type; - - using type = typename General::original_type; - using general_type = typename General::type; - using function_type = typename base::function_object_type; - using invoke_arg_types = typename base::arg_types; + using type = T; + using function_type = typename Base::function_object_type; + using invoke_arg_types = typename Base::arg_types; static constexpr const bool value = - std::is_class::value && !is_integral_constant::value; + std::is_class>::value && !is_integral_constant::value; using traits = function_object; using class_type = invalid_type; using invoke_type = invalid_type; using is_function_object = std::integral_constant::value>; + std::is_class>::value>; using is_member_pointer = std::false_type; using is_member_function_pointer = std::false_type; @@ -99,8 +61,8 @@ namespace callable_traits { using remove_function_cv = invalid_type; }; - template - struct function_object > + template + struct function_object : default_callable_traits {}; } } diff --git a/include/callable_traits/detail/fwd/function_object_fwd.hpp b/include/callable_traits/detail/fwd/function_object_fwd.hpp index 362b1b4..d682876 100644 --- a/include/callable_traits/detail/fwd/function_object_fwd.hpp +++ b/include/callable_traits/detail/fwd/function_object_fwd.hpp @@ -10,14 +10,50 @@ Distributed under the Boost Software License, Version 1.0. #ifndef CALLABLE_TRAITS_DETAIL_FWD_FUNCTION_OBJECT_FWD_HPP #define CALLABLE_TRAITS_DETAIL_FWD_FUNCTION_OBJECT_FWD_HPP +#include +#include + namespace callable_traits { namespace detail { template - struct function_object; + struct has_normal_call_operator + { + template struct check { check(std::nullptr_t) {} }; + template + static std::int8_t test(check); + + template + static std::int16_t test(...); + + static constexpr bool value = sizeof(test(nullptr)) == sizeof(std::int8_t); + + }; + + struct callable_dummy { + void operator()() {} + }; + + template + using default_to_function_object = typename std::conditional< + has_normal_call_operator::value, + T, + callable_dummy + >::type; + + template + using function_object_base = typename std::conditional< + has_normal_call_operator::value, + pmf::operator())>, + default_callable_traits + >::type; + + //Base is defaulted + template>> + struct function_object; } } -#endif \ No newline at end of file +#endif diff --git a/include/callable_traits/detail/generalized_class.hpp b/include/callable_traits/detail/generalized_class.hpp index 6c00467..543bd52 100644 --- a/include/callable_traits/detail/generalized_class.hpp +++ b/include/callable_traits/detail/generalized_class.hpp @@ -17,62 +17,11 @@ Distributed under the Boost Software License, Version 1.0. namespace callable_traits { - namespace detail { - - template - struct can_make_reference { - - template - static U& test(std::nullptr_t); - - static dummy test(...); - - using type = decltype(can_make_reference::test(nullptr)); - static constexpr bool value = !std::is_same::value; - }; - - template - struct is_class_after_dereference - { - template::value, U>::type, - typename Dereferenced = decltype(*std::declval()), - typename C = typename std::remove_reference::type, - typename std::enable_if::value, int>::type = 0> - static std::int8_t test(int); - - template - static std::int16_t test(...); - - static constexpr const bool value = - sizeof(test(0)) == sizeof(std::int8_t); - }; - - template - using can_dereference_to_class = std::integral_constant::value - >; - - template - using cannot_dereference_to_class = std::integral_constant::value - >; - - template - struct generalized_class; - - template - struct generalized_class::type>> { - using type = typename std::remove_reference::type; - using original_type = T; - }; - - template - struct generalized_class::type>> { - using type = typename std::remove_reference())>::type; - using original_type = T; - }; - } + template + struct generalized_class { + using type = typename std::remove_reference::type; + using original_type = T; + }; } #endif diff --git a/include/callable_traits/detail/traits.hpp b/include/callable_traits/detail/traits.hpp index c2ef968..5cf5c05 100644 --- a/include/callable_traits/detail/traits.hpp +++ b/include/callable_traits/detail/traits.hpp @@ -10,8 +10,6 @@ Distributed under the Boost Software License, Version 1.0. #ifndef CALLABLE_TRAITS_DETAIL_TRAITS_HPP #define CALLABLE_TRAITS_DETAIL_TRAITS_HPP - -#include #include #include #include @@ -32,14 +30,14 @@ namespace callable_traits { T >::type; - template + template> using traits = typename disjunction< - bind_expression_traits>, - function_object>, + bind_expression_traits, + function_object, function>, - pmf>, - pmd>, - function_object> + pmf, + pmd, + function_object >::traits; } } diff --git a/qtcreator/callable_traits.pro b/qtcreator/callable_traits.pro index c74236e..2d9d068 100644 --- a/qtcreator/callable_traits.pro +++ b/qtcreator/callable_traits.pro @@ -1,2 +1,7 @@ TEMPLATE = subdirs -SUBDIRS += main tests_and_examples include doc + +SUBDIRS += main \ + tests_and_examples \ + include \ + doc \ + project diff --git a/qtcreator/doc/doc.pro b/qtcreator/doc/doc.pro index 4c65dc2..6d834de 100644 --- a/qtcreator/doc/doc.pro +++ b/qtcreator/doc/doc.pro @@ -1,4 +1,4 @@ TEMPLATE = aux CONFIG -= qt -OTHER_FILES += ../../doc/* \ No newline at end of file +OTHER_FILES += ../../doc/* diff --git a/qtcreator/main/main.cpp b/qtcreator/main/main.cpp index 6b55b38..792e99c 100644 --- a/qtcreator/main/main.cpp +++ b/qtcreator/main/main.cpp @@ -1,40 +1,144 @@ -/*!<- -Copyright (c) 2016 Barrett Adair - +/*<- +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) +(See accompanying file LICENSE.md or copy at http ://boost.org/LICENSE_1_0.txt) ->*/ -//[ calling_convention_cdecl - -#define CALLABLE_TRAITS_ENABLE_STDCALL -#define CALLABLE_TRAITS_ENABLE_FASTCALL +//[ intro +//` This short program showcases some, but not all, of the features available in [libname]. #include -#include -#include -#include +#include +#include +#include namespace ct = callable_traits; -struct foo {}; +// foo is an example of a function object +struct foo { + void operator()(int, int&&, const int&, void* = nullptr) const {} +}; int main() { - using f = void(__fastcall *)(int); + // indexed argument types + using second_arg = ct::arg_at<1, foo>; + static_assert(std::is_same::value, ""); - static_assert(ct::has_calling_convention(), ""); - static_assert(!ct::has_calling_convention(), ""); + // 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, ""); - using expect = void(__stdcall *)(int); + // ``[libns]``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, ""); - static_assert(ct::has_calling_convention(), ""); - static_assert(!ct::has_calling_convention(), ""); + // By design, the ``libname`` interface uses constexpr + // std::integral_constant functions (whenever sensible). + // By also defining the appropriate overloads, this gives + // programmers the option of using either type arguments + // or a value arguments: + static_assert(ct::arity() == 4, ""); + static_assert(ct::arity(foo{}) == 4, ""); - using g = ct::remove_calling_convention; - using test = ct::add_calling_convention; + // 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::has_calling_convention(), ""); - static_assert(std::is_same::value, ""); + 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; + + // ``[libns]``can_invoke allows us to preview whether std::invoke + // would compile with the given arguments. + static_assert(ct::can_invoke(foo{}, 0, 0, i), ""); + // no error: std::invoke(foo{}, 0, 0, i); + + static_assert(!ct::can_invoke(foo{}, nullptr), ""); + // error: std::invoke(foo{}, nullptr); + + // For function objects, the following checks are determined by the + // value. 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_qualified(), ""); + static_assert(!ct::is_volatile_qualified(), ""); + static_assert(!ct::is_reference_qualified(), ""); + static_assert(!ct::is_lvalue_qualified(), ""); + static_assert(!ct::is_rvalue_qualified(), ""); + + // 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_function_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_function_rvalue; + using with_rvalue = void (foo::*)(int, int&&, const int&, void*) const &&; + static_assert(std::is_same::value, ""); + + // Just like std::add_rvalue_reference, ``[libns]``add_function_rvalue + // follows C++11 reference collapsing rules. While remove_function_const + // and add_function_rvalue 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! ``libns``remove_function_const accepts abominable + // types too: + + using not_abominable = ct::remove_function_const; + using expected_fn2 = void (int, int&&, const int&, void*); + static_assert(std::is_same::value, ""); } //] diff --git a/qtcreator/project/project.pro b/qtcreator/project/project.pro new file mode 100644 index 0000000..1f8b5d2 --- /dev/null +++ b/qtcreator/project/project.pro @@ -0,0 +1,4 @@ +TEMPLATE = aux +CONFIG -= qt + +OTHER_FILES += ../../* diff --git a/scripts/posix/clang-libcxx.sh b/scripts/posix/clang-libcxx.sh index 79ba921..d9cf337 100755 --- a/scripts/posix/clang-libcxx.sh +++ b/scripts/posix/clang-libcxx.sh @@ -1,8 +1,9 @@ #!/bin/sh -/bin/sh ./internal/build.sh \ -../../ \ -../../build_clang_libcxx \ --DCMAKE_CXX_COMPILER=clang++ \ --DCMAKE_CXX_FLAGS="-stdlib=libc++" \ --DCMAKE_EXE_LINKER_FLAGS="-stdlib=libc++ -lc++ -lc++abi" +/bin/sh ./internal/build.sh \ +../../ \ +../../build_clang_libcxx \ +-DCMAKE_CXX_COMPILER=clang++ \ +-DCMAKE_CXX_FLAGS="-stdlib=libc++" \ +-DCMAKE_EXE_LINKER_FLAGS="-stdlib=libc++ -lc++ -lc++abi" \ +-DCALLABLE_TRAITS_BUILD_EXPERIMENTAL=ON diff --git a/scripts/posix/clang-libstdcxx.sh b/scripts/posix/clang-libstdcxx.sh index a03c839..6738065 100755 --- a/scripts/posix/clang-libstdcxx.sh +++ b/scripts/posix/clang-libstdcxx.sh @@ -1,8 +1,9 @@ #!/bin/sh -/bin/sh ./internal/build.sh \ -../../ \ -../../build_clang_libstdcxx \ --DCMAKE_CXX_COMPILER=clang++ \ --DCMAKE_CXX_FLAGS="-stdlib=libstdc++" \ --DCMAKE_EXE_LINKER_FLAGS="-stdlib=libstdc++ -lstdc++" +/bin/sh ./internal/build.sh \ +../../ \ +../../build_clang_libstdcxx \ +-DCMAKE_CXX_COMPILER=clang++ \ +-DCMAKE_CXX_FLAGS="-stdlib=libstdc++" \ +-DCMAKE_EXE_LINKER_FLAGS="-stdlib=libstdc++ -lstdc++" \ +-DCALLABLE_TRAITS_BUILD_EXPERIMENTAL=ON diff --git a/scripts/posix/gcc-libstdcxx.sh b/scripts/posix/gcc-libstdcxx.sh index 89a909d..1bf010b 100755 --- a/scripts/posix/gcc-libstdcxx.sh +++ b/scripts/posix/gcc-libstdcxx.sh @@ -1,6 +1,7 @@ #!/bin/sh -/bin/sh ./internal/build.sh \ -../../ \ -../../build_gcc_libstdcxx \ --DCMAKE_CXX_COMPILER=g++ \ No newline at end of file +/bin/sh ./internal/build.sh \ +../../ \ +../../build_gcc_libstdcxx \ +-DCMAKE_CXX_COMPILER=g++ \ +-DCALLABLE_TRAITS_BUILD_EXPERIMENTAL=ON \ No newline at end of file diff --git a/scripts/posix/internal/build.sh b/scripts/posix/internal/build.sh index 1f7b486..df89d70 100755 --- a/scripts/posix/internal/build.sh +++ b/scripts/posix/internal/build.sh @@ -36,7 +36,7 @@ shift 2 echo $@ # run cmake, expanding the remaining arguments -echo cmake ${project_dir} "$@" +echo cmake ${project_dir} "$@" cmake ${project_dir} "$@" diff --git a/scripts/windows/internal/LLVM-vs2014.bat b/scripts/windows/internal/LLVM-vs2014.bat index 0c87ab4..b5dc3c6 100644 --- a/scripts/windows/internal/LLVM-vs2014.bat +++ b/scripts/windows/internal/LLVM-vs2014.bat @@ -7,10 +7,10 @@ set project_name=%4 ::process 1st argument if "%~1"=="x64" ( - set cmake_cmd=cmake .. -TLLVM-vs2014 -G"Visual Studio 14 2015 Win64" -DCMAKE_CXX_FLAGS="-Qunused-arguments" + set cmake_cmd=cmake .. -TLLVM-vs2014 -G"Visual Studio 14 2015 Win64" -DCMAKE_CXX_FLAGS="-Qunused-arguments" -DCALLABLE_TRAITS_BUILD_EXPERIMENTAL ) else ( if "%~1"=="Win32" ( - set cmake_cmd=cmake .. -TLLVM-vs2014 -G"Visual Studio 14 2015" + set cmake_cmd=cmake .. -TLLVM-vs2014 -G"Visual Studio 14 2015" -DCALLABLE_TRAITS_BUILD_EXPERIMENTAL=ON ) ) if not defined cmake_cmd ( diff --git a/scripts/windows/internal/MinGW.bat b/scripts/windows/internal/MinGW.bat index 1f53360..f0d0ea9 100644 --- a/scripts/windows/internal/MinGW.bat +++ b/scripts/windows/internal/MinGW.bat @@ -36,7 +36,7 @@ goto main_logic if not exist ".\Makefile" ( @echo on - cmake -G "MinGW Makefiles" .. -DCMAKE_CXX_COMPILER=g++ + cmake -G "MinGW Makefiles" .. -DCMAKE_CXX_COMPILER=g++ -DCALLABLE_TRAITS_BUILD_EXPERIMENTAL=ON @echo off ) else ( @echo on diff --git a/test/best_match_bind.cpp b/test/best_match_bind.cpp index 06c85fa..22c09db 100644 --- a/test/best_match_bind.cpp +++ b/test/best_match_bind.cpp @@ -55,11 +55,11 @@ int main() { { auto b = ct::bind( - &foo, - ct::bind(&take_vampire, _1), - ct::bind(&take_robot, _1), - ct::bind(&take_dog, _1), - ct::bind(&take_vampire_robot_poodle, _1) + foo, + ct::bind(take_vampire, _1), + ct::bind(take_robot, _1), + ct::bind(take_dog, _1), + ct::bind(take_vampire_robot_poodle, _1) ); using args = ct::args; @@ -72,11 +72,11 @@ int main() { { auto b = ct::bind( - &foo, - ct::bind(&take_vampire_robot_poodle, _1), - ct::bind(&take_vampire, _1), - ct::bind(&take_robot, _1), - ct::bind(&take_dog, _1) + foo, + ct::bind(take_vampire_robot_poodle, _1), + ct::bind(take_vampire, _1), + ct::bind(take_robot, _1), + ct::bind(take_dog, _1) ); using args = ct::args;