density
C++11 library for paged memory management, function queues, heterogeneous queues and lifo memory management
Classes | Public Types | Public Member Functions | Static Public Attributes | Friends | List of all members
heter_queue< RUNTIME_TYPE, ALLOCATOR_TYPE > Class Template Reference

#include <heter_queue.h>

Classes

class  consume_operation
 
class  put_transaction
 
class  reentrant_consume_operation
 
class  reentrant_put_transaction
 

Public Types

using runtime_type = RUNTIME_TYPE
 
using value_type = dynamic_reference< RUNTIME_TYPE >
 
using allocator_type = ALLOCATOR_TYPE
 
using pointer = value_type *
 
using const_pointer = const value_type *
 
using reference = value_type
 
using const_reference = const value_type &
 
using size_type = std::size_t
 
using difference_type = std::ptrdiff_t
 

Public Member Functions

constexpr heter_queue () noexcept
 
constexpr heter_queue (const ALLOCATOR_TYPE &i_source_allocator) noexcept
 
constexpr heter_queue (ALLOCATOR_TYPE &&i_source_allocator) noexcept
 
 heter_queue (heter_queue &&i_source) noexcept
 
 heter_queue (const heter_queue &i_source)
 
heter_queueoperator= (heter_queue &&i_source) noexcept
 
heter_queueoperator= (const heter_queue &i_source)
 
allocator_type get_allocator () noexcept(std::is_nothrow_copy_constructible< allocator_type >::value)
 
allocator_type & get_allocator_ref () noexcept
 
const allocator_type & get_allocator_ref () const noexcept
 
 ~heter_queue ()
 
bool empty () const noexcept
 
void clear () noexcept
 
template<typename ELEMENT_TYPE >
void push (ELEMENT_TYPE &&i_source)
 
template<typename ELEMENT_TYPE , typename... CONSTRUCTION_PARAMS>
void emplace (CONSTRUCTION_PARAMS &&...i_construction_params)
 
void dyn_push (const RUNTIME_TYPE &i_type)
 
void dyn_push_copy (const RUNTIME_TYPE &i_type, const void *i_source)
 
void dyn_push_move (const RUNTIME_TYPE &i_type, void *i_source)
 
template<typename ELEMENT_TYPE >
put_transaction< typename std::decay< ELEMENT_TYPE >::type > start_push (ELEMENT_TYPE &&i_source)
 
template<typename ELEMENT_TYPE , typename... CONSTRUCTION_PARAMS>
put_transaction< ELEMENT_TYPE > start_emplace (CONSTRUCTION_PARAMS &&...i_construction_params)
 
put_transaction start_dyn_push (const RUNTIME_TYPE &i_type)
 
put_transaction start_dyn_push_copy (const RUNTIME_TYPE &i_type, const void *i_source)
 
put_transaction start_dyn_push_move (const RUNTIME_TYPE &i_type, void *i_source)
 
void pop () noexcept
 
bool try_pop () noexcept
 
consume_operation try_start_consume () noexcept
 
bool try_start_consume (consume_operation &i_consume) noexcept
 
template<typename ELEMENT_TYPE >
void reentrant_push (ELEMENT_TYPE &&i_source)
 
template<typename ELEMENT_TYPE , typename... CONSTRUCTION_PARAMS>
void reentrant_emplace (CONSTRUCTION_PARAMS &&...i_construction_params)
 
void reentrant_dyn_push (const RUNTIME_TYPE &i_type)
 
void reentrant_dyn_push_copy (const RUNTIME_TYPE &i_type, const void *i_source)
 
void reentrant_dyn_push_move (const RUNTIME_TYPE &i_type, void *i_source)
 
template<typename ELEMENT_TYPE >
reentrant_put_transaction< typename std::decay< ELEMENT_TYPE >::type > start_reentrant_push (ELEMENT_TYPE &&i_source)
 
template<typename ELEMENT_TYPE , typename... CONSTRUCTION_PARAMS>
reentrant_put_transaction< ELEMENT_TYPE > start_reentrant_emplace (CONSTRUCTION_PARAMS &&...i_construction_params)
 
reentrant_put_transaction start_reentrant_dyn_push (const RUNTIME_TYPE &i_type)
 
reentrant_put_transaction start_reentrant_dyn_push_copy (const RUNTIME_TYPE &i_type, const void *i_source)
 
reentrant_put_transaction start_reentrant_dyn_push_move (const RUNTIME_TYPE &i_type, void *i_source)
 
void reentrant_pop () noexcept
 
bool try_reentrant_pop () noexcept
 
reentrant_consume_operation try_start_reentrant_consume () noexcept
 
bool try_start_reentrant_consume (reentrant_consume_operation &i_consume) noexcept
 
iterator begin () noexcept
 
iterator end () noexcept
 
const_iterator begin () const noexcept
 
const_iterator end () const noexcept
 
const_iterator cbegin () const noexcept
 
const_iterator cend () const noexcept
 
bool operator== (const heter_queue &i_source) const
 
bool operator!= (const heter_queue &i_source) const
 

Static Public Attributes

static constexpr size_t min_alignment
 
static constexpr bool concurrent_puts = false
 
static constexpr bool concurrent_consumes = false
 
static constexpr bool concurrent_put_consumes = false
 
static constexpr bool is_seq_cst = true
 

Friends

void swap (heter_queue< RUNTIME_TYPE, ALLOCATOR_TYPE > &i_first, heter_queue< RUNTIME_TYPE, ALLOCATOR_TYPE > &i_second) noexcept
 

