|  | Home | Libraries | People | FAQ | More | 
Copyright © 2016-2018 Barrett Adair
Distributed under the Boost Software License, Version 1.0. (See accompanying file LICENSE.md or copy at http://www.boost.org/LICENSE_1_0.txt)
Table of Contents
      Boost.CallableTraits is a C++11 header-only library for
      the inspection, synthesis, and decomposition of callable types. Boost.CallableTraits
      aims to be the "complete type manipulation facility for function types"
      mentioned in the final paragraph of C++17 proposal p0172,
      and removes the need for template specializations for different function signatures.
      C++17 noexcept and the Transactional
      Memory TS are also supported if available.
    
#include <type_traits> #include <tuple> #include <boost/callable_traits.hpp> namespace ct = boost::callable_traits; // This function template helps keep our example code neat template<typename A, typename B> void assert_same(){ static_assert(std::is_same<A, B>::value, ""); } // foo is a function object struct foo { void operator()(int, char, float) const {} }; int main() { // Use args_t to retrieve a parameter list as a std::tuple: assert_same< ct::args_t<foo>, std::tuple<int, char, float> >(); // has_void_return lets us perform a quick check for a void return type static_assert(ct::has_void_return<foo>::value, ""); // Detect C-style variadics (ellipses) in a signature (e.g. printf) static_assert(!ct::has_varargs<foo>::value, ""); // pmf is a pointer-to-member function: void (foo::*)(int, char, float) const using pmf = decltype(&foo::operator()); // remove_member_const_t lets you remove the const member qualifier assert_same< ct::remove_member_const_t<pmf>, void (foo::*)(int, char, float) /*no const!*/ >(); // Conversely, add_member_const_t adds a const member qualifier assert_same< pmf, ct::add_member_const_t<void (foo::*)(int, char, float)> >(); // is_const_member_v checks for the presence of member const static_assert(ct::is_const_member<pmf>::value, ""); }
“Don't try to write helper code to detect PMFs/PMDs and dispatch on them -- it is an absolute nightmare. PMF types are the worst types by far in the core language.”
-- Stephan T. Lavavej, CppCon 2015, "functional: What's New, And Proper Usage"
Consider for a moment the code below, which defines all 48 template specializations necessary to specialize for every valid function type in pure C++17:
template<typename T> struct foo; template<class Return, class... Args> struct foo<Return(Args...)> {}; template<class Return, class... Args> struct foo<Return(Args...) &> {}; template<class Return, class... Args> struct foo<Return(Args...) &&> {}; template<class Return, class... Args> struct foo<Return(Args...) const> {}; template<class Return, class... Args> struct foo<Return(Args...) const &> {}; template<class Return, class... Args> struct foo<Return(Args...) const &&> {}; template<class Return, class... Args> struct foo<Return(Args...) volatile> {}; template<class Return, class... Args> struct foo<Return(Args...) volatile &> {}; template<class Return, class... Args> struct foo<Return(Args...) volatile &&> {}; template<class Return, class... Args> struct foo<Return(Args...) const volatile> {}; template<class Return, class... Args> struct foo<Return(Args...) const volatile &> {}; template<class Return, class... Args> struct foo<Return(Args...) const volatile &&> {}; template<class Return, class... Args> struct foo<Return(Args..., ...)> {}; template<class Return, class... Args> struct foo<Return(Args..., ...) &> {}; template<class Return, class... Args> struct foo<Return(Args..., ...) &&> {}; template<class Return, class... Args> struct foo<Return(Args..., ...) const> {}; template<class Return, class... Args> struct foo<Return(Args..., ...) const &> {}; template<class Return, class... Args> struct foo<Return(Args..., ...) const &&> {}; template<class Return, class... Args> struct foo<Return(Args..., ...) volatile> {}; template<class Return, class... Args> struct foo<Return(Args..., ...) volatile &> {}; template<class Return, class... Args> struct foo<Return(Args..., ...) volatile &&> {}; template<class Return, class... Args> struct foo<Return(Args..., ...) const volatile> {}; template<class Return, class... Args> struct foo<Return(Args..., ...) const volatile &> {}; template<class Return, class... Args> struct foo<Return(Args..., ...) const volatile &&> {}; template<class Return, class... Args> struct foo<Return(Args...) noexcept> {}; template<class Return, class... Args> struct foo<Return(Args...) & noexcept> {}; template<class Return, class... Args> struct foo<Return(Args...) && noexcept> {}; template<class Return, class... Args> struct foo<Return(Args...) const noexcept> {}; template<class Return, class... Args> struct foo<Return(Args...) const & noexcept> {}; template<class Return, class... Args> struct foo<Return(Args...) const && noexcept> {}; template<class Return, class... Args> struct foo<Return(Args...) volatile noexcept> {}; template<class Return, class... Args> struct foo<Return(Args...) volatile & noexcept> {}; template<class Return, class... Args> struct foo<Return(Args...) volatile && noexcept> {}; template<class Return, class... Args> struct foo<Return(Args...) const volatile noexcept> {}; template<class Return, class... Args> struct foo<Return(Args...) const volatile & noexcept> {}; template<class Return, class... Args> struct foo<Return(Args...) const volatile && noexcept> {}; template<class Return, class... Args> struct foo<Return(Args..., ...) noexcept> {}; template<class Return, class... Args> struct foo<Return(Args..., ...) & noexcept> {}; template<class Return, class... Args> struct foo<Return(Args..., ...) && noexcept> {}; template<class Return, class... Args> struct foo<Return(Args..., ...) const noexcept> {}; template<class Return, class... Args> struct foo<Return(Args..., ...) const & noexcept> {}; template<class Return, class... Args> struct foo<Return(Args..., ...) const && noexcept> {}; template<class Return, class... Args> struct foo<Return(Args..., ...) volatile noexcept> {}; template<class Return, class... Args> struct foo<Return(Args..., ...) volatile & noexcept> {}; template<class Return, class... Args> struct foo<Return(Args..., ...) volatile && noexcept> {}; template<class Return, class... Args> struct foo<Return(Args..., ...) const volatile noexcept> {}; template<class Return, class... Args> struct foo<Return(Args..., ...) const volatile & noexcept> {}; template<class Return, class... Args> struct foo<Return(Args..., ...) const volatile && noexcept> {};
        Things get even more complicated with member function pointers, function
        pointers, function references, function objects, and transaction_safe.
      
Granted, use cases for such obscure specializations are vitually nonexistent in run-of-the-mill application codebases. Even in library code, these are exceedingly rare. However, there are a handful of metaprogramming scenarios that can only be solved with this kind of template "spam". Writing, testing, and maintaining such code is tedious and costly.
        Boost.CallableTraits offers a final and decisive library-level
        solution to this problem, and removes the need for these specializations
        entirely (platform-specific calling conventions notwithstanding).
      
        The features in Boost.CallableTraits largely overlap with
        Boost.FunctionTypes.
        Here are some reasons why you might prefer Boost.CallableTraits:
      
Boost.FunctionTypes is tightly coupled to Boost.MPL
            sequences, while Boost.CallableTraits has no dependencies
            other than the standard library.
          Boost.CallableTraits targets C++11 and later:
            Boost.CallableTraits treats function objects/lambdas
                  as first-class citizens.
                Boost.CallableTraits supports lvalue/rvalue
                  reference member qualifiers.
                Boost.CallableTraits supports noexcept and transaction_safe.
                Boost.FunctionTypes does not attempt to factor all
            callable types into a unified, INVOKE-aware
            interface.
          Boost.FunctionTypes relies heavily on "tag"
            types, while Boost.CallableTraits follows the style
            of <type_traits> instead. Supporting C++11 and later in Boost.FunctionTypes
            would have required significant proliferation of these tags.
          
        For example, here is how to remove member const
        from a member function pointer type in the Boost.FunctionTypes
        library:
      
#include <type_traits> #include <boost/function_types/components.hpp> #include <boost/function_types/member_function_pointer.hpp> struct foo { void bar() const {} }; using const_removed = typename boost::function_types::member_function_pointer< typename boost::function_types::components<decltype(&foo::bar)>::types, boost::function_types::non_const>::type; static_assert(std::is_same<const_removed, void(foo::*)()>::value, ""); int main(){}
        Boost.CallableTraits makes this easier:
      
#include <type_traits> #include <boost/callable_traits/remove_member_const.hpp> struct foo { void bar() const {} }; using const_removed = boost::callable_traits::remove_member_const_t<decltype(&foo::bar)>; static_assert(std::is_same<const_removed, void(foo::*)()>::value, ""); int main(){}
        The Boost.FunctionTypes library includes an excellent
        example
        for generating type-erased interfaces (implementation here).
        This example was re-implemented
        using Boost.CallableTraits to yield a slightly
        more intuitive interface.
      
        Boost.FunctionTypes is a fine library, but its interface
        left room for improvement.
      
        Boost.CallableTraits supports on GCC 4.7.4+, Clang 3.5.2+,
        XCode 6.4+, and Visual Studio 2015+. Continuous integration is managed on
        Appveyor
        for Visual Studio, and on Travis
        for everything else. The Intel C++ Compiler is not officially supported yet,
        although the 2017 version for Linux does pass a handful of test cases.
      
Table 1. GCC Support
| feature | GCC 8.2.0 | GCC 7.3.0 | GCC 6.3.0 | GCC 5.4.0 | GCC 4.9.2 | GCC 4.8.2 | GCC 4.7.4 | 
|---|---|---|---|---|---|---|---|
| c++11 | c++11 | c++11 | c++11 | c++11 | c++11 (no abominables) | c++11 (no abominables) | |
| c++11 | c++11 | c++11 | c++11 | c++11 | c++11 (no abominables) | c++11 (no abominables) | |
| c++11 | c++11 | c++11 | c++11 | c++11 | static_assert fails on instantiation | static_assert fails on instantiation | |
| c++11 | c++11 | c++11 | c++11 | c++11 | static_assert fails on instantiation | static_assert fails on instantiation | |
| c++11 | c++11 | c++11 | c++11 | c++11 | c++11 (no abominables) | c++11 (no abominables) | |
| c++17 | c++17 | c++17 | static_assert fails on instantiation | static_assert fails on instantiation | static_assert fails on instantiation | static_assert fails on instantiation | |
| c++17 (requires -fgnu-tm) | c++17 (requires -fgnu-tm) | c++17 (requires -fgnu-tm) | static_assert fails on instantiation | static_assert fails on instantiation | static_assert fails on instantiation | static_assert fails on instantiation | |
| c++11 | c++11 | c++11 | c++11 | c++11 | c++11 | c++11 | |
| c++11 | c++11 | c++11 | c++11 | c++11 | c++11 | c++11 | |
| c++11 | c++11 | c++11 | c++11 | c++11 | c++11 | c++11 | |
| c++11 | c++11 | c++11 | c++11 | c++11 | c++11 | c++11 | |
| c++11 | c++11 | c++11 | c++11 | c++11 | c++11 | c++11 | |
| c++11 | c++11 | c++11 | c++11 | c++11 | c++11 | c++11 | |
| c++11 | c++11 | c++11 | c++11 | c++11 | c++11 (no abominables) | c++11 (no abominables) | |
| c++11 | c++11 | c++11 | c++11 | c++11 | c++11 | c++11 | |
| c++11 | c++11 | c++11 | c++11 | c++11 | c++11 | c++11 | |
| c++11 | c++11 | c++11 | c++11 | c++11 | c++11 (no abominables) | c++11 (no abominables) | |
| c++11 | c++11 | c++11 | c++11 | c++11 | c++11 (no abominables) | c++11 (no abominables) | |
| c++11 | c++11 | c++11 | c++11 | c++11 | unknown | unknown | |
| c++11 | c++11 | c++11 | c++11 | c++11 | c++11 (always false) | c++11 (always false) | |
| c++17 | c++17 | c++17 | c++11 (always false) | c++11 (always false) | c++11 (always false) | c++11 (always false) | |
| c++11 | c++11 | c++11 | c++11 | c++11 | c++11 (always false) | c++11 (always false) | |
| c++11 | c++11 | c++11 | c++11 | c++11 | c++11 (always false) | c++11 (always false) | |
| c++17 (requires -fgnu-tm) | c++17 (requires -fgnu-tm) | c++17 (requires -fgnu-tm) | c++11 (always false) | c++11 (always false) | c++11 (always false) | c++11 (always false) | |
| c++11 | c++11 | c++11 | c++11 | c++11 | c++11 (no abominables) | c++11 (no abominables) | |
| c++11 | c++11 | c++11 | c++11 | c++11 | c++11 | c++11 | |
| c++11 | c++11 | c++11 | c++11 | c++11 | c++11 (no abominables) | c++11 (no abominables) | |
| c++11 | c++11 | c++11 | c++11 | c++11 | c++11 (no abominables) | c++11 (no abominables) | |
| c++11 | c++11 | c++11 | c++11 | c++11 | c++11 (no effect) | c++11 (no effect) | |
| c++11 | c++11 | c++11 | c++11 | c++11 | c++11 (no abominables) | c++11 (no abominables) | |
| c++17 | c++17 | c++17 | c++11 (no effect) | c++11 (no effect) | c++11 (no effect) | c++11 (no effect) | |
| c++17 (requires -fgnu-tm) | c++17 (requires -fgnu-tm) | c++17 (requires -fgnu-tm) | c++11 (no effect) | c++11 (no effect) | c++11 (no effect) | c++11 (no effect) | |
| c++11 | c++11 | c++11 | c++11 | c++11 | c++11 | c++11 | |
| c++11 | c++11 | c++11 | c++11 | c++11 | c++11 | c++11 | 
Table 2. LLVM/Clang Support
| feature | Clang 7.0.1 | Clang 6.0.0 | Clang 5.0.1 | Clang 4.0.0 | Clang 3.8.0 | Clang 3.7.1 | Clang 3.6.2 | Clang 3.5.2 | 
|---|---|---|---|---|---|---|---|---|
| c++11 | c++11 | c++11 | c++11 | c++11 | c++11 | c++11 | c++11 | |
| c++11 | c++11 | c++11 | c++11 | c++11 | c++11 | c++11 | c++11 | |
| c++11 | c++11 | c++11 | c++11 | c++11 | c++11 | c++11 | c++11 | |
| c++11 | c++11 | c++11 | c++11 | c++11 | c++11 | c++11 | c++11 | |
| c++11 | c++11 | c++11 | c++11 | c++11 | c++11 | c++11 | c++11 | |
| c++11 | c++17 | c++17 | c++17 | static_assert fails on instantiation | static_assert fails on instantiation | static_assert fails on instantiation | static_assert fails on instantiation | |
| unknown | unknown | unknown | unknown | static_assert fails on instantiation | static_assert fails on instantiation | static_assert fails on instantiation | static_assert fails on instantiation | |
| c++11 | c++11 | c++11 | c++11 | c++11 | c++11 | c++11 | c++11 | |
| c++11 | c++11 | c++11 | c++11 | c++11 | c++11 | c++11 | c++11 | |
| c++11 | c++11 | c++11 | c++11 | c++11 | c++11 | c++11 | c++11 | |
| c++11 | c++11 | c++11 | c++11 | c++11 | c++11 | c++11 | c++11 | |
| c++11 | c++11 | c++11 | c++11 | c++11 | c++11 | c++11 | c++11 | |
| c++11 | c++11 | c++11 | c++11 | c++11 | c++11 | c++11 | c++11 | |
| c++11 | c++11 | c++11 | c++11 | c++11 | c++11 | c++11 | c++11 | |
| c++11 | c++11 | c++11 | c++11 | c++11 | c++11 | c++11 | c++11 | |
| c++11 | c++11 | c++11 | c++11 | c++11 | c++11 | c++11 | c++11 | |
| c++11 | c++11 | c++11 | c++11 | c++11 | c++11 | c++11 | c++11 | |
| c++11 | c++11 | c++11 | c++11 | c++11 | c++11 | c++11 | c++11 | |
| c++11 | c++11 | c++11 | c++11 | c++11 | c++11 | c++11 | c++11 | |
| c++11 | c++11 | c++11 | c++11 | c++11 | c++11 | c++11 | c++11 | |
| c++17 | c++17 | c++17 | c++17 | c++11 (always false) | c++11 (always false) | c++11 (always false) | c++11 (always false) | |
| c++11 | c++11 | c++11 | c++11 | c++11 | c++11 | c++11 | c++11 | |
| c++11 | c++11 | c++11 | c++11 | c++11 | c++11 | c++11 | c++11 | |
| unknown | unknown | unknown | unknown | c++11 (always false) | c++11 (always false) | c++11 (always false) | c++11 (always false) | |
| c++11 | c++11 | c++11 | c++11 | c++11 | c++11 | c++11 | c++11 | |
| c++11 | c++11 | c++11 | c++11 | c++11 | c++11 | c++11 | c++11 | |
| c++11 | c++11 | c++11 | c++11 | c++11 | c++11 | c++11 | c++11 | |
| c++11 | c++11 | c++11 | c++11 | c++11 | c++11 | c++11 | c++11 | |
| c++11 | c++11 | c++11 | c++11 | c++11 | c++11 | c++11 | c++11 | |
| c++11 | c++11 | c++11 | c++11 | c++11 | c++11 | c++11 | c++11 | |
| c++17 | c++17 | c++17 | c++17 | c++11 (no effect) | c++11 (no effect) | c++11 (no effect) | c++11 (no effect) | |
| unknown | unknown | unknown | unknown | c++11 (no effect) | c++11 (no effect) | c++11 (no effect) | c++11 (no effect) | |
| c++11 | c++11 | c++11 | c++11 | c++11 | c++11 | c++11 | c++11 | |
| c++11 | c++11 | c++11 | c++11 | c++11 | c++11 | c++11 | c++11 | 
Table 3. XCode/AppleClang Support
| feature | XCode 8 | XCode 7.3 | XCode 7.2 | XCode 7.1 | XCode 6.4 | 
|---|---|---|---|---|---|
| c++11 | c++11 | c++11 | c++11 | c++11 | |
| c++11 | c++11 | c++11 | c++11 | c++11 | |
| c++11 | c++11 | c++11 | c++11 | c++11 | |
| c++11 | c++11 | c++11 | c++11 | c++11 | |
| c++11 | c++11 | c++11 | c++11 | c++11 | |
| unknown | static_assert fails on instantiation | static_assert fails on instantiation | static_assert fails on instantiation | static_assert fails on instantiation | |
| unknown | static_assert fails on instantiation | static_assert fails on instantiation | static_assert fails on instantiation | static_assert fails on instantiation | |
| c++11 | c++11 | c++11 | c++11 | c++11 | |
| c++11 | c++11 | c++11 | c++11 | c++11 | |
| c++11 | c++11 | c++11 | c++11 | c++11 | |
| c++11 | c++11 | c++11 | c++11 | c++11 | |
| c++11 | c++11 | c++11 | c++11 | c++11 | |
| c++11 | c++11 | c++11 | c++11 | c++11 | |
| c++11 | c++11 | c++11 | c++11 | c++11 | |
| c++11 | c++11 | c++11 | c++11 | c++11 | |
| c++11 | c++11 | c++11 | c++11 | c++11 | |
| c++11 | c++11 | c++11 | c++11 | c++11 | |
| c++11 | c++11 | c++11 | c++11 | c++11 | |
| c++11 | c++11 | c++11 | c++11 | c++11 | |
| c++11 | c++11 | c++11 | c++11 | c++11 | |
| unknown | c++11 (always false) | c++11 (always false) | c++11 (always false) | c++11 (always false) | |
| c++11 | c++11 | c++11 | c++11 | c++11 | |
| c++11 | c++11 | c++11 | c++11 | c++11 | |
| unknown | c++11 (always false) | c++11 (always false) | c++11 (always false) | c++11 (always false) | |
| c++11 | c++11 | c++11 | c++11 | c++11 | |
| c++11 | c++11 | c++11 | c++11 | c++11 | |
| c++11 | c++11 | c++11 | c++11 | c++11 | |
| c++11 | c++11 | c++11 | c++11 | c++11 | |
| c++11 | c++11 | c++11 | c++11 | c++11 | |
| c++11 | c++11 | c++11 | c++11 | c++11 | |
| unknown | c++11 (no effect) | c++11 (no effect) | c++11 (no effect) | c++11 (no effect) | |
| unknown | c++11 (no effect) | c++11 (no effect) | c++11 (no effect) | c++11 (no effect) | |
| c++11 | c++11 | c++11 | c++11 | c++11 | |
| c++11 | c++11 | c++11 | c++11 | c++11 | 
Table 4. Visual Studio Support
| feature | MSVC with Visual Studio 2017 | MSVC with Visual Studio 2015 (latest update) | 
|---|---|---|
| c++11 | c++11 | |
| c++11 | c++11 | |
| c++11 | c++11 | |
| c++11 | c++11 | |
| c++11 | c++11 | |
| static_assert fails on instantiation | static_assert fails on instantiation | |
| static_assert fails on instantiation | static_assert fails on instantiation | |
| c++11 (define BOOST_DISABLE_WIN32 to disable __cdecl on PMF varargs) | c++11 (define BOOST_DISABLE_WIN32 to disable __cdecl on PMF varargs) | |
| c++11 | c++11 | |
| c++11 | c++11 | |
| c++11 | c++11 | |
| c++11 | c++11 | |
| c++11 | c++11 | |
| c++11 | c++11 | |
| c++11 (define BOOST_DISABLE_WIN32 to disable __cdecl on PMF varargs) | c++11 (define BOOST_DISABLE_WIN32 to disable __cdecl on PMF varargs) | |
| c++11 | c++11 | |
| c++11 | c++11 | |
| c++11 | c++11 | |
| c++11 | c++11 (always false for functions that are simultaneously ref and cv-qualified due to compiler bug) | |
| c++11 | c++11 | |
| c++11 (always false) | c++11 (always false) | |
| c++11 | c++11 | |
| c++11 | c++11 | |
| c++11 (always false) | c++11 (always false) | |
| c++11 | c++11 | |
| c++11 | c++11 | |
| c++11 | c++11 | |
| c++11 | c++11 | |
| c++11 | c++11 | |
| c++11 | c++11 | |
| c++11 (no effect) | c++11 (no effect) | |
| c++11 (no effect) | c++11 (no effect) | |
| c++11 (define BOOST_DISABLE_WIN32 to disable __cdecl on PMF varargs) | c++11 (define BOOST_DISABLE_WIN32 to disable __cdecl on PMF varargs) | |
| c++11 | c++11 |