Function traits

Traits for function type inspection and assembly. For traits specific to member functions, see Member function traits.

This is a complete analog of Boost.CallableTraits (excluding some niche features listed in the Design section) implemented with much less code, so expect reduced compilation times!

Reference

Concepts

Free functions are typically used in examples for simplicify, but most traits support all the following function concepts, including their const and reference-qualified versions:

#include <actl/functional/traits/FreeFunction.hpp>
template<typename T>
concept FreeFunction

Concept of a free function, for example, float(int, int), which is the type of a function like this

float divide(int x, int y);

Function reference like float(&)(int, int) and function pointer like float(*)(int, int) also satisfy the concept, because they also support function call syntax.

Note

It’s impossible to declare a variable of a function type, it has to be either a reference or a pointer.

#include <actl/functional/traits/MemberFunction.hpp>
template<typename T>
concept MemberFunction

Concept of a class member function pointer, often called a method.

For example, for the following class

class Class {
    bool member(int) const;
};
the member function type can be acquired by decltype(&Class::member), which is bool (Class::*)(int) const.

Important properties of a member function:

  • Its type is always a pointer, there’s no corresponding value type.

  • The first parameter is always the enclosing class.

  • To support function call syntax it has to be wrapped into std::mem_fn, otherwise the following call syntax has to be used

    auto member = &Class::member;
    Class instance;
    (instance.*member)(1);
    

#include <actl/functional/traits/FunctionObject.hpp>
template<typename T>
concept FunctionObject

Concept of a function object, that is a type with a non-overloaded operator(), for example

template<typename T>
struct plus {
    constexpr T operator()(T const& l, T const& r) const {
        return l + r;
    }
};
Non-template lambdas are also function objects.

#include <actl/functional/traits/Callable.hpp>
template<typename Function, typename ...Args>
concept Callable

Concept of a type that can be called as a function with the given Args, that is as fn(args...).

Not all the types that are considered to be functions satisfy this concept. In particular, member functions are called using special syntax

(instance.*member)(args...);

C++ provides std::invoke as a unified way to call all the functions. However, we highly recommend not to use it and instead wrap member functions into std::mem_fn when they need to be called in a unified way. That’s because std::invoke adds 1 or 2 nested function calls for every function (not just member functions that are rarely called this way), which in debug mode require more manual actions to step into, and overall decrease performance if not inlined. This problem isn’t as critical because it’s specific to debug builds. But there’s a simple std::mem_fn solution to avoid it completely.

Note

Some libraries support pointers to data members similarly to pointers to member functions. Data members are obviously not functions, but they are indeed related and this makes sense, for example, for std::mem_fn. However, the result of applying a pointer to data member to a class object inherits the qualifiers of this object. So, it defines not a single function, but an overload set, which is not supported by the library as of now.

Traits

#include <actl/functional/traits/function_traits.hpp>
template<typename T>
struct function_traits

If T is a function, then the following members are specified:

enum class ac::function_category

Values:

enumerator free
enumerator member
enumerator object

Return type

#include <actl/functional/traits/return.hpp>
template<typename Function>
using ac::return_t = typename function_traits<Function>::return_type
template<typename Function>
bool ac::returns_void_v = std::is_same_v<return_t<Function>, void>
template<typename T, typename ReturnType>
using ac::with_return_type_t = typename with_return_type<T, ReturnType>::type

Resulting type is different for different input types T:

  • If T is a function, then it’s a function with the given return type, but otherwise identical to T.

  • If T is an ac::type_array template instantiaion, then it’s a free function with the given return type and T as its parameters list.

Trait name

Example 1

Example 2

Input type Fn

float(int, int) noexcept

void(const char*, ...)

return_t

float

void

returns_void_v

false

true

with_return_type_t<Fn, int>

int(int, int) noexcept

int(const char*, ...)

Parameters