Detailed Description

template<typename RUNTIME_TYPE = runtime_type<>, typename ALLOCATOR_TYPE = default_allocator>
class density::heter_queue< RUNTIME_TYPE, ALLOCATOR_TYPE >

Class template implementing an heterogeneous FIFO pseudo-container.

A value of heter_queue is a pair of a runtime type object bound to a type E, and an object of type E (called element). heter_queue is an heterogeneous pseudo-container: elements in the same queue can have different types. Elements can be added only at the end (put operation), and can be removed only at the beginning (consume operation). When doing a put, the user may associate one or more raw memory blocks to the element. Raw blocks are deallocated automatically when the value is consumed. heter_queue supports iterators, but they are just Input Iterators so heter_queue is not a container.

Template Parameters
RUNTIME_TYPERuntime-type object used to store the actual complete type of each element. This type must satisfy the requirements of RuntimeType. The default is runtime_type.
ALLOCATOR_TYPEAllocator type to be used. This type must satisfy the requirements of both UntypedAllocator and PagedAllocator. The default is density::default_allocator.


Thread safeness: None. The user is responsible of avoiding data races.
Exception safeness: Any function of heter_queue is noexcept or provides the strong exception guarantee.

Basic usage

Elements can be added with push or emplace:

heter_queue<> queue;
queue.push(19); // the parameter can be an l-value or an r-value
queue.emplace<std::string>(8, '*'); // pushes "********"

In the above code the type of the element is fixed at compile time. In the case of emplace, it is not dependent on the type of the arguments, so it must be explicitly specified.

Put operations can be transactional, in which case the name of the function contains start_:

heter_queue<> queue;
struct MessageInABottle
{
const char * m_text = nullptr;
};
auto transaction = queue.start_emplace<MessageInABottle>();
transaction.element().m_text = transaction.raw_allocate_copy("Hello world!");
transaction.commit();

Transactional puts returns an object of type put_transaction that should be used to commit or cancel the transaction. If a transaction is destroyed before being committed, it is canceled automatically. Before being committed, a transaction has no observable side effects (it does have non-observable side effects anyway, like the reservation of space in a page). The functions raw_allocate and raw_allocate_copy allows to associate one or more raw memory blocks to the element. In this case the element should keep the pointer to the blocks (otherwise consumers are not able to access the blocks). Only transactional puts can allocate raw blocks.

The function try_start_consume can be used to consume an element. The returned object has type consume_operation, which is similar to put_transaction (it can be canceled or committed), with the difference that it has observable side effects before commit or cancel is called: the element disappears from the queue when try_start_consume is called, and re-appears whenever cancel is called (or the consume_operation is destroyed without being committed).

auto consume = queue.try_start_consume();
if (consume.complete_type().is<std::string>())
{
std::cout << consume.element<std::string>() << std::endl;
}
else if (consume.complete_type().is<MessageInABottle>())
{
std::cout << consume.element<MessageInABottle>().m_text << std::endl;
}
consume.commit();

The example above searches for an exact match of the type being consumed (using runtime_type::is). Anyway runtime_type (the default RUNTIME_TYPE) allows to add custom functions that can be called regardless of the type. The following example uses the built-in f_ostream:

using namespace density;
/* a runtime_type is internally like a pointer to a v-table, but it can
contain functions or data (like in the case of f_size and f_alignment). */
using MyRunTimeType = runtime_type<
f_default_construct,
f_copy_construct,
f_destroy,
f_size,
f_alignment,
queue.push(4);
queue.push(std::complex<double>(1., 4.));
queue.emplace<std::string>("Hello!!");
// queue.emplace<std::thread>(); - This would not compile because std::thread does not have a << operator for streams
// consume all the elements
while (auto consume = queue.try_start_consume())
{
/* this is like: give me the function at the 6-th row in the v-table. The type f_ostream
is converted to an index at compile time. */
auto const ostream_feature = consume.complete_type().get_feature<f_ostream>();
ostream_feature(std::cout, consume.element_ptr()); // this invokes the feature
std::cout << "\n";
consume
.commit(); // don't forget the commit, otherwise the element will remain in the queue
}

An element of a type unknown at compile time may be pushed, in which case dyn_push can be used:

// this local function reads from std::cin an object of a given type and puts it in the queue
auto ask_and_put = [&](const MyRunTimeType & i_type) {
// for this we exploit the feature f_rtti that we have included in MyRunTimeType
std::cout << "Enter a " << i_type.type_info().name() << std::endl;
auto const istream_feature = i_type.get_feature<f_istream>();
auto put = queue.start_dyn_push(i_type);
istream_feature(std::cin, put.element_ptr());
/* if an exception is thrown before the commit, the put is canceled without ever
having observable side effects. */
put.commit();
};
ask_and_put(MyRunTimeType::make<int>());
ask_and_put(MyRunTimeType::make<std::string>());

Member functions containing reentrant_ in their names support reentrancy: while they are in progress, other puts, consumes, iterations and any non-life-time operation are allowed, but only in the same thread (reentrancy has nothing to do with multithreading).
In contrast, while a non-reentrant operation is in progress, the queue is not in a consistent state: if during a put the the operation member functions on the same queue are directly or indirectly called, the behavior is undefined. Reentrant and non-reentrant operation can be mixed, provided that the above constraint is respected.

