5caf4ef842
The function2 library is a header-only library which provides support for defining move-only function types, similar to the proposed std::move_only_function in C++23, but with support for additional customization. This appears to be the first time we've vendored code using the boost license, so I've added it to license.html and moz_yaml.py, and have requested review to ensure it is OK to use code with this license. Differential Revision: https://phabricator.services.mozilla.com/D145690 |
||
---|---|---|
.. | ||
include/function2 | ||
.clang-format | ||
.editorconfig | ||
.travis.yml | ||
CMakeLists.txt | ||
Findfunction2.cmake | ||
LICENSE.txt | ||
Readme.md | ||
appveyor.yml | ||
conanfile.py | ||
moz.yaml |
Readme.md
fu2::function an improved drop-in replacement to std::function
Provides improved implementations of std::function
:
- copyable
fu2::function
- move-only
fu2::unique_function
(capable of holding move only types) - non-owning
fu2::function_view
(capable of referencing callables in a non owning way)
that provide many benefits and improvements over std::function
:
- const, volatile, reference and noexcept 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 to
std::function
, which doesn't provide support anymore - covered by many unit tests and continuous integration services (GCC, Clang and MSVC)
- header only, just copy and include
function.hpp
in your project - permissively licensed under the boost license
Table of Contents
- Documentation
- Performance and optimization
- Coverage and runtime checks
- Compatibility
- License
- Similar implementations
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 convertibility of functions for details.
A function wrapper is declared as following:
fu2::function<void(int, float) const>
// Return type ~^ ^ ^ ^
// Parameters ~~~~~|~~~~~| ^
// 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.
- r-value (one-shot) functions
ReturnType operator() (Args...) &&
- one-shot functions which are invalidated after the first call (can be mixed with
const
,volatile
andnoexcept
). Can only wrap callable objects which call operator is also qualified as&&
(r-value callable). Normal (C) functions are considered to be r-value callable by default.
- one-shot functions which are invalidated after the first call (can be mixed with
- noexcept functions
ReturnType operator() (Args...) noexcept
- such functions are guaranteed not to throw an exception (can be mixed with
const
,volatile
and&&
). Can only wrap functions or callable objects which call operator is also qualified asnoexcept
. Requires enabled C++17 compilation to work (support is detected automatically). Empty function calls to such a wrapped function will lead to a call tostd::abort
regardless the wrapper is configured to support exceptions or not (see adapt function2).
- such functions are guaranteed not to throw an exception (can be mixed with
- no qualifier provides
- 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();
Non owning functions
A fu2::function_view
can be used to create a non owning view on a persistent object. Note that the view is only valid as long as the object lives.
auto callable = [ptr = std::make_unique<bool>(true)] {
return *ptr;
};
fu2::function_view<bool() const> view(callable);
Convertibility 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
- The functions are
noexcept
correct when:callable = callable
callable = noexcept callable
noexcept callable = noexcept callable
Convertibility 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:
- IsOwning: defines whether the function owns its contained object
- Copyable: defines if the function is copyable or not.
- Capacity: defines the internal capacity used for sfo optimization:
struct my_capacity {
static constexpr std::size_t capacity = sizeof(my_type);
static constexpr std::size_t alignment = alignof(my_type);
};
- IsThrowing defines if empty function calls throw an
fu2::bad_function_call
exception, otherwisestd::abort
is called. - HasStrongExceptGuarantee defines whether the strong exception guarantees shall be met.
- Signatures: defines the signatures of the function.
The following code defines an owning function with a variadic signature which is copyable and sfo optimization is disabled:
template<typename Signature>
using my_function = fu2::function_base<true, true, fu2::capacity_none, true, false, Signature>;
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<true, false, fu2::capacity_fixed<100U>,
true, false, void(Arg)&&>;
// 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:
- pmed/fixed_size_function
- stdex::function - A multi-signature function implementation.
- multifunction - Example from Boost.TypeErasure, another multi-signature function.
- std::function - Standard.
- boost::function - The one from Boost.
- func::function - From this blog.
- generic::delegate - Fast delegate in C++11, also see here.
- ssvu::FastFunc - Another Don Clugston's FastDelegate, as shown here.
- cxx_function::function - By David Krauss
Also check out the amazing CxxFunctionBenchmark which compares several implementations.