VLink  2.0.0
A high-performance communication middleware
functional.h File Reference

Pool-backed type-erased callables: copyable vlink::Function and move-only vlink::MoveFunction. More...

#include <functional>
#include <cstddef>
#include <new>
#include <type_traits>
#include <typeinfo>
#include <utility>
#include "./macros.h"
#include "./memory_pool.h"
Include dependency graph for functional.h:
This graph shows which files directly or indirectly include this file:

Go to the source code of this file.

Classes

struct  vlink::detail::IsStdFunction< TypeT >
 
struct  vlink::detail::IsStdFunction< std::function< SignatureT > >
 
struct  vlink::detail::IsVlinkFunction< TypeT >
 
struct  vlink::detail::IsVlinkFunction< Function< SignatureT, SboSizeT > >
 
struct  vlink::detail::IsVlinkMoveFunction< TypeT >
 
struct  vlink::detail::IsVlinkMoveFunction< MoveFunction< SignatureT, SboSizeT > >
 
class  vlink::Function< ReturnT(ArgsT...), SboSizeT >
 
class  vlink::MoveFunction< ReturnT(ArgsT...), SboSizeT >
 

Namespaces

 
 

Macros

#define VLINK_ENABLE_BASE_FUNCTIONAL
 

Typedefs

template<typename SignatureT >
using vlink::LargeFunction = Function< SignatureT, 256U >
 
template<typename SignatureT >
using vlink::LargeMoveFunction = MoveFunction< SignatureT, 256U >
 
template<typename SignatureT >
using vlink::function = Function< SignatureT >
 
template<typename SignatureT >
using vlink::move_only_function = MoveFunction< SignatureT >
 

Functions

VLINK_EXPORT void vlink::detail::throw_bad_function_call ()
 
template<typename ReturnT , typename... ArgsT, size_t SboSizeT>
void vlink::swap (Function< ReturnT(ArgsT...), SboSizeT > &lhs, Function< ReturnT(ArgsT...), SboSizeT > &rhs) noexcept
 Free-function swap; defers to the member swap of lhs. More...
 
template<typename ReturnT , typename... ArgsT, size_t SboSizeT>
bool vlink::operator== (const Function< ReturnT(ArgsT...), SboSizeT > &cb, std::nullptr_t) noexcept
 Equality with nullptr; true when cb has no stored target. More...
 
template<typename ReturnT , typename... ArgsT, size_t SboSizeT>
bool vlink::operator== (std::nullptr_t, const Function< ReturnT(ArgsT...), SboSizeT > &cb) noexcept
 Commutative overload of operator==(Function, nullptr_t). More...
 
template<typename ReturnT , typename... ArgsT, size_t SboSizeT>
bool vlink::operator!= (const Function< ReturnT(ArgsT...), SboSizeT > &cb, std::nullptr_t) noexcept
 Inequality with nullptr; true when cb stores a target. More...
 
template<typename ReturnT , typename... ArgsT, size_t SboSizeT>
bool vlink::operator!= (std::nullptr_t, const Function< ReturnT(ArgsT...), SboSizeT > &cb) noexcept
 Commutative overload of operator!=(Function, nullptr_t). More...
 
template<typename ReturnT , typename... ArgsT, size_t SboSizeT>
void vlink::swap (MoveFunction< ReturnT(ArgsT...), SboSizeT > &lhs, MoveFunction< ReturnT(ArgsT...), SboSizeT > &rhs) noexcept
 Free-function swap; defers to the member swap of lhs. More...
 
template<typename ReturnT , typename... ArgsT, size_t SboSizeT>
bool vlink::operator== (const MoveFunction< ReturnT(ArgsT...), SboSizeT > &cb, std::nullptr_t) noexcept
 Equality with nullptr; true when cb has no stored target. More...
 
template<typename ReturnT , typename... ArgsT, size_t SboSizeT>
bool vlink::operator== (std::nullptr_t, const MoveFunction< ReturnT(ArgsT...), SboSizeT > &cb) noexcept
 Commutative overload of operator==(MoveFunction, nullptr_t). More...
 
template<typename ReturnT , typename... ArgsT, size_t SboSizeT>
bool vlink::operator!= (const MoveFunction< ReturnT(ArgsT...), SboSizeT > &cb, std::nullptr_t) noexcept
 Inequality with nullptr; true when cb stores a target. More...
 
