C++ Variadic Template Bundlers

Detailed Doxygen Documentation.

Several flavors of variadic template classes for creating a single handle to multiple components are provided. In general, these components all have identical interfaces and the vast majority of their member functions accept any and all arguments. These member functions will take the given arguments and attempt to invoke the member function of the component with a similar name, e.g. component_tuple<wall_clock, cpu_clock>::start() will attempt to invoke wall_clock::start() and cpu_clock::start(). When arguments are provided, these bundlers will go through a series of checks at compile-time:

  1. Attempt to find the component member function that accepts those exact set of arguments

  2. Attempt to find the component member function which accepts a subset of those arguments (requires type-trait configuration)

  3. Attempt to find the component member function which accepts no arguments

  4. Accept the component does not have the member function and invoke no member function

In the event, this workflow require specialization for a particular component, a custom specialization of the struct in the tim::operation namespace can be written.

Static Polymorphic Base Classes

template<typename ...Tp>
class bundle

Static polymorphic base class for component bundlers.

template<typename ...Types>
class auto_base_bundle

Static polymorphic base class for automatic start/stop bundlers.

template<typename Tag, typename BundleT, typename TupleT>
class tim::bundle<Tag, BundleT, TupleT> : public tim::api_bundle<Tag, TupleT::available_type>

Example: bundle<Tag, component_bundle<Foo>, mixed_wrapper_types<concat<Bar, Baz>>> will use Tag + trait::is_available<Tag> or trait::runtime_available<Tag> to disable this bundle at compile-time or run-time, respectively. It will covert component_bundle<Foo> to component_bundle<Foo, Bar, Baz> for purposes of function signatures and it will instantiate std::tuple<...> depending on the compile-time availability of Bar and Baz. mixed_wrapper_types is a dummy type with the appropriate aliases to perform these conversions. Here is a theoretical implementation of mixed_wrapper_types (which supports allocating components on the stack and the heap):

template <typename... T>
struct heap_wrapper_types
{
    TIMEMORY_DELETED_OBJECT(heap_wrapper_types)

    /// the set of types, unaltered, in a type_list
    using type_list_type = type_list<T...>;

    /// the set of types without any pointers
    using reference_type = type_list<std::remove_pointer_t<T>...>;

    /// type list of the available types
    using available_type = type_list_t<reference_type>;

    /// the original bundle type
    template <typename BundleT>
    using this_type = convert_t<type_list<T...>, BundleT>;

    /// the type after available_t<concat<...>>
    template <typename BundleT>
    using type = convert_t<available_type, BundleT>;

    /// conversion to equivalent wrapper requiring explicit start/stop
    template <typename BundleT>
    using component_type = convert_t<type_list<T...>, BundleT>;

    /// conversion to equivalent wrapper which automatically starts/stops
    template <typename BundleT>
    using auto_type = concepts::auto_type_t<convert_t<type_list_type, BundleT>>;

    /// the valid types to instantiate in a tuple
    template <typename ApiT = TIMEMORY_API>
    using data_type = conditional_t<
        trait::is_available<ApiT>::value,
        convert_t<add_pointer_if_not_t<non_placeholder_t<non_quirk_t<type_list_t<T...>>>>,
                  std::tuple<>>,
        std::tuple<>>;
};
tparam Tag

API tag type, e.g. TIMEMORY_API

tparam BundleT

The empty or empty + tag derived type.

tparam TupleT

The set of components wrapped in a type which provides the appropriate aliases.

Public Functions

this_type &push()

tells each component to push itself into the call-stack hierarchy

this_type &pop()

tells each component to pop itself off of the call-stack hierarchy

template<typename ...Tp>
this_type &push(mpl::piecewise_select<Tp...>)

selective push

template<typename ...Tp>
this_type &push(mpl::piecewise_ignore<Tp...>)

selective push

template<typename ...Tp>
this_type &push(mpl::piecewise_select<Tp...>, scope::config)

selective push with scope configuration

template<typename ...Tp>
this_type &push(mpl::piecewise_ignore<Tp...>, scope::config)