// start 3 reentrant put transactions
auto put_1 = queue.start_reentrant_push(1);
auto put_2 = queue.start_reentrant_emplace<std::string>("Hello world!");
double pi = 3.14;
auto put_3 = queue.start_reentrant_dyn_push_copy(runtime_type<>::make<double>(), &pi);
assert(
queue.empty()); // the queue is still empty, because no transaction has been committed
// commit and start consuming "Hello world!"
put_2.commit();
auto consume2 = queue.try_start_reentrant_consume();
assert(!consume2.empty() && consume2.complete_type().is<std::string>());
// commit and start consuming 1
put_1.commit();
auto consume1 = queue.try_start_reentrant_consume();
assert(!consume1.empty() && consume1.complete_type().is<int>());
// cancel 3.14, and commit the consumes
put_3.cancel();
consume1.commit();
consume2.commit();
assert(queue.empty());

A value in the queue has the type std::pair<const RUNTIME_TYPE &, void* const>. Iterators are conceptually pointers to such pairs. They don't provide the multipass guarantee, so they are not Forward Iterators, but just Input Iterators. For this reason heter_queue is not a container. Insertions invalidate no iterators. Removals invalidate only the iterators pointing to the element being removed. Past-the-end iterators are never invalidated, and they compare equal each other and with a default constructed iterator:

heter_queue<> queue_1, queue_2;
queue_1.push(42);
assert(queue_1.end() == queue_2.end() && queue_1.end() == heter_queue<>::iterator());
for (const auto & value : queue_1)
{
assert(value.as<int>() == 42);
}

The following table is a summary of the put functions. Functions containing dyn_ in their name allow to put an element whose type is not known at compile type (they take as first argument an object of type RUNTIME_TYPE).

Put functions
Group Functions Type binding Constructor
[start_][reentrant_]push Compile time Copy/Move
[start_][reentrant_]emplace Compile time Any
[start_][reentrant_]dyn_push Runtime Default
[start_][reentrant_]dyn_push_copy Runtime Copy
[start_][reentrant_]dyn_push_move Runtime Move

Implementation and performance notes

An heter_queue is basically composed by an ordered set of pages (whose size is determined by the allocator), an head pointer and tail pointer. The first page is the head page, that is the one that contains the address the head pointer points to. The last page is the tail page, that is the one that contains the address the tail pointer points to.

Values are allocated linearly in the memory pages as tightly as the alignment requirements allow. A value is allocated in the queue by adding its size to the tail pointer. The memory layout of a value is composed by:

When a page overflow occurs, a new page is requested to the allocator. Whenever a value is too large to fit in a page, it is allocated outside the pages, with a legacy allocation. Raw memory blocks are allocated in the same way of values, with the difference that they don't have a runtime type associated.

When a value is consumed, its size is added to the head pointer. Pages are not recycled: when the last value of a page is consumed (that is the head moves to another page), the empty page is deallocated. The default allocator, that is default_allocator, is designed to handle efficiently page allocations and deallocations.

Values are never moved by the queue, and are copied only in case of copy-construction or copy assignment of the queue.

Notes:

Constructor & Destructor Documentation

constexpr heter_queue ( )
inlinenoexcept

Default constructor. The allocator is default-constructed.

Complexity: constant.
Throws: nothing.
Exception guarantee: strong (in case of exception the function has no observable effects).
Implementation notes: This constructor does not allocate memory and never throws.

heter_queue<> queue;
assert(queue.empty());
assert(std::distance(queue.begin(), queue.end()) == 0);
assert(queue.cbegin() == queue.cend());
constexpr heter_queue ( const ALLOCATOR_TYPE &  i_source_allocator)
inlineexplicitnoexcept

Constructor with allocator parameter. The allocator is copy-constructed.

Parameters
i_source_allocatorsource used to copy-construct the allocator.

Complexity: constant.
Throws: whatever the copy constructor of the allocator throws.
Exception guarantee: strong (in case of exception the function has no observable effects).
Implementation notes: This constructor does not allocate memory. It throws anything the copy constructor of the allocator throws.

default_allocator allocator;
heter_queue<> queue(allocator);
assert(queue.empty());
constexpr heter_queue ( ALLOCATOR_TYPE &&  i_source_allocator)
inlineexplicitnoexcept

Constructor with allocator parameter. The allocator is move-constructed.

Parameters
i_source_allocatorsource used to move-construct the allocator.

Complexity: constant.
Throws: whatever the move constructor of the allocator throws.
Exception guarantee: strong (in case of exception the function has no observable effects).
Implementation notes: This constructor does not allocate memory. It throws anything the move constructor of the allocator throws.

default_allocator allocator;
heter_queue<> queue(std::move(allocator));
assert(queue.empty());
heter_queue ( heter_queue< RUNTIME_TYPE, ALLOCATOR_TYPE > &&  i_source)
inlinenoexcept

Move constructor. The allocator is move-constructed from the one of the source.

Parameters
i_sourcesource to move the elements from. After the call the source is left in some valid but indeterminate state.

Complexity: constant.
Throws: nothing.
Implementation notes:

  • After the call the source is left empty.
using MyRunTimeType = runtime_type<
f_default_construct,
f_copy_construct,
f_destroy,
f_size,
f_alignment,
f_equal>;
heter_queue<MyRunTimeType> queue;
queue.push(std::string());
queue.push(std::make_pair(4., 1));
heter_queue<MyRunTimeType> queue_1(std::move(queue));
assert(queue.empty());
assert(std::distance(queue.begin(), queue.end()) == 0);
assert(queue.cbegin() == queue.cend());
assert(!queue_1.empty());
assert(std::distance(queue_1.begin(), queue_1.end()) == 2);
assert(queue_1.cbegin() != queue_1.cend());
heter_queue ( const heter_queue< RUNTIME_TYPE, ALLOCATOR_TYPE > &  i_source)
inline

