function2

Improved alternative to std::function which supports move only types

View project on GitHub

fu2::function an improved drop-in replacement to std::function

Build Status Build status

Provides two improved implementations of std::function:

  • copyable fu2::function
  • move-only fu2::unique_function (capable of holding move only types)

that provide many benefits and improvements over std::function:

  • const, volatile and reference correct (qualifiers are part of the operator() signature).
  • convertible to and from std::function as well as other callable types.
  • adaptable through fu2::function_base (internal capacity, copyable and exception guarantees)
  • overloadable with an arbitrary count of signatures (fu2::function<bool(int), bool(float)>)
  • full allocator support in contrast of std::function which doesn’t provide support anymore
  • covered by unit tests and continuous integration (GCC, Clang and MSVC).
  • header only, just copy and include function.hpp in your project, permissive licensed under boost.

Table of Contents

Documentation

How to use

function2 is implemented in one header (function.hpp), no compilation is required. Just copy the function.hpp header in your project and include it to start. It’s recommended to import the library as git submodule using CMake:

# Shell:
git submodule add https://github.com/Naios/function2.git
# CMake file:
add_subdirectory(function2)
# function2 provides an interface target which makes it's
# headers available to all projects using function2
target_link_libraries(my_project function2)

Use fu2::function as a wrapper for copyable function wrappers and fu2::unique_function for move only types. The standard implementation std::function and fu2::function are convertible to each other, see the chapter converbility of functions for details.

A function wrapper is declared as following:

fu2::function<void(int, float) const>
// Return type ~^   ^     ^     ^
// Arguments ~~~~~~~|~~~~~|     ^
// Qualifier ~~~~~~~~~~~~~~~~~~~|
  • Return type: The return type of the function to wrap.
  • Arguments: The argument types of the function to wrap. Any argument types are allowed.
  • Qualifiers: There are several qualifiers allowed:
    • no qualifier provides ReturnType operator() (Args...)
      • Can be assigned from const and no const objects (mutable lambdas for example).
    • const provides ReturnType operator() (Args...) const
      • Requires that the assigned functor is const callable (won’t work with mutable lambdas),
    • volatile provides ReturnType operator() (Args...) volatile
      • Can only be assigned from volatile qualified functors.
    • const volatile provides ReturnType operator() (Args...) const volatile
      • Same as const and volatile together.
    • Also there is support for r-value functions ReturnType operator() (Args...) &&
      • one-shot functions which are invalidated after the first call.
  • Multiple overloads: The library is capable of providing multiple overloads:
    fu2::function<int(std::vector<int> const&),
                  int(std::set<int> const&) const> fn = [] (auto const& container) {
                    return container.size());
                  };
    

Constructing a function

fu2::function and fu2::unique_function (non copyable) are easy to use:

fu2::function<void() const> fun = [] {
  // ...
};

// fun provides void operator()() const now
fun();

Non copyable unique functions

fu2::unique_function also works with non copyable functors/ lambdas.

fu2::unique_function<bool() const> fun = [ptr = std::make_unique<bool>(true)] {
  return *ptr;
};

// unique functions are move only
fu2::unique_function<bool() const> otherfun = std::move(fun):

otherfun();

Converbility of functions

fu2::function, fu2::unique_function and std::function are convertible to each other when:

  • The return type and parameter type match.
  • The functions are both volatile or not.
  • The functions are const correct:
    • noconst = const
    • const = const
    • noconst = noconst
  • The functions are copyable correct when:
    • unique = unique
    • unique = copyable
    • copyable = copyable
  • The functions are reference correct when:
    • lvalue = lvalue
    • lvalue = rvalue
    • rvalue = rvalue
Cobvertible from \ to fu2::function fu2::unique_function std::function
fu2::function Yes Yes Yes
fu2::unique_function No Yes No
std::function Yes Yes Yes
fu2::function<void()> fun = []{};
// OK
std::function<void()> std_fun = fun;
// OK
fu2::unique_function<void()> un_fun = fun;

// Error (non copyable -> copyable)
fun = un_fun;
// Error (non copyable -> copyable)
fun = un_fun;

Adapt function2

function2 is adaptable through fu2::function_base which allows you to set:

  • Signature: defines the signature of the function.
  • Copyable: defines if the function is copyable or not.
  • Capacity: defines the internal capacity used for sfo optimization.
  • Throwing defines if empty function calls throw an fu2::bad_function_call exception, otherwise std::abort is called.

The following code defines a function with a variadic signature which is copyable and sfo optimization is disabled:

template<typename Signature>
using my_function = fu2::function_base<Signature, 0UL, true>;

The following code defines a non copyable function which just takes 1 argument, and has a huge capacity for internal sfo optimization. Also it must be called as r-value.

template<typename Arg>
using my_consumer = fu2::function_base<void(Arg)&&, 100UL, false>;

// Example
my_consumer<int, float> consumer = [](int, float) { }
std::move(consumer)(44, 1.7363f);

Performance and optimization

Small functor optimization

function2 uses small functor optimization like the most common std::function implementations which means it allocates a small internal capacity to evade heap allocation for small functors.

Smart heap allocation moves the inplace allocated functor automatically to the heap to speed up moving between objects.

It’s possible to disable small functor optimization through setting the internal capacity to 0.

Coverage and runtime checks

Function2 is checked with unit tests and valgrind (for memory leaks), where the unit tests provide coverage for all possible template parameter assignments.

Compatibility

Tested with:

  • Visual Studio 2017+ Update 3
  • Clang 3.8+
  • GCC 5.4+

Every compiler with modern C++14 support should work. function2 only depends on the standard library.

License

function2 is licensed under the very permissive Boost 1.0 License.

Similar implementations

There are similar implementations of a function wrapper:

Also check out the amazing CxxFunctionBenchmark which compares several implementations. ```