selective push with scope configuration

template<typename ...Tp>
this_type &pop(mpl::piecewise_select<Tp...>)

selective pop

template<typename ...Tp>
this_type &pop(mpl::piecewise_ignore<Tp...>)

selective pop

template<typename ...Args>
this_type &measure(Args&&...)

requests each component record a measurment

template<typename ...Args>
this_type &sample(Args&&...)

requests each component take a sample (if supported)

template<typename ...Args>
this_type &start(Args&&...)

invokes start on all the components

template<typename ...Args>
this_type &stop(Args&&...)

invokes stop on all the components

template<typename ...Args>
this_type &record(Args&&...)

requests each component perform a measurement

template<typename ...Args>
this_type &reset(Args&&...)

invokes reset member function on all the components

template<typename ...Args>
this_type &start(mpl::lightweight, Args&&...)

variant of start() which excludes push()

template<typename ...Args>
this_type &stop(mpl::lightweight, Args&&...)

variant of stop() which excludes pop()

template<typename ...Tp, typename ...Args>
this_type &start(mpl::piecewise_select<Tp...>, Args&&...)

variant of start() which only gets applied to Tp types

template<typename ...Tp, typename ...Args>
this_type &start(mpl::piecewise_ignore<Tp...>, Args&&...)

variant of start() which gets applied to non-Tp types

template<typename ...Tp, typename ...Args>
this_type &stop(mpl::piecewise_select<Tp...>, Args&&...)

variant of stop() which only gets applied to Tp types

template<typename ...Tp, typename ...Args>
this_type &stop(mpl::piecewise_ignore<Tp...>, Args&&...)

variant of stop() which gets applied to non-Tp types

uint64_t count()

number of objects that will be performing measurements

template<typename FuncT, typename ...Args>
decltype(auto) execute(FuncT &&func, Args&&... args)

when chaining together operations, this function enables executing a function inside the chain

template<typename ...Args>
this_type &construct(Args&&... _args)

construct the objects that have constructors with matching arguments

template<typename ...Args>
this_type &assemble(Args&&... _args)

provide preliminary info to the objects with matching arguments. This is typically used to notify a component that it has been bundled alongside another component that it can extract data from.

template<typename ...Args>
this_type &derive(Args&&... _args)

provide conclusive info to the objects with matching arguments. This is typically used by components to extract data from another component it has been bundled alongside, e.g. the cpu_util component can extract data from tim::component::wall_clock and tim::component::cpu_clock

template<typename ...Args>
this_type &mark(Args&&... _args)

mark an atomic event

template<typename ...Args>
this_type &mark_begin(Args&&... _args)

mark a beginning position in the execution (typically used by asynchronous structures)

template<typename ...Args>
this_type &mark_end(Args&&... _args)

mark a beginning position in the execution (typically used by asynchronous structures)

template<typename ...Args>
this_type &store(Args&&... _args)

store a value

template<typename ...Args>
this_type &audit(Args&&... _args)

allow the components to inspect the incoming arguments before start or out-going return value before returning (typically using in GOTCHA components)

template<typename ...Args>
this_type &add_secondary(Args&&... _args)

perform an add_secondary operation. This operation allows components to add additional entries to storage which are their direct descendant

template<typename ...Args>
this_type &update_statistics(Args&&... _args)

perform an add_secondary operation. This operation allows components to add additional entries to storage which are their direct descendant

template<template<typename> class OpT, typename ...Args>
this_type &invoke(Args&&... _args)

generic member function for invoking user-provided operations

Template Parameters

OpT – Operation struct

template<template<typename> class OpT, typename ...Tp, typename ...Args>
this_type &invoke(mpl::piecewise_select<Tp...>, Args&&... _args)

generic member function for invoking user-provided operations on a specific set of component types

Template Parameters

OpT – Operation struct

template<template<typename> class OpT, typename ...Tp, typename ...Args>
this_type &invoke(mpl::piecewise_ignore<Tp...>, Args&&... _args)