Copy constructor. The allocator is copy-constructed from the one of the source.


Requires:

Complexity: linear in the number of elements of the source.
Throws: unspecified.
Exception guarantee: strong (in case of exception the function has no observable effects).

using MyRunTimeType = runtime_type<
f_default_construct,
f_copy_construct,
f_destroy,
f_size,
f_alignment,
f_equal>;
heter_queue<MyRunTimeType> queue;
queue.push(std::string());
queue.push(std::make_pair(4., 1));
heter_queue<MyRunTimeType> queue_1(queue);
assert(queue == queue_1);
~heter_queue ( )
inline

Destructor.

Complexity: linear.
Effects on iterators: any iterator pointing to this queue is invalidated.
Throws: Nothing.

Member Function Documentation

heter_queue& operator= ( heter_queue< RUNTIME_TYPE, ALLOCATOR_TYPE > &&  i_source)
inlinenoexcept

Move assignment. The allocator is move-assigned from the one of the source.

Parameters
i_sourcesource to move the elements from. After the call the source is left in some valid but indeterminate state.

Complexity: Unspecified.
Effects on iterators: Any iterator pointing to this queue or to the source queue is invalidated.
Throws: Nothing.


Implementation notes:

  • After the call the source is left empty.
  • The complexity is linear in the number of elements in this queue.
using MyRunTimeType = runtime_type<
f_default_construct,
f_copy_construct,
f_destroy,
f_size,
f_alignment,
f_equal>;
heter_queue<MyRunTimeType> queue;
queue.push(std::string());
queue.push(std::make_pair(4., 1));
heter_queue<MyRunTimeType> queue_1;
queue_1 = std::move(queue);
assert(queue.empty());
assert(std::distance(queue.begin(), queue.end()) == 0);
assert(queue.cbegin() == queue.cend());
assert(!queue_1.empty());
assert(std::distance(queue_1.begin(), queue_1.end()) == 2);
assert(queue_1.cbegin() != queue_1.cend());
heter_queue& operator= ( const heter_queue< RUNTIME_TYPE, ALLOCATOR_TYPE > &  i_source)
inline

Copy assignment. The allocator is copy-assigned from the one of the source.

Parameters
i_sourcesource to move the elements from. After the call the source is left in some valid but indeterminate state.

Complexity: linear.
Effects on iterators: Any iterator pointing to this queue is invalidated.
Throws: unspecified.
Exception guarantee: strong (in case of exception the function has no observable effects).

using MyRunTimeType = runtime_type<
f_default_construct,
f_copy_construct,
f_destroy,
f_size,
f_alignment,
f_equal>;
heter_queue<MyRunTimeType> queue;
queue.push(std::string());
queue.push(std::make_pair(4., 1));
heter_queue<MyRunTimeType> queue_1;
queue_1 = queue;
assert(queue == queue_1);
allocator_type get_allocator ( )
inlinenoexcept

Returns a copy of the allocator

heter_queue<> queue;
assert(queue.get_allocator() == default_allocator());
allocator_type& get_allocator_ref ( )
inlinenoexcept

Returns a reference to the allocator

heter_queue<> queue;
assert(queue.get_allocator_ref() == default_allocator());
const allocator_type& get_allocator_ref ( ) const
inlinenoexcept

Returns a const reference to the allocator

heter_queue<> queue;
auto const & queue_ref = queue;
assert(queue_ref.get_allocator_ref() == default_allocator());
bool empty ( ) const
inlinenoexcept

Returns whether the queue contains no elements.

Complexity: Unspecified.
Throws: Nothing.

heter_queue<> queue;
assert(queue.empty());
queue.push(1);
assert(!queue.empty());
void clear ( )
inlinenoexcept

Deletes all the elements in the queue.

Complexity: linear.
Effects on iterators: any iterator is invalidated
Throws: Nothing.

heter_queue<> queue;
queue.push(1);
queue.clear();
assert(queue.empty());
void push ( ELEMENT_TYPE &&  i_source)
inline

Appends at the end of the queue an element of type ELEMENT_TYPE, copy-constructing or move-constructing it from the source.

Parameters
i_sourceobject to be used as source to construct of new element.
  • If this argument is an l-value, the new element copy-constructed
  • If this argument is an r-value, the new element move-constructed


Requires:

  • ELEMENT_TYPE must be copy constructible (in case of l-value) or move constructible (in case of r-value)

Complexity: constant.
Effects on iterators: no iterator is invalidated
Throws: unspecified.
Exception guarantee: strong (in case of exception the function has no observable effects).

Examples

queue.push(12);
queue.push(std::string("Hello world!!"));
void emplace ( CONSTRUCTION_PARAMS &&...  i_construction_params)
inline

Appends at the end of the queue an element of type ELEMENT_TYPE, in-place-constructing it from a perfect forwarded parameter pack.
Note: the template argument ELEMENT_TYPE can't be deduced from the parameters so it must explicitly specified.

Parameters
i_construction_paramsconstruction parameters for the new element.


Requires:

  • ELEMENT_TYPE must be constructible with std::forward<CONSTRUCTION_PARAMS>(i_construction_params)...

Complexity: constant.
Effects on iterators: no iterator is invalidated
Throws: unspecified.
Exception guarantee: strong (in case of exception the function has no observable effects).

Examples

queue.emplace<int>();
queue.emplace<std::string>(12, '-');
void dyn_push ( const RUNTIME_TYPE &  i_type)
inline

