Page MenuHomePhorge

No OneTemporary

Size
20 KB
Referenced Files
None
Subscribers
None
diff --git a/include/rosa/support/writer/split_tuple_writer.hpp b/include/rosa/support/writer/split_tuple_writer.hpp
index 22e3a35..d98ace2 100644
--- a/include/rosa/support/writer/split_tuple_writer.hpp
+++ b/include/rosa/support/writer/split_tuple_writer.hpp
@@ -1,554 +1,562 @@
//===-- 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/sequence.hpp"
#include <memory>
#include <tuple>
#include <queue>
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
+ 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:
ASSERT(false && "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

Mime Type
text/x-diff
Expires
Thu, Jul 3, 7:51 PM (6 h, 42 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
157336
Default Alt Text
(20 KB)

Event Timeline