generic member function for invoking user-provided operations on all types that are not listed

Template Parameters

OpT – Operation struct

template<typename ...Args>
auto get(Args&&...) const

returns a tuple of invoking get() on all the components

template<typename ...Args>
auto get_labeled(Args&&...) const

returns a tuple of the component label + invoking get() on all the components

data_type &data()

returns a reference to the underlying tuple of components

const data_type &data() const

returns a const reference to the underlying tuple of components

template<typename U>
decltype(auto) get()

get a component from the bundle

template<typename U>
decltype(auto) get() const

get a component from the bundle

template<typename U, typename FuncT>
decltype(auto) get(FuncT&&)

get a component from the bundle and apply function if the pointer is valid

template<typename U, typename FuncT>
decltype(auto) get(FuncT&&) const

get a component from the bundle

this_type &get(void *&ptr, hash_value_t _hash) const

performs an opaque search. Opaque searches are generally provided by user_bundles with a functor such as this:

auto _get = [](void* v_this, void*& ptr, size_t _hash) {
{
    if(!ptr && v_this && _hash == typeid_hash<Tp>())
    {
        Tp* _this = static_cast<Tp*>(v_this);
        _this->get(ptr, _hash);
    }
    return ptr;
};

And the component provides this function:

template <typename Tp, typename Value>
void
base<Tp, Value>::get(void*& ptr, size_t _hash) const
{
    if(!ptr && _hash == typeid_hash<Tp>())
        ptr = reinterpret_cast<void*>(const_cast<base_type*>(this));
}

template<typename U>
auto get_component(enable_if_t<trait::is_available<remove_pointer_decay_t<U>>::value && is_one_of<remove_pointer_decay_t<U>, data_type>::value, int> = 0)

this is a simple alternative to get<T>() when used from SFINAE in operation namespace which has a struct get also templated. Usage there can cause error with older compilers

template<typename U>
auto &get_reference(enable_if_t<trait::is_available<remove_pointer_decay_t<U>>::value && is_one_of<remove_pointer_decay_t<U>, data_type>::value, int> = 0)

returns a reference from a stack component instead of a pointer

template<typename U>
auto &get_reference(enable_if_t<trait::is_available<remove_pointer_decay_t<U>>::value && is_one_of<remove_pointer_decay_t<U>*, data_type>::value, int> = 0)

returns a reference from a heap component instead of a pointer

template<typename U, typename T = remove_pointer_decay_t<U>, typename ...Args>
inline enable_if_t<will_heap_init<T>() && !will_opaque_init<T>(), bool> init(Args&&... _args, enable_if_t<is_constructible<T, Args...>(), int> = 0)

create an optional type that is in variadic list AND is available AND accepts arguments

template<typename U, typename T = remove_pointer_decay_t<U>, typename ...Args>
inline enable_if_t<will_heap_init<T>() && !will_opaque_init<T>(), bool> init(Args&&..., enable_if_t<!is_constructible<T, Args...>() && is_default_constructible<T>(), long> = 0)

create an optional type that is in variadic list AND is available but is not constructible with provided arguments

template<typename U, typename T = remove_pointer_decay_t<U>, typename ...Args>
inline bool init(Args&&... _args, enable_if_t<can_stack_init<T>(), int> = 0)

try to re-create a stack object with provided arguments

template<typename U, typename T = remove_pointer_decay_t<U>, typename ...Args>
inline bool init(Args&&..., enable_if_t<will_opaque_init<T>(), long> = 0)

if a type is not in variadic list but a tim::component::user_bundle is available, add it in there

template<typename U, typename T = remove_pointer_decay_t<U>, typename ...Args>
inline enable_if_t<!trait::is_available<T>::value || !(is_one_of<T*, data_type>::value || is_one_of<T, data_type>::value || bundle_type::has_user_bundle_v), bool> init(Args&&...)

do nothing if type not available, not one of the variadic types, and there is no user bundle available

template<typename ...T, typename ...Args>
std::array<bool, sizeof...(T)> initialize(Args&&... args)

variadic initialization

Template Parameters
  • T – components to initialize

  • Args – arguments to pass to the construction of the component

template<typename ...Tail>
this_type &disable()

delete any optional types currently allocated

template<typename T, typename Func, typename ...Args, enable_if_t<is_one_of<T, data_type>::value, int> = 0>
inline this_type &type_apply(Func &&_func, Args&&... _args)

apply a member function to a stack type that is in variadic list AND is available

template<typename T, typename Func, typename ...Args, enable_if_t<trait::is_available<T>::value, int> = 0>
this_type &type_apply(Func &&_func, Args&&... _args)

apply a member function to either a heap type or a type that is in a user_bundle

template<typename T, typename Func, typename ...Args, enable_if_t<!trait::is_available<T>::value, int> = 0>
this_type &type_apply(Func&&, Args&&...)

ignore applying a member function because the type is not present

scope::transient_destructor get_scope_destructor()

returns a stack-object for calling stop

scope::transient_destructor get_scope_destructor(utility::transient_function<void(this_type&)>)

returns a stack-object for calling some member functions when the scope is exited.

Public Static Functions

template<typename U>
static inline constexpr bool is_this_type()

Query whether type matches this_type.

static inline constexpr bool has_user_bundle()

Query at compile-time whether a user_bundle exists in the set of components. user_bundle are more restricted versions of component bundlers but allow runtime insertion of components.

template<typename U>
static inline constexpr bool can_stack_init()

Query at compile-time whether initialization can occur on the stack.

template<typename U>
static inline constexpr bool can_heap_init()

Query at compile-time whether initialization can occur on the heap.

template<typename U>
static inline constexpr bool can_placement_init()

Query at compile-time whether initialization can occur via a placement new. Placement new init allows for the combination of optional initialization without a heap allocation. Not currently available.

template<typename U>
static inline constexpr bool can_init()

Query at compile-time whether the specified type can be initialized.

template<typename U>
static inline constexpr bool will_heap_init()

Query at compile-time whether initialization will occur on the heap. can_heap_init<T>() && !will_heap_init<T>() will indicate that a stack-allocated instance of the same type exists.

template<typename U>
static inline constexpr bool will_opaque_init()

Query at compile-time whether initialization will happen with an opaque wrapper (i.e. via user bundle). In this situation, initialization arguments are ignored and the component will only be initialized with the tim::scope::config of the bundle.

template<typename U, typename ...Args, typename T = remove_pointer_decay_t<U>, enable_if_t<trait::is_available<T>::value> = 0>
static inline constexpr bool is_constructible()

Query at compile-time whether type can be constructed with the given argument types. if type is not available, then it probably won’t be defined, so this places the trait::is_available<T> check in the template parameters and the std::is_constructible<T, ...> in the body to avoid undefined behavior.

template<typename U, typename ...Args, typename T = remove_pointer_decay_t<U>, enable_if_t<!trait::is_available<T>::value> = 0>
static inline constexpr bool is_constructible()

Return false if the type is not available.

template<typename U, typename T = remove_pointer_decay_t<U>, enable_if_t<trait::is_available<T>::value> = 0>
static inline constexpr bool is_default_constructible()

Query at compile-time whether type supports default construction. If type is not available, then it probably won’t be defined, so this places the trait::is_available<T> check in the template parameters and the std::is_constructible<T, ...> in the body to avoid undefined behavior.

template<typename U, typename T = remove_pointer_decay_t<U>, enable_if_t<!trait::is_available<T>::value> = 0>
static inline constexpr bool is_default_constructible()

Return false if the type is not available.

static void init_storage()

requests the component initialize their storage

static constexpr uint64_t fixed_count()

query the number of (compile-time) fixed components

static constexpr uint64_t optional_count()

query the number of (run-time) optional components

template<typename Tag, typename CompT, typename BundleT>
class tim::auto_base_bundle<Tag, CompT, BundleT> : public tim::concepts::wrapper, public tim::concepts::variadic, public tim::concepts::auto_wrapper, public tim::concepts::tagged

Example: auto_base_bundle<Tag, component_bundle<Tag>, auto_bundle<Tag, Types...>> will use Tag + trait::is_available<Tag> or trait::runtime_available<Tag> to disable this bundle at compile-time or run-time, respectively. It will wrap auto-start/stop around component_bundle<Tag, Types...> and use auto_bundle<Tag, Types...> for function signatures.

tparam Tag

Unique identifying type for the bundle which when tim::trait::is_available<Tag> is false at compile-time or tim::trait::runtime_enabled<Tag>() is false at runtime, then none of the components will be collected

tparam CompT

The empty or empty + tag non-auto type which will be wrapped

tparam BundleT

Derived data type

Public Functions

inline operator component_type&()

implicit conversion to underlying component_type

inline operator const component_type&() const

implicit conversion to const ref of underlying component_type

inline auto count()

count number of active components in an instance

template<typename FuncT, typename ...Args>
inline decltype(auto) execute(FuncT &&func, Args&&... args)

when chaining together operations, this enables executing a function inside the chain

template<typename ...Args>
this_type &push(Args&&... args)

push components into call-stack storage

template<typename ...Args>
this_type &pop(Args&&... args)

pop components off call-stack storage

template<typename ...Args>
this_type &measure(Args&&... args)

execute a measurement

template<typename ...Args>
this_type &record(Args&&... args)

record some data

template<typename ...Args>
this_type &sample(Args&&... args)

execute a sample

template<typename ...Args>
this_type &start(Args&&... args)

invoke start member function on all components

template<typename ...Args>
this_type &stop(Args&&... args)

invoke stop member function on all components

template<typename ...Args>
this_type &assemble(Args&&... args)

invoke assemble member function on all components to determine if measurements can be derived from other components in a bundle

template<typename ...Args>
this_type &derive(Args&&... args)

invoke derive member function on all components to extract measurements from other components in the bundle

template<typename ...Args>
this_type &mark(Args&&... args)

invoke mark member function on all components

template<typename ...Args>
this_type &mark_begin(Args&&... args)

invoke mark_begin member function on all components

template<typename ...Args>
this_type &mark_end(Args&&... args)

invoke mark_begin member function on all components

template<typename ...Args>
this_type &store(Args&&... args)

invoke store member function on all components

template<typename ...Args>
this_type &audit(Args&&... args)

invoke audit member function on all components

template<typename ...Args>
this_type &add_secondary(Args&&... args)

add secondary data

template<typename ...Args>
this_type &reset(Args&&... args)

reset the data

template<typename ...Args>
this_type &set_scope(Args&&... args)

modify the scope of the push operation

template<typename ...Args>
this_type &set_prefix(Args&&... args)

set the key

template<template<typename> class OpT, typename ...Args>
this_type &invoke(Args&&... _args)

invoke the provided operation on all components

template<typename ...Args>
inline auto get(Args&&... args) const

invoke get member function on all components to get their data

template<typename ...Args>
inline auto get_labeled(Args&&... args) const

invoke get member function on all components to get data labeled with component name

bool enabled() const

check whether enabled

bool report_at_exit() const

check whether reporting at exit

bool store() const

check whether storing data

int64_t laps() const

get the number of laps

uint64_t hash() const

get the key hash

std::string key() const

get the key

data_type &data()

get tuple data

const data_type &data() const

get tuple data

this_type &report_at_exit(bool val)

write to stdout during destruction

this_type &rekey(string_view_cref_t _key)

update key

this_type &rekey(captured_location_t _loc)

update key

this_type &rekey(uint64_t _hash)

update key

scope::transient_destructor get_scope_destructor()

returns a stack-object for calling stop

scope::transient_destructor get_scope_destructor(utility::transient_function<void(this_type&)>)

returns a stack-object for calling some member functions when the scope is exited.

Public Static Functions

static inline constexpr auto fixed_count()

query the number of (compile-time) fixed components

static inline constexpr auto optional_count()

query the number of (run-time) optional components