![]() |
Home | Libraries | People | FAQ | More |
Copyright © 2016 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
#include <callable_traits/callable_traits.hpp> #include <type_traits> #include <tuple> using std::is_same; using std::tuple; using namespace callable_traits; struct number { int value; auto add(int n) const { return value + n; } }; using pmf = decltype(&number::add);
Manipulate member functions pointers with ease:
static_assert(is_same< remove_member_const_t<pmf>, int(number::*)(int) >{}, ""); static_assert(is_same< add_member_volatile_t<pmf>, int(number::*)(int) const volatile >{}, ""); static_assert(is_same< parent_class_of_t<pmf>, number >{}, ""); static_assert(is_same< remove_member_pointer_t<pmf>, int (int) const >{}, "");
INVOKE-aware metafunctions:
static_assert(is_same< args_t<pmf>, tuple<const number&, int> >{}, ""); static_assert(is_same< arg_at_t<0, pmf>, const number& >{}, ""); static_assert(is_same< arg_at_t<1, pmf>, int >{}, ""); static_assert(is_same< result_of_t<pmf>, int >{}, ""); static_assert(is_same< function_type_t<pmf>, int(const number&, int) >{}, ""); static_assert(is_same< qualified_parent_class_of_t<pmf>, const number& >{}, "");
Here are a few other trait examples:
static_assert(is_const_member<pmf>{}, ""); static_assert(!is_volatile_member<pmf>{}, ""); static_assert(!has_void_return<pmf>{}, ""); static_assert(!has_varargs<pmf>{}, "");
You can use CallableTraits to manipulate parameter lists
(not defined in terms of INVOKE, since that wouldn't make sense here):
using pmf_2 = push_back_args_t<pmf, char, short, long>; static_assert(is_same< pmf_2, int(number::*)(int, char, short, long) const >{}, ""); static_assert(is_same< pop_front_args_t<pmf_2>, int(number::*)(char, short, long) const >{}, ""); static_assert(is_same< insert_args_t<2, pmf_2, short*, long*>, int(number::*)(int, char, short*, long*, short, long) const >{}, ""); static_assert(is_same< replace_args_t<2, pmf_2, short*, long*>, int(number::*)(int, char, short*, long*) const >{}, ""); static_assert(is_same< remove_args_t<2, pmf_2>, int(number::*)(int, char, long) const >{}, ""); static_assert(is_same< clear_args_t<pmf_2>, int(number::*)() const >{}, ""); static_assert(is_same< add_varargs_t<pmf_2>, int(number::*)(int, char, short, long, ...) const >{}, "");
CallableTraits is a C++11/14/17 header-only library for
the inspection, synthesis, and decomposition of callable types. From const volatile && to container-like manipulation of
parameter lists, CallableTraits provides all the tools you
need to rid your codebase of function type specializations. CallableTraits
offers a comprehensive, fine-grained assortment of traits and metafunctions
for building and ripping apart C++'s most complicated and obscure types with
ease. CallableTraits fills the gaps where existing library
solutions fall short, aiming to be the "complete type manipulation facility
for function types" mentioned in the last section of p0172,
the C++17 proposal regarding "abominable function types". CallableTraits
currently supports GCC 4.8 and later, Clang 3.5 and later, AppleClang from
XCode 6.3 and later, and Visual Studio 2015.
CallableTraits is header-only, and does not depend on any
headers outside the standard library.
CallableTraits is currently hosted at GitHub.
CallableTraits is not a Boost library.
This documentation will be most beneficial to readers who posess a basic understanding of the following C++ features:
INVOKE
rules
operator()
“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 class template below, which defines all 48 template specializations necessary to account for all valid function types and member function pointer types in C++11 and C++14:
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 &&> {}; //member function pointer type without varargs template<class Return, class T, class... Args> struct foo<Return(T::*)(Args...)> {}; template<class Return, class T, class... Args> struct foo<Return(T::*)(Args...) &> {}; template<class Return, class T, class... Args> struct foo<Return(T::*)(Args...) &&> {}; template<class Return, class T, class... Args> struct foo<Return(T::*)(Args...) const> {}; template<class Return, class T, class... Args> struct foo<Return(T::*)(Args...) const &> {}; template<class Return, class T, class... Args> struct foo<Return(T::*)(Args...) const &&> {}; template<class Return, class T, class... Args> struct foo<Return(T::*)(Args...) volatile> {}; template<class Return, class T, class... Args> struct foo<Return(T::*)(Args...) volatile &> {}; template<class Return, class T, class... Args> struct foo<Return(T::*)(Args...) volatile &&> {}; template<class Return, class T, class... Args> struct foo<Return(T::*)(Args...) const volatile> {}; template<class Return, class T, class... Args> struct foo<Return(T::*)(Args...) const volatile &> {}; template<class Return, class T, class... Args> struct foo<Return(T::*)(Args...) const volatile &&> {}; //member function pointer type with varargs template<class Return, class T, class... Args> struct foo<Return(T::*)(Args..., ...)> {}; template<class Return, class T, class... Args> struct foo<Return(T::*)(Args..., ...) &> {}; template<class Return, class T, class... Args> struct foo<Return(T::*)(Args..., ...) &&> {}; template<class Return, class T, class... Args> struct foo<Return(T::*)(Args..., ...) const> {}; template<class Return, class T, class... Args> struct foo<Return(T::*)(Args..., ...) const &> {}; template<class Return, class T, class... Args> struct foo<Return(T::*)(Args..., ...) const &&> {}; template<class Return, class T, class... Args> struct foo<Return(T::*)(Args..., ...) volatile> {}; template<class Return, class T, class... Args> struct foo<Return(T::*)(Args..., ...) volatile &> {}; template<class Return, class T, class... Args> struct foo<Return(T::*)(Args..., ...) volatile &&> {}; template<class Return, class T, class... Args> struct foo<Return(T::*)(Args..., ...) const volatile> {}; template<class Return, class T, class... Args> struct foo<Return(T::*)(Args..., ...) const volatile &> {}; template<class Return, class T, class... Args> struct foo<Return(T::*)(Args..., ...) const volatile &&> {};
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 such template "spam". While these use cases
are indeed rare, the writing and testing of these templates is incredibly
tedious and time consuming. On this premise, CallableTraits
offers a final and decisive library-level solution, so that authors of generic
code will never again need to write these
specializations, for any reason.
Template specializations like those in the code snippet above still
do not account for function pointers, function references, function objects/lambdas,
or calling conventions. CallableTraits goes the extra
mile by accounting for all of them.
![]() |
Note |
|---|---|
The upcoming ISO standard for C++17 includes a change
to the core language which adds the |
The use cases for CallableTraits are closely related to
those of Boost.FunctionTypes.
Here are some reasons why you might prefer CallableTraits:
Boost.FunctionTypes is tightly coupled to Boost.MPL
sequences, while CallableTraits generally takes a
lower-level approach. No knowledge of MPL terminology is needed to use
CallableTraits.
Boost.TypeTraits
and <type_traits>. CallableTraits
gives callable types similar attention, without additional metaprogramming
dependencies.
CallableTraits aims to eliminate function type template
specializations. Boost.FunctionTypes does not.
CallableTraits targets C++11/14/17 features. Boost.FunctionTypes
does not.
Boost.FunctionTypes interface relies heavily on
tag types. CallableTraits does not.
Boost.FunctionTypes is a good tool for projects already
dependent on the MPL, which must also support very old compilers. However,
the Boost.FunctionTypes interface is unpleasant. It relies
heavily on both the MPL and tag types, for problems that are more simply
solved with neither. Using Boost.FunctionTypes requires
an understanding of the library's "big picture."
CallableTraits borrows and extends much of the functionality
found in Boost.FunctionTypes, re-packaging it in a more
accessible type_traits-style
interface. There is nothing inherently wrong with Boost.FunctionTypes,
but an MPL sequence-based solution with no C++11/14/17 support should not
be the only library option for inspecting and manipulating callable types.