├── meson.build ├── test ├── readme_example.cpp └── test_function_traits.cpp ├── LICENSE_1_0.txt ├── .appveyor.yml ├── .travis.yml ├── design.md ├── reference.md ├── readme.md └── function_traits.hpp /meson.build: -------------------------------------------------------------------------------- 1 | project('function_traits', 'cpp', default_options : 'cpp_std=c++17') 2 | 3 | test('test function_traits', 4 | executable('test_function_traits', 'test/test_function_traits.cpp') 5 | ) 6 | 7 | test('test readme_example', 8 | executable('readme_example', 'test/readme_example.cpp') 9 | ) 10 | -------------------------------------------------------------------------------- /test/readme_example.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "function_traits.hpp" 3 | 4 | struct Log0 { int log(char const* fmt) const noexcept; }; 5 | struct LogV { int log(char const* fmt,...) const & noexcept; }; 6 | 7 | template 8 | int logger(F C::* log_mf, Vargs... vargs) 9 | { 10 | static_assert( std::is_function_v ); 11 | 12 | static_assert( ltl::function_is_const_v ); 13 | static_assert( ltl::function_is_noexcept_v ); 14 | static_assert( ltl::function_is_variadic_v 15 | == bool{sizeof...(vargs)} ); 16 | 17 | using R = ltl::function_return_type_t; 18 | using Ps = ltl::function_arg_types; 19 | using P0 = std::tuple_element_t<0,Ps>; 20 | 21 | static_assert( std::is_same_v< R, int> ); 22 | static_assert( std::is_same_v< P0, char const*> ); 23 | 24 | return (C{}.*log_mf)("logger",vargs...); 25 | } 26 | 27 | template int logger(decltype(&Log0::log)); 28 | template int logger(decltype(&LogV::log), int); 29 | // Compile fail; variadic logger without any forwarded varargs 30 | //template int logger(decltype(&LogV::log)); 31 | 32 | int main() 33 | { 34 | return 0; 35 | } -------------------------------------------------------------------------------- /LICENSE_1_0.txt: -------------------------------------------------------------------------------- 1 | Boost Software License - Version 1.0 - August 17th, 2003 2 | 3 | Permission is hereby granted, free of charge, to any person or organization 4 | obtaining a copy of the software and accompanying documentation covered by 5 | this license (the "Software") to use, reproduce, display, distribute, 6 | execute, and transmit the Software, and to prepare derivative works of the 7 | Software, and to permit third-parties to whom the Software is furnished to 8 | do so, all subject to the following: 9 | 10 | The copyright notices in the Software and this entire statement, including 11 | the above license grant, this restriction and the following disclaimer, 12 | must be included in all copies of the Software, in whole or in part, and 13 | all derivative works of the Software, unless such copies or derivative 14 | works are solely in the form of machine-executable object code generated by 15 | a source language processor. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT 20 | SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE 21 | FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, 22 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 23 | DEALINGS IN THE SOFTWARE. 24 | -------------------------------------------------------------------------------- /.appveyor.yml: -------------------------------------------------------------------------------- 1 | build: 2 | verbosity: detailed 3 | 4 | branches: 5 | only: 6 | - master 7 | 8 | platform: x64 9 | 10 | environment: 11 | matrix: 12 | # Visual Studio 2017 13 | - GENERATOR: Visual Studio 15 2017 Win64 14 | STDFLAGS: /std:c++latest 15 | SCRIPT: support/vs.py 16 | APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017 17 | 18 | install: 19 | - git submodule -q update --init 20 | # Install Ninja 21 | - cmd: mkdir C:\ninja-build 22 | - ps: (new-object net.webclient).DownloadFile('https://github.com/mesonbuild/cidata/raw/master/ninja.exe', 'C:\ninja-build\ninja.exe') 23 | # Set paths to dependencies (based on architecture) 24 | - cmd: set PYTHON_ROOT=C:\python37-x64 25 | # Print out dependency paths 26 | - cmd: echo Using Python at %PYTHON_ROOT% 27 | # Add neccessary paths to PATH variable 28 | - cmd: set PATH=%cd%;C:\ninja-build;%PYTHON_ROOT%;%PYTHON_ROOT%\Scripts;%PATH% 29 | # Install meson 30 | - cmd: pip install meson 31 | - cmd: meson --version 32 | - cmd: ninja --version 33 | 34 | before_build: 35 | # Enable the Visual C++ toolset for command-line builds. 36 | - IF "%APPVEYOR_BUILD_WORKER_IMAGE%"=="Visual Studio 2017" ( 37 | call "C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Auxiliary\Build\vcvars64.bat" 38 | ) 39 | 40 | build_script: 41 | - cmd: echo Building on %arch% with %compiler% 42 | - cmd: meson --backend=ninja build 43 | - cmd: ninja -C build 44 | 45 | test_script: 46 | - cmd: ninja -C build test 47 | 48 | only_commits: 49 | files: 50 | - .appveyor.yml 51 | - meson.build 52 | - function_traits.hpp 53 | - test/ 54 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | dist: xenial 2 | language: python 3 | python: 3.6 4 | sudo: required 5 | 6 | matrix: 7 | include: 8 | ############## 9 | # GCC 10 | ############## 11 | - os: linux 12 | compiler: gcc 13 | env: GCC_VERSION=8 14 | - CC=gcc-8 15 | - CXX=g++-8 16 | - CXX_STANDARD=17 17 | addons: 18 | apt: 19 | sources: 20 | - sourceline: 'ppa:ubuntu-toolchain-r/test' 21 | packages: ['g++-8', 'ninja-build'] 22 | 23 | ############## 24 | # CLANG 25 | ############## 26 | - os: linux 27 | compiler: clang 28 | env: CLANG_VERSION=7 29 | - CC=clang-7 30 | - CXX=clang++-7 31 | - CXX_STANDARD=17 32 | addons: 33 | apt: 34 | sources: 35 | - llvm-toolchain-xenial 36 | - llvm-toolchain-trusty-7 37 | - sourceline: 'ppa:ubuntu-toolchain-r/test' 38 | packages: ['clang-7', 'libc++-7-dev', 'libc++abi-7-dev', 'ninja-build'] 39 | 40 | ############## 41 | # OSX / APPLECLANG (Python download failing) 42 | ############## 43 | #- os: osx 44 | # osx_image: xcode10.1 45 | # env: 46 | # - CXX=clang 47 | # - OSX=1 48 | # - CXX_STANDARD=17 49 | 50 | before_install: 51 | - echo "Before install" 52 | - | 53 | git submodule init 54 | git submodule update 55 | - | 56 | if [ "${CLANG_VERSION}" == "7" ]; then 57 | export CXXFLAGS="-stdlib=libc++" 58 | fi 59 | 60 | install: 61 | - mkdir -p build 62 | - pip install meson 63 | 64 | script: 65 | - which $CXX 66 | - $CXX --version 67 | - meson build 68 | - cd build 69 | - ninja 70 | -------------------------------------------------------------------------------- /design.md: -------------------------------------------------------------------------------- 1 | # Design 2 | 3 | ## Naming 4 | 5 | This library consistently uses the term 'function' where P0172 uses 'member': 6 | 7 | ### 'function' 8 | 9 | >**`function`**: a type `F` for which [`std::is_function`](https://en.cppreference.com/w/cpp/types/is_function) is `true_type`. 10 | Not to be confused with [`std::function`](https://en.cppreference.com/w/cpp/utility/functional/function): 11 | a wrapper for [*`Callable`*](https://en.cppreference.com/w/cpp/named_req/Callable) types. 12 | 13 | This unfortunate C++11 naming clash muddied future `std` usage of 'function'. 14 | 15 | Despite this, the term 'function' has established meaning, in the traits domain, 16 | as 'C++ function type' in general, including abominable function types. 17 | 18 | On the other hand, outside of the traits domain, 'C++ function' or 'function' itself 19 | implies a free function whose type is never abominable. Now, `std::function` 20 | has conflated 'function' with function objects and callables of all kinds. 21 | 22 | With `function_traits` squarely in the traits domain and wholly outwith the 23 | `std` namespace, it is entirely appropriate to reappropriate the term 'function'. 24 | 25 | `function_traits` uses `ltl::function_` prefix for all but one of its traits. 26 | 27 | The one remaining trait uses `_function` suffix to refine `std::is_function`; 28 | `ltl::is_free_function` is true for non-abominable functions. 29 | 30 | ### 'member' 31 | 32 | [P0172](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/p0172r0.html) uses '`_member`', e.g. `is_const_member`, `add_member_const` 33 | 34 | This usage is not justified in the paper, though it is discussed at the end of 35 | section 2.1 "Definition" (my **emphasis**): 36 | 37 | >Note that it is not possible to create a function that has an abominable type. 38 | Rather, the cv-ref qualifier applies to the implicit `*this` reference when 39 | calling a member function. However, **abominable function types are 40 | specifically function types, and not member function types.** 41 | This is evident from the lack of ability to specify the type of class the 42 | abominable function would be a member of, when declaring such a type. 43 | 44 | * P0172R0 **Abominable Function Types** by Alisdair Meredith, Nov 2015 45 | 46 | The emphasized text appears to argue against the use of 'member' to refer 47 | to general function types, whether abominable or not, and argue for 'function'. 48 | 49 | To be more precise, there is no such thing as a member function type, only the 50 | related type of its pointer-to-member-function so, equally, free function types 51 | are not (pointer-to-) member function types. It is odd to use the term 'member' 52 | for general function types that include the types of free functions. 53 | 54 | On the other hand, it is true that general function types, abominable or not, 55 | can be extracted from (pointer-to-) member function types whereas the types 56 | of free functions can only be non-abominable. In this sense, 'member' may be 57 | used to emphasize 'general' function type. 58 | 59 | Within the type system, function types merely model aspects of actual functions 60 | as needed for type checking. They need not be bound to function entities at all. 61 | Types can be used to encode pure information. Function types are rich - their 62 | parameters encode a tuple of (decayed) types and their qualifiers can encode 63 | 24 or 48 (with varargs) discrete values. 64 | 65 | Section 2.5 explains "Why do Abominable Function Types Exist". 66 | Adapting the example code: 67 | 68 | ```c++ 69 | struct S { void f() const; }; 70 | 71 | template auto type(F S::*) -> F; 72 | 73 | using F = decltype(type(&S::f)); 74 | ``` 75 | 76 | Given a const-qualified member function `f` of `S`: 77 | `&S::f` is a pointer-to-member-function of type `void(S::*)() const` 78 | or, more concisely, `F S::*` with `F` = `void() const` 79 | 80 | So, both 'member' and 'function' are misleading terms on their own. 81 | This library, outside the `std` namespace, is free to use 'function' 82 | as the more consistent choice. 83 | 84 | ### Order 85 | 86 | This library always puts `function` up front and qualifiers at the end. 87 | E.g. for the predicates: 88 | 89 | | P0172 proposed `std` name | `function_trait` name | 90 | |---|---| 91 | |`is_const_member` |`function_is_const` | 92 | |`is_volatile_member` |`function_is_volatile` | 93 | |`is_cv_member` |`function_is_cv` | 94 | |`is_lvalue_reference_member` |`function_is_reference_lvalue`| 95 | |`is_rvalue_reference_member` |`function_is_reference_rvalue`| 96 | |`has_member_qualifiers` |`function_is_cvref` | 97 | |`is_noexcept` (Boost.CallableTraits)|`function_is_noexcept` | 98 | 99 | A function_trait name such as `function_is_const` 100 | can be read more verbosely as `function_type_is_const_qualified` 101 | (the is_noexcept trait reads as `function_type_has_noexcept_specifier`) 102 | 103 | Why put '`function`' upfront? 104 | Why not put '`is`' upfront, as is convention for predicates? 105 | 106 | `is_function_const` | `is_function_type_const_qualified` ? 107 | 108 | 1. In this design, the global trait name reflects the member trait name 109 | `function_is_noexcept` 'global trait' 110 | `function::is_noexcept` 'member trait' 111 | 112 | 2. The `function_traits` are only defined for `function` types. 113 | For use with non-function types, guard with `std::is_function`: 114 | 115 | ```c++ 116 | std::is_function_v && function_is_noexcept_v 117 | ``` 118 | 119 | * This reads "is the type T a function and, if so, is it noexcept?". 120 | This combined trait is more appropriately called `is_function_noexcept`. 121 | 122 | ### Setters vs add / remove 123 | 124 | From P0172: 125 | 126 | >Some obvious issues with this proposal are deferred [...]. For example, 127 | it is not clear whether the following should be supported, or what it should mean: 128 | 129 | ```c++ 130 | using result = add_member_lvalue_reference_t; 131 | ``` 132 | 133 | More examples: the 'mutating' traits: 134 | | P0172 proposed `std` name | `function_trait` name | 135 | |---|---| 136 | |`add_member_const` |`function_add_const` | 137 | |`add_member_volatile` |`function_add_volatile` | 138 | |`add_member_cv` |`function_add_cv` | 139 | |`add_member_lvalue_reference` |`function_set_reference_lvalue`| 140 | |`add_member_rvalue_reference` |`function_set_reference_rvalue`| 141 | |`remove_member_const` |`function_remove_const` | 142 | |`remove_member_volatile` |`function_remove_volatile` | 143 | |`remove_member_cv` |`function_remove_cv` | 144 | |`remove_member_lvalue_reference`|`function_remove_reference` | 145 | |`remove_member_rvalue_reference`|`function_remove_reference` | 146 | |`remove_all_member_qualifiers` |`function_remove_cvref` | 147 | 148 | Note how '`set`' is used for function reference qualifier in place of `add`. 149 | 150 | 151 | Compound types and compounding 152 | 153 | Arbitrarily complex types are constructed from fundamental types by compounding; 154 | adding pointers, array extents or top-level reference, or forming enums or classes 155 | or function types: 156 | 157 | >A C++ function type is a [compound type](https://en.cppreference.com/w/cpp/types/is_compound): 158 | Its function 'signature' is a compound of its return type and argument types. 159 | Any `cvref` qualifiers or `noexcept` are not compounded onto the signature - 160 | they are part of the function type. 161 | 162 | Forming a function type is a compounding operation. 163 | Changing the type itself is not a compounding operation. 164 | 165 | Clearly, changing its signature changes it to a different function type. 166 | Similarly, changing cvref qualifiers changes the type itself to a differently- 167 | qualified type, even though with the same signature. In other words, 168 | the function qualifiers do not 'compound' or 'nest' onto a base signature. 169 | 170 | The `add` and `remove` variants of conventional `std` traits have 'compounding' 171 | as a mental model; `add_pointer` and `add_extent` compound their base type. 172 | On the other hand, adding `cv` or `ref` qualifiers does not always compound - 173 | `cv` qualifiers attach to their base type, so a cv-qualified type is not a compound, 174 | while a reference qualifier can only be 175 | 176 | and 'decompounding' modifiers. For instance, adding a 177 | reference qualifier 178 | 179 | 180 | ## function_traits and P0172 equivalent 181 | 182 | | function_trait\ | P0172R0 / callable_trait\ | 183 | |----|----| 184 | | `is_function_*`|`is_*_member`| 185 | 186 |
Predicate traits 187 | 188 | Predicate traits test a true-or-false property of a type 189 | returning a type derived from [`std::bool_constant`](https://en.cppreference.com/w/cpp/types/integral_constant) 190 | i.e. inherited from `std::true_type` or `std::false_type` 191 | 192 | The bool value itself can be extracted from the `bool_constant` 193 | via its `value` member or by invoking its function call operator. 194 | Alternatively, a `_v` suffix defines a templated boolean variable - 195 | i.e. `is_*_v` directly gives `true` or `false` value for property `*`. 196 | For example, these are all equivalent: 197 | 198 | function_is_noexcept::value 199 | function_is_noexcept() 200 | function_is_noexcept_v 201 | 202 | |function_trait\ | P0172R0 / callable_trait\ | 203 | |----|----| 204 | |`is_free_function`|`not has_member_qualifiers`| 205 | 206 | |function_trait\ | P0172R0 / callable_trait\ | 207 | |----|----| 208 | |`is_free_function`|`not has_member_qualifiers`| 209 | |`function_is_const`|`is_const_member`| 210 | |`function_is_volatile`|`is_volatile_member`| 211 | |`function_is_cv` (Note: const *OR* volatile)| `is_cv_member` (Note: const *AND* volatile)| 212 | |`function_is_reference_lvalue`|`is_lvalue_reference_member`| 213 | |`function_is_reference_rvalue`|`is_rvalue_reference_member`| 214 | |`function_is_reference`|`is_reference_member`| 215 | |`function_is_cvref`|`has_member_qualifiers`| 216 | |`function_is_noexcept`|`is_noexcept`| 217 | |`function_is_variadic`|`has_varargs`| 218 | 219 |
220 | 221 |
Modifying traits: add | remove 222 | 223 | |function_trait\ | P0172R0 / callable_trait\ | 224 | |----|----| 225 | |`function_add_const`
`function_set_const`
`function_set_cv`|`add_member_const`| 226 | |`function_remove_const`|`remove_member_const`| 227 | |`function_add_volatile`
`function_set_volatile`
`function_set_cv`|`add_member_volatile`| 228 | |`function_remove_volatile`|`remove_member_volatile`| 229 | |`function_set_cv`|`add_member_cv`| 230 | |`function_remove_cv`|`remove_member_cv`| 231 | |`function_set_reference_lvalue`
`function_set_reference_lvalue_`|`add_member_lvalue_reference`| 232 | |`function_set_reference_rvalue`
`function_set_reference_rvalue_`|`add_member_rvalue_reference`| 233 | |`function_remove_reference`|`remove_member_reference`| 234 | |`function_remove_cvref`| c.f. `function_type`| 235 | |`function_signature`
`function_signature_noexcept`
`function_remove_cvref`|`function_type`| 236 | |`function_set_cvref`
`function_set_cvref_as`|| 237 | |`function_add_noexcept`
`function_set_noexcept`|`add_noexcept`| 238 | |`function_remove_noexcept`
`function_set_noexcept`|`remove_noexcept`| 239 | |`function_add_variadic`
`function_set_variadic`|`add_varargs`| 240 | |`function_remove_variadic`|`remove_varargs`| 241 | |`function_set_return_type`|`apply_return`| 242 | |`function_set_signature`|| 243 | |`function_arg_types`|`args`| 244 | |`function_return_type`|`return_type`| 245 | |`function_signature`| c.f. `function_type`| 246 | ||`qualified_class_of`| 247 | ||`remove_transaction_safe`| 248 | ||`add_transaction_safe`| 249 | ||`class_of`| 250 | ||`apply_member_pointer`| 251 | 252 |
-------------------------------------------------------------------------------- /test/test_function_traits.cpp: -------------------------------------------------------------------------------- 1 | #include "function_traits.hpp" 2 | 3 | #define SAME(...) static_assert(std::is_same_v<__VA_ARGS__> ); 4 | 5 | // Test if type T has a member called 'value' 6 | template 7 | inline constexpr bool has_value = false; 8 | 9 | template 10 | inline constexpr bool has_value> = true; 11 | 12 | template struct wotype; 13 | 14 | // Check that ref_qual_v addition does reference collapse correctly 15 | static_assert( ltl::null_ref_v + ltl::null_ref_v == ltl::null_ref_v ); 16 | static_assert( ltl::null_ref_v + ltl::lval_ref_v == ltl::lval_ref_v ); 17 | static_assert( ltl::null_ref_v + ltl::rval_ref_v == ltl::rval_ref_v ); 18 | 19 | static_assert( ltl::lval_ref_v + ltl::null_ref_v == ltl::lval_ref_v ); 20 | static_assert( ltl::lval_ref_v + ltl::lval_ref_v == ltl::lval_ref_v ); 21 | static_assert( ltl::lval_ref_v + ltl::rval_ref_v == ltl::lval_ref_v ); // Coll 22 | 23 | static_assert( ltl::rval_ref_v + ltl::null_ref_v == ltl::rval_ref_v ); 24 | static_assert( ltl::rval_ref_v + ltl::lval_ref_v == ltl::lval_ref_v ); // apse 25 | static_assert( ltl::rval_ref_v + ltl::rval_ref_v == ltl::rval_ref_v ); 26 | 27 | // Check reference_v function 28 | static_assert( ltl::reference_v == ltl::null_ref_v ); 29 | static_assert( ltl::reference_v == ltl::null_ref_v ); 30 | static_assert( ltl::reference_v == ltl::lval_ref_v ); 31 | static_assert( ltl::reference_v == ltl::rval_ref_v ); 32 | 33 | static_assert( ltl::reference_v == ltl::lval_ref_v ); 34 | static_assert( ltl::reference_v == ltl::rval_ref_v ); 35 | 36 | static_assert( ltl::reference_v == ltl::null_ref_v ); 37 | static_assert( ltl::reference_v == ltl::null_ref_v ); 38 | static_assert( ltl::reference_v == ltl::null_ref_v ); 39 | static_assert( ltl::reference_v ==ltl::null_ref_v ); 40 | static_assert( ltl::reference_v 41 | == ltl::null_ref_v ); 42 | 43 | // Check function_reference_v function 44 | static_assert( ltl::function_reference_v == ltl::null_ref_v ); 45 | static_assert( ltl::function_reference_v == ltl::null_ref_v ); 46 | static_assert( ltl::function_reference_v == ltl::lval_ref_v ); 47 | static_assert( ltl::function_reference_v == ltl::rval_ref_v ); 48 | static_assert( ltl::function_reference_v 49 | == ltl::lval_ref_v ); 50 | 51 | // Test is_function trait 52 | static_assert( ltl::is_function_v); 53 | static_assert(!ltl::is_function_v); 54 | 55 | // Test is_free_function trait 56 | static_assert( ! ltl::is_free_function_v ); 57 | static_assert( ! ltl::is_free_function_v ); 58 | static_assert( ! ltl::is_free_function_v ); 59 | static_assert( ! ltl::is_free_function_v ); 60 | 61 | static_assert( ltl::is_free_function_v ); 62 | static_assert( ltl::is_free_function_v ); 63 | 64 | // Bad definition gives compile error for non-function type Arg: 65 | template 66 | inline constexpr bool bad_is_free_function_v 67 | = ltl::is_function_v && !ltl::function_is_cvref_v; 68 | 69 | static_assert( bad_is_free_function_v ); 70 | static_assert( ! bad_is_free_function_v ); 71 | // Compile error for non-function type Arg: 72 | //static_assert( ! bad_is_free_function_v ); 73 | 74 | template 75 | inline constexpr bool good_is_free_function_v 76 | = std::conjunction_v< ltl::is_function, 77 | std::negation>>; 78 | 79 | static_assert( good_is_free_function_v ); 80 | static_assert( ! good_is_free_function_v ); 81 | static_assert( ! good_is_free_function_v ); 82 | 83 | // The 'is_function_*' predicate traits are defined for all types so 84 | // these 'lazy' traits gives no error for non-function type 85 | using ifr = ltl::is_function_reference; 86 | // note - ltl::function_is_reference causes a compile error 87 | static_assert( !has_value, 88 | "is_function_trait should not have a value member"); 89 | 90 | using ifrf = ltl::is_function_reference; 91 | static_assert( has_value, 92 | "is_function_trait should have a value member"); 93 | 94 | using firf = ltl::function_is_reference; 95 | static_assert( has_value, 96 | "function_is_trait should have a value member"); 97 | 98 | namespace auto_void 99 | { 100 | // Test function_traits member traits for simple func F=void() 101 | using f = void(); 102 | using fc = void() const; 103 | using fv = void() volatile; 104 | using fcv = void() const volatile; 105 | using fl = void() &; 106 | using fr = void() &&; 107 | using fcl = void() const&; 108 | using F = ltl::function_traits; 109 | using Fc = ltl::function_traits; 110 | using Fv = ltl::function_traits; 111 | using Fcv = ltl::function_traits; 112 | using Fl = ltl::function_traits; 113 | using Fr = ltl::function_traits; 114 | using Fcl = ltl::function_traits; 115 | 116 | static_assert(! typename F::is_const()); 117 | static_assert(! typename F::is_volatile()); 118 | static_assert(! typename F::is_cv()); 119 | static_assert(! typename F::is_reference_lvalue()); 120 | static_assert(! typename F::is_reference_rvalue()); 121 | static_assert(! typename F::is_reference()); 122 | static_assert(! typename F::is_cvref()); 123 | static_assert(! typename F::is_noexcept()); 124 | static_assert(! typename F::is_variadic()); 125 | 126 | SAME( typename F::type, void() ); 127 | SAME( typename F::return_type_t, void ); 128 | SAME( typename F::signature_t, void() ); 129 | SAME( typename F::remove_cvref_t, void() ); 130 | SAME( typename F::arg_types<>, ltl::arg_types<> ); 131 | 132 | SAME( typename F::set_const_t, fc ); 133 | SAME( typename F::set_volatile_t, fv ); 134 | SAME( typename F::set_cv_t, fcv ); 135 | SAME( typename F::set_reference_t, fl ); 136 | SAME( typename F::set_reference_t, fr ); 137 | SAME( typename F::set_cvref_t, fcl ); 138 | SAME( typename F::set_cvref_t, fc ); 139 | SAME( typename F::set_noexcept_t, void() noexcept ); 140 | SAME( typename F::set_variadic_t, void(...) ); 141 | SAME( typename F::set_return_type_t, int() ); 142 | 143 | SAME( typename F::set_const, Fc ); 144 | SAME( typename F::set_volatile, Fv ); 145 | SAME( typename F::set_cv, Fcv ); 146 | SAME( typename F::set_reference, Fl ); 147 | SAME( typename F::set_reference, Fr ); 148 | SAME( typename F::set_cvref, Fcl ); 149 | SAME( typename F::set_cvref, Fc ); 150 | SAME( typename F::set_noexcept, ltl::function_traits ); 151 | SAME( typename F::set_cvref_noexcept_t, 152 | void() const volatile && noexcept ); 153 | SAME( typename F::set_signature_t, int(bool) ); 154 | 155 | // Test function traits for simple func F=void() 156 | static_assert(! ltl::function_is_const()); 157 | static_assert(! ltl::function_is_volatile()); 158 | static_assert(! ltl::function_is_cv()); 159 | static_assert(! ltl::function_is_reference_lvalue()); 160 | static_assert(! ltl::function_is_reference_rvalue()); 161 | static_assert(! ltl::function_is_reference()); 162 | static_assert(! ltl::function_is_cvref()); 163 | static_assert(! ltl::function_is_noexcept()); 164 | static_assert(! ltl::function_is_variadic()); 165 | 166 | static_assert(! ltl::function_is_const_v); 167 | static_assert(! ltl::function_is_volatile_v); 168 | static_assert(! ltl::function_is_cv_v); 169 | static_assert(! ltl::function_is_reference_lvalue_v); 170 | static_assert(! ltl::function_is_reference_rvalue_v); 171 | static_assert(! ltl::function_is_reference_v); 172 | static_assert(! ltl::function_is_cvref_v); 173 | static_assert(! ltl::function_is_noexcept_v); 174 | static_assert(! ltl::function_is_variadic_v); 175 | 176 | SAME( ltl::function_return_type_t, void ); 177 | SAME( ltl::function_signature_t, void() ); 178 | SAME( ltl::function_remove_cvref_t, void() ); 179 | SAME( ltl::function_arg_types, ltl::arg_types<> ); 180 | 181 | SAME( ltl::function_set_const_t, fc ); 182 | SAME( ltl::function_set_volatile_t, fv ); 183 | SAME( ltl::function_set_cv_t, fcv ); 184 | SAME( ltl::function_set_reference_t, fl ); 185 | SAME( ltl::function_set_reference_t, fr ); 186 | SAME( ltl::function_set_cvref_t, fcl ); 187 | SAME( ltl::function_set_cvref_t, fc ); 188 | SAME( ltl::function_set_noexcept_t, void() noexcept ); 189 | SAME( ltl::function_set_variadic_t, void(...) ); 190 | SAME( ltl::function_set_return_type_t, int() ); 191 | SAME( ltl::function_set_signature_t, int(bool)); 192 | 193 | SAME( ltl::function_set_const, Fc ); 194 | SAME( ltl::function_set_volatile, Fv ); 195 | SAME( ltl::function_set_cv, Fcv ); 196 | SAME( ltl::function_set_reference, Fl ); 197 | SAME( ltl::function_set_reference, Fr ); 198 | SAME( ltl::function_set_cvref, Fcl ); 199 | SAME( ltl::function_set_cvref, Fc ); 200 | SAME( ltl::function_set_noexcept, ltl::function_traits ); 201 | SAME( ltl::function_set_variadic, ltl::function_traits ); 202 | SAME( ltl::function_set_signature, ltl::function_traits ); 203 | } 204 | 205 | namespace cmplx_func 206 | { 207 | // Test function_traits member traits for more complex free func 208 | struct R {}; 209 | using P = void const*; 210 | using Q = R &&; 211 | 212 | using f = R(P,Q,...); 213 | using fc = R(P, Q, ...) const; 214 | using fv = R(P, Q, ...) volatile; 215 | using fcv = R(P, Q, ...) const volatile; 216 | using fl = R(P, Q, ...) &; 217 | using fr = R(P, Q, ...) &&; 218 | using fnx = R(P, Q, ...) noexcept; 219 | using fclnx = R(P, Q, ...) const & noexcept; 220 | 221 | static_assert(std::conjunction< 222 | ltl::is_function, 223 | ltl::is_function, 224 | ltl::is_function, 225 | ltl::is_function, 226 | ltl::is_function, 227 | ltl::is_function, 228 | ltl::is_function, 229 | ltl::is_function>() 230 | ); 231 | 232 | static_assert(std::conjunction< 233 | ltl::is_free_function, 234 | ltl::is_free_function>() 235 | ); 236 | 237 | static_assert( ! std::disjunction< 238 | ltl::is_free_function, 239 | ltl::is_free_function, 240 | ltl::is_free_function, 241 | ltl::is_free_function, 242 | ltl::is_free_function, 243 | ltl::is_free_function>() 244 | ); 245 | 246 | using F = ltl::function_traits; 247 | using Fc = ltl::function_traits; 248 | using Fv = ltl::function_traits; 249 | using Fcv = ltl::function_traits; 250 | using Fl = ltl::function_traits; 251 | using Fr = ltl::function_traits; 252 | using Fnx = ltl::function_traits; 253 | using Fclnx = ltl::function_traits; 254 | 255 | static_assert( 256 | !typename F::is_const() 257 | && !typename F::is_volatile() 258 | && !typename F::is_cv() 259 | && !typename F::is_reference_lvalue() 260 | && !typename F::is_reference_rvalue() 261 | && !typename F::is_reference() 262 | && !typename F::is_cvref() 263 | && !typename F::is_noexcept() 264 | && typename F::is_variadic() 265 | ); 266 | static_assert( 267 | typename Fc::is_const() 268 | && !typename Fc::is_volatile() 269 | && typename Fc::is_cv() 270 | && !typename Fc::is_reference_lvalue() 271 | && !typename Fc::is_reference_rvalue() 272 | && !typename Fc::is_reference() 273 | && typename Fc::is_cvref() 274 | && !typename Fc::is_noexcept() 275 | && typename Fc::is_variadic() 276 | ); 277 | static_assert( 278 | !typename Fv::is_const() 279 | && typename Fv::is_volatile() 280 | && typename Fv::is_cv() 281 | && !typename Fv::is_reference_lvalue() 282 | && !typename Fv::is_reference_rvalue() 283 | && !typename Fv::is_reference() 284 | && typename Fv::is_cvref() 285 | && !typename Fv::is_noexcept() 286 | && typename Fv::is_variadic() 287 | ); 288 | static_assert( 289 | typename Fcv::is_const() 290 | && typename Fcv::is_volatile() 291 | && typename Fcv::is_cv() 292 | && !typename Fcv::is_reference_lvalue() 293 | && !typename Fcv::is_reference_rvalue() 294 | && !typename Fcv::is_reference() 295 | && typename Fcv::is_cvref() 296 | && !typename Fcv::is_noexcept() 297 | && typename Fcv::is_variadic() 298 | ); 299 | static_assert( 300 | !typename Fl::is_const() 301 | && !typename Fl::is_volatile() 302 | && !typename Fl::is_cv() 303 | && typename Fl::is_reference_lvalue() 304 | && !typename Fl::is_reference_rvalue() 305 | && typename Fl::is_reference() 306 | && typename Fl::is_cvref() 307 | && !typename Fl::is_noexcept() 308 | && typename Fl::is_variadic() 309 | ); 310 | static_assert( 311 | !typename Fr::is_const() 312 | && !typename Fr::is_volatile() 313 | && !typename Fr::is_cv() 314 | && !typename Fr::is_reference_lvalue() 315 | && typename Fr::is_reference_rvalue() 316 | && typename Fr::is_reference() 317 | && typename Fr::is_cvref() 318 | && !typename Fr::is_noexcept() 319 | && typename Fr::is_variadic() 320 | ); 321 | static_assert( 322 | typename Fclnx::is_const() 323 | && !typename Fclnx::is_volatile() 324 | && typename Fclnx::is_cv() 325 | && typename Fclnx::is_reference_lvalue() 326 | && !typename Fclnx::is_reference_rvalue() 327 | && typename Fclnx::is_reference() 328 | && typename Fclnx::is_cvref() 329 | && typename Fclnx::is_noexcept() 330 | && typename Fclnx::is_variadic() 331 | ); 332 | 333 | 334 | static_assert( 335 | !ltl::function_is_const() 336 | && !ltl::function_is_volatile() 337 | && !ltl::function_is_cv() 338 | && !ltl::function_is_reference_lvalue() 339 | && !ltl::function_is_reference_rvalue() 340 | && !ltl::function_is_reference() 341 | && !ltl::function_is_cvref() 342 | && !ltl::function_is_noexcept() 343 | && ltl::function_is_variadic() 344 | ); 345 | static_assert( 346 | ltl::function_is_const() 347 | && !ltl::function_is_volatile() 348 | && ltl::function_is_cv() 349 | && !ltl::function_is_reference_lvalue() 350 | && !ltl::function_is_reference_rvalue() 351 | && !ltl::function_is_reference() 352 | && ltl::function_is_cvref() 353 | && !ltl::function_is_noexcept() 354 | && ltl::function_is_variadic() 355 | ); 356 | static_assert( 357 | !ltl::function_is_const() 358 | && ltl::function_is_volatile() 359 | && ltl::function_is_cv() 360 | && !ltl::function_is_reference_lvalue() 361 | && !ltl::function_is_reference_rvalue() 362 | && !ltl::function_is_reference() 363 | && ltl::function_is_cvref() 364 | && !ltl::function_is_noexcept() 365 | && ltl::function_is_variadic() 366 | ); 367 | static_assert( 368 | ltl::function_is_const() 369 | && ltl::function_is_volatile() 370 | && ltl::function_is_cv() 371 | && !ltl::function_is_reference_lvalue() 372 | && !ltl::function_is_reference_rvalue() 373 | && !ltl::function_is_reference() 374 | && ltl::function_is_cvref() 375 | && !ltl::function_is_noexcept() 376 | && ltl::function_is_variadic() 377 | ); 378 | static_assert( 379 | !ltl::function_is_const() 380 | && !ltl::function_is_volatile() 381 | && !ltl::function_is_cv() 382 | && ltl::function_is_reference_lvalue() 383 | && !ltl::function_is_reference_rvalue() 384 | && ltl::function_is_reference() 385 | && ltl::function_is_cvref() 386 | && !ltl::function_is_noexcept() 387 | && ltl::function_is_variadic() 388 | ); 389 | static_assert( 390 | !ltl::function_is_const() 391 | && !ltl::function_is_volatile() 392 | && !ltl::function_is_cv() 393 | && !ltl::function_is_reference_lvalue() 394 | && ltl::function_is_reference_rvalue() 395 | && ltl::function_is_reference() 396 | && ltl::function_is_cvref() 397 | && !ltl::function_is_noexcept() 398 | && ltl::function_is_variadic() 399 | ); 400 | static_assert( 401 | ltl::function_is_const() 402 | && !ltl::function_is_volatile() 403 | && ltl::function_is_cv() 404 | && ltl::function_is_reference_lvalue() 405 | && !ltl::function_is_reference_rvalue() 406 | && ltl::function_is_reference() 407 | && ltl::function_is_cvref() 408 | && ltl::function_is_noexcept() 409 | && ltl::function_is_variadic() 410 | ); 411 | 412 | SAME(typename F::type, f); 413 | SAME(typename F::return_type_t, R); 414 | SAME(typename F::signature_t, f); 415 | SAME(typename F::remove_cvref_t, f); 416 | SAME(typename F::arg_types<>, ltl::arg_types); 417 | 418 | SAME(typename F::set_const_t, fc); 419 | SAME(typename F::set_volatile_t, fv); 420 | SAME(typename F::set_cv_t, fcv); 421 | SAME(typename F::set_reference_t, fl); 422 | SAME(typename F::set_reference_t, fr); 423 | SAME(typename F::set_cvref_t, R(P,Q,...)const&); 424 | SAME(typename F::set_cvref_t, fc); 425 | SAME(typename F::set_noexcept_t, fnx); 426 | SAME(typename F::set_variadic_t, R(P,Q)); 427 | SAME(typename F::set_return_type_t, int(P,Q,...)); 428 | 429 | SAME(typename F::set_cvref_noexcept_t, 430 | R(P, Q, ...) const volatile && noexcept); 431 | SAME(typename Fclnx::set_signature_t, int(bool) const & noexcept); 432 | 433 | SAME(typename F::set_const, Fc); 434 | SAME(typename F::set_volatile, Fv); 435 | SAME(typename F::set_cv, Fcv); 436 | SAME(typename F::set_reference, Fl); 437 | SAME(typename F::set_reference, Fr); 438 | SAME(typename F::set_cvref, Fl); 439 | SAME(typename F::set_cvref, Fc); 440 | SAME(typename F::set_noexcept, Fnx); 441 | 442 | // Test function traits for simple func F=void() 443 | SAME(ltl::function_return_type_t, R); 444 | SAME(ltl::function_signature_t, f); 445 | SAME(ltl::function_remove_cvref_t, f); 446 | SAME(ltl::function_arg_types, ltl::arg_types); 447 | 448 | SAME(ltl::function_set_const_t, fc); 449 | SAME(ltl::function_set_volatile_t, fv); 450 | SAME(ltl::function_set_cv_t, fcv); 451 | SAME(ltl::function_set_reference_t, fl); 452 | SAME(ltl::function_set_reference_t, fr); 453 | SAME(ltl::function_set_cvref_t, ltl::function_remove_noexcept_t); 454 | SAME(ltl::function_set_cvref_t, fc); 455 | SAME(ltl::function_set_noexcept_t, fnx); 456 | SAME(ltl::function_set_variadic_t, R(P,Q)); 457 | SAME(ltl::function_set_return_type_t, int(P,Q,...)); 458 | SAME(ltl::function_set_signature_t, int(bool) const & noexcept); 459 | 460 | SAME(ltl::function_set_const, Fc); 461 | SAME(ltl::function_set_volatile, Fv); 462 | SAME(ltl::function_set_cv, Fcv); 463 | SAME(ltl::function_set_reference, Fl); 464 | SAME(ltl::function_set_reference, Fr); 465 | SAME(ltl::function_set_cvref, ltl::function_remove_noexcept); 466 | SAME(ltl::function_set_cvref, Fc); 467 | SAME(ltl::function_set_variadic, ltl::function_traits); 468 | SAME(ltl::function_set_return_type, ltl::function_traits); 469 | SAME(ltl::function_set_signature, ltl::function_traits); 470 | } 471 | 472 | 473 | static_assert( ltl::function_traits::is_noexcept()); 474 | static_assert( ltl::function_traits::is_noexcept()); 475 | 476 | static_assert( ltl::function_is_const_v); 477 | static_assert(!ltl::function_is_const_v); 478 | 479 | static_assert( 480 | std::is_same_v< ltl::function_add_const_t, 481 | void() const&> ); 482 | static_assert(!ltl::function_is_const_v); 483 | 484 | using FVCX = void() const noexcept; 485 | 486 | static_assert( 487 | ltl::function_is_const_v< FVCX > 488 | && ltl::function_is_cvref_v< FVCX > 489 | && ltl::function_is_noexcept_v< FVCX > 490 | ); 491 | 492 | using FVFB = void(char,bool()); 493 | 494 | static_assert( 495 | std::is_void_v< ltl::function_return_type_t< FVFB > > 496 | && std::is_same_v< ltl::function_arg_types< FVFB >, 497 | ltl::arg_types > 498 | ); 499 | 500 | int main() 501 | { 502 | return 0; 503 | } 504 | -------------------------------------------------------------------------------- /reference.md: -------------------------------------------------------------------------------- 1 | # `function_traits` reference 2 | 3 | ## `namespace ltl` 4 | 5 | **`ltl`** is the namespace for all traits and utilities in **`function_traits`**. 6 | 7 | >Pronounce it as you like; LTL as in STL or 'litl' as 'little Italy' said fast. 8 | 9 | * Traits prefixed with **`function_`** are defined for C++ function types only 10 | * Traits prefixed with **`is_`** are defined for any C++ type ('safe' predicates) 11 | 12 | ## Synopsis 13 | 14 |
List of traits (total 50, or 86 including _t or _v variants) 15 | 16 | ```c++ 17 | // Key 18 | // === 19 | template // Indicates trait is defined for all C++ types T 20 | template // Indicates trait is defined for function types F 21 | // *only* (concept Function = is_function_v) 22 | 23 | P // P is the predicate of a predicate trait; type -> bool 24 | P, P // The predicate P evaluated on type T or function type F 25 | 26 | predicate_base // An empty struct for non-function type T, or 27 | predicate_base // bool_constant> for function type F 28 | 29 | FuncTr // FuncTr is the function type modifier of a function trait 30 | FuncTr // The modified function type result of the function trait 31 | // (may take Args: FuncTr : F -> F' function type) 32 | 33 | RefQual // 3-valued ref_qual; is T lval-ref, rval-ref or not a ref 34 | FuncRefQual // 3-valued ref_qual; has func F lval, rval or no ref qual 35 | 36 | function_traits // A class containing public member 'type' alias for F 37 | // using type = F; 38 | ``` 39 | 40 | ```c++ 41 | // 'Top level' predicate traits is_*function, is_*function_v 42 | // ============================ 43 | template struct is_*function : bool_constant> {}; 44 | template 45 | inline constexpr bool is_*function_v = bool{P>}; 46 | 47 | is_function // equivalent to std::is_function 48 | is_free_function // true for a non-cvref-qualified function type 49 | ``` 50 | 51 | ```c++ 52 | // Function predicate traits is_function_* 53 | // ========================= 54 | template struct is_function_* : predicate_base {}; 55 | 56 | is_function_const 57 | is_function_volatile 58 | is_function_cv 59 | is_function_reference 60 | is_function_reference_lvalue 61 | is_function_reference_rvalue 62 | is_function_cvref 63 | is_function_noexcept 64 | is_function_variadic 65 | ``` 66 | 67 | ```c++ 68 | // Function predicate traits function_is_* 69 | // ========================= 70 | template using function_is_* = bool_constant>; 71 | template 72 | inline constexpr bool function_is_*_v = bool{>}; 73 | 74 | function_is_const 75 | function_is_volatile 76 | function_is_cv 77 | function_is_reference 78 | function_is_reference_lvalue 79 | function_is_reference_rvalue 80 | function_is_cvref 81 | function_is_noexcept 82 | function_is_variadic 83 | ``` 84 | 85 | ```c++ 86 | // Reference qualifier value traits *reference_v 87 | // ================================ 88 | template 89 | inline constexpr ref_qual reference_v = RefQual; 90 | template 91 | inline constexpr ref_qual function_reference_v = FuncRefQual; 92 | 93 | reference_v // Ordinary top level reference qualifier value 94 | function_reference_v // Function reference qualifier value 95 | ``` 96 | 97 | ```c++ 98 | // Function signature traits 99 | // ========================= 100 | template using function_return_type_t = /* Return type of F */ 101 | template struct function_return_type /* class typedef type */ 102 | template typename T = ltl::arg_types> 103 | using function_arg_types = T; // P... arg types 104 | 105 | function_return_type_t // alias to the return type R of F 106 | function_return_type // class containing public member type alias for R 107 | function_arg_types // a typelist (arg_types default) of F's arg types 108 | ``` 109 | 110 | ```c++ 111 | // Function modifying traits taking no arguments 112 | // ========================= 113 | template using function_**_t = FuncTr; 114 | template using function_** = function_traits>; 115 | 116 | function_signature // could be 'function_remove_cvref_noexcept' 117 | 118 | function_add_const function_remove_const 119 | function_add_volatile function_remove_volatile 120 | /* *** no add_cv *** */ function_remove_cv 121 | 122 | function_add_noexcept function_remove_noexcept 123 | function_add_variadic function_remove_variadic 124 | 125 | function_remove_reference 126 | function_set_reference_lvalue 127 | function_set_reference_rvalue 128 | 129 | function_remove_cvref 130 | ``` 131 | 132 | ```c++ 133 | // Function modifying traits taking arguments 134 | // ========================= 135 | template using function_**_t = FuncTr; 136 | template using function_** = function_traits< 137 | FuncTr>; 138 | 139 | function_add_reference // add does reference-collapse 140 | function_set_reference // set does not ref-collapse 141 | 142 | function_set_const 143 | function_set_volatile 144 | function_set_noexcept 145 | function_set_variadic 146 | 147 | function_set_cv 148 | 149 | function_set_cvref 150 | 151 | function_set_return_type // requires R = valid return type 152 | function_set_signature // requires FuncSig = a signature 153 | function_set_cvref_as 154 | ``` 155 | 156 |
157 | 158 | ## Traits indexed by group 159 | 160 | Following `std` convention, there are `_t` or `_v` variants as appropriate 161 | 162 | * [Top level predicate traits](#top-level-predicates) classify C++ function types among all C++ types 163 | `is_function` equivalent to `std::is_function` 164 | `is_free_function` true for `T` a non-cvref-qualified function type 165 | 166 | * [Function predicate traits](#function-predicate-traits): `is_function_*`, `function_is_*` 167 | For`*` in `const`, `volatile`, `cv`, `cvref`, `noexcept`, `variadic`, 168 | `reference`, `reference_lvalue`, `reference_rvalue` 169 | 170 | * [Reference value traits](#reference-value-traits): evaluate to a value of enum type `ltl::ref_qual` 171 | `function_reference_v` for function type reference qualification 172 | `reference_v` for ordinary top-level reference qualification 173 | 174 | * [Signature type traits](#signature-type-traits): 'getters' for function return type and arg types 175 | `function_return_type` returns the return type of F 176 | `function_arg_types` returns a type-list of parameter types of F 177 | `function_signature` returns just the `R(P...)` or `R(P...,...)` 178 | 'signature' (so could be called `function_remove_cvref_noexcept`) 179 | 180 | * [Add / remove traits](#add-/-remove-traits): `function_add_*`, `function_remove_*` 181 | (with no Args) For`*` in `const`, `volatile`, `noexcept`, `variadic` 182 | (with no Args) `function_remove_cvref` (no add; remove only) 183 | (with no Args) `function_remove_reference` (remove only) 184 | (one ref Arg) `function_add_reference` (ref-collapse) 185 | 186 | * [Set traits](#set-traits): (property 'setters'): `function_set_*` 187 | (with no Args) For`*` in `reference_lvalue`, `reference_rvalue` 188 | (one bool Arg) For`*` in `const`, `volatile`, `noexcept`, `variadic` 189 | (two bool Args, C,V) `function_set_cv` 190 | (one ref_qual Arg) `function_set_reference` 191 | (two bool, one ref) `function_set_cvref` 192 | (one ret-type arg) `function_set_return_type` 193 | (one signature Arg) `function_set_signature` 194 | 195 | * [Copy trait](#copy-trait): `function_set_cvref_as` 196 | (`function_set_signature` can copy cvref and noexcept) 197 | (individual qualifiers can be copied using `function_set_*` traits) 198 | 199 | ---- 200 | 201 | ## Terminology 202 | 203 | ### Terms to classify C++ function subtypes 204 | 205 | |function subtype name| has `cvref` qualifiers
[`const`] [`volatile`] [`&`\|`&&`] | is `noexcept`
i.e. `noexcept(true)`| 206 | |-| - |-| 207 | |'**function**' type
(general function type)| don't care
(if it has qualifiers or not)| don't care | 208 | |'**free**' function type
('normal', 'ordinary', 'plain') | NO | don't care | 209 | |function '**signature**' type
(a free function subtype) | NO | NO ==
`noexcept(false)`| 210 | |'abominable' function type
(**cvref**-qualified function type)| YES | don't care | 211 | 212 | >'free' indicates that these subtypes are the valid types of free functions. 213 | 'signature', as used here, is chosen to exclude any exception spec. 214 | 'abominable' is a recognised, colloquial, term for 'cvref-qualified'. 215 | 216 | ---- 217 | 218 | ## Properties of function types 219 | 220 | See the [Readme](readme.md) material and reference [P0172R0](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/p0172r0.html) for an introduction. 221 | 222 | All the properties of a C++ function type are on one level - its signature, 223 | cvref qualifiers and exception specification are all equally part of its type. 224 | 225 | For the purposes of this API a function type is broken down as: 226 | 227 | 1. Return type `R` and parameter types `P...` 228 | 2. 'Flags' for its c,v,ref and noexcept properties, and variadic-ness 229 | 230 | Function property 'flags': 231 | 232 | * A pair of `bool` flags for `const`, `volatile` 233 | * A `ref_qual` flag for [`&`|`&&`] 234 | (`ref_qual` is a 3-valued enum; `null_ref_v`, `lval_ref_v`, `rval_ref_v`) 235 | * A `bool` flag for `noexcept(bool)` 236 | * A `bool` flag for `R(P...)` or `R(P...,...)` 237 | (non-variadic or variadic function signature). 238 | 239 | Function properties are modified by '`add`', '`remove`' and '`set`' traits. 240 | Generally, '`add`' and '`remove`' traits modify a fully named property, so take no 241 | extra arguments, while '`set`' traits take arguments for property 'flag' values to set. 242 | 243 | Reference qualifiers are the exception, because '`add`' implies reference-collapse 244 | (which is not necessarily a natural semantics for function reference qualifiers). 245 | The 'no argument' traits are called '`set`' (rather than '`add`') with name ordered 246 | as '`set_reference_lvalue`' to make clear it "sets reference flag to lval-ref". 247 | There is both an '`add`' and a '`set`' trait taking a single `ref_qual` argument, 248 | which the '`add`' trait implementing reference collapse behaviour: 249 | 250 | function_remove_reference // no ambiguity with 'remove' 251 | 252 | function_set_reference_lvalue // not 'set_lvalue_reference' 253 | function_set_reference_rvalue 254 | 255 | function_add_reference // reference-collapse 256 | function_set_reference // no ref-collapse 257 | 258 | ---- 259 | 260 | ## Predicate traits 261 | 262 | Predicate traits test a true-or-false property of a type 263 | 264 | 'Top level' predicates, `is_function` and `is_free_function` evaluate true | false 265 | for any C++ type 266 | (type trait inherits from `std::true_type` | `std::false_type`). 267 | 268 | The remaining function predicate traits treat non-function types differently: 269 | 270 | * `is_function_*` return an empty type for non-function type `T` 271 | * `function_is_*` cause a compile error on non-function type `T` 272 | 273 | (`is_function_*` is the 'safe' or 'SFINAE-friendly' version of `function_is_*`) 274 | (`is_function_*` has no `_v` suffix variant - use equivalent `function_is_*_v`). 275 | 276 | ### Top Level Predicates 277 | 278 | A pair of 'top level' predicate traits classify C++ function types among all C++ types: 279 | 280 | * **`ltl::is_function`** equivalent to [`std::is_function`](https://en.cppreference.com/w/cpp/types/is_function) 281 | * **`ltl::is_free_function`** true for `T` a non-cvref-qualified function type 282 | 283 | ```C++ 284 | template struct is_*function : bool_constant> {}; 285 | template 286 | inline constexpr bool is_*function_v = bool{P>}; 287 | ``` 288 | 289 | #### `is_function` 290 | 291 | Use **`ltl::is_function`** in preference to `std::is_function` in a condition 292 | guarding instantiation of a function trait (because it saves redundant work) 293 | (for example, see implementation of `is_free_function_v` next). 294 | 295 | #### `is_free_function` 296 | 297 | Checks if the argument type is a free function type: 298 | >`true` if `F` is a function type without cvref qualifiers 299 | `false` if `F` is not a function type or is a cvref qualified function type 300 | 301 | Example implementation of **`is_free_function_v`** 302 | 303 | ```c++ 304 | template 305 | inline constexpr bool is_free_function_v = []{ 306 | if constexpr (is_function_v) 307 | return !function_is_cvref_v; 308 | return false; }(); 309 | ``` 310 | 311 | ### Function predicate traits 312 | 313 | #### SFINAE-friendly predicates: `is_function_*` 314 | 315 | ```c++ 316 | // predicate_base // An empty struct for non-function type T, or 317 | // predicate_base // bool_constant> for function type F 318 | template struct is_function_* : predicate_base {}; 319 | ``` 320 | 321 | Note: no `_v` suffix variant - use equivalent `function_is_*_v` 322 | 323 | * **`is_function_const`** 324 | * **`is_function_volatile`** 325 | * **`is_function_cv`** 326 | * **`is_function_reference`** 327 | * **`is_function_reference_lvalue`** 328 | * **`is_function_reference_rvalue`** 329 | * **`is_function_cvref`** 330 | * **`is_function_noexcept`** 331 | * **`is_function_variadic`** 332 | 333 | #### Simple predicates: `function_is_*` 334 | 335 | ```c++ 336 | template using function_is_* = bool_constant>; 337 | template 338 | inline constexpr bool function_is_*_v = bool{>}; 339 | ``` 340 | 341 | >As type trait, alias to `std` `true_type` / `false_type` 342 | As value trait `_v`, evaluate true | false 343 | 344 | Compile fail for non-function type arguments. 345 | 346 | * **`function_is_const`** 347 | * **`function_is_volatile`** 348 | * **`function_is_cv`** 349 | * **`function_is_reference`** 350 | * **`function_is_reference_lvalue`** 351 | * **`function_is_reference_rvalue`** 352 | * **`function_is_cvref`** 353 | * **`function_is_noexcept`** 354 | * **`function_is_variadic`** 355 | 356 | ---- 357 | 358 | ## Reference value traits 359 | 360 | Reflect reference qualification of a type by returning an enumerated value. 361 | 362 | * **`function_reference_v`** for function type reference qualification 363 | * **`reference_v`** for ordinary top-level reference qualification 364 | 365 | Evaluate to a value of enum type `ltl::ref_qual` 366 | 367 | ```c++ 368 | enum ref_qual { null_ref_v, rval_ref_v, lval_ref_v }; 369 | 370 | template 371 | inline constexpr ref_qual reference_v = RefQual; 372 | template 373 | inline constexpr ref_qual function_reference_v = FuncRefQual; 374 | ``` 375 | 376 | The **addition operator** for `ref_qual` values is defined to do reference collapse: 377 | 378 | ```c++ 379 | constexpr ref_qual operator+( ref_qual a, ref_qual b); 380 | ``` 381 | 382 | ---- 383 | 384 | ## Signature type traits 385 | 386 | These traits are 'getters' for function return type and arg types: 387 | 388 | * **`function_return_type`** returns the return type of F 389 | * **`function_arg_types`** returns a type-list of parameter types of F 390 | 391 | ```c++ 392 | template using function_return_type_t = /* Return type of F */ 393 | template struct function_return_type { 394 | using type = function_return_type_t; 395 | }; 396 | 397 | template typename T = ltl::arg_types> 398 | using function_arg_types = T; // P... arg types 399 | ``` 400 | 401 | For example, to get the parameter types of a function type in a `std::tuple`: 402 | 403 | ```c++ 404 | ltl::function_arg_types< int(char, bool[4]), std::tuple >; 405 | // Evaluates to std::tuple< char, bool* > 406 | // Note the usual decay in array type function arguments 407 | ``` 408 | 409 | >There is no corresponding '`set`' trait to set the argument types (instead, construct 410 | and set the function signature, including return type and possible varargs). 411 | 412 | The function signature getter could be called `function_remove_cvref_noexcept` 413 | (and so included in the next section on add and remove traits) 414 | 415 | * **`function_signature`** returns just the `R(P...)` or `R(P...,...)` 416 | 417 | ```c++ 418 | template using function_signature_t = /* function signature */ 419 | template 420 | using function_signature = function_traits>; 421 | ``` 422 | 423 | ---- 424 | 425 | ## Add / remove traits 426 | 427 | * **`function_add/remove_const`** 428 | * **`function_add/remove_volatile`** 429 | * **`function_add/remove_noexcept`** 430 | * **`function_add/remove_variadic`** 431 | 432 | Remove-only: 433 | 434 | * **`function_remove_reference`** 435 | * **`function_remove_cvref`** 436 | 437 | ```c++ 438 | template using function_**_t = FuncTr; 439 | template using function_** = function_traits>; 440 | ``` 441 | 442 | The above are all modifying traits that take no arguments. 443 | 444 | The trait to 'add' a reference qualifier takes an argument of type `ref_qual` 445 | and 'adds' it to any existing reference qualifier, performing reference collapse: 446 | 447 | * **`function_add_reference`** (ref-collapse) 448 | 449 | ```c++ 450 | template 451 | using function_add_reference = 452 | function_set_reference + R>; 453 | 454 | template 455 | using function_add_reference_t = 456 | function_set_reference_t + R>; 457 | ``` 458 | 459 | ---- 460 | 461 | ## Set traits 462 | 463 | ```c++ 464 | template using function_**_t = FuncTr; 465 | template using function_** = function_traits< 466 | FuncTr>; 467 | ``` 468 | 469 | Reference qualifier setter, takes one `ref_qual` Arg. No reference collapse 470 | (see `function_add_reference` above for trait with reference collapse): 471 | 472 | One `ref_qual` Arg: 473 | 474 | * **`function_set_reference `** 475 | 476 | One `bool` Arg: 477 | 478 | * **`function_set_const `** 479 | * **`function_set_volatile `** 480 | * **`function_set_noexcept `** 481 | * **`function_set_variadic `** 482 | 483 | Two `bool` Args: 484 | 485 | * **`function_set_cv `** 486 | 487 | Two `bool`, one `ref_qual` Arg: 488 | 489 | * **`function_set_cvref `** 490 | 491 | Other Args: 492 | 493 | * **`function_set_return_type `** 494 | * **`function_set_signature `** 495 | 496 | Setting return type requires a valid return type (not array or function type). 497 | Setting signature requires FuncSig is a simple function signature argument. 498 | 499 | ---- 500 | 501 | ## Copy trait 502 | 503 | * **`function_set_cvref_as `** 504 | 505 | This is a particular case of the previous section "Set traits". 506 | It is provided for convenience in copying cvref qualifiers from a 507 | source to a target function type: 508 | 509 | ```c++ 510 | template 511 | using function_set_cvref_as = 512 | function_set_cvref, 513 | function_is_volatile_v, 514 | function_reference_v>; 515 | ``` 516 | 517 | with similar implementation for `function_set_cvref_as_t` alias trait. 518 | 519 | ### Other copy techniques 520 | 521 | Clearly, individual properties can be copied in the same way as above, using 522 | `function_set_*` traits and predicates or 'getter' traits for the properties. 523 | 524 | `function_set_signature`, in particular, can copy cvref and noexcept 525 | from a source to a target function type: 526 | 527 | ```c++ 528 | function_set_signature> 529 | ``` 530 | 531 | effectively copies `S`'s cvref qualifiers and exception spec to `F`'s signature 532 | (note the reversed Args - it actually copies `F`'s signature to `S`) 533 | 534 | ```c++ 535 | template 536 | using function_set_cvref_noexcept_as = 537 | function_set_signature>; 538 | ``` 539 | 540 | ---- 541 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # **C++ `function_traits` library** 2 | 3 | ## Type traits for properties of C++ function types 4 | 5 | Complete reflection of C++ function types and modification of their properties. 6 | 7 | **Anatomy** of a general C++17 function type: 8 | 9 | ```c++ 10 | template 11 | using 12 | function = R(P...[,...]) [const] [volatile] [&|&&] noexcept(X); 13 | ``` 14 | 15 |
Dissected - a breakdown of the general type, with library API terminology 16 | 17 | >'`A`|`B`' for `A` or `B` alternatives - '[`C`]' for optional `C` term: 18 | 19 | ```c++ 20 | /* 21 | _signature_ ________cvref________ noexcept_ * 22 | | | | | | | */ 23 | function = R(P...[,...]) [const] [volatile] [&|&&] noexcept(X); /* 24 | | |__| |__| |_____ _______| | | * 25 | return_type | variadic cv reference * 26 | arg_types lvalue | rvalue */ 27 | ``` 28 | 29 | Function **signature** (all API terms in **bold**): 30 | 31 | * **`R(P...)`**|**`R(P...,...)`** : **signature** = **return_type** `R` and **arg_types** `P...` 32 | 33 | >Here, '**signature**' refers to **return_type** `R` and **arg_types** (parameter) `P...` 34 | including any C-style varargs (termed '**variadic**', denoted by trailing ellipsis **`...`**) 35 | excluding everything after the function parens (i.e. no cvref or exception spec). 36 | 37 | Function varargs existence is treated as a (`bool`) property for API purposes: 38 | 39 | * **variadic** : API property name for presence of ellipsis: true | false 40 | 41 | Function **noexcept** property (`bool`): 42 | 43 | * **`noexcept(X)`** : Function exception specification; X = true | false 44 | 45 | Function **cvref** properties (`bool`, `bool`, `ref_qual`): 46 | 47 | * [**`const`**] [**`volatile`**] [**`&`**|**`&&`**] : Function **cvref** qualifiers; 12 combos 48 | 49 | >Warning: the **cvref** API terms may be familiar from the `std` traits but have 50 | different meanings and behaviour as function type qualifiers (see API refs): 51 | > 52 | >* **const**, **volatile**, **cv** (const | volatile) 53 | >* **reference_lvalue**, **reference_rvalue**, **reference** (lval | rval) 54 | >* **cvref** (const | volatile | reference) 55 | 56 | The **cvref** qualifiers divide the function types into two top level categories: 57 | 58 | * **free** function types, with no cvref qualifiers - the valid types of free functions 59 | * **cvref** qualified function types, the so-called 'abominable' function types 60 | 61 | Test with function traits `is_free_function` or `function_is_cvref` 62 | 63 |
64 | 65 | ---- 66 | 67 |
Copyright © 2019 Will Wray. Distributed under the Boost Software License, V1.0 68 | 69 | ### **Boost Software License** - Version 1.0 - August 17th, 2003 70 | 71 | ```txt 72 | Permission is hereby granted, free of charge, to any person or organization 73 | obtaining a copy of the software and accompanying documentation covered by 74 | this license (the "Software") to use, reproduce, display, distribute, 75 | execute, and transmit the Software, and to prepare derivative works of the 76 | Software, and to permit third-parties to whom the Software is furnished to 77 | do so, all subject to the following: 78 | 79 | The copyright notices in the Software and this entire statement, including 80 | the above license grant, this restriction and the following disclaimer, 81 | must be included in all copies of the Software, in whole or in part, and 82 | all derivative works of the Software, unless such copies or derivative 83 | works are solely in the form of machine-executable object code generated by 84 | a source language processor. 85 | 86 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 87 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 88 | FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT 89 | SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE 90 | FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, 91 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 92 | DEALINGS IN THE SOFTWARE. 93 | ``` 94 | 95 | [![License](https://img.shields.io/badge/license-boost%201.0-blue.svg)](https://www.boost.org/LICENSE_1_0.txt) 96 | 97 | Also at [boost.org](http://www.boost.org/LICENSE_1_0.txt) and accompanying file [LICENSE_1_0.txt](LICENSE_1_0.txt) 98 | 99 |
100 | 101 | ---- 102 | 103 |
Background: C++ function types, the free & the abominable (P0172) 104 | 105 | *
C++ function types 106 | 107 | The `std` type trait `is_function_v` is true for all C++ function types. 108 | 109 | C++ function types include the types of ordinary C/C++ free functions, 110 | referred to here as 'free' function types: 111 | 112 | ```c++ 113 | // free function types 114 | 115 | void(int) or auto(int) -> void 116 | char*() noexcept or auto() noexcept -> char* 117 | int(char const*,...) or auto(char const*,...) -> int 118 | ``` 119 | 120 | >C++ function types can also have cvref qualifiers: 121 | 122 | ```c++ 123 | int() const& or auto() const& -> int 124 | void() && noexcept or auto() && noexcept -> void 125 | void(int) volatile or auto(int) volatile -> void 126 | ``` 127 | 128 | >Such cvref-qualified function types are an artifact of the C++ type system. 129 | Member functions carry cvref qualifiers for the implicit `*this` reference 130 | used in calling the member function, so cvref-qualified function types arise 131 | as part of pointer-to-member-function types. 132 | You cannot declare an ordinary free function with a cvref type and it is 133 | forbidden to form a pointer or a reference to a cvref-qualified function type. 134 |
135 | 136 | *
P0172R0 Abominable Function Types by Alisdair Meredith, Nov 2015 137 | 138 | Quoting from [P0172R0](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/p0172r0.html) section **2.1, Definition**: 139 | 140 | >[...] an *abominable* function type is the type produced by writing 141 | a function type followed by a cv-ref qualifier. 142 | 143 | Example: 144 | 145 | ```cpp 146 | using regular = void(); 147 | using abominable = void() const volatile &&; 148 | ``` 149 | 150 | >In the example above, `regular` names a familiar function type [...], 151 | `abominable` also names a function type, not a reference type, and 152 | despite appearances, is neither a const nor a volatile qualified type. 153 | There is no such thing as a cv-qualified function type in the type system, 154 | and the abominable function type is something else entirely. 155 |
156 | 157 | *
Boost.CallableTraits: A P0172 implementation and more 158 | 159 | >[Boost.CallableTraits](https://www.boost.org/doc/libs/develop/libs/callable_traits/doc/html/) implements P0172R0's suggested library interface, 160 | extended to support general [Callable](https://en.cppreference.com/w/cpp/named_req/Callable) types on top of C++ function types. 161 | It is a robust, reviewed library with tests, compatibility matrix and CI. 162 |
163 | 164 |
165 | 166 | ## **Description | Motivation | Aims** 167 | 168 |
Description 169 | 170 | >* **Type trait**: 171 | a template-based interface to query or modify the properties of types. 172 | 173 | >**`function_traits`** is a library of traits for C++17 function types - 174 | no more, no less; it does not provide traits for general [Callable](https://en.cppreference.com/w/cpp/named_req/Callable) types 175 | (function traits can ease implementation of facilities like callable traits). 176 | > 177 | >It depends on std `` which it complements with function traits. 178 | The library uses `namespace ltl` for its traits, types and functions. 179 | > 180 | >It targets C++17 on recent gcc / clang / msvc compilers. 181 | Backwards compatibility, for older compilers or for pre-17, is not a priority. 182 | It is an 'alpha' design with an experimental interface, subject to change. 183 | Once C++20 is available, constraints will be added. 184 | 185 |
186 | 187 |
Motivation: Provide the 24 (or 48) required specializations 188 | 189 | >See also [Boost.CallableTraits Motivation](https://www.boost.org/doc/libs/develop/libs/callable_traits/doc/html/index.html#callable_traits.introduction.motivation) 190 | 191 | **Function traits** are necessary to reflect the properties of function types. 192 | They may be useful in generic code that must handle general function types. 193 | 194 | >'Abominable' function cvref qualifiers cannot be deduced concisely. 195 | C-style varargs - a trailing ellipsis ... - cannot be deduced concisely. 196 | A total of 24 separate template specializations are needed to match 197 | a possibly abominable or variadic function type: 198 | 199 | * 12 combinations of cvref qualifiers (4 cv x 3 ref) 200 | * x 2 for presence of C-style varargs (trailing ellipsis...) 201 | 202 | >If `noexcept` is not deduced directly then 48 specializations are needed: 203 | 204 | * x 2 for `noexcept` true or false 205 | 206 | >It is tedious to have to write all of the necessary specializations. 207 | This library provides the specializations wrapped up as function traits. 208 | > 209 | >Since all 24/48 specializations are needed to implement *any* function trait 210 | with full generality, one might as well write a full collection of traits. 211 | > 212 | >**'Setter' traits** 213 | > 214 | >I wanted traits to copy qualifiers from source to target function types (e.g. 215 | Boost.CallableTraits has an open [issue](https://github.com/boostorg/callable_traits/issues/139) to add a `copy_member_cvref` trait 216 | and `std::copy_*` traits are proposed in [P1016](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p1016r0.pdf) "...type manipulation utilities") 217 | > 218 | >This library provides a couple of options: 219 | >`function_set_cvref_as` copies `G`'s cvref qualifiers to `F`, or 220 | `function_set_signature>` 221 | effectively copies `G`'s cvref qualifiers and exception spec to `F`'s signature. 222 | 223 |
224 | 225 |
The 24 (or 48) function pattern specializations 226 | 227 | 24 template specializations are required to match any function type pattern 228 | (assuming that `noexcept` is deducible in partial specializations - see note below): 229 | 230 | ```c++ 231 | // Primary template 232 | template struct fun; 233 | 234 | // The 24 template partial specializations 235 | // to match cvref qualifiers (x12) and presence of varargs (x2) 236 | // while deducing return type R, parameters P... and noexcept(bool) 237 | template struct fun {}; 238 | template struct fun {}; 239 | template struct fun {}; 240 | template struct fun {}; 241 | template struct fun {}; 242 | template struct fun {}; 243 | template struct fun {}; 244 | template struct fun {}; 245 | template struct fun {}; 246 | template struct fun {}; 247 | template struct fun {}; 248 | template struct fun {}; 249 | 250 | template struct fun {}; 251 | template struct fun {}; 252 | template struct fun {}; 253 | template struct fun {}; 254 | template struct fun {}; 255 | template struct fun {}; 256 | template struct fun {}; 257 | template struct fun {}; 258 | template struct fun {}; 259 | template struct fun {}; 260 | template struct fun {}; 261 | template struct fun {}; 262 | ``` 263 | 264 | Both GCC and Clang deduce noexcept as intended... 265 | Unfortunately, when `noexcept` was introduced as part of the type system 266 | the standard was not also updated to specify deduction of noexcept. 267 | This oversight should be corrected by a defect report before C++2a. 268 | 269 | Currently (start of 2019) MSVC does not deduce noexcept and so requires 270 | the noexcept cases to be expanded via 48 specializations: 271 | 272 | ```c++ 273 | template struct fun {}; 274 | template struct fun {}; 275 | template struct fun {}; 276 | template struct fun {}; 277 | template struct fun {}; 278 | template struct fun {}; 279 | template struct fun {}; 280 | template struct fun {}; 281 | template struct fun {}; 282 | template struct fun {}; 283 | template struct fun {}; 284 | template struct fun {}; 285 | template struct fun {}; 286 | template struct fun {}; 287 | template struct fun {}; 288 | template struct fun {}; 289 | template struct fun {}; 290 | template struct fun {}; 291 | template struct fun {}; 292 | template struct fun {}; 293 | template struct fun {}; 294 | template struct fun {}; 295 | template struct fun {}; 296 | template struct fun {}; 297 | 298 | template struct fun {}; 299 | template struct fun {}; 300 | template struct fun {}; 301 | template struct fun {}; 302 | template struct fun {}; 303 | template struct fun {}; 304 | template struct fun {}; 305 | template struct fun {}; 306 | template struct fun {}; 307 | template struct fun {}; 308 | template struct fun {}; 309 | template struct fun {}; 310 | template struct fun {}; 311 | template struct fun {}; 312 | template struct fun {}; 313 | template struct fun {}; 314 | template struct fun {}; 315 | template struct fun {}; 316 | template struct fun {}; 317 | template struct fun {}; 318 | template struct fun {}; 319 | template struct fun {}; 320 | template struct fun {}; 321 | template struct fun {}; 322 | ``` 323 | 324 | These 48 specializations are also listed in [Boost.CallableTraits](https://www.boost.org/doc/libs/develop/libs/callable_traits/doc/html/index.html#callable_traits.introduction.motivation) and [cppreference](https://en.cppreference.com/w/cpp/types/is_function) `is_function` 325 | 326 |
327 | 328 |
Aims: A complete, minimal, forward looking, simple dependency 329 | 330 | *
A complete yet minimal set of function type traits 331 | 332 | **Complete**: provide a way to do any query or modification that may be needed; 333 | if you see something that is not reasonably easy to do then open an issue. 334 | 335 | **Minimal**: avoid bloat and duplication in the interface (not easy - 50 traits!). 336 | Narrow scope, single responsibility - function traits only, no more, no less. 337 |
338 | 339 | *
In a single header, simple to take as a dependency 340 | 341 | **Simple dependency**: single header, self contained with docs. 342 | Mesonbuild example as subproject / git submodule. CMake ToDo. 343 | Of course, you can just copy the header or cut-n-paste. 344 | 345 | **Single header**: rather than 'fine-grain' headers per trait. 346 | Because each trait has to pull in the full 24 (or 48) specializations, 347 | even if a user may only want one of the many traits, 348 | it seems not worth the complexity of providing individual headers 349 | (if you can show benefits worth the complexity then open an issue). 350 |
351 | 352 | *
Forward looking: to concepts - down with SFINAE! 353 | 354 | Look towards concepts and contraints with no need for SFINAE tricks 355 | No concern for backward **compatibility** or support of old compilers 356 | **Diverge** from the P0172R0 suggested interface as appropriate 357 | A clean, modern implementation (macro use confined to header). 358 |
359 | 360 |
361 | 362 | ## [Reference](reference.md) 363 | 364 | ## **Examples** 365 | 366 |
Getting started 367 | 368 | >First, put the header file where you want it and configure your include path. 369 | Here, the `ltl` include directory reflects `function_traits` namespace, `ltl` 370 | (or, just cut and paste the header): 371 | 372 | ```c++ 373 | #include 374 | ``` 375 | 376 | >All `function_*` traits are defined only for function types. 377 | Calling a `function_*` trait with a non function type gives a hard, 378 | non-SFINAE, error (with a nasty error message from the compiler). 379 | 380 | ```c++ 381 | ltl::function_is_cvref< int > // compile error 382 | ``` 383 | 384 | >The `function_is_*` predicate traits have SFINAE-friendly siblings: 385 | 386 | ```c++ 387 | ltl::is_function_cvref< int > // empty class 388 | ``` 389 | 390 | >Other `function_*` traits have no safe / SFINAE-friendly variants. 391 | To use these function traits with non-function types, you can guard the trait 392 | instantiation with `if constexpr (is_function_v)`: 393 | 394 | ```c++ 395 | template 396 | inline constexpr bool is_free_function_v = []{ 397 | if constexpr (is_function_v) 398 | return !function_is_cvref_v; 399 | return false; }(); 400 | ``` 401 | 402 | >(`conditional_t` doesn't work here as both branches instantiate.) 403 | 404 |
405 | 406 |
Predicate traits and type property traits 407 | 408 | >Test if a function type is variadic and const and noexcept: 409 | 410 | ```c++ 411 | using Fc = void(...) const noexcept; 412 | 413 | static_assert( 414 | ltl::function_is_variadic_v< Fc > 415 | && ltl::function_is_const_v< Fc > 416 | && ltl::function_is_noexcept_v< Fc > 417 | ); 418 | ``` 419 | 420 | >Get the return type of a function type and a type-list of its parameter types: 421 | 422 | ```c++ 423 | #include 424 | #include 425 | 426 | using Fcb = void( char, bool() ); 427 | 428 | static_assert( 429 | std::is_void_v< ltl::function_return_type_t< Fcb > > 430 | && std::is_same_v< ltl::function_arg_types< Fcb, std::tuple > 431 | , std::tuple< char, bool(*)() > > 432 | ); // ^^^ 433 | // note decay 434 | ``` 435 | 436 |
437 | 438 |
Modifying traits; add / remove and set traits 439 | 440 | >Conventional `add_*`, `remove_*` traits modify the given property `*`. 441 | They generally take no arguments beyond the function type to modify: 442 | 443 | ```c++ 444 | using namespace ltl; 445 | static_assert( 446 | std::is_same_v< function_add_const_t, 447 | void() const& > 448 | && std::is_same_v< function_remove_cvref_t, 449 | void() > 450 | ); 451 | ``` 452 | 453 | >Some property traits act as `remove_*` traits; the 'signature' property trait 454 | effectively removes both cvref and noexcept: 455 | 456 | ```c++ 457 | static_assert( 458 | std::is_same_v< function_signature_t, 459 | void() > 460 | ); 461 | ``` 462 | 463 | >`set_*` traits are more programmatic than `add_*` and `remove_*` traits. 464 | Setters for function cv qualifiers, noexcept and variadic take `bool` arguments: 465 | 466 | ```c++ 467 | static_assert( function_is_noexcept_v< 468 | function_set_noexcept_t >); 469 | ``` 470 | 471 | >The `set` trait for reference qualifiers takes a `ltl::ref_qual` argument 472 | (an enum type with values `lval_ref_v`, `rval_ref_v` or `null_ref_v`) 473 | 474 | ```c++ 475 | static_assert( 476 | std::is_same_v< function_set_reference_t, 477 | void() && > 478 | ); 479 | ``` 480 | 481 | >Reference collapse is not necessarily natural for function reference qualifiers. 482 | If you need it, `function_add_reference` does reference collapse 483 | 484 | ```c++ 485 | static_assert( 486 | std::is_same_v< function_add_reference_t, 487 | void() & > 488 | ); 489 | ``` 490 | 491 | >('adding' an rvalue-ref to an lvalue-ref yields an lvalue-ref, consistent with 492 | `std::add_rvalue_reference` for ordinary reference types; `&` + `&&` => `&`) 493 | > 494 | >Setters for type properties take type arguments; to change function return type: 495 | 496 | ```c++ 497 | static_assert( 498 | std::is_same_v< function_set_return_type_t, 499 | void() >); 500 | ``` 501 | 502 | >The `set_cvref_as` trait provides a way to copy qualifiers to the target function type 503 | from a source function type: 504 | 505 | ```c++ 506 | static_assert( std::is_same_v< 507 | function_set_cvref_as_t, 508 | void() & >); 509 | ``` 510 | 511 |
512 | 513 |
A small example of function_traits usage 514 | 515 | A contrived example that type-checks `printf`-like member functions that may 516 | or may not be variadic, then forwards a C++ argument pack to the C varargs 517 | (the vargs could be matched and type checked against the format string). 518 | 519 | ```cpp 520 | #include 521 | #include "function_traits.hpp" 522 | 523 | struct Log0 { int log(char const* fmt) const noexcept; }; 524 | struct LogV { int log(char const* fmt,...) const & noexcept; }; 525 | 526 | template 527 | int logger(F C::* log_mf, Vargs... vargs) noexcept 528 | { 529 | static_assert( std::is_function_v ); 530 | 531 | static_assert( ltl::function_is_const_v ); 532 | static_assert( ltl::function_is_noexcept_v ); 533 | static_assert( ltl::function_is_variadic_v 534 | == bool{sizeof...(vargs)} ); 535 | 536 | using R = ltl::function_return_type_t; 537 | using Ps = ltl::function_arg_types; 538 | using P0 = std::tuple_element_t<0,Ps>; 539 | 540 | static_assert( std::is_same_v< R, int> ); 541 | static_assert( std::is_same_v< P0, char const*> ); 542 | 543 | return (C{}.*log_mf)("logger",vargs...); 544 | } 545 | 546 | template int logger(decltype(&Log0::log)); 547 | template int logger(decltype(&LogV::log),int); 548 | ``` 549 | 550 |
551 | 552 | ---- 553 | 554 | ## Build 555 | 556 | Meson build script provided, e.g. use with ninja backend 557 | 558 | ```bash 559 | meson build 560 | ninja -C build 561 | ninja -C build test 562 | ``` 563 | 564 | | Linux Travis| Windows Appveyor| 565 | | :---: | :---: | 566 | |gcc-8, clang-7
-std=c++17|MSVC 15.9.4
/std:c++latest| 567 | | [![Build Status](https://travis-ci.org/willwray/function_traits.svg?branch=master)](https://travis-ci.org/willwray/function_traits) | [![Build status](https://ci.appveyor.com/api/projects/status/yxe3sawukwt5oqan?svg=true)](https://ci.appveyor.com/project/willwray/function-traits) | 568 | -------------------------------------------------------------------------------- /function_traits.hpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2019 Will Wray https://keybase.io/willwray 2 | // 3 | // Distributed under the Boost Software License, Version 1.0. 4 | // (http://www.boost.org/LICENSE_1_0.txt) 5 | // 6 | // Repo: https://github.com/willwray/function_traits 7 | 8 | #include 9 | 10 | /* 11 | "function_traits.hpp": function signature, cvref and noexcept traits 12 | ^^^^^^^^^^^^^^^^^^^ 13 | Type trait: A compile-time template-based interface to 14 | query or modify the properties of types. 15 | 16 | This header provides traits for C++ function types: 17 | 18 | - Function signature: R(P...) or R(P..., ...) 19 | - Return type R 20 | - Parameter types P... 21 | - Presence of variadic parameter pack ... 'varargs' 22 | ...C-style trailing elipsis: 2 combinations 23 | 24 | - Function cvref qualifiers 12 combinations total: 25 | - cv: const and/or volatile 4 combinations 26 | - ref: lvalue & or rvalue && x3 combinations 27 | 28 | - Function exception specification: 29 | - noexcept(bool): true|false 2 combinations 30 | 31 | Function 'signature' here refers to return type R and parameters P... 32 | (with optional C-style varargs but without qualifiers and noexcept). 33 | The function_signature trait extracts just this R(P...[,...]) part: 34 | 35 | function_signature_t // type alias yielding a function type = 36 | // F with cvref quals and noexcept removed 37 | function_signature // class with public type alias member 38 | // type = function_signature_t 39 | Other 'signature' traits: 40 | 41 | function_return_type // class with member type alias for R 42 | function_arg_types // a typelist of F's arg types P... 43 | 44 | This library follows std trait conventions as appropriate: 45 | 46 | '_t' suffix for result type (the trait is a type alias template) 47 | '_v' suffix for result value (the trait is a variable template) 48 | no suffix for a result class (the trait is a class template 49 | with 'type' or 'value' member) 50 | 51 | ** All traits prefixed with 'function_' are *only* well defined for ** 52 | ** function types; instantiating with non-function type is an error. ** 53 | 54 | Predicate traits 55 | ================ 56 | Two 'top level' predicates classify function types among all types 57 | 58 | is_function // ltl:: equivalent of std::is_function 59 | is_free_function // Is T a valid type for a free function? 60 | 61 | These traits always evaluate true or false (for the _v variant; 62 | the trait class inherits from std::true_type | std::false_type). 63 | 64 | * 'Free' function types are function types without cvref qualifiers. 65 | * Function types with cvref qualifiers are 'abominable' - see docs. 66 | 67 | Nine function_trait predicates test for; const, volatile, cv, cvref, 68 | reference, reference_lvalue, reference_rvalue, noexcept and variadic. 69 | 70 | There are two versions: 71 | is_function_* // empty class for non-function type T 72 | function_is_* // compile error for non-function type 73 | I.e.: 74 | The function_is_* traits are for Fs known to be of function type. 75 | 76 | The is_function_* traits are 'SFINAE-friendly' predicates that 77 | - inherit from std::bool_constant<*> for function type T or 78 | - inherit from an empty class for non-function type T 79 | (there is no 'is_function_*_v' variant - use 'function_is_*_v'). 80 | 81 | For example, here are bad and good definitions of is_free_function_v: 82 | 83 | template 84 | inline constexpr bool is_free_function_v 85 | 86 | (1) = ltl::is_function_v && !ltl::function_is_cvref_v; // BAD 87 | 88 | (2) = std::conjunction_v< ltl::is_function, // CORRECT 89 | std::negation> >; 90 | 91 | (3) = [] { if constexpr (ltl::is_function_v) // ALSO CORRECT 92 | return !ltl::function_is_cvref_v; 93 | return false; }(); 94 | 95 | Instantiation of function_is_cvref fails for non-function type T 96 | so it must be 'guarded' e.g. by logic traits (2) or constexpr-if (3) 97 | (note - ltl::is_function avoids redundant work of std::is_function). 98 | 99 | Modifying traits 100 | ================ 101 | Conventional 'add' and 'remove' traits modify their named trait: 102 | 103 | function_add_noexcept_t // add noexcept specifier 104 | function_remove_cvref_t // remove all cv and ref qualifiers 105 | // leaving signature and exception spec 106 | 107 | Unconventionally, this library also provides transforming / mutating 108 | 'set' traits that take extra template arguments, e.g.: 109 | 110 | function_set_noexcept_t // set noexcept(B) for bool const B 111 | function_set_signature_t // set S as the function signature 112 | // keeping cvref-nx of function F 113 | 114 | Reference qualifiers are represented by an enum type ref_qual: 115 | 116 | - null_ref_v no reference qualifier, same as ref_qual{} 117 | - lval_ref_v lvalue reference qualifier: & 118 | - rval_ref_v rvalue reference qualifier: && 119 | 120 | Addition of ref_qual values gives the same reference-collapsed result 121 | as compounding references on ordinary object types. 122 | 123 | A pair of variable templates give the reference type of T or F: 124 | 125 | reference_v // ordinary type top-level ref_qual value 126 | function_reference_v // function type ref_qual value 127 | 128 | The 'set_reference' traits then allow to copy between function types: 129 | 130 | // copy the reference qualifiers from G to F (with no collapse) 131 | function_set_reference> 132 | 133 | function_set_reference // set ref qual to & 134 | function_set_reference_lvalue // set ref qual to & 135 | 136 | function_set_reference // set ref qual to none 137 | function_remove_reference // set ref qual to none 138 | 139 | function_add_reference // does reference collapse 140 | // (so F& + && = F&) 141 | 142 | A trait is provided to copy all cvref qualifiers, otherwise verbose: 143 | 144 | function_set_cvref_as_t // copy cvref quals of G to F 145 | */ 146 | 147 | #if !defined(__cpp_noexcept_function_type) 148 | #error function_traits requires c++17 support for noexcept function types \ 149 | (MSVC: /Zc:noexceptTypes- must not be used) 150 | #endif 151 | 152 | // GCC and Clang deduce noexcept via partial specialization 153 | // MSVC doesn't deduce yet (early 2019 V 15.9.4 Preview 1.0) 154 | #if defined(__GNUC__) 155 | # define NOEXCEPT_DEDUCED 156 | #endif 157 | 158 | // Fallback macro switch for lack of noexcept deduction 159 | #if defined(NOEXCEPT_DEDUCED) 160 | # define NOEXCEPT_ND(NON, ...) __VA_ARGS__ 161 | #else 162 | # define NOEXCEPT_ND(NON, ...) NON 163 | #endif 164 | 165 | // Test noexcept deduction - compile fail if deduction fails 166 | #if defined(NOEXCEPT_DEDUCED) 167 | namespace test { 168 | constexpr void voidfn(); 169 | // GCC appears to need R introduced & deduced in order to deduce X 170 | template 171 | constexpr auto noexcept_deduction(R() noexcept(X)) -> std::true_type; 172 | constexpr auto noexcept_deduction(...) -> std::false_type; 173 | static_assert(decltype(noexcept_deduction(voidfn))(), 174 | "NOEXCEPT_DEDUCED flag is set but deduction fails"); 175 | } // namespace test 176 | #endif 177 | 178 | namespace ltl 179 | { 180 | // function_traits 181 | // class template: a collection of member traits for function type F 182 | // or an incomplete type for non-function type F 183 | template class function_traits; 184 | // Note: the 24 (or 48) partial specializations for function_traits 185 | // are generated by macro-expansion. 186 | 187 | // A default type-list type for returning function parameter types 188 | template struct arg_types; 189 | 190 | // ref_qual: a value to represent a reference qualifier 191 | // null_ref_v no reference qualifier 192 | // rval_ref_v rvalue reference qualifier: && 193 | // lval_ref_v lvalue reference qualifier: & 194 | enum ref_qual { null_ref_v, rval_ref_v, lval_ref_v = 3 }; 195 | 196 | // ref_qual operator+( ref_qual, ref_qual) 197 | // 'adds' reference qualifiers with reference collapse 198 | constexpr ref_qual operator+( ref_qual a, ref_qual b) 199 | { 200 | return static_cast(a|b); 201 | } 202 | 203 | // reference_v is a ref_qual value inidicating whether type T is 204 | // lvalue-reference, rvalue-reference or not a reference. 205 | template 206 | inline constexpr 207 | ref_qual 208 | reference_v = std::is_lvalue_reference_v ? lval_ref_v 209 | : std::is_rvalue_reference_v ? rval_ref_v : null_ref_v; 210 | 211 | // function_reference_v is a ref_qual value of the reference qualifier on a 212 | // function type - it is well defined only for function type arguments. 213 | template 214 | inline constexpr 215 | ref_qual 216 | function_reference_v = 217 | typename function_traits::is_reference_lvalue() ? lval_ref_v 218 | : typename function_traits::is_reference_rvalue() ? rval_ref_v : null_ref_v; 219 | 220 | 221 | namespace impl 222 | { 223 | // function_cvref_nx 224 | // Convenience collection of type aliases for cvref & noexcept properties. 225 | // Injected setter template set_cvref_nx is used for template set_* aliases. 226 | template