Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F1497475
split_tuple_writer.hpp
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Size
19 KB
Referenced Files
None
Subscribers
None
split_tuple_writer.hpp
View Options
//===-- rosa/support/writer/split_tuple_writer.hpp ---------------*- C++ -*-===/
//
// The RoSA Framework
//
//===----------------------------------------------------------------------===/
///
/// \file rosa/support/writer/split_tuple_writer.hpp
///
/// \authors David Juhasz (david.juhasz@tuwien.ac.at)
///
/// \date 2019
///
/// \brief Facilities to split writers of \c std::tuple into writers of
/// their elements.
///
//===----------------------------------------------------------------------===/
#ifndef ROSA_SUPPORT_WRITER_SPLIT_TUPLE_WRITER_HPP
#define ROSA_SUPPORT_WRITER_SPLIT_TUPLE_WRITER_HPP
#include
"rosa/support/debug.hpp"
#include
"rosa/support/log.h"
#include
"rosa/support/sequence.hpp"
#include
<memory>
#include
<queue>
#include
<tuple>
namespace
rosa
{
namespace
writer
{
/// What to do when splitted element writers got destructed so that some
/// incomplete tuples remain in the buffer.
enum
IncompleteTuplePolicy
{
Ignore
,
///< ignore incomplete tuples, data discarded
Flush
,
///< flush incomplete tuples, use default value for missing elements
#ifndef NDEBUG
DbgFail
///< fail with an assertion, available only in debug build
#endif
// !defined NDEBUG
};
/// Anonymous namespace providing implementation for splitting tuple writers;
/// consider it private.
namespace
{
/// Writes values into a buffer based on an index.
///
/// The values are first buffered and flushed out later once a corresponding
/// tuple gets complete (i.e., other elementwise writers put the corresponding
/// values into the buffer). Since the underlying buffer waits for tuples to
/// become complete, the elementwise writers are expected to provide the same
/// number of values for the buffer (i.e., completing all tuples that have an
/// element). That is, however, not enforced in any ways.
///
/// Once all elementwise writers are destructed, the underlying buffer gets
/// destructed as well. Should the buffer contain some values, which are
/// elements of incomplete tuples, the destructor of the buffer handles the
/// situation according to the \c rosa::writer::IncompleteTuplePolicy set when
/// the elementwise writers were created by \c rosa::writer::splitTupleWriter().
///
/// \tparam TB buffer to write into
/// \tparam I index of the values to write to \p TB
///
/// \note \p TB is expected to implemnet an interface matching that of \c
/// TupleWriterBuffer.
///
/// \note the
template
<
typename
TB
,
size_t
I
>
class
SplittedElementWriter
{
public
:
/// Type alias for the values the writer writes.
using
T
=
typename
TB
::
template
element_type
<
I
>
;
/// \defgroup SplittedElemenWriterTypedefs Typedefs of
/// \c SplittedElementWriter
///
/// Useful `typedef`s for writers.
///
///@{
typedef
T
value_type
;
///< Type of values written.
typedef
T
&
reference
;
///< Reference to the type written
///@}
/// Creates a new instance.
///
/// \param Buffer buffer \p this object writes values into
///
/// \note \p Buffer is captured as a \c std::shared_ptr because it is shared
/// among writers for its element positions.
SplittedElementWriter
(
std
::
shared_ptr
<
TB
>
Buffer
)
:
Buffer
(
Buffer
)
{}
/// Tells how many values are still in the buffer.
///
/// Values are buffered as long as all elements of their corresponding tuples
/// are written into the buffer by other elementwise writers and in turn
/// complete tuples written by the buffer itself. This function tells how many
/// values are still in the buffer waiting for their containing tuples to be
/// complete and written out from the buffer.
///
/// \note The function simply calles the corresponding function of \p TB.
///
/// \return how many values are still in the buffer.
size_t
buffered
(
void
)
const
noexcept
{
return
Buffer
->
template
buffered
<
I
>
();
}
/// Tells if the last write operation of the buffer was successful.
///
/// Values are buffered until their corresponding tuples gets complete. The
/// buffer writes complete tuples by the underlying writer. This function
/// tells if the buffer successfully has written the latest complete tuple by
/// the underlying writer.
///
/// Once this function returns \c false, further \c write() calls has no
/// effect and the last \c buffered() values will not be written to the
/// underlying writer of \c std::tuple.
///
/// \note The function simply calles the corresponding function of \p TB.
///
/// \return if the last write operation was successful
bool
good
(
void
)
const
noexcept
{
return
Buffer
->
good
();
}
/// Writes an entry to the buffer.
///
/// The function has no effect if an earlier write operation of the buffer has
/// failed, that is \c good() returns \c false.
///
/// \note The function simply calles the corresponding function of \p TB.
///
/// \param V value to write
void
write
(
const
T
&
V
)
{
Buffer
->
template
write
<
I
>
(
V
);
}
private
:
std
::
shared_ptr
<
TB
>
Buffer
;
///< Buffer \p this object writes into
};
/// Writes an element of a tuple.
///
/// \see SplittedElementWriter
///
/// \tparam TB type of the buffer \p W writes into
/// \tparam I index of the values \p W write to \p TB
///
/// \param [in,out] W object to write with
/// \param V value to write
///
/// \return \p W after writing \p V with it
template
<
typename
TB
,
size_t
I
>
SplittedElementWriter
<
TB
,
I
>
&
operator
<<
(
SplittedElementWriter
<
TB
,
I
>
&
W
,
const
typename
SplittedElementWriter
<
TB
,
I
>::
value_type
&
V
)
{
W
.
write
(
V
);
return
W
;
}
///\defgroup TupleBufferContainer Type converter turning a \c std::tuple of
/// types into a \c std::tuple of container of those types.
///
/// The new type is used for buffering elements of tuples separately.
///
///@{
/// Template declaration.
///
/// \tparam T type to convert
///
/// \note The template is defined only when \p T is a \c std::tuple.
///
/// Usage for a type \c Tuple as:\code
/// typename TupleBufferContainer<Tuple>::Type
/// \endcode
template
<
typename
T
>
struct
TupleBufferContainer
;
/// Template definition for \c std::tuple.
template
<
typename
...
Ts
>
struct
TupleBufferContainer
<
std
::
tuple
<
Ts
...
>>
{
/// The converted type.
using
Type
=
std
::
tuple
<
std
::
queue
<
Ts
>
...
>
;
};
///@}
/// Convenience template type alias for easy use of \c TupleBufferContainer.
///
/// Converts a \c std::tuple of types into a \c std::tuple of container of those
/// types.
///
/// \tparam Tuple type to convert
template
<
typename
Tuple
>
using
tuple_buffer_container_t
=
typename
TupleBufferContainer
<
Tuple
>::
Type
;
/// Buffer for element values for writer of \c std::tuple.
///
/// The class can be instantiated with a writer of \c std::tuple and provides
/// an interface for writing elements into tuples one by one.
///
/// \note The class is utilized by \c SplittedElementWriter.
///
/// \tparam TupleWriter the writer type that handles values of \c std::tuple
///
/// \note The elements of the written \c std::tuple need to be default
/// constructible because of \c rosa::writer::IncompleteTuplePolicy::Flush.
///
/// \todo Consider thread safety of the class.
template
<
typename
TupleWriter
>
class
TupleWriterBuffer
{
public
:
/// Type alias for the value type of \p TupleWriter.
/// \note The type is expected to be \c std::tuple.
using
writer_value_type
=
typename
TupleWriter
::
value_type
;
/// The number of elements of \c writer_value_type.
static
constexpr
size_t
writer_value_size
=
std
::
tuple_size_v
<
writer_value_type
>
;
/// Template type alias to get element types of \c writer_value_type by
/// index.
/// \tparam I the index of the element
template
<
size_t
I
>
using
element_type
=
typename
std
::
tuple_element
<
I
,
writer_value_type
>::
type
;
/// Type alias for index sequence for accessing elements of \c
/// writer_value_size.
using
element_idx_seq_t
=
seq_t
<
writer_value_size
>
;
/// Creates a new instance.
///
/// \param Writer the writer \p this object uses to write tuples
/// \param Policy what to do with incomplete tuples when destructing \p
/// this object
TupleWriterBuffer
(
TupleWriter
&&
Writer
,
const
IncompleteTuplePolicy
Policy
)
noexcept
:
Writer
(
Writer
),
Policy
(
Policy
),
Buffer
()
{}
/// Destructor.
///
/// Should \p this object has some values in \c Buffer (i.e., incomplete
/// tuples), the destructor takes care of them according to \c Policy.
///
/// \note The destructor may flush the buffered values if the last write
/// operation was successful, that is \c good() returns \c true.
~
TupleWriterBuffer
(
void
)
noexcept
{
if
(
good
())
{
switch
(
Policy
)
{
default
:
LOG_ERROR
(
"Unexpected Policy"
);
case
Ignore
:
// Do not care about incomplete tuples in the buffer.
break
;
case
Flush
:
// Whatever we have in the buffer, flush it out.
flush
();
break
;
#ifndef NDEBUG
case
DbgFail
:
ASSERT
(
empty
(
element_idx_seq_t
())
&&
"Non-empty buffer"
);
break
;
#endif
// !defined NDEBUG
}
}
}
/// Tells how many values are in the buffer for index \p I.
///
/// \tparam I the index of the element to check
///
/// \return the number of values in buffer for index \p I
template
<
size_t
I
>
size_t
buffered
(
void
)
const
noexcept
{
return
std
::
get
<
I
>
(
Buffer
).
size
();
}
/// Tells if the last write operation was successful.
///
/// Values are buffered until their corresponding tuples gets complete.
/// Once a tuple becomes complete, it is written by \c Writer. This function
/// tells if writing the latest complete tuple was successful.
///
/// Once this function returns \c false, further \c write<I>() calls has no
/// effect and the last \c buffered<I>() values will not be written to the
/// underlying writer of \c std::tuple.
///
/// \return if the last write operation was successful
bool
good
(
void
)
const
noexcept
{
return
Writer
.
good
();
}
/// Writes an entry to the buffer for index \p I.
///
/// The function has no effect if an earlier write operation of the buffer has
/// failed, that is \c good() returns \c false.
///
/// The value is buffered first and written by \p Writer once its
/// corresponding tuple becomes complete (i.e., all other elements of the
/// tuple are written into \p this object by corresponding elementwise
/// writers).
///
/// \tparam I the index of the element to write
///
/// \param V value to write
template
<
size_t
I
>
void
write
(
const
element_type
<
I
>
&
V
)
{
ASSERT
(
!
hasCompleteTuple
(
element_idx_seq_t
()));
if
(
good
())
{
std
::
get
<
I
>
(
Buffer
).
push
(
V
);
if
(
hasCompleteTuple
(
element_idx_seq_t
()))
{
writeTuple
(
false
);
}
}
ASSERT
(
!
hasCompleteTuple
(
element_idx_seq_t
()));
}
private
:
/// Tells whether \c Buffer is completely empty, all elements of it are empty.
///
/// \tparam S0 indices for accessing elements of \c std::tuple
///
/// \note The parameter provides indices as template parameter \p S0 and its
/// actual value is ignored.
///
/// \return whether all elements of \c Buffer are empty
template
<
size_t
...
S0
>
bool
empty
(
Seq
<
S0
...
>
)
const
noexcept
{
return
(
true
&&
...
&&
std
::
get
<
S0
>
(
Buffer
).
empty
());
}
/// Tells whether \c Buffer contains at least one complete tuple.
///
/// \tparam S0 indices for accessing elements of \c std::tuple
///
/// \note The parameter provides indices as template parameter \p S0 and its
/// actual value is ignored.
///
/// \return whether there is at least one complete tuple in \c Buffer
template
<
size_t
...
S0
>
bool
hasCompleteTuple
(
Seq
<
S0
...
>
)
const
noexcept
{
return
(
true
&&
...
&&
!
std
::
get
<
S0
>
(
Buffer
).
empty
());
}
/// Makes sure \c Buffer has one complete tuple in front.
///
/// If the tuple in front of \c Buffer is not complete, missing elements are
/// default constructed in \c Buffer to complete the tuple.
///
/// \tparam S0 indices for accessing elements of \c std::tuple
///
/// \note The parameter provides indices as template parameter \p S0 and its
/// actual value is ignored.
template
<
size_t
...
S0
>
void
makeCompleteTuple
(
Seq
<
S0
...
>
)
noexcept
{
auto
addDefaultIfEmpty
=
[](
auto
&
Queue
)
{
if
(
Queue
.
empty
())
{
Queue
.
emplace
();
// default construct a value in the empty queue.
}
};
(
addDefaultIfEmpty
(
std
::
get
<
S0
>
(
Buffer
)),
...);
}
/// Gives the first complete tuple from \c Buffer.
///
/// \tparam S0 indices for accessing elements of \c std::tuple
///
/// \note The parameter provides indices as template parameter \p S0 and its
/// actual value is ignored.
///
/// \return the first complete tuple from \c Buffer
///
/// \pre There is at least one complete tuple in \c Buffer:\code
/// hasCompleteTuple(element_idx_seq_t())
/// \endcode
template
<
size_t
...
S0
>
writer_value_type
front
(
Seq
<
S0
...
>
)
const
noexcept
{
ASSERT
(
hasCompleteTuple
(
element_idx_seq_t
()));
return
{
std
::
get
<
S0
>
(
Buffer
).
front
()...};
}
/// Removes the first complete tuple from \c Buffer.
///
/// \tparam S0 indices for accessing elements of \c std::tuple
///
/// \note The parameter provides indices as template parameter \p S0 and its
/// actual value is ignored.
///
/// \pre There is at least one complete tuple in \c Buffer:\code
/// hasCompleteTuple(element_idx_seq_t())
/// \endcode
template
<
size_t
...
S0
>
void
pop
(
Seq
<
S0
...
>
)
noexcept
{
ASSERT
(
hasCompleteTuple
(
element_idx_seq_t
()));
(
std
::
get
<
S0
>
(
Buffer
).
pop
(),
...);
}
/// Takes the first tuple from \c Buffer and writes it with \c Writer.
///
/// \c Buffer need to contain a complete tuple to write it with \c Writer. The
/// function makes sure that the tuple in front of \c Buffer is complete if \p
/// MakeComplete is true. The tuple in fron of \c Buffer are expected to be
/// complete otherwise.
///
/// \param MakeComplete whether to make the first tuple complete
///
/// \pre There is at least one complete tuple in \c Buffer if not \p
/// MakeComplete: \code
/// MakeComplete || hasCompleteTuple(element_idx_seq_t())
/// \endcode
void
writeTuple
(
const
bool
MakeComplete
)
noexcept
{
if
(
MakeComplete
)
{
makeCompleteTuple
(
element_idx_seq_t
());
}
ASSERT
(
hasCompleteTuple
(
element_idx_seq_t
()));
Writer
.
write
(
front
(
element_idx_seq_t
()));
pop
(
element_idx_seq_t
());
}
/// Empties \c Buffer by writing out all tuples from it so that missing
/// elements of incomplete tuples are extended with default constructed
/// values.
void
flush
(
void
)
noexcept
{
while
(
!
empty
(
element_idx_seq_t
()))
{
ASSERT
(
!
hasCompleteTuple
(
element_idx_seq_t
()));
// Sanity check.
writeTuple
(
true
);
}
}
TupleWriter
Writer
;
///< The splitted writer
const
IncompleteTuplePolicy
Policy
;
///< what to do with incomplete tuples
///< when destructing \p this object
tuple_buffer_container_t
<
writer_value_type
>
Buffer
;
///< Container for elementwise buffering
};
/// Template type alias for writer of an element based on buffered writer of
/// \c std::tuple.
///
/// The alias utilizes \c SplittedElementWriter for writing element values by
/// \p TB.
///
/// \tparam TB buffer to write into
/// \tparam I index of the values to write to \p TB
template
<
typename
TB
,
size_t
I
>
using
element_writer_t
=
SplittedElementWriter
<
TB
,
I
>
;
///\defgroup ElementWriters Type converter turning a buffer of \c std::tuple
/// into a \c std::tuple of corresponding \c element_writer_t.
///
///@{
/// Template declaration.
///
/// \tparam TB buffer to write into
/// \tparam S type providing indices for accessing elements of \c std::tuple
///
/// \note \p TB is expected to implement an interface matching that of \c
/// TupleWriterBuffer.
///
/// \note The template is defined only when \p S is a \c rosa::Seq.
///
/// Usage for a proper buffer type \c TB:\code
/// typename ElementIteratorWriters<TB, typename TB::element_idx_seq_t>::Type
/// \endcode
template
<
typename
TB
,
typename
S
>
struct
ElementWriters
;
/// Template definition.
template
<
typename
TB
,
size_t
...
S0
>
struct
ElementWriters
<
TB
,
Seq
<
S0
...
>>
{
/// The converted type.
using
Type
=
std
::
tuple
<
element_writer_t
<
TB
,
S0
>
...
>
;
};
///@}
/// Convenience template type alias for easy use of \c ElementIteratorWriters.
///
/// Converts a buffer of \c std::tuple into a \c std::tuple of corresponding \c
/// element_writer_t.
///
/// \tparam TB buffer to write into
///
/// \note \p TB is expected to implement an interface matching that of \c
/// TupleWriterBuffer.
template
<
typename
TB
>
using
element_writers_t
=
typename
ElementWriters
<
TB
,
typename
TB
::
element_idx_seq_t
>::
Type
;
/// Template type alias for turning a writer of \c std::tuple into
/// corresponding \c element_writers_t.
///
/// The alias utilizes \c TupleWriterBuffer for buffering values before writing
/// them by \p TupleWriter. The elementwise writers are \c
/// SplittedElementWriter.
///
/// \tparam TupleWriter writer of \c std::tuple
template
<
typename
TupleWriter
>
using
splitted_tuple_writers_t
=
element_writers_t
<
TupleWriterBuffer
<
TupleWriter
>>
;
/// Creates elementwise writers for a writer of \c std::tuple.
///
/// \note Implementation for \c rosa::iterator::splitTupleWriter.
///
/// \tparam TupleWriter writer of \c std::tuple
/// \tparam S0 indices for accessing elements of \c std::tuple
///
/// \param Writer the writer to write tuples with
/// \param Policy how to handle incomplete tuples when destructing the
/// underlying buffer
///
/// \note The last parameter provides indices as template parameter \p S0 and
/// its actual value is ignored.
///
/// \return \c std::tuple of elementwise writers corresponding to \p Writer
template
<
typename
TupleWriter
,
size_t
...
S0
>
splitted_tuple_writers_t
<
TupleWriter
>
splitTupleWriterImpl
(
TupleWriter
&&
Writer
,
const
IncompleteTuplePolicy
Policy
,
Seq
<
S0
...
>
)
noexcept
{
using
TB
=
TupleWriterBuffer
<
TupleWriter
>
;
// Create a buffer in shared pointer. The buffer will be destructed once
// all corresponding element writers (created below) have been destructed.
auto
Buffer
=
std
::
make_shared
<
TB
>
(
std
::
move
(
Writer
),
Policy
);
return
{
element_writer_t
<
TB
,
S0
>
(
Buffer
)...};
}
}
// End namespace
/// Creates elementwise writers for a writer of \c std::tuple.
///
/// \note The implementation utilizes \c splitTupleWriterImpl.
///
/// Obtain elementwise writers for a writer \p Writer of type \c TupleWriter of
/// \c std::tuple<T1, T2, T3> as \code
/// auto [T1Writer, T2Writer, T3Writer] = splitTupleWriter(std::move(Writer),
/// Ignore);
/// \endcode
///
/// The function returns a tuple of writers, which can be assigned to variables
/// using structured binding.
///
/// The function moves its first argument (i.e., \p Writer cannot be used
/// directly after splitting it). If the first argument is a variable, it needs
/// to be moved explicitly by \c std::move() as in the example.
///
/// While a tuple could be written with \p Writer directly as \code
/// Writer << std::make_tuple(T1(), T2(), T3());
/// \endcode The same tuple is written with splitted writers as \code
/// T1Writer << T1();
/// T2Writer << T2();
/// T3Writer << T3();
/// \endcode
///
/// \tparam TupleWriter writer of \c std::tuple
///
/// \note The elements of the written \c std::tuple need to be default
/// constructible because of \c rosa::writer::IncompleteTuplePolicy::Flush.
///
/// \param Writer the writer to write tuples with
/// \param Policy what to do with incomplete tuples when destructing the
/// underlying buffer
///
/// \return \c std::tuple of elementwise writers corresponding to \p Writer
template
<
typename
TupleWriter
>
splitted_tuple_writers_t
<
TupleWriter
>
splitTupleWriter
(
TupleWriter
&&
Writer
,
const
IncompleteTuplePolicy
Policy
)
noexcept
{
return
splitTupleWriterImpl
(
std
::
move
(
Writer
),
Policy
,
seq_t
<
std
::
tuple_size_v
<
typename
TupleWriter
::
value_type
>>
());
}
}
// End namespace writer
}
// End namespace rosa
#endif
// ROSA_SUPPORT_WRITER_SPLIT_TUPLE_WRITER_HPP
File Metadata
Details
Attached
Mime Type
text/x-c++
Expires
Sun, Mar 1, 9:34 PM (1 d, 18 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
286755
Default Alt Text
split_tuple_writer.hpp (19 KB)
Attached To
Mode
R20 SoC_Rosa_repo
Attached
Detach File
Event Timeline
Log In to Comment