Home | Libraries | People | FAQ | More |
Copyright © 2016, 2017 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 24 template specializations necessary to account for all the function types in C++11:
template<typename T> struct foo; //function type without varargs 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 &&> {}; //function type with varargs 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 &&> {};
Things get even more complicated with member function pointers, function
pointers, function references, function objects, and transaction_safe
/noexcept
.
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 very specific metaprogramming scenarios that can only be solved with this kind of template "spam". Writing and testing these templates is tedious and time consuming, and often requires lots of code duplication.
Boost.CallableTraits
offers a final and decisive library-level
solution to this problem, and removes the need for these specializations
entirely (except for platform-specific calling conventions).
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
is tested on GCC 4.7.4 and later,
Clang 3.5.2 and later, XCode 6.4 and later, 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,
although the 2017 version for Linux does pass a handful of test cases.
Table 1. GCC Support
feature |
GCC 7.1.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 (no abominables) |
c++11 (no abominables) |
|
c++11 |
c++11 |
c++11 |
c++11 |
c++11 (no abominables) |
c++11 (no abominables) |
|
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 |
static_assert fails on instantiation |
static_assert fails on instantiation |
|
c++11 |
c++11 |
c++11 |
c++11 |
c++11 (no abominables) |
c++11 (no abominables) |
|
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) |
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 (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 (no abominables) |
c++11 (no abominables) |
|
c++11 |
c++11 |
c++11 |
c++11 |
c++11 (no abominables) |
c++11 (no abominables) |
|
c++11 |
c++11 |
c++11 |
c++11 |
unknown |
unknown |
|
c++11 |
c++11 |
c++11 |
c++11 |
c++11 (always false) |
c++11 (always false) |
|
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 (always false) |
c++11 (always false) |
|
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++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 (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 (no abominables) |
c++11 (no abominables) |
|
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 (no effect) |
c++11 (no effect) |
|
c++11 |
c++11 |
c++11 |
c++11 |
c++11 (no abominables) |
c++11 (no abominables) |
|
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++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 |
Table 2. LLVM/Clang Support
feature |
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++17 |
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 |
|
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 |
|
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++17 |
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 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 |