Adds at the end of the queue an element of a type known at runtime, default-constructing it.

Parameters
i_typetype of the new element.


Requires:

  • The function RUNTIME_TYPE::default_construct must be invokable. If RUNTIMETYPE is runtime_type default_construct must be included in the feature list.

Complexity: constant.
Effects on iterators: no iterator is invalidated
Throws: unspecified.
Exception guarantee: strong (in case of exception the function has no observable effects).

Examples

using MyRunTimeType = runtime_type<f_default_construct, f_destroy, f_size, f_alignment>;
heter_queue<MyRunTimeType> queue;
auto const type = MyRunTimeType::make<int>();
queue.dyn_push(type); // appends 0
void dyn_push_copy ( const RUNTIME_TYPE &  i_type,
const void *  i_source 
)
inline

Appends at the end of the queue an element of a type known at runtime, copy-constructing it from the source.

Parameters
i_typetype of the new element.
i_sourcepointer to the object to use as source. If this pointer does dot point to an object whose dynamic type is the the target type i_type was bound to, the behavior is undefined.


Requires:

  • The function RUNTIME_TYPE::copy_construct must be invokable. If RUNTIMETYPE is runtime_type copy_construct must be included in the feature list.

Complexity: constant.
Effects on iterators: no iterator is invalidated
Throws: unspecified.
Exception guarantee: strong (in case of exception the function has no observable effects).

Examples

using MyRunTimeType = runtime_type<f_copy_construct, f_destroy, f_size, f_alignment>;
heter_queue<MyRunTimeType> queue;
std::string const source("Hello world!!");
auto const type = MyRunTimeType::make<decltype(source)>();
queue.dyn_push_copy(type, &source);
void dyn_push_move ( const RUNTIME_TYPE &  i_type,
void *  i_source 
)
inline

Adds at the end of the queue an element of a type known at runtime, move-constructing it from the source.

Parameters
i_typetype of the new element
i_sourcepointer to the object to use as source. If this pointer does dot point to an object whose dynamic type is the the target type i_type was bound to, the behavior is undefined.


Requires:

  • The function RUNTIME_TYPE::move_construct must be invokable. If RUNTIMETYPE is runtime_type move_construct must be included in the feature list.

Complexity: constant.
Effects on iterators: no iterator is invalidated
Throws: unspecified.
Exception guarantee: strong (in case of exception the function has no observable effects).

Examples

using MyRunTimeType = runtime_type<f_move_construct, f_destroy, f_size, f_alignment>;
heter_queue<MyRunTimeType> queue;
std::string source("Hello world!!");
auto const type = MyRunTimeType::make<decltype(source)>();
queue.dyn_push_move(type, &source);
put_transaction<typename std::decay<ELEMENT_TYPE>::type> start_push ( ELEMENT_TYPE &&  i_source)
inline

Begins a transaction that appends an element of type ELEMENT_TYPE, copy-constructing or move-constructing it from the source.
This function allocates the required space, constructs the new element, and returns a transaction object that may be used to allocate raw space associated to the element being inserted, or to alter the element in some way.
Call the member function commit on the returned transaction in order to make the effects observable. If the transaction is destroyed before commit has been called, the transaction is canceled and it has no observable effects. Until the returned transaction is committed or canceled, the queue is not in a consistent state. If any function is called in this timespan, the behavior is undefined.

Parameters
i_sourceobject to be used as source to construct of new element.
  • If this argument is an l-value, the new element copy-constructed.
  • If this argument is an r-value, the new element move-constructed.
Returns
The associated transaction object.


Requires:

  • ELEMENT_TYPE must be copy constructible (in case of l-value) or move constructible (in case of r-value)

Complexity: constant.
Effects on iterators: no iterator is invalidated
Throws: unspecified.
Exception guarantee: strong (in case of exception the function has no observable effects).

Examples

auto put = queue.start_push(12);
put.element() += 2;
put.commit(); // commits a 14
put_transaction<ELEMENT_TYPE> start_emplace ( CONSTRUCTION_PARAMS &&...  i_construction_params)
inline

Begins a transaction that appends an element of a type ELEMENT_TYPE, in-place-constructing it from a perfect forwarded parameter pack.
This function allocates the required space, constructs the new element, and returns a transaction object that may be used to allocate raw space associated to the element being inserted, or to alter the element in some way.
Call the member function commit on the returned transaction in order to make the effects observable. If the transaction is destroyed before commit has been called, the transaction is canceled and it has no observable effects. Until the returned transaction is committed or canceled, the queue is not in a consistent state. If any function is called in this timespan, the behavior is undefined.

Parameters
i_construction_paramsconstruction parameters for the new element.
Returns
The associated transaction object.


Requires:

  • ELEMENT_TYPE must be constructible with std::forward<CONSTRUCTION_PARAMS>(i_construction_params)...

Complexity: constant.
Effects on iterators: no iterator is invalidated
Throws: unspecified.
Exception guarantee: strong (in case of exception the function has no observable effects).

Examples

auto put = queue.start_emplace<std::string>(4, '*');
put.element() += "****";
put.commit(); // commits a "********"
put_transaction start_dyn_push ( const RUNTIME_TYPE &  i_type)
inline

Begins a transaction that appends an element of a type known at runtime, default-constructing it.
This function allocates space for and constructs the new element, and returns a transaction object that may be used to allocate raw space associated to the element being inserted, or to alter the element in some way.
Call the member function commit on the returned transaction in order to make the effects observable. If the transaction is destroyed before commit has been called, the transaction is canceled and it has no observable effects. Until the returned transaction is committed or canceled, the queue is not in a consistent state. If any function is called in this timespan, the behavior is undefined.

