diff --git a/include/seqan3/alignment/aligned_sequence/debug_stream_alignment.hpp b/include/seqan3/alignment/aligned_sequence/debug_stream_alignment.hpp index c78b1f920d..535dc88c8b 100644 --- a/include/seqan3/alignment/aligned_sequence/debug_stream_alignment.hpp +++ b/include/seqan3/alignment/aligned_sequence/debug_stream_alignment.hpp @@ -91,27 +91,31 @@ void stream_alignment(debug_stream_type & stream, namespace seqan3 { -/*!\brief Stream operator for alignments, which are represented as tuples of aligned sequences. + +/*!\brief The printer for alignment. + * \tparam alignment_t The type of the alignment; must model seqan3::tuple_like and all sequences must be + * seqan3::aligned_sequence. * \ingroup alignment_aligned_sequence - * - * \tparam alignment_t The alignment type, must satisfy tuple_like and its size must be at least 2. - * - * \param[in,out] stream The target stream for the formatted output. - * \param[in] alignment The alignment that shall be formatted. All sequences must be equally long. - * - * \return The given stream to which the alignment representation is appended. */ -template - requires (detail::debug_streamable_tuple - && detail::all_model_aligned_seq>>) -inline debug_stream_type & operator<<(debug_stream_type & stream, alignment_t && alignment) +template + requires tuple_like && detail::all_model_aligned_seq> +struct alignment_printer { - constexpr size_t sequence_count = std::tuple_size_v>; + /*!\brief The function call operator that pretty prints the alignment to the stream. + * \tparam stream_t The type of the stream. + * \tparam arg_t The type of the argument. + * \param[in,out] stream The target stream for the formatted output. + * \param[in] arg The alignment that shall be formatted. All sequences must be equally long. + */ + template + constexpr void operator()(stream_t & stream, arg_t && arg) const + { + constexpr size_t sequence_count = std::tuple_size_v>; - static_assert(sequence_count >= 2, "An alignment requires at least two sequences."); + static_assert(sequence_count >= 2, "An alignment requires at least two sequences."); - detail::stream_alignment(stream, alignment, std::make_index_sequence{}); - return stream; -} + detail::stream_alignment(stream, std::forward(arg), std::make_index_sequence{}); + } +}; } // namespace seqan3 diff --git a/include/seqan3/alignment/matrix/detail/advanceable_alignment_coordinate.hpp b/include/seqan3/alignment/matrix/detail/advanceable_alignment_coordinate.hpp index 5100314105..23af51f03a 100644 --- a/include/seqan3/alignment/matrix/detail/advanceable_alignment_coordinate.hpp +++ b/include/seqan3/alignment/matrix/detail/advanceable_alignment_coordinate.hpp @@ -294,23 +294,26 @@ class advanceable_alignment_coordinate namespace seqan3 { -/*!\brief A seqan3::detail::advanceable_alignment_coordinate can be printed to the seqan3::debug_stream. - * \tparam coordinate_type The alignment coordinate type. - * \param[in] s The seqan3::debug_stream. - * \param[in] c The alignment coordinate to print. - * \relates seqan3::debug_stream_type +/*!\brief The printer for seqan3::detail::advanceable_alignment_coordinate. * - * \details + * Prints the alignment coordinate as a tuple of the column and row index. * - * Prints the alignment coordinate as a tuple. + * \tparam state_t The state of the detail::advanceable_alignment_coordinate. + * \ingroup alignment_matrix */ -template - requires detail::is_value_specialisation_of_v, - detail::advanceable_alignment_coordinate> -inline debug_stream_type & operator<<(debug_stream_type & s, coordinate_type && c) +template +struct advanceable_alignment_coordinate_printer> { - s << std::tie(c.first, c.second); - return s; -} + /*!\brief The function call operator that prints the coordinate to the given stream. + * \tparam stream_t The type of the stream. + * \param[in,out] stream The stream to print to. + * \param[in] arg The alignment coordinate to print. + */ + template + constexpr void operator()(stream_t & stream, detail::advanceable_alignment_coordinate const arg) const + { + stream << std::tie(arg.first, arg.second); + } +}; } // namespace seqan3 diff --git a/include/seqan3/alignment/matrix/detail/debug_matrix.hpp b/include/seqan3/alignment/matrix/detail/debug_matrix.hpp index 615abe26b5..302dff33a4 100644 --- a/include/seqan3/alignment/matrix/detail/debug_matrix.hpp +++ b/include/seqan3/alignment/matrix/detail/debug_matrix.hpp @@ -467,33 +467,67 @@ debug_matrix(matrix_t &&, namespace seqan3 { -/*!\brief An alignment matrix can be printed to the seqan3::debug_stream. - * \tparam alignment_matrix_t Type of the alignment matrix to be printed; must model seqan3::detail::matrix. - * \param s The seqan3::debug_stream. - * \param matrix The alignment matrix. - * \relates seqan3::debug_stream_type + +/*!\brief The printer for alignment scoring and trace matrices. * - * \details + * Prints the alignment matrix to the given formatted ouput stream. * - * This prints out an alignment matrix which can be a score matrix or a trace matrix. + * \tparam alignment_matrix_t The type of the alignment matrix; must model seqan3::detail::matrix. + * \ingroup alignment_matrix */ -template -inline debug_stream_type & operator<<(debug_stream_type & s, alignment_matrix_t && matrix) +template +struct alignment_matrix_printer { - detail::debug_matrix debug{std::forward(matrix)}; - - std::stringstream sstream{}; - debug.stream_matrix(sstream, s.flags2()); - s << sstream.str(); - return s; -} - -//!\overload -template - requires detail::debug_stream_range_guard && detail::matrix -inline debug_stream_type & operator<<(debug_stream_type & s, alignment_matrix_t && matrix) -{ - return s << detail::debug_matrix{std::forward(matrix)}; -} + /*!\brief Prints the alignment matrix into the given stream using formatting specified by seqan3::fmtflags2. + * + * This overload is selected if the stream is a seqan3::debug_stream_type and has a `flags2()` member function that + * returns a seqan3::fmtflags2 object. + * Using the flags2() member function allows to print the matrix with unicode characters if seqan3::fmtflags2::utf8 + * is set to the seqan3::debug_stream. + * + * \tparam stream_t The type of the stream. + * \tparam arg_t The type of the argument. + * \param stream The stream to print to. + * \param arg The alignment matrix to print + */ + template + requires detail::is_type_specialisation_of_v + constexpr void operator()(stream_t & stream, arg_t && arg) const + { + print_impl(stream.get_underlying_stream(), stream.flags2(), std::forward(arg)); + } + + /*!\brief Prints the alignment matrix into the given stream using ascii formatting. + * + * This overload is selected if the stream is \b not a seqan3::debug_stream_type and always prints + * with seqan3::fmtflags2::none. + * + * \tparam stream_t The type of the stream. + * \tparam arg_t The type of the argument. + * \param stream The stream to print to. + * \param arg The alignment matrix to print. + */ + template + constexpr void operator()(stream_t & stream, arg_t && arg) const + { + print_impl(stream, fmtflags2::none, std::forward(arg)); + } + +private: + /*!\brief Prints the alignment matrix into the given formatted output stream. + * \tparam stream_t The type of the stream. + * \tparam arg_t The type of the argument. + * \param stream The stream to print to. + * \param flags The flags to modify the way the matrix is printed. + * \param arg The alignment matrix to print. + */ + template + void print_impl(stream_t & stream, fmtflags2 const flags, arg_t && arg) const + { + detail::debug_matrix debug{std::forward(arg)}; + + debug.stream_matrix(stream, flags); + } +}; } // namespace seqan3 diff --git a/include/seqan3/alignment/matrix/detail/trace_directions.hpp b/include/seqan3/alignment/matrix/detail/trace_directions.hpp index 1cb9648d94..f518a95b79 100644 --- a/include/seqan3/alignment/matrix/detail/trace_directions.hpp +++ b/include/seqan3/alignment/matrix/detail/trace_directions.hpp @@ -9,8 +9,12 @@ #pragma once +#include +#include + #include #include +#include namespace seqan3::detail { @@ -54,12 +58,7 @@ template <> inline constexpr bool add_enum_bitwise_operators = true; //!\endcond -/*!\brief All trace_directions can be printed as ascii or as utf8 to the seqan3::debug_stream. - * \param s The seqan3::debug_stream. - * \param trace The trace direction. - * \relates seqan3::debug_stream_type - * - * \details +/*!\brief Prints `trace_directions` as ascii or as utf8 to output stream. * * The following table shows the printed symbol of a particular seqan3::detail::trace_directions: * @@ -71,23 +70,71 @@ inline constexpr bool add_enum_bitwise_operators -inline debug_stream_type & operator<<(debug_stream_type & s, detail::trace_directions const trace) +template <> +struct trace_directions_printer { - static char const * unicode[32]{"↺", "↖", "↑", "↖↑", "⇡", "↖⇡", "↑⇡", "↖↑⇡", "←", "↖←", "↑←", - "↖↑←", "⇡←", "↖⇡←", "↑⇡←", "↖↑⇡←", "⇠", "↖⇠", "↑⇠", "↖↑⇠", "⇡⇠", "↖⇡⇠", - "↑⇡⇠", "↖↑⇡⇠", "←⇠", "↖←⇠", "↑←⇠", "↖↑←⇠", "⇡←⇠", "↖⇡←⇠", "↑⇡←⇠", "↖↑⇡←⇠"}; +private: + //!\brief The unicode representation of the trace directions. + static constexpr std::array unicode{ + "↺", "↖", "↑", "↖↑", "⇡", "↖⇡", "↑⇡", "↖↑⇡", "←", "↖←", "↑←", "↖↑←", "⇡←", "↖⇡←", "↑⇡←", "↖↑⇡←", + "⇠", "↖⇠", "↑⇠", "↖↑⇠", "⇡⇠", "↖⇡⇠", "↑⇡⇠", "↖↑⇡⇠", "←⇠", "↖←⇠", "↑←⇠", "↖↑←⇠", "⇡←⇠", "↖⇡←⇠", "↑⇡←⇠", "↖↑⇡←⇠"}; + + //!\brief The ascii representation of the trace directions. + static constexpr std::array csv{ + "N", "D", "U", "DU", "u", "Du", "Uu", "DUu", "L", "DL", "UL", "DUL", "uL", "DuL", "UuL", "DUuL", + "l", "Dl", "Ul", "DUl", "ul", "Dul", "Uul", "DUul", "Ll", "DLl", "ULl", "DULl", "uLl", "DuLl", "UuLl", "DUuLl"}; - static char const * csv[32]{"N", "D", "U", "DU", "u", "Du", "Uu", "DUu", "L", "DL", "UL", - "DUL", "uL", "DuL", "UuL", "DUuL", "l", "Dl", "Ul", "DUl", "ul", "Dul", - "Uul", "DUul", "Ll", "DLl", "ULl", "DULl", "uLl", "DuLl", "UuLl", "DUuLl"}; +public: + /*!\brief Prints the trace directions into the given stream. + * + * This overload is only available if the stream has a member function `flags2` that returns a `fmtflags2`. + * Using the flags2() member function allows to print the trace with unicode characters if seqan3::fmtflags2::utf8 + * is set to the seqan3::debug_stream. + * + * \tparam stream_t The type of the stream. + * \param stream The stream to print to. + * \param trace The trace directions to print. + */ + template + requires detail::is_type_specialisation_of_v + constexpr void operator()(stream_t & stream, detail::trace_directions const trace) const + { + print_impl(stream, stream.flags2(), trace); + } - bool is_unicode = (s.flags2() & fmtflags2::utf8) == fmtflags2::utf8; - auto const & trace_dir = is_unicode ? unicode : csv; + /*!\brief Prints the trace directions into the given stream. + * + * This overload is only available if the stream has no member function `flags2`. In this case it will use + * ascii characters to print the trace. + * + * \tparam stream_t The type of the stream. + * \param stream The stream to print to. + * \param trace The trace directions to print. + */ + template + constexpr void operator()(stream_t & stream, detail::trace_directions const trace) const + { + print_impl(stream, fmtflags2::none, trace); + } - s << trace_dir[static_cast(trace)]; - return s; -} +private: + /*!\brief Prints the trace directions + * \tparam stream_t The type of the stream. + * \param stream The stream to print to. + * \param flag The flags of the stream. + * \param trace The trace directions to print. + */ + template + constexpr void print_impl(stream_t & stream, fmtflags2 const flag, detail::trace_directions const trace) const + { + bool const is_unicode = (flag & fmtflags2::utf8) == fmtflags2::utf8; + auto const & trace_dir = is_unicode ? unicode : csv; + + stream << trace_dir[static_cast(trace)]; + } +}; } // namespace seqan3 diff --git a/include/seqan3/alignment/pairwise/alignment_result.hpp b/include/seqan3/alignment/pairwise/alignment_result.hpp index 04774ce76d..8ef58b6751 100644 --- a/include/seqan3/alignment/pairwise/alignment_result.hpp +++ b/include/seqan3/alignment/pairwise/alignment_result.hpp @@ -10,6 +10,7 @@ #pragma once +#include #include #include @@ -171,6 +172,9 @@ class alignment_result requires seqan3::detail::is_type_specialisation_of_v friend class detail::policy_alignment_result_builder; + template + friend struct alignment_result_printer; + public: /*!\name Constructors, destructor and assignment * \{ @@ -401,54 +405,58 @@ struct alignment_result_value_type_accessor> namespace seqan3 { -/*!\brief Streams the seqan3::alignment_result to the seqan3::debug_stream. + +/*!\brief The printer used for formatted output of the alignment result. * - * \tparam char_t The underlying character type of the seqan3::debug_stream_type. - * \tparam alignment_result_t A type specialisation of seqan3::alignment_result. + * The type of the printer must be a seqan3::alignment_result type. * - * \param[in,out] stream The output stream. - * \param[in] result The alignment result to print. - * \relates seqan3::debug_stream_type + * \tparam result_value_t The type of the alignment result value. + * \ingroup alignment_pairwise */ -template - requires detail::is_type_specialisation_of_v, alignment_result> -inline debug_stream_type & operator<<(debug_stream_type & stream, alignment_result_t && result) +template +struct alignment_result_printer> { - using disabled_t = std::nullopt_t *; - using result_data_t = - typename detail::alignment_result_value_type_accessor>::type; - - constexpr bool has_sequence1_id = !std::is_same_v().sequence1_id), disabled_t>; - constexpr bool has_sequence2_id = !std::is_same_v().sequence2_id), disabled_t>; - constexpr bool has_score = !std::is_same_v().score), disabled_t>; - constexpr bool has_end_positions = - !std::is_same_v().end_positions), disabled_t>; - constexpr bool has_begin_positions = - !std::is_same_v().begin_positions), disabled_t>; - constexpr bool has_alignment = !std::is_same_v().alignment), disabled_t>; - - bool prepend_comma = false; - auto append_to_stream = [&](auto &&... args) + /*!\brief Prints the formatted output of the alignment result to the stream. + * \tparam stream_t The type of the stream. + * \tparam arg_t The type of the argument. + * \param[in,out] stream The output stream. + * \param[in] arg The alignment result to print. + */ + template + constexpr void operator()(stream_t & stream, arg_t && arg) const noexcept { - ((stream << (prepend_comma ? std::string{", "} : std::string{})) << ... << std::forward(args)); - prepend_comma = true; - }; - - stream << '{'; - if constexpr (has_sequence1_id) - append_to_stream("sequence1 id: ", result.sequence1_id()); - if constexpr (has_sequence2_id) - append_to_stream("sequence2 id: ", result.sequence2_id()); - if constexpr (has_score) - append_to_stream("score: ", result.score()); - if constexpr (has_begin_positions) - append_to_stream("begin: (", result.sequence1_begin_position(), ",", result.sequence2_begin_position(), ")"); - if constexpr (has_end_positions) - append_to_stream("end: (", result.sequence1_end_position(), ",", result.sequence2_end_position(), ")"); - if constexpr (has_alignment) - append_to_stream("\nalignment:\n", result.alignment()); - stream << '}'; - - return stream; -} + using disabled_t = std::nullopt_t *; + using result_t = std::remove_cvref_t; + constexpr bool has_sequence1_id = !std::is_same_v; + constexpr bool has_sequence2_id = !std::is_same_v; + constexpr bool has_score = !std::is_same_v; + constexpr bool has_end_positions = !std::is_same_v; + constexpr bool has_begin_positions = !std::is_same_v; + constexpr bool has_alignment = !std::is_same_v; + + bool prepend_comma = false; + auto append_to_stream = [&](auto &&... args) + { + ((stream << (prepend_comma ? std::string{", "} : std::string{})) + << ... << std::forward(args)); + prepend_comma = true; + }; + + stream << '{'; + if constexpr (has_sequence1_id) + append_to_stream("sequence1 id: ", arg.sequence1_id()); + if constexpr (has_sequence2_id) + append_to_stream("sequence2 id: ", arg.sequence2_id()); + if constexpr (has_score) + append_to_stream("score: ", arg.score()); + if constexpr (has_begin_positions) + append_to_stream("begin: (", arg.sequence1_begin_position(), ",", arg.sequence2_begin_position(), ")"); + if constexpr (has_end_positions) + append_to_stream("end: (", arg.sequence1_end_position(), ",", arg.sequence2_end_position(), ")"); + if constexpr (has_alignment) + append_to_stream("\nalignment:\n", arg.alignment()); + stream << '}'; + } +}; + } // namespace seqan3 diff --git a/include/seqan3/alphabet/cigar/cigar.hpp b/include/seqan3/alphabet/cigar/cigar.hpp index 1463901d7c..fd006ac69f 100644 --- a/include/seqan3/alphabet/cigar/cigar.hpp +++ b/include/seqan3/alphabet/cigar/cigar.hpp @@ -207,13 +207,26 @@ class cigar : public alphabet_tuple_base -inline debug_stream_type & operator<<(debug_stream_type & s, cigar const c) +/*!\brief The printer used for formatted output of the cigar alphabets. + * + * The type of the printer must be a seqan3::cigar type. + * + * \ingroup alphabet_cigar + */ +template <> +struct cigar_printer { - s << c.to_string(); - return s; -} + /*!\brief Prints the formatted output of the cigar symbol to the stream. + * \tparam stream_t The type of the stream. + * \param[in,out] stream The output stream. + * \param[in] arg The cigar symbol to print. + */ + template + constexpr void operator()(stream_t & stream, cigar const arg) const noexcept + { + stream << arg.to_string(); + } +}; inline namespace literals { diff --git a/include/seqan3/alphabet/concept.hpp b/include/seqan3/alphabet/concept.hpp index 5e43ebf3ce..80e03b063e 100644 --- a/include/seqan3/alphabet/concept.hpp +++ b/include/seqan3/alphabet/concept.hpp @@ -1065,8 +1065,7 @@ concept writable_alphabet = alphabet && writable_semialphabet && requires * * \{ */ -/*! - * \brief Save an alphabet letter to stream. +/*!\brief Save an alphabet letter to stream. * \tparam archive_t Must satisfy seqan3::cereal_output_archive. * \tparam alphabet_t Type of l; must satisfy seqan3::semialphabet. * \param l The alphabet letter. diff --git a/include/seqan3/alphabet/detail/debug_stream_alphabet.hpp b/include/seqan3/alphabet/detail/debug_stream_alphabet.hpp index 9b7e8e0fe7..a75bc026d3 100644 --- a/include/seqan3/alphabet/detail/debug_stream_alphabet.hpp +++ b/include/seqan3/alphabet/detail/debug_stream_alphabet.hpp @@ -15,38 +15,53 @@ namespace seqan3 { -/*!\name Formatted output overloads - * \{ - */ -/*!\brief All alphabets can be printed to the seqan3::debug_stream by their char representation. - * \tparam alphabet_t Type of the alphabet to be printed; must model seqan3::alphabet. - * \param s The seqan3::debug_stream. - * \param l The alphabet letter. - * \relates seqan3::debug_stream_type + +/*!\brief The printer used for formatted output of seqan3::alphabet types. + * + * Prints the char representation of the given alphabet letter. + * + * \tparam alphabet_t The type of the alphabet to be printed. + * \ingroup alphabet */ -template -inline debug_stream_type & operator<<(debug_stream_type & s, alphabet_t && l) - requires (!output_stream_over, alphabet_t>) +template +struct alphabet_printer { - return s << to_char(l); -} + /*!\brief Print the alphabet to the stream + * \tparam stream_t The type of the stream. + * \param[in,out] stream The stream to print to. + * \param[in] letter The alphabet letter. + */ + template + constexpr void operator()(stream_t & stream, alphabet_t const letter) const noexcept + { + stream << to_char(letter); + } +}; // forward declare seqan3::mask class mask; -/*!\brief Overload for the seqan3::mask alphabet. - * \tparam char_t Type char type of the debug_stream. - * \param s The seqan3::debug_stream. - * \param l The mask alphabet letter. - * \relates seqan3::debug_stream_type +/*!\brief The printer used for formatted output of seqan3::mask alphabet. + * + * Prints "MASKED" if the letter is masked and "UNMASKED" otherwise. + * + * \tparam mask_t The type of the alphabet to be printed. Must be seqan3::mask. + * \ingroup alphabet_mask */ -template -inline debug_stream_type & operator<<(debug_stream_type & s, alphabet_t && l) - requires std::same_as, mask> +template mask_t> +struct mask_printer { - return s << (l == alphabet_t{} ? "UNMASKED" : "MASKED"); -} - -//!\} + /*!\brief Print the mask alphabet to the stream + * \tparam stream_t The type of the stream. + * \param[in,out] stream The stream to print to. + * \param[in] arg The mask alphabet letter. + */ + template + constexpr void operator()(stream_t & stream, mask_t const arg) const noexcept + { + // seqan3::mask is incomplete at this point, so we cannot use `arg == mask{}` + stream << (arg == mask_t{} ? "UNMASKED" : "MASKED"); + } +}; } // namespace seqan3 diff --git a/include/seqan3/argument_parser/auxiliary.hpp b/include/seqan3/argument_parser/auxiliary.hpp index d5af167bca..54461e18e8 100644 --- a/include/seqan3/argument_parser/auxiliary.hpp +++ b/include/seqan3/argument_parser/auxiliary.hpp @@ -201,33 +201,39 @@ concept argument_parser_compatible_option = input_stream_over || named_enumeration; //!\endcond -/*!\name Formatted output overloads - * \{ - */ /*!\brief A type (e.g. an enum) can be made debug streamable by customizing the seqan3::enumeration_names. - * \tparam option_type Type of the enum to be printed. - * \param s The seqan3::debug_stream. - * \param op The value to print. - * \relates seqan3::debug_stream_type - * - * \details * * This searches the seqan3::enumeration_names of the respective type for the value \p op and prints the * respective string if found or '\' if the value cannot be found in the map. + * + * \tparam enum_t Type of the enum to be printed; must model seqan3::named_enumeration. + * \ingroup argument_parser */ -template - requires named_enumeration> -inline debug_stream_type & operator<<(debug_stream_type & s, option_type && op) +template +struct enumeration_printer { - for (auto & [key, value] : enumeration_names) + /*!\brief Prints the associated label of the given enum value. + * \tparam stream_t The type of the stream. + * \param[in,out] stream The output stream. + * \param[in] arg The enum value to print. + * + * If for the given enumeration value no enumeration name can be found, "" is printed. + */ + template + constexpr void operator()(stream_t & stream, enum_t const arg) const { - if (op == value) - return s << key; - } + for (auto & [label, enumerator] : enumeration_names) + { + if (arg == enumerator) + { + stream << label; + return; + } + } - return s << ""; -} -//!\} + stream << ""; + } +}; /*!\brief Used to further specify argument_parser options/flags. * \ingroup argument_parser diff --git a/include/seqan3/core/debug_stream/byte.hpp b/include/seqan3/core/debug_stream/byte.hpp index 2c39246f1e..6bccbcf1af 100644 --- a/include/seqan3/core/debug_stream/byte.hpp +++ b/include/seqan3/core/debug_stream/byte.hpp @@ -16,23 +16,22 @@ namespace seqan3 { -/*!\name Formatted output overloads - * \{ - */ /*!\brief A std::byte can be printed by printing its value as integer. - * \tparam byte_type The type of the input; must be equal to `std::byte`. - * \param[in] s The seqan3::debug_stream. - * \param[in] arg The std::byte. - * \relates seqan3::debug_stream_type + * \ingroup core_debug_stream */ -template - requires std::same_as, std::byte> -inline debug_stream_type & operator<<(debug_stream_type & s, byte_type && arg) +template <> +struct std_byte_printer { - s << std::to_integer(arg); - return s; -} - -//!\} + /*!\brief Prints the byte as uint8_t value. + * \tparam stream_t The type of the stream. + * \param[in,out] stream The output stream. + * \param[in] arg The byte argument to print. + */ + template + constexpr void operator()(stream_t & stream, std::byte const arg) const + { + stream << std::to_integer(arg); + } +}; } // namespace seqan3 diff --git a/include/seqan3/core/debug_stream/debug_stream_type.hpp b/include/seqan3/core/debug_stream/debug_stream_type.hpp index 56c74def92..18458972ea 100644 --- a/include/seqan3/core/debug_stream/debug_stream_type.hpp +++ b/include/seqan3/core/debug_stream/debug_stream_type.hpp @@ -9,9 +9,13 @@ #pragma once +#include #include +#include #include +#include +#include namespace seqan3 { @@ -114,6 +118,13 @@ class debug_stream_type { stream = &out; } + + //!\brief Retrieve the underlying stream. + std::basic_ostream & get_underlying_stream() const noexcept + { + assert(stream != nullptr); + return *stream; + } //!\} /*!\name Formatted output @@ -121,7 +132,19 @@ class debug_stream_type */ //!\brief Forwards to the underlying stream object. template - friend debug_stream_type & operator<<(debug_stream_type & s, t && v); + friend debug_stream_type & operator<<(debug_stream_type & s, t && v) + { + if constexpr (printable_with) + { + std::invoke(default_printer{}, s, std::forward(v)); + } + else + { + std::string const msg = "debug_stream has no print overload for type: " + detail::type_name_as_string; + throw std::runtime_error{msg}; + } + return s; + } //!\brief This overloads enables forwarding std::endl and other manipulators. debug_stream_type & operator<<(std::ostream & (*fp)(std::ostream &)) @@ -130,27 +153,14 @@ class debug_stream_type return *this; } - //!\cond - debug_stream_type & operator<<(int8_t const v) - { - if ((flags2() & fmtflags2::small_int_as_number) == fmtflags2::small_int_as_number) - *stream << static_cast(v); - else - *stream << v; - return *this; - } - - debug_stream_type & operator<<(uint8_t const v) - { - if ((flags2() & fmtflags2::small_int_as_number) == fmtflags2::small_int_as_number) - *stream << static_cast(v); - else - *stream << v; - return *this; - } - //!\endcond //!\} + template + friend struct debug_stream_printer; + + template + friend struct std_printer; + //!\brief This type is std::ios_base::fmtflags using fmtflags = typename std::basic_ostream::fmtflags; @@ -182,17 +192,6 @@ class debug_stream_type stream->unsetf(flag); } -// fmtflags is an enum in libstdc++ and an unsigned in libc++ -#ifdef _LIBCPP_VERSION - static_assert(std::same_as); -#else - //!\copybrief setf() - debug_stream_type & operator<<(fmtflags const flag) - { - setf(flag); - return *this; - } -#endif //!\} /*!\name Format flags (seqan3::fmtflags2) @@ -224,12 +223,6 @@ class debug_stream_type flgs2 &= ~flag; } - //!\copybrief setf() - debug_stream_type & operator<<(fmtflags2 const flag) - { - setf(flag); - return *this; - } //!\} private: @@ -240,12 +233,72 @@ class debug_stream_type fmtflags2 flgs2{fmtflags2::default_}; }; -//!\brief Forwards to the underlying stream object. -template -debug_stream_type & operator<<(debug_stream_type & s, t && v) +/*!\brief A struct that provides a debug stream printer for a specific value type. + * + * This struct provides operator() overloads for printing values of type int8_t, uint8_t, and seqan3::fmtflags2 + * to a debug stream. The operator() overloads handle the formatting of the values based on the + * fmtflags2 settings of the debug stream. + * + * \tparam value_t The type of the value to be printed. + * \ingroup core_debug_stream + */ +template + requires (std::is_same_v, int8_t> + || std::is_same_v, uint8_t> + || std::is_same_v, fmtflags2>) +struct debug_stream_printer { - (*s.stream) << v; - return s; -} + /*!\brief Prints an int8_t value to the debug stream. + * + * \tparam char_t The character type of the debug stream. + * \param stream The debug stream to print to. + * \param v The int8_t value to be printed. + * + * This function prints the int8_t value to the debug stream, taking into account the + * fmtflags2 settings of the stream. If the fmtflags2::small_int_as_number flag is set, + * the value is printed as an int, otherwise it is printed as is. + */ + template + constexpr void operator()(debug_stream_type & stream, int8_t const v) const + { + if ((stream.flags2() & fmtflags2::small_int_as_number) == fmtflags2::small_int_as_number) + *stream.stream << static_cast(v); + else + *stream.stream << v; + } + + /*!\brief Prints a uint8_t value to the debug stream. + * + * \tparam char_t The character type of the debug stream. + * \param stream The debug stream to print to. + * \param v The uint8_t value to be printed. + * + * This function prints the uint8_t value to the debug stream, taking into account the + * fmtflags2 settings of the stream. If the fmtflags2::small_int_as_number flag is set, + * the value is printed as an unsigned int, otherwise it is printed as is. + */ + template + constexpr void operator()(debug_stream_type & stream, uint8_t const v) const + { + if ((stream.flags2() & fmtflags2::small_int_as_number) == fmtflags2::small_int_as_number) + *stream.stream << static_cast(v); + else + *stream.stream << v; + } + + /*!\brief Sets the fmtflags2 of the debug stream. + * + * \tparam char_t The character type of the debug stream. + * \param stream The debug stream to set the fmtflags2 for. + * \param flag The fmtflags2 value to set. + * + * This function sets the fmtflags2 of the debug stream to the specified flag value. + */ + template + constexpr void operator()(debug_stream_type & stream, fmtflags2 const flag) const + { + stream.setf(flag); + } +}; } // namespace seqan3 diff --git a/include/seqan3/core/debug_stream/default_printer.hpp b/include/seqan3/core/debug_stream/default_printer.hpp new file mode 100644 index 0000000000..d0f24d6bcc --- /dev/null +++ b/include/seqan3/core/debug_stream/default_printer.hpp @@ -0,0 +1,236 @@ +// SPDX-FileCopyrightText: 2006-2024 Knut Reinert & Freie Universität Berlin +// SPDX-FileCopyrightText: 2016-2024 Knut Reinert & MPI für molekulare Genetik +// SPDX-License-Identifier: BSD-3-Clause + +/*!\file + * \author Marcel Ehrhardt + * \brief Provides seqan3::default_printer. + */ + +#pragma once + +#include +#include +#include +#include + +#include + +namespace seqan3 +{ + +// clang-format off +/*!\brief A tag that indicates that no printer was found for the given type. + * \ingroup core_debug_stream + */ +struct no_printer_found{}; +template struct advanceable_alignment_coordinate_printer {}; +template struct alignment_matrix_printer {}; +template struct alignment_printer {}; +template struct alignment_result_printer {}; +template struct alphabet_printer {}; +template struct cigar_printer {}; +template struct debug_stream_printer {}; +template struct dynamic_bitset_printer {}; +template struct enumeration_printer {}; +template struct input_range_printer {}; +template struct integer_sequence_printer {}; +template struct integral_printer {}; +template struct mask_printer {}; +template struct optional_printer {}; +template struct sam_flag_printer {}; +template struct sequence_printer {}; +template struct search_result_printer {}; +template struct simd_printer {}; +template struct std_byte_printer {}; +template struct std_variant_printer {}; +template struct std_printer {}; +template struct strong_type_printer {}; +template struct char_sequence_printer {}; +template struct trace_directions_printer {}; +template struct tuple_printer {}; +// clang-format on + +/*! + * \interface seqan3::printable_with <> + * \brief The concept for a printable object. + * + * A printable object is a printer that can print an argument to a stream. + * The printer must be invocable with a stream and an argument. + * + * \tparam printer_t The type of the printer. + * \tparam stream_t The type of the stream. + * \tparam arg_t The type of the argument. + * \ingroup core_debug_stream + */ +template +concept printable_with = std::invocable; + +/*!\brief The printer for standard output streams. + * + * The std_printer is used as a generic fallback to print regular types that can be printed to a + * standard output stream, e.g. std::cout. + * + * \tparam type_t The type of the printable argument. + * \ingroup core_debug_stream + */ +template + requires requires (std::ostream & cout, type_t const & value) { + { cout << value }; + } +struct std_printer +{ + /*!\brief The function call operator that prints the value to the stream. + * + * This function call operator prints the value to underlying stream of the seqan3::debug_stream. + * This overload is only provided if the stream is a seqan3::debug_stream. + * + * \tparam stream_t The type of the stream. + * \tparam arg_t The type of the argument. + * \param[in,out] stream The stream to print to. + * \param[in] arg The value to print. + */ + template + requires requires (stream_t & stream) { stream.stream; } + constexpr void operator()(stream_t & stream, arg_t && arg) const + { + *stream.stream << arg; + } +}; + +/*! + 8 \brief The printer for integral types. + * + * The integral_printer is used to print integral types to a stream. + * + * \tparam integral_t The type of the integral. + * \ingroup core_debug_stream + */ +template +struct integral_printer +{ + /*!\brief The function call operator that prints the integral to the stream. + * \tparam stream_t The type of the stream. + * \param[in,out] stream The stream to print to. + * \param[in] arg The integral to print. + */ + template + constexpr void operator()(stream_t & stream, integral_t const arg) const + { + // note that we assume here that we always can print all std::integral's, + // but this is not correct since std::cout << char32_t{5}; is not possible. + // since char32_t is also an alphabet, we avoid infinite recursion here. + if constexpr (printable_with, stream_t &, integral_t>) + std::invoke(std_printer{}, stream, arg); + else + static_assert(std::same_as, "This type is not printable."); + // We actually want `static_assert(false, "This type is not printable.");`. + // But this only works starting with GCC13. Before that, also the `if constexpr` branches that are not taken + // are evaluated and `static_assert(false)` will always result in an error. As a pre-GCC13 workaround, + // we can make the `false` dependent on some template type, which then will only be evaluated if the branch is + // taken. + } +}; + +/*!\brief The printer_order is a variadic template that defines the order of the printers. + * + * The printer_order is a variadic template that defines the order of the printers. + * It is used to find the first valid printer among all given printers that can print the argument to the given stream. + * The printer_order is used by the seqan3::default_printer. + * + * \tparam printer_templates_t The printer class templates that are used to print the arguments. + * \ingroup core_debug_stream + */ +template