#include <actl/functional/traits/parameters.hpp>
template<typename Function>
using ac::parameters_t = typename function_traits<Function>::parameters_type

Parameter types of a function as ac::type_array.

Note

Arguments name is sometimes incorrectly used instead of parameters. Arguments are the values passed to the function when called, so they are not a property of the function itself.

template<typename Function>
size_t ac::arity_v = parameters_t<Function>::length

Arity of the function, that is its parameter count excluding the variadic arguments.

template<typename Function, size_t Index>
using ac::parameter_at_t = at_t<parameters_t<Function>, Index>
template<typename Function>
using ac::unique_parameters_t = typename unique_parameters<Function>::type

Member functions with either empty or & qualifier both take the class parameter by reference. To disambiguate these cases, unique parameter types contain class parameter with the same qualifiers as the member function, without the implicitly added reference.

Trait name

Example 1

Example 2

Input type Fn

float(int, int) noexcept

void(const char*, ...)

parameters_t

ac::type_array<int, int>

ac::type_array<const char*>

arity_v

2

1

parameter_at_t<Fn, 0>

int

const char*

Variadic arguments

#include <actl/functional/traits/variadic_arguments.hpp>
template<typename Function>
bool ac::accepts_variadic_arguments_v = function_traits<Function>::accepts_variadic_arguments

Checks if the function accepts variadic arguments, see https://en.cppreference.com/w/cpp/language/variadic_arguments.html

template<typename Function>
using ac::add_variadic_arguments_t = typename add_variadic_arguments<Function>::type
template<typename Function>
using ac::remove_variadic_arguments_t = typename remove_variadic_arguments<Function>::type

Trait name

Example 1

Example 2

Input type

float(int, int) noexcept

void(const char*, ...)

accepts_variadic_arguments_v

false

true

add_variadic_arguments_t

float(int, int, ...) noexcept

void(const char*, ...)

remove_variadic_arguments_t

float(int, int) noexcept

void(const char*)

noexcept

#include <actl/functional/traits/noexcept.hpp>
template<typename Function>
bool ac::is_noexcept_v = function_traits<Function>::is_noexcept

Checks if the function is noexcept, see https://en.cppreference.com/w/cpp/language/noexcept_spec.html

template<typename Function>
using ac::add_noexcept_t = typename add_noexcept<Function>::type
template<typename Function>
using ac::remove_noexcept_t = typename remove_noexcept<Function>::type

Trait name

Example 1

Example 2

Input type

float(int, int) noexcept

void(const char*, ...)

is_noexcept_v

true

false

add_noexcept_t

float(int, int) noexcept

void(const char*, ...) noexcept

remove_noexcept_t

float(int, int)

void(const char*, ...)

Function type assembly

#include <actl/functional/traits/assemble_function.hpp>
template<function_category Category, typename Return, typename ParametersList, bool AcceptsVArgs, bool IsNoexcept>
using ac::assemble_function_t = typename assemble_function<Category, Return, ParametersList, AcceptsVArgs, IsNoexcept>::type

Assembles a function type with the given properties.

template<typename Function>
using ac::as_free_function_t = typename as_free_function<Function>::type

Free function with the same parameters and return type as Function.

See tests at tests/functional/traits/

Design

The following reference was used at first, but it was heavily expanded.

Qualified free functions

Boost.CallableTraits supports qualified free function types like

Return(Args...) const volatile &

These types indeed exist in C++, but declaring a function like void f() const {} results in a compilation error. This is confirmed by cppreference:

cv - const/volatile qualification, only allowed in non-static member function declarations

ref - (since C++11) ref-qualification, only allowed in non-static member function declarations

It’s not clear why such types exist if they cannot be used in a declaration. For this reason, we don’t support them.

transaction_safe

transaction_safe keyword from Transactional Memory TS isn’t supported, because it’s still experimental for a long time without notable progress.

We prefer not to add extra complexity to the library to support non-standard extensions.