Parameters
i_typetype of the new element.
Returns
The associated transaction object.

Complexity: constant.
Effects on iterators: no iterator is invalidated
Throws: unspecified.
Exception guarantee: strong (in case of exception the function has no observable effects).

Examples

using MyRunTimeType = runtime_type<f_default_construct, f_destroy, f_size, f_alignment>;
heter_queue<MyRunTimeType> queue;
auto const type = MyRunTimeType::make<int>();
auto put = queue.start_dyn_push(type);
put.commit();
put_transaction start_dyn_push_copy ( const RUNTIME_TYPE &  i_type,
const void *  i_source 
)
inline

Begins a transaction that appends an element of a type known at runtime, copy-constructing it from the source..
This function allocates space for and constructs the new element, and returns a transaction object that may be used to allocate raw space associated to the element being inserted, or to alter the element in some way.
Call the member function commit on the returned transaction in order to make the effects observable. If the transaction is destroyed before commit has been called, the transaction is canceled and it has no observable effects. Until the returned transaction is committed or canceled, the queue is not in a consistent state. If any function is called in this timespan, the behavior is undefined.

Parameters
i_typetype of the new element.
i_sourcepointer to the object to use as source. If this pointer does dot point to an object whose dynamic type is the the target type i_type was bound to, the behavior is undefined.
Returns
The associated transaction object.

Complexity: constant.
Effects on iterators: no iterator is invalidated
Throws: unspecified.
Exception guarantee: strong (in case of exception the function has no observable effects).

Examples

using MyRunTimeType = runtime_type<f_copy_construct, f_destroy, f_size, f_alignment>;
heter_queue<MyRunTimeType> queue;
std::string const source("Hello world!!");
auto const type = MyRunTimeType::make<decltype(source)>();
auto put = queue.start_dyn_push_copy(type, &source);
put.commit();
put_transaction start_dyn_push_move ( const RUNTIME_TYPE &  i_type,
void *  i_source 
)
inline

Begins a transaction that appends an element of a type known at runtime, move-constructing it from the source..
This function allocates space for and constructs the new element, and returns a transaction object that may be used to allocate raw space associated to the element being inserted, or to alter the element in some way.
Call the member function commit on the returned transaction in order to make the effects observable. If the transaction is destroyed before commit has been called, the transaction is canceled and it has no observable effects. Until the returned transaction is committed or canceled, the queue is not in a consistent state. If any function is called in this timespan, the behavior is undefined.

Parameters
i_typetype of the new element.
i_sourcepointer to the object to use as source. If this pointer does dot point to an object whose dynamic type is the the target type i_type was bound to, the behavior is undefined.
Returns
The associated transaction object.

Complexity: constant.
Effects on iterators: no iterator is invalidated
Throws: unspecified.
Exception guarantee: strong (in case of exception the function has no observable effects).

Examples

using MyRunTimeType = runtime_type<f_move_construct, f_destroy, f_size, f_alignment>;
heter_queue<MyRunTimeType> queue;
std::string source("Hello world!!");
auto const type = MyRunTimeType::make<decltype(source)>();
auto put = queue.start_dyn_push_move(type, &source);
put.commit();
void pop ( )
inlinenoexcept

Removes and destroy the first element of the queue. This function discards the element. Use a consume function if you want to access the element before it gets destroyed.

This function is equivalent to:

try_start_consume().commit();

Precondition
The behavior is undefined if the queue is empty.

Complexity: constant
Effects on iterators: any iterator pointing to the first element is invalidated
Throws: nothing

heter_queue<> queue;
queue.push(1);
queue.push(2);
queue.pop();
auto consume = queue.try_start_consume();
assert(consume.element<int>() == 2);
consume.commit();
bool try_pop ( )
inlinenoexcept

Removes and destroy the first element of the queue, if the queue is not empty. Otherwise it has no effect. This function discards the element. Use a consume function if you want to access the element before it gets destroyed.

Returns
whether an element was actually removed.

Complexity: constant.
Effects on iterators: any iterator pointing to the first element is invalidated
Throws: nothing

heter_queue<> queue;
bool pop_result = queue.try_pop();
assert(pop_result == false);
queue.push(1);
queue.push(2);
pop_result = queue.try_pop();
assert(pop_result == true);
auto consume = queue.try_start_consume();
assert(consume.element<int>() == 2);
consume.commit();
consume_operation try_start_consume ( )
inlinenoexcept

Tries to start a consume operation.

Returns
a consume_operation which is empty if there are no elements to consume

A non-empty consume must be committed for the consume to have effect.

heter_queue<> queue;
auto consume_1 = queue.try_start_consume();
assert(!consume_1);
queue.push(42);
auto consume_2 = queue.try_start_consume();
assert(consume_2);
assert(consume_2.element<int>() == 42);
consume_2.commit();
bool try_start_consume ( consume_operation i_consume)
inlinenoexcept

Tries to start a consume operation using an existing consume_operation object.

Parameters
i_consumereference to a consume_operation to be used. If it is non-empty it gets canceled before trying to start the new consume.
Returns
whether i_consume is non-empty after the call, that is whether the queue was not empty.

A non-empty consume must be committed for the consume to have effect.

This overload is similar to the one taking no arguments and returning a consume_operation. For an heter_queue there is no performance difference between the two overloads. Anyway for lock-free concurrent queue this overload may be faster.