template<typename ReturnT , typename... ArgsT, size_t SboSizeT>
bool vlink::operator!= (std::nullptr_t, const MoveFunction< ReturnT(ArgsT...), SboSizeT > &cb) noexcept
 Commutative overload of operator!=(MoveFunction, nullptr_t). More...
 

Detailed Description

Pool-backed type-erased callables: copyable vlink::Function and move-only vlink::MoveFunction.

vlink::Function tracks the public surface of std::function while vlink::MoveFunction tracks std::move_only_function. Both store the target in a configurable small-buffer region (64 bytes by default) and spill to vlink::MemoryPool::global_instance when the target does not fit. This eliminates the heap-allocation surprise common to std::function on hot paths while still permitting arbitrarily large closures.

Comparison vs the standard library
Property std::function std::move_only_function vlink::Function vlink::MoveFunction
Copyable yes no yes no
Movable yes yes yes yes
Default SBO size ~16-24 B (impl) ~16-24 B (impl) 64 B (tunable) 64 B (tunable)
Spill allocator ::operator new ::operator new MemoryPool tier MemoryPool tier
Empty-call behaviour throws bad_func UB throws bad_func throws bad_func
RTTI inspection target / type not provided target / type target / type
Storage predicate (kIsInline)
A target FunctorT is held inline only when all three hold simultaneously: sizeof(FunctorT) <= SboSizeT, alignof(FunctorT) <= alignof(std::max_align_t) and FunctorT is nothrow move-constructible. Anything else goes to the heap path through MemoryPool::global_instance; the original sizeof / alignof values are passed back to deallocate so the block returns to its source tier. This mirrors libstdc++'s __stored_locally rule.
SBO sizing
Both wrappers expose SboSizeT as the second non-type template argument. Convenience aliases vlink::LargeFunction / vlink::LargeMoveFunction pick 256 bytes; the bound SboSizeT >= sizeof(void*) is enforced by static_assert so the heap-fallback pointer always fits in the SBO. Distinct SboSizeT instantiations are distinct types but may still convert through the generic functor path – a Function<Sig, 64> serves as a regular callable target for Function<Sig, 256>.
Empty-state propagation
Constructing or assigning from any of these sources yields an empty wrapper with no vtable and no allocation:
  • an empty function-wrapper source (std::function, vlink::Function, and – when the target is MoveFunctionstd::move_only_function / vlink::MoveFunction);
  • a null raw function pointer;
  • a null pointer-to-member.
Exception safety
Copy operations use copy-and-swap and offer the strong guarantee; swap, move construction and move assignment are noexcept. The kIsInline predicate guarantees that inline-path moves never throw, while the heap path simply moves a pointer. Inline construction uses placement-new directly into the SBO; heap construction is wrapped in try / catch so the pool block is returned on a constructor exception.
Object-lifetime model
Inline targets are placement-new'd into the SBO. Heap storage holds a FunctorT*; the pointer object's lifetime is started explicitly via ::new(dst) FunctorT*(p) at every site that introduces a new slot, avoiding reliance on the C++20 [intro.object]/10 implicit-object rule and keeping the code well-defined under C++17. Subsequent accesses go through std::launder to satisfy [basic.life]/8.
RTTI surface
When __cpp_rtti is defined both wrappers expose target_type() and target<FunctorT>() with std::function semantics. This is an extension over std::move_only_function which omits target inspection.
Example
vlink::Function<int(int)> f = [](int x) { return x * 2; };
int y = f(21); // == 42
auto pkg = std::packaged_task<int()>([] { return 7; });
vlink::MoveFunction<void()> mf = std::move(pkg); // move-only target
mf(); // runs the task
vlink::LargeMoveFunction<void()> heavy = ...heavy capture...; // 256-byte SBO
Note
Only the unqualified ReturnT(ArgsT...) signature is specialised; cv / ref / noexcept-qualified function types resolve to the undefined primary template (hard error). This header self-defines VLINK_ENABLE_BASE_FUNCTIONAL when absent so normal builds always select the VLink implementation; the standard-library alias block at the bottom is not exposed via a compile-command opt-out.

Macro Definition Documentation

◆ VLINK_ENABLE_BASE_FUNCTIONAL

#define VLINK_ENABLE_BASE_FUNCTIONAL