heter_queue<> queue;
heter_queue<>::consume_operation consume_1;
bool const bool_1 = queue.try_start_consume(consume_1);
assert(!bool_1 && !consume_1);
queue.push(42);
heter_queue<>::consume_operation consume_2;
auto bool_2 = queue.try_start_consume(consume_2);
assert(consume_2 && bool_2);
assert(consume_2.element<int>() == 42);
consume_2.commit();
void reentrant_push ( ELEMENT_TYPE &&  i_source)
inline

Same to heter_queue::push, but allows reentrancy: during the construction of the element the queue is in a valid state.

Examples

queue.reentrant_push(12);
queue.reentrant_push(std::string("Hello world!!"));
void reentrant_emplace ( CONSTRUCTION_PARAMS &&...  i_construction_params)
inline

Same to heter_queue::emplace, but allows reentrancy: during the construction of the element the queue is in a valid state.

Examples

queue.reentrant_emplace<int>();
queue.reentrant_emplace<std::string>(12, '-');
void reentrant_dyn_push ( const RUNTIME_TYPE &  i_type)
inline

Same to heter_queue::dyn_push, but allows reentrancy: during the construction of the element the queue is in a valid state.

Examples

using MyRunTimeType = runtime_type<f_default_construct, f_destroy, f_size, f_alignment>;
heter_queue<MyRunTimeType> queue;
auto const type = MyRunTimeType::make<int>();
queue.reentrant_dyn_push(type); // appends 0
void reentrant_dyn_push_copy ( const RUNTIME_TYPE &  i_type,
const void *  i_source 
)
inline

Same to heter_queue::dyn_push_copy, but allows reentrancy: during the construction of the element the queue is in a valid state.

Examples

using MyRunTimeType = runtime_type<f_copy_construct, f_destroy, f_size, f_alignment>;
heter_queue<MyRunTimeType> queue;
std::string const source("Hello world!!");
auto const type = MyRunTimeType::make<decltype(source)>();
queue.reentrant_dyn_push_copy(type, &source);
void reentrant_dyn_push_move ( const RUNTIME_TYPE &  i_type,
void *  i_source 
)
inline

Same to heter_queue::dyn_push_move, but allows reentrancy: during the construction of the element the queue is in a valid state.

Examples

using MyRunTimeType = runtime_type<f_move_construct, f_destroy, f_size, f_alignment>;
heter_queue<MyRunTimeType> queue;
std::string source("Hello world!!");
auto const type = MyRunTimeType::make<decltype(source)>();
queue.reentrant_dyn_push_move(type, &source);
reentrant_put_transaction<typename std::decay<ELEMENT_TYPE>::type> start_reentrant_push ( ELEMENT_TYPE &&  i_source)
inline

Same to heter_queue::start_push, but allows reentrancy: during the construction of the element, and until the state of the transaction gets destroyed, the queue is in a valid state.

Examples

auto put = queue.start_reentrant_push(12);
put.element() += 2;
put.commit(); // commits a 14
reentrant_put_transaction<ELEMENT_TYPE> start_reentrant_emplace ( CONSTRUCTION_PARAMS &&...  i_construction_params)
inline

Same to heter_queue::start_emplace, but allows reentrancy: during the construction of the element, and until the state of the transaction gets destroyed, the queue is in a valid state.

Examples

auto put = queue.start_reentrant_emplace<std::string>(4, '*');
put.element() += "****";
put.commit(); // commits a "********"
reentrant_put_transaction start_reentrant_dyn_push ( const RUNTIME_TYPE &  i_type)
inline

Same to heter_queue::start_dyn_push, but allows reentrancy: during the construction of the element, and until the state of the transaction gets destroyed, the queue is in a valid state.

Examples

using MyRunTimeType = runtime_type<f_default_construct, f_destroy, f_size, f_alignment>;
heter_queue<MyRunTimeType> queue;
auto const type = MyRunTimeType::make<int>();
auto put = queue.start_reentrant_dyn_push(type);
put.commit();
reentrant_put_transaction start_reentrant_dyn_push_copy ( const RUNTIME_TYPE &  i_type,
const void *  i_source 
)
inline

Same to heter_queue::start_dyn_push_copy, but allows reentrancy: during the construction of the element, and until the state of the transaction gets destroyed, the queue is in a valid state.

Examples

using MyRunTimeType = runtime_type<f_copy_construct, f_destroy, f_size, f_alignment>;
heter_queue<MyRunTimeType> queue;
std::string const source("Hello world!!");
auto const type = MyRunTimeType::make<decltype(source)>();
auto put = queue.start_reentrant_dyn_push_copy(type, &source);
put.commit();
reentrant_put_transaction start_reentrant_dyn_push_move ( const RUNTIME_TYPE &  i_type,
void *  i_source 
)
inline

Same to heter_queue::start_dyn_push_move, but allows reentrancy: during the construction of the element, and until the state of the transaction gets destroyed, the queue is in a valid state.

Examples

using MyRunTimeType = runtime_type<f_move_construct, f_destroy, f_size, f_alignment>;
heter_queue<MyRunTimeType> queue;
std::string source("Hello world!!");
auto const type = MyRunTimeType::make<decltype(source)>();
auto put = queue.start_reentrant_dyn_push_move(type, &source);
put.commit();
void reentrant_pop ( )
inlinenoexcept

Removes and destroy the first element of the queue. This is the reentrant version of pop. This function discards the element. Use a consume function if you want to access the element before it gets destroyed.

This function is equivalent to:

try_start_reentrant_consume().commit();

Precondition
The behavior is undefined if the queue is empty.

Complexity: constant
Effects on iterators: any iterator pointing to the first element is invalidated
Throws: nothing

heter_queue<> queue;
queue.push(1);
queue.push(2);
queue.reentrant_pop();
auto consume = queue.try_start_consume();
assert(consume.element<int>() == 2);
consume.commit();
bool try_reentrant_pop ( )
inlinenoexcept

Removes and destroy the first element of the queue, if the queue is not empty. Otherwise it has no effect. This is the reentrant version of try_pop. This function discards the element. Use a consume function if you want to access the element before it gets destroyed.

Returns
whether an element was actually removed.

Complexity: constant.
Effects on iterators: any iterator pointing to the first element is invalidated
Throws: nothing

heter_queue<> queue;
bool pop_result = queue.try_reentrant_pop();
assert(pop_result == false);
queue.push(1);
queue.push(2);
pop_result = queue.try_reentrant_pop();
assert(pop_result == true);
auto consume = queue.try_start_reentrant_consume();
assert(consume.element<int>() == 2);
consume.commit();
reentrant_consume_operation try_start_reentrant_consume ( )
inlinenoexcept

Tries to start a reentrant consume operation. This is the reentrant version of try_start_consume.

Returns
a consume_operation which is empty if there are no elements to consume

A non-empty consume must be committed for the consume to have effect.

heter_queue<> queue;
auto consume_1 = queue.try_start_reentrant_consume();
assert(!consume_1);
queue.push(42);
auto consume_2 = queue.try_start_reentrant_consume();
assert(consume_2);
assert(consume_2.element<int>() == 42);
consume_2.commit();
bool try_start_reentrant_consume ( reentrant_consume_operation i_consume)
inlinenoexcept

Tries to start a consume operation using an existing consume_operation object. This is the reentrant version of try_start_consume.

Parameters
i_consumereference to a consume_operation to be used. If it is non-empty it gets canceled before trying to start the new consume.
Returns
whether i_consume is non-empty after the call, that is whether the queue was not empty.

A non-empty consume must be committed for the consume to have effect.

This overload is similar to the one taking no arguments and returning a consume_operation. For an heter_queue there is no performance difference between the two overloads. Anyway for lock-free concurrent queue this overload may be faster.

heter_queue<> queue;
heter_queue<>::reentrant_consume_operation consume_1;
bool const bool_1 = queue.try_start_reentrant_consume(consume_1);
assert(!bool_1 && !consume_1);
queue.push(42);
heter_queue<>::reentrant_consume_operation consume_2;
auto bool_2 = queue.try_start_reentrant_consume(consume_2);
assert(consume_2 && bool_2);
assert(consume_2.element<int>() == 42);
consume_2.commit();
bool operator== ( const heter_queue< RUNTIME_TYPE, ALLOCATOR_TYPE > &  i_source) const
inline

Returns whether this queue and another queue compare equal. Two heter_queue are equal if:

  • they have the same number of elements
  • the i-th RUNTIME_TYPE in the first queue is equal to the i-th RUNTIME_TYPE in the second queue
  • the i-th element in the first queue is equal to the i-th element in the second queue

Raw block are not relevant for comparison.

Requires:

  • the runtime type must support the feature f_equal

Complexity: linear in the number of elements
Throws: unspecified.
Exception guarantee: strong (in case of exception the function has no observable effects).

bool operator!= ( const heter_queue< RUNTIME_TYPE, ALLOCATOR_TYPE > &  i_source) const
inline

Returns whether this queue and another queue does not compare equal. Two heter_queue are equal if:

  • they have the same number of elements
  • the i-th RUNTIME_TYPE in the first queue is equal to the i-th RUNTIME_TYPE in the second queue
  • the i-th element in the first queue is equal to the i-th element in the second queue

Raw block are not relevant for comparison.

Requires:

  • the runtime type must support the feature f_equal

Complexity: linear in the number of elements
Throws: unspecified.
Exception guarantee: strong (in case of exception the function has no observable effects).

Friends And Related Function Documentation

void swap ( heter_queue< RUNTIME_TYPE, ALLOCATOR_TYPE > &  i_first,
heter_queue< RUNTIME_TYPE, ALLOCATOR_TYPE > &  i_second 
)
friend

Swaps two queues.

heter_queue<> queue, queue_1;
queue.push(1);
swap(queue, queue_1);
assert(queue.empty());
assert(std::distance(queue.begin(), queue.end()) == 0);
assert(queue.cbegin() == queue.cend());
assert(!queue_1.empty());
assert(std::distance(queue_1.begin(), queue_1.end()) == 1);
assert(queue_1.cbegin() != queue_1.cend());

Member Data Documentation

constexpr size_t min_alignment
static
Initial value:
= detail::size_max(
detail::Queue_AllFlags + 1, alignof(ControlBlock), alignof(RUNTIME_TYPE))

Minimum guaranteed alignment for every element. The actual alignment of an element may be stricter if the type requires it.

constexpr bool concurrent_puts = false
static

Whether multiple threads can do put operations on the same queue without any further synchronization.

constexpr bool concurrent_consumes = false
static

Whether multiple threads can do consume operations on the same queue without any further synchronization.

constexpr bool concurrent_put_consumes = false
static

Whether puts and consumes can be done concurrently without any further synchronization. In any case unsynchronized concurrency is constrained by concurrent_puts and concurrent_consumes.

constexpr bool is_seq_cst = true
static

Whether this queue is sequential consistent.


The documentation for this class was generated from the following file: