Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Get seqan3::debug_stream straight once and for all. #63

Closed
14 tasks
marehr opened this issue Apr 25, 2020 · 2 comments · May be fixed by seqan/seqan3#3227
Closed
14 tasks

Get seqan3::debug_stream straight once and for all. #63

marehr opened this issue Apr 25, 2020 · 2 comments · May be fixed by seqan/seqan3#3227
Labels
Epic needs refinement A story that was not discussed and/or estimated by the team yet but is planned for upcoming sprints.

Comments

@marehr
Copy link
Member

marehr commented Apr 25, 2020

Description

As a developer / user I want that the seqan3::debug_stream is as lightweight as possible so that I debug sections of my code without pulling the complete library in and making my problem worst.

Some properties of debug_stream:

  1. value of a type can only be printed if the type was defined before
  2. It must have an order to know which printer it should use to print. This should be done at a single place.
  3. The definition of a debug_stream overload should be cheap. (It should not include more headers)
  4. The definition of a debug_stream overload should be local to what it overloads to.

We currently have the following issues:

  • including seqan3/core/debug_stream.hpp includes
    • seqan3/alignment/aligned_sequence/aligned_sequence_concept.hpp (pulls in alignment stuff)
    • seqan3/core/detail/debug_stream_alphabet.hpp (should only be defined if any alphabet is included; pulls in mask alphabet)
    • seqan3/core/detail/debug_stream_optional.hpp (pulls in std::optional: we could forward declare everything, because of property (1) this would work )
    • seqan3/core/detail/debug_stream_range.hpp (pulls in alphabet concept, char and uint adaptation) This uses the terminology of "sequences" (see Concepts for sequence and alignment #60) and prints a std::vector<char> as it was a std::string. Pulls in complete seqan3/std/ranges. Not sure if we somehow can forward declare this. The ranges library has forward header, we should definitely add a seqan3/std/ranges_fwd.hpp header. We should assume that the <ranges> header is stable and works properly.
    • seqan3/core/detail/debug_stream_tuple.hpp (could be forward declared; pulls also tuple_like concept in; pulls in alphabets and ranges?!; put generic debug_stream overload in seqan3/core/concept/tuple.hpp?)
    • seqan3/core/detail/debug_stream_type.hpp (this defines unused features like an own flag definition which make the design much more complicated)
    • seqan3/core/detail/debug_stream_variant.hpp (could be forward declared)
  • including seqan3/core/to_string.hpp includes seqan3/core/debug_stream.hpp (see problems above)
  • including seqan3/test/pretty_printing.hpp includes seqan3/core/debug_stream.hpp (see problems above)
  • We can't select (internally) what we want to debug print, for example a range or a sequence. This is critical if you want to print values where you know of which kind they are
  • It is hard to know which overload resolution is used and we sometimes need to pull in more headers to exclude other overloads even if we don't need them. For example seqan3/core/detail/debug_stream_tuple.hpp pulls in the "completely" related alphabets and ranges in to resolve an ambiguous overload an alphabet_tuple and a more generic tuple.
  • debug_stream accepts anything, but can't handle everything; big oof debug_stream << unknown_type{}; (this is also not SFINAE friendly, because syntactically from the signature debug_stream allows any type but it might not be able to handle it) If we accept anything, then we should also compile for everything and give a runtime exception if an overload is missing.
  • We can't differentiate if a debug_stream overload comes from us or if it comes from std::ostream. This would be handy for googletest overload of PrintTo.
  • Overloading << has the same issues as for std::ostream! A HUUUGGGGH stack trace. These overloads should be kept to a absolutely minimum.
  • All these dependencies make it really hard to add support new compilers and platforms since every little thing can break and if every test includes seqan3/test/pretty_print.hpp it basically pulls in half of our library which makes it extremely hard to find the issue.
  • It also breaks the idea that a unit test is the smallest possible subset to test a functionality.
  • We should only include in tests the debug_stream overloads that we need. (If a test does not need ranges, we should not include it)

Acceptance Criteria

  • test 1
  • test 2
  • test 3

Tasks

  • task 1
  • task 2
  • task 3

Definition of Done

  • Implementation and design approved
  • Unit tests pass
  • Test coverage = 100%
  • Microbenchmarks added and/or affected microbenchmarks < 5% performance drop
  • API documentation added
  • Tutorial/teaching material added
  • Test suite compiles in less than 30 seconds (on travis)
  • Changelog entry added
@marehr marehr added the needs refinement A story that was not discussed and/or estimated by the team yet but is planned for upcoming sprints. label Apr 25, 2020
@rrahn
Copy link
Contributor

rrahn commented May 11, 2020

Team resolution:

@smehringer
Copy link
Member

smehringer commented Oct 20, 2022

FMPOV this is done

marehr added a commit to marehr/seqan3 that referenced this issue Jan 1, 2024
This PR will resolve seqan/product_backlog#63.

This issue is a long-standing regret of mine. I hope that you can take it over and push it over the finishing line.
The state of the current PR is just a draft of an idea.

I'll comment on multiple code locations to point out the advantages of the new design.

# The old design

The major idea of the design is due to the following observation:

> We use overload resolution and in particular overload ordering via concepts to determine the order in which we should print

Just to give a small example (the more down, the more specialized):

```cpp
  std::cout //-printable [1]
< seqan3::tuple_like // [4]
< std::ranges::input_range // [2]
< std::vector<uint8_t> // [3]
< seqan3::sequence // [3]
< char * // [2]

  std::cout //-printable [1]
< char // [5]
< seqan3::tuple_like // [4]
< seqan3::alphabet // [5]
< seqan3::mask // [6]
```

NOTE: that using concepts as overload resolution always uses a partially ordered set, which can be depicted by as a [Hasse Diagram](https://en.wikipedia.org/wiki/Hasse_diagram), and by using the except clauses via `requires` we give it a total order.

# The new design

### Before anything, note that the new design does not break any existing code. As existing `seqan3::debugstream << ` overloads, still take place in overload resolution ().

The idea is simple:
* Have a list of printers.
* The order of the printers dictates in which order an object should be printed.
* We allow that multiple printers might be viable to print a type.
* Each `printer<type>` either has a function object / lambda `print` or not; depending on whether the `printer` can print that `type` or not (implemented by [template specialization](https://en.cppreference.com/w/cpp/language/template_specialization) )

So put together: For a given type, ask every printer in order whether it can print that type and the first one to answer yes, is the selected printer.

----

[1] If all overloads do not work, use `std::ostream`
https://github.com/seqan/seqan3/blob/6b681fb2eae5ab2997d293e99fc6a7f869a20316/include/seqan3/core/debug_stream/debug_stream_type.hpp#L242-L247
[2] Use this for all `std::ranges::input_range`s except if type is something like `std::filesystem` (type == range_value_t) or `char *`
https://github.com/seqan/seqan3/blob/6b681fb2eae5ab2997d293e99fc6a7f869a20316/include/seqan3/core/debug_stream/range.hpp#L96-L98
https://github.com/seqan/seqan3/blob/6b681fb2eae5ab2997d293e99fc6a7f869a20316/include/seqan3/core/debug_stream/range.hpp#L38-L45
[3] Same as [2] where value_type is an alphabet but only if the alphabet is not an `unsigned int` (this condition has no test case?!)
https://github.com/seqan/seqan3/blob/6b681fb2eae5ab2997d293e99fc6a7f869a20316/include/seqan3/core/debug_stream/range.hpp#L138-L141
[4] Use this for all `std::tuple`-like types except if it is a `std::ranges::input_range` (what is a tuple and ranges at the same time?!) and an `seqan3::alphabet` (basically `seqan3::alphabet_tuple_base` derived types)
https://github.com/seqan/seqan3/blob/6b681fb2eae5ab2997d293e99fc6a7f869a20316/include/seqan3/core/debug_stream/tuple.hpp#L53-L56
[5] Use this for all `seqan3::alphabet`s except if it can be printed by `std::cout` (like `char`)
https://github.com/seqan/seqan3/blob/6b681fb2eae5ab2997d293e99fc6a7f869a20316/include/seqan3/alphabet/detail/debug_stream_alphabet.hpp#L30-L32
[6] Type must be `seqan3::semialphabet` and `seqan3::mask`
https://github.com/seqan/seqan3/blob/6b681fb2eae5ab2997d293e99fc6a7f869a20316/include/seqan3/alphabet/detail/debug_stream_alphabet.hpp#L46-L48
marehr added a commit to marehr/seqan3 that referenced this issue Jan 1, 2024
This PR will resolve seqan/product_backlog#63.

This issue is a long-standing regret of mine. I hope that you can take it over and push it over the finishing line.
The state of the current PR is just a draft of an idea.

I'll comment on multiple code locations to point out the advantages of the new design.

# The old design

The major idea of the design is due to the following observation:

> We use overload resolution and in particular overload ordering via concepts to determine the order in which we should print

Just to give a small example (the more down, the more specialized):

```cpp
  std::cout //-printable [1]
< seqan3::tuple_like // [4]
< std::ranges::input_range // [2]
< std::vector<uint8_t> // [3]
< seqan3::sequence // [3]
< char * // [2]

  std::cout //-printable [1]
< char // [5]
< seqan3::tuple_like // [4]
< seqan3::alphabet // [5]
< seqan3::mask // [6]
```

NOTE: that using concepts as overload resolution always uses a partially ordered set, which can be depicted by as a [Hasse Diagram](https://en.wikipedia.org/wiki/Hasse_diagram), and by using the except clauses via `requires` we give it a total order.

# The new design

### Before anything, note that the new design does not break any existing code. As existing `seqan3::debugstream << ` overloads, still take place in overload resolution ().

The idea is simple:
* Have a list of printers.
* The order of the printers dictates in which order an object should be printed.
* We allow that multiple printers might be viable to print a type.
* Each `printer<type>` either has a function object / lambda `print` or not; depending on whether the `printer` can print that `type` or not (implemented by [template specialization](https://en.cppreference.com/w/cpp/language/template_specialization) )

So put together: For a given type, ask every printer in order whether it can print that type and the first one to answer yes, is the selected printer.

----

[1] If all overloads do not work, use `std::ostream`
https://github.com/seqan/seqan3/blob/6b681fb2eae5ab2997d293e99fc6a7f869a20316/include/seqan3/core/debug_stream/debug_stream_type.hpp#L242-L247
[2] Use this for all `std::ranges::input_range`s except if type is something like `std::filesystem` (type == range_value_t) or `char *`
https://github.com/seqan/seqan3/blob/6b681fb2eae5ab2997d293e99fc6a7f869a20316/include/seqan3/core/debug_stream/range.hpp#L96-L98
https://github.com/seqan/seqan3/blob/6b681fb2eae5ab2997d293e99fc6a7f869a20316/include/seqan3/core/debug_stream/range.hpp#L38-L45
[3] Same as [2] where value_type is an alphabet but only if the alphabet is not an `unsigned int` (this condition has no test case?!)
https://github.com/seqan/seqan3/blob/6b681fb2eae5ab2997d293e99fc6a7f869a20316/include/seqan3/core/debug_stream/range.hpp#L138-L141
[4] Use this for all `std::tuple`-like types except if it is a `std::ranges::input_range` (what is a tuple and ranges at the same time?!) and an `seqan3::alphabet` (basically `seqan3::alphabet_tuple_base` derived types)
https://github.com/seqan/seqan3/blob/6b681fb2eae5ab2997d293e99fc6a7f869a20316/include/seqan3/core/debug_stream/tuple.hpp#L53-L56
[5] Use this for all `seqan3::alphabet`s except if it can be printed by `std::cout` (like `char`)
https://github.com/seqan/seqan3/blob/6b681fb2eae5ab2997d293e99fc6a7f869a20316/include/seqan3/alphabet/detail/debug_stream_alphabet.hpp#L30-L32
[6] Type must be `seqan3::semialphabet` and `seqan3::mask`
https://github.com/seqan/seqan3/blob/6b681fb2eae5ab2997d293e99fc6a7f869a20316/include/seqan3/alphabet/detail/debug_stream_alphabet.hpp#L46-L48
marehr added a commit to marehr/seqan3 that referenced this issue Jan 1, 2024
This PR will resolve seqan/product_backlog#63.

This issue is a long-standing regret of mine. I hope that you can take it over and push it over the finishing line.
The state of the current PR is just a draft of an idea.

I'll comment on multiple code locations to point out the advantages of the new design.

# The old design

The major idea of the design is due to the following observation:

> We use overload resolution and in particular overload ordering via concepts to determine the order in which we should print

Just to give a small example (the more down, the more specialized):

```cpp
  std::cout //-printable [1]
< seqan3::tuple_like // [4]
< std::ranges::input_range // [2]
< std::vector<uint8_t> // [3]
< seqan3::sequence // [3]
< char * // [2]

  std::cout //-printable [1]
< char // [5]
< seqan3::tuple_like // [4]
< seqan3::alphabet // [5]
< seqan3::mask // [6]
```

NOTE: that using concepts as overload resolution always uses a partially ordered set, which can be depicted by as a [Hasse Diagram](https://en.wikipedia.org/wiki/Hasse_diagram), and by using the except clauses via `requires` we give it a total order.

# The new design

### Before anything, note that the new design does not break any existing code. As existing `seqan3::debugstream << ` overloads, still take place in overload resolution ().

The idea is simple:
* Have a list of printers.
* The order of the printers dictates in which order an object should be printed.
* We allow that multiple printers might be viable to print a type.
* Each `printer<type>` either has a function object / lambda `print` or not; depending on whether the `printer` can print that `type` or not (implemented by [template specialization](https://en.cppreference.com/w/cpp/language/template_specialization) )

So put together: For a given type, ask every printer in order whether it can print that type and the first one to answer yes, is the selected printer.

----

[1] If all overloads do not work, use `std::ostream`
https://github.com/seqan/seqan3/blob/6b681fb2eae5ab2997d293e99fc6a7f869a20316/include/seqan3/core/debug_stream/debug_stream_type.hpp#L242-L247
[2] Use this for all `std::ranges::input_range`s except if type is something like `std::filesystem` (type == range_value_t) or `char *`
https://github.com/seqan/seqan3/blob/6b681fb2eae5ab2997d293e99fc6a7f869a20316/include/seqan3/core/debug_stream/range.hpp#L96-L98
https://github.com/seqan/seqan3/blob/6b681fb2eae5ab2997d293e99fc6a7f869a20316/include/seqan3/core/debug_stream/range.hpp#L38-L45
[3] Same as [2] where value_type is an alphabet but only if the alphabet is not an `unsigned int` (this condition has no test case?!)
https://github.com/seqan/seqan3/blob/6b681fb2eae5ab2997d293e99fc6a7f869a20316/include/seqan3/core/debug_stream/range.hpp#L138-L141
[4] Use this for all `std::tuple`-like types except if it is a `std::ranges::input_range` (what is a tuple and ranges at the same time?!) and an `seqan3::alphabet` (basically `seqan3::alphabet_tuple_base` derived types)
https://github.com/seqan/seqan3/blob/6b681fb2eae5ab2997d293e99fc6a7f869a20316/include/seqan3/core/debug_stream/tuple.hpp#L53-L56
[5] Use this for all `seqan3::alphabet`s except if it can be printed by `std::cout` (like `char`)
https://github.com/seqan/seqan3/blob/6b681fb2eae5ab2997d293e99fc6a7f869a20316/include/seqan3/alphabet/detail/debug_stream_alphabet.hpp#L30-L32
[6] Type must be `seqan3::semialphabet` and `seqan3::mask`
https://github.com/seqan/seqan3/blob/6b681fb2eae5ab2997d293e99fc6a7f869a20316/include/seqan3/alphabet/detail/debug_stream_alphabet.hpp#L46-L48
marehr added a commit to marehr/seqan3 that referenced this issue Jan 1, 2024
This PR will resolve seqan/product_backlog#63.

This issue is a long-standing regret of mine. I hope that you can take it over and push it over the finishing line.
The state of the current PR is just a draft of an idea.

I'll comment on multiple code locations to point out the advantages of the new design.

# The old design

The major idea of the design is due to the following observation:

> We use overload resolution and in particular overload ordering via concepts to determine the order in which we should print

Just to give a small example (the more down, the more specialized):

```cpp
  std::cout //-printable [1]
< seqan3::tuple_like // [4]
< std::ranges::input_range // [2]
< std::vector<uint8_t> // [3]
< seqan3::sequence // [3]
< char * // [2]

  std::cout //-printable [1]
< char // [5]
< seqan3::tuple_like // [4]
< seqan3::alphabet // [5]
< seqan3::mask // [6]
```

NOTE: that using concepts as overload resolution always uses a partially ordered set, which can be depicted by as a [Hasse Diagram](https://en.wikipedia.org/wiki/Hasse_diagram), and by using the except clauses via `requires` we give it a total order.

# The new design

### Before anything, note that the new design does not break any existing code. As existing `seqan3::debugstream << ` overloads, still take place in overload resolution ().

The idea is simple:
* Have a list of printers.
* The order of the printers dictates in which order an object should be printed.
* We allow that multiple printers might be viable to print a type.
* Each `printer<type>` either has a function object / lambda `print` or not; depending on whether the `printer` can print that `type` or not (implemented by [template specialization](https://en.cppreference.com/w/cpp/language/template_specialization) )

So put together: For a given type, ask every printer in order whether it can print that type and the first one to answer yes, is the selected printer.

----

[1] If all overloads do not work, use `std::ostream`
https://github.com/seqan/seqan3/blob/6b681fb2eae5ab2997d293e99fc6a7f869a20316/include/seqan3/core/debug_stream/debug_stream_type.hpp#L242-L247
[2] Use this for all `std::ranges::input_range`s except if type is something like `std::filesystem` (type == range_value_t) or `char *`
https://github.com/seqan/seqan3/blob/6b681fb2eae5ab2997d293e99fc6a7f869a20316/include/seqan3/core/debug_stream/range.hpp#L96-L98
https://github.com/seqan/seqan3/blob/6b681fb2eae5ab2997d293e99fc6a7f869a20316/include/seqan3/core/debug_stream/range.hpp#L38-L45
[3] Same as [2] where value_type is an alphabet but only if the alphabet is not an `unsigned int` (this condition has no test case?!)
https://github.com/seqan/seqan3/blob/6b681fb2eae5ab2997d293e99fc6a7f869a20316/include/seqan3/core/debug_stream/range.hpp#L138-L141
[4] Use this for all `std::tuple`-like types except if it is a `std::ranges::input_range` (what is a tuple and ranges at the same time?!) and an `seqan3::alphabet` (basically `seqan3::alphabet_tuple_base` derived types)
https://github.com/seqan/seqan3/blob/6b681fb2eae5ab2997d293e99fc6a7f869a20316/include/seqan3/core/debug_stream/tuple.hpp#L53-L56
[5] Use this for all `seqan3::alphabet`s except if it can be printed by `std::cout` (like `char`)
https://github.com/seqan/seqan3/blob/6b681fb2eae5ab2997d293e99fc6a7f869a20316/include/seqan3/alphabet/detail/debug_stream_alphabet.hpp#L30-L32
[6] Type must be `seqan3::semialphabet` and `seqan3::mask`
https://github.com/seqan/seqan3/blob/6b681fb2eae5ab2997d293e99fc6a7f869a20316/include/seqan3/alphabet/detail/debug_stream_alphabet.hpp#L46-L48
marehr added a commit to marehr/seqan3 that referenced this issue Jan 2, 2024
This PR will resolve seqan/product_backlog#63.

This issue is a long-standing regret of mine. I hope that you can take it over and push it over the finishing line.
The state of the current PR is just a draft of an idea.

I'll comment on multiple code locations to point out the advantages of the new design.

# The old design

The major idea of the design is due to the following observation:

> We use overload resolution and in particular overload ordering via concepts to determine the order in which we should print

Just to give a small example (the more down, the more specialized):

```cpp
  std::cout //-printable [1]
< seqan3::tuple_like // [4]
< std::ranges::input_range // [2]
< std::vector<uint8_t> // [3]
< seqan3::sequence // [3]
< char * // [2]

  std::cout //-printable [1]
< char // [5]
< seqan3::tuple_like // [4]
< seqan3::alphabet // [5]
< seqan3::mask // [6]
```

NOTE: that using concepts as overload resolution always uses a partially ordered set, which can be depicted by as a [Hasse Diagram](https://en.wikipedia.org/wiki/Hasse_diagram), and by using the except clauses via `requires` we give it a total order.

# The new design

### Before anything, note that the new design does not break any existing code. As existing `seqan3::debugstream << ` overloads, still take place in overload resolution ().

The idea is simple:
* Have a list of printers.
* The order of the printers dictates in which order an object should be printed.
* We allow that multiple printers might be viable to print a type.
* Each `printer<type>` either has a function object / lambda `print` or not; depending on whether the `printer` can print that `type` or not (implemented by [template specialization](https://en.cppreference.com/w/cpp/language/template_specialization) )

So put together: For a given type, ask every printer in order whether it can print that type and the first one to answer yes, is the selected printer.

----

[1] If all overloads do not work, use `std::ostream`
https://github.com/seqan/seqan3/blob/6b681fb2eae5ab2997d293e99fc6a7f869a20316/include/seqan3/core/debug_stream/debug_stream_type.hpp#L242-L247
[2] Use this for all `std::ranges::input_range`s except if type is something like `std::filesystem` (type == range_value_t) or `char *`
https://github.com/seqan/seqan3/blob/6b681fb2eae5ab2997d293e99fc6a7f869a20316/include/seqan3/core/debug_stream/range.hpp#L96-L98
https://github.com/seqan/seqan3/blob/6b681fb2eae5ab2997d293e99fc6a7f869a20316/include/seqan3/core/debug_stream/range.hpp#L38-L45
[3] Same as [2] where value_type is an alphabet but only if the alphabet is not an `unsigned int` (this condition has no test case?!)
https://github.com/seqan/seqan3/blob/6b681fb2eae5ab2997d293e99fc6a7f869a20316/include/seqan3/core/debug_stream/range.hpp#L138-L141
[4] Use this for all `std::tuple`-like types except if it is a `std::ranges::input_range` (what is a tuple and ranges at the same time?!) and an `seqan3::alphabet` (basically `seqan3::alphabet_tuple_base` derived types)
https://github.com/seqan/seqan3/blob/6b681fb2eae5ab2997d293e99fc6a7f869a20316/include/seqan3/core/debug_stream/tuple.hpp#L53-L56
[5] Use this for all `seqan3::alphabet`s except if it can be printed by `std::cout` (like `char`)
https://github.com/seqan/seqan3/blob/6b681fb2eae5ab2997d293e99fc6a7f869a20316/include/seqan3/alphabet/detail/debug_stream_alphabet.hpp#L30-L32
[6] Type must be `seqan3::semialphabet` and `seqan3::mask`
https://github.com/seqan/seqan3/blob/6b681fb2eae5ab2997d293e99fc6a7f869a20316/include/seqan3/alphabet/detail/debug_stream_alphabet.hpp#L46-L48
marehr added a commit to marehr/seqan3 that referenced this issue Jan 6, 2024
This PR will resolve seqan/product_backlog#63.

This issue is a long-standing open to-do of mine. I hope that you can take it over and push it over the finishing line.
The state of the current PR is just a draft of an idea.

I'll comment on multiple code locations to point out the advantages of the new design.

# The old design

The major idea of the design is due to the following observation:

> We use overload resolution and in particular overload ordering via concepts to determine the order in which we should print

Just to give a small example (the more down, the more specialized):

```cpp
  std::cout //-printable [1]
< seqan3::tuple_like // [4]
< std::ranges::input_range // [2]
< std::vector<uint8_t> // [3]
< seqan3::sequence // [3]
< char * // [2]

  std::cout //-printable [1]
< char // [5]
< seqan3::tuple_like // [4]
< seqan3::alphabet // [5]
< seqan3::mask // [6]
```

NOTE: that using concepts as overload resolution always uses a partially ordered set, which can be depicted by as a [Hasse Diagram](https://en.wikipedia.org/wiki/Hasse_diagram), and by using the except clauses via `requires` we give it a total order.

# The new design

### Before anything, note that the new design does not break any existing code. As existing `seqan3::debugstream << ` overloads, still take place in overload resolution.

The idea is simple:
* Have a list of printers.
* The order of the printers dictates in which order an object should be printed.
* We allow that multiple printers might be viable to print a type.
* Each `printer<type>` either has a function object / lambda `print` or not;
  depending on whether the `printer` can print that `type` or not (implemented by
  [template specialization](https://en.cppreference.com/w/cpp/language/template_specialization))
* We can explicitly use `printer` in other printer's, if we know that
  only that overload should be used,

So put together: For a given type, ask every printer in order whether it can print that type and the first one to answer yes, is the selected printer.

----

[1] If all overloads do not work, use `std::ostream`
https://github.com/seqan/seqan3/blob/6b681fb2eae5ab2997d293e99fc6a7f869a20316/include/seqan3/core/debug_stream/debug_stream_type.hpp#L242-L247
[2] Use this for all `std::ranges::input_range`s except if type is something like `std::filesystem` (type == range_value_t) or `char *`
https://github.com/seqan/seqan3/blob/6b681fb2eae5ab2997d293e99fc6a7f869a20316/include/seqan3/core/debug_stream/range.hpp#L96-L98
https://github.com/seqan/seqan3/blob/6b681fb2eae5ab2997d293e99fc6a7f869a20316/include/seqan3/core/debug_stream/range.hpp#L38-L45
[3] Same as [2] where value_type is an alphabet but only if the alphabet is not an `unsigned int` (this condition has no test case?!)
https://github.com/seqan/seqan3/blob/6b681fb2eae5ab2997d293e99fc6a7f869a20316/include/seqan3/core/debug_stream/range.hpp#L138-L141
[4] Use this for all `std::tuple`-like types except if it is a `std::ranges::input_range` (what is a tuple and ranges at the same time?!) and an `seqan3::alphabet` (basically `seqan3::alphabet_tuple_base` derived types)
https://github.com/seqan/seqan3/blob/6b681fb2eae5ab2997d293e99fc6a7f869a20316/include/seqan3/core/debug_stream/tuple.hpp#L53-L56
[5] Use this for all `seqan3::alphabet`s except if it can be printed by `std::cout` (like `char`)
https://github.com/seqan/seqan3/blob/6b681fb2eae5ab2997d293e99fc6a7f869a20316/include/seqan3/alphabet/detail/debug_stream_alphabet.hpp#L30-L32
[6] Type must be `seqan3::semialphabet` and `seqan3::mask`
https://github.com/seqan/seqan3/blob/6b681fb2eae5ab2997d293e99fc6a7f869a20316/include/seqan3/alphabet/detail/debug_stream_alphabet.hpp#L46-L48
marehr added a commit to marehr/seqan3 that referenced this issue Jan 25, 2024
This PR will resolve seqan/product_backlog#63.

This issue is a long-standing open to-do of mine. I hope that you can take it over and push it over the finishing line.
The state of the current PR is just a draft of an idea.

I'll comment on multiple code locations to point out the advantages of the new design.

# The old design

The major idea of the design is due to the following observation:

> We use overload resolution and in particular overload ordering via concepts to determine the order in which we should print

Just to give a small example (the more down, the more specialized):

```cpp
  std::cout //-printable [1]
< seqan3::tuple_like // [4]
< std::ranges::input_range // [2]
< std::vector<uint8_t> // [3]
< seqan3::sequence // [3]
< char * // [2]

  std::cout //-printable [1]
< char // [5]
< seqan3::tuple_like // [4]
< seqan3::alphabet // [5]
< seqan3::mask // [6]
```

NOTE: that using concepts as overload resolution always uses a partially ordered set, which can be depicted by as a [Hasse Diagram](https://en.wikipedia.org/wiki/Hasse_diagram), and by using the except clauses via `requires` we give it a total order.

# The new design

### Before anything, note that the new design does not break any existing code. As existing `seqan3::debugstream << ` overloads, still take place in overload resolution.

The idea is simple:
* Have a list of printers.
* The order of the printers dictates in which order an object should be printed.
* We allow that multiple printers might be viable to print a type.
* Each `printer<type>` either has a function object / lambda `print` or not;
  depending on whether the `printer` can print that `type` or not (implemented by
  [template specialization](https://en.cppreference.com/w/cpp/language/template_specialization))
* We can explicitly use `printer` in other printer's, if we know that
  only that overload should be used,

So put together: For a given type, ask every printer in order whether it can print that type and the first one to answer yes, is the selected printer.

----

[1] If all overloads do not work, use `std::ostream`
https://github.com/seqan/seqan3/blob/6b681fb2eae5ab2997d293e99fc6a7f869a20316/include/seqan3/core/debug_stream/debug_stream_type.hpp#L242-L247
[2] Use this for all `std::ranges::input_range`s except if type is something like `std::filesystem` (type == range_value_t) or `char *`
https://github.com/seqan/seqan3/blob/6b681fb2eae5ab2997d293e99fc6a7f869a20316/include/seqan3/core/debug_stream/range.hpp#L96-L98
https://github.com/seqan/seqan3/blob/6b681fb2eae5ab2997d293e99fc6a7f869a20316/include/seqan3/core/debug_stream/range.hpp#L38-L45
[3] Same as [2] where value_type is an alphabet but only if the alphabet is not an `unsigned int` (this condition has no test case?!)
https://github.com/seqan/seqan3/blob/6b681fb2eae5ab2997d293e99fc6a7f869a20316/include/seqan3/core/debug_stream/range.hpp#L138-L141
[4] Use this for all `std::tuple`-like types except if it is a `std::ranges::input_range` (what is a tuple and ranges at the same time?!) and an `seqan3::alphabet` (basically `seqan3::alphabet_tuple_base` derived types)
https://github.com/seqan/seqan3/blob/6b681fb2eae5ab2997d293e99fc6a7f869a20316/include/seqan3/core/debug_stream/tuple.hpp#L53-L56
[5] Use this for all `seqan3::alphabet`s except if it can be printed by `std::cout` (like `char`)
https://github.com/seqan/seqan3/blob/6b681fb2eae5ab2997d293e99fc6a7f869a20316/include/seqan3/alphabet/detail/debug_stream_alphabet.hpp#L30-L32
[6] Type must be `seqan3::semialphabet` and `seqan3::mask`
https://github.com/seqan/seqan3/blob/6b681fb2eae5ab2997d293e99fc6a7f869a20316/include/seqan3/alphabet/detail/debug_stream_alphabet.hpp#L46-L48
eseiler pushed a commit to marehr/seqan3 that referenced this issue Jan 26, 2024
This PR will resolve seqan/product_backlog#63.

This issue is a long-standing open to-do of mine. I hope that you can take it over and push it over the finishing line.
The state of the current PR is just a draft of an idea.

I'll comment on multiple code locations to point out the advantages of the new design.

# The old design

The major idea of the design is due to the following observation:

> We use overload resolution and in particular overload ordering via concepts to determine the order in which we should print

Just to give a small example (the more down, the more specialized):

```cpp
  std::cout //-printable [1]
< seqan3::tuple_like // [4]
< std::ranges::input_range // [2]
< std::vector<uint8_t> // [3]
< seqan3::sequence // [3]
< char * // [2]

  std::cout //-printable [1]
< char // [5]
< seqan3::tuple_like // [4]
< seqan3::alphabet // [5]
< seqan3::mask // [6]
```

NOTE: that using concepts as overload resolution always uses a partially ordered set, which can be depicted by as a [Hasse Diagram](https://en.wikipedia.org/wiki/Hasse_diagram), and by using the except clauses via `requires` we give it a total order.

# The new design

### Before anything, note that the new design does not break any existing code. As existing `seqan3::debugstream << ` overloads, still take place in overload resolution.

The idea is simple:
* Have a list of printers.
* The order of the printers dictates in which order an object should be printed.
* We allow that multiple printers might be viable to print a type.
* Each `printer<type>` either has a function object / lambda `print` or not;
  depending on whether the `printer` can print that `type` or not (implemented by
  [template specialization](https://en.cppreference.com/w/cpp/language/template_specialization))
* We can explicitly use `printer` in other printer's, if we know that
  only that overload should be used,

So put together: For a given type, ask every printer in order whether it can print that type and the first one to answer yes, is the selected printer.

----

[1] If all overloads do not work, use `std::ostream`
https://github.com/seqan/seqan3/blob/6b681fb2eae5ab2997d293e99fc6a7f869a20316/include/seqan3/core/debug_stream/debug_stream_type.hpp#L242-L247
[2] Use this for all `std::ranges::input_range`s except if type is something like `std::filesystem` (type == range_value_t) or `char *`
https://github.com/seqan/seqan3/blob/6b681fb2eae5ab2997d293e99fc6a7f869a20316/include/seqan3/core/debug_stream/range.hpp#L96-L98
https://github.com/seqan/seqan3/blob/6b681fb2eae5ab2997d293e99fc6a7f869a20316/include/seqan3/core/debug_stream/range.hpp#L38-L45
[3] Same as [2] where value_type is an alphabet but only if the alphabet is not an `unsigned int` (this condition has no test case?!)
https://github.com/seqan/seqan3/blob/6b681fb2eae5ab2997d293e99fc6a7f869a20316/include/seqan3/core/debug_stream/range.hpp#L138-L141
[4] Use this for all `std::tuple`-like types except if it is a `std::ranges::input_range` (what is a tuple and ranges at the same time?!) and an `seqan3::alphabet` (basically `seqan3::alphabet_tuple_base` derived types)
https://github.com/seqan/seqan3/blob/6b681fb2eae5ab2997d293e99fc6a7f869a20316/include/seqan3/core/debug_stream/tuple.hpp#L53-L56
[5] Use this for all `seqan3::alphabet`s except if it can be printed by `std::cout` (like `char`)
https://github.com/seqan/seqan3/blob/6b681fb2eae5ab2997d293e99fc6a7f869a20316/include/seqan3/alphabet/detail/debug_stream_alphabet.hpp#L30-L32
[6] Type must be `seqan3::semialphabet` and `seqan3::mask`
https://github.com/seqan/seqan3/blob/6b681fb2eae5ab2997d293e99fc6a7f869a20316/include/seqan3/alphabet/detail/debug_stream_alphabet.hpp#L46-L48
eseiler pushed a commit to marehr/seqan3 that referenced this issue Jan 30, 2024
This PR will resolve seqan/product_backlog#63.

This issue is a long-standing open to-do of mine. I hope that you can take it over and push it over the finishing line.
The state of the current PR is just a draft of an idea.

I'll comment on multiple code locations to point out the advantages of the new design.

# The old design

The major idea of the design is due to the following observation:

> We use overload resolution and in particular overload ordering via concepts to determine the order in which we should print

Just to give a small example (the more down, the more specialized):

```cpp
  std::cout //-printable [1]
< seqan3::tuple_like // [4]
< std::ranges::input_range // [2]
< std::vector<uint8_t> // [3]
< seqan3::sequence // [3]
< char * // [2]

  std::cout //-printable [1]
< char // [5]
< seqan3::tuple_like // [4]
< seqan3::alphabet // [5]
< seqan3::mask // [6]
```

NOTE: that using concepts as overload resolution always uses a partially ordered set, which can be depicted by as a [Hasse Diagram](https://en.wikipedia.org/wiki/Hasse_diagram), and by using the except clauses via `requires` we give it a total order.

# The new design

### Before anything, note that the new design does not break any existing code. As existing `seqan3::debugstream << ` overloads, still take place in overload resolution.

The idea is simple:
* Have a list of printers.
* The order of the printers dictates in which order an object should be printed.
* We allow that multiple printers might be viable to print a type.
* Each `printer<type>` either has a function object / lambda `print` or not;
  depending on whether the `printer` can print that `type` or not (implemented by
  [template specialization](https://en.cppreference.com/w/cpp/language/template_specialization))
* We can explicitly use `printer` in other printer's, if we know that
  only that overload should be used,

So put together: For a given type, ask every printer in order whether it can print that type and the first one to answer yes, is the selected printer.

----

[1] If all overloads do not work, use `std::ostream`
https://github.com/seqan/seqan3/blob/6b681fb2eae5ab2997d293e99fc6a7f869a20316/include/seqan3/core/debug_stream/debug_stream_type.hpp#L242-L247
[2] Use this for all `std::ranges::input_range`s except if type is something like `std::filesystem` (type == range_value_t) or `char *`
https://github.com/seqan/seqan3/blob/6b681fb2eae5ab2997d293e99fc6a7f869a20316/include/seqan3/core/debug_stream/range.hpp#L96-L98
https://github.com/seqan/seqan3/blob/6b681fb2eae5ab2997d293e99fc6a7f869a20316/include/seqan3/core/debug_stream/range.hpp#L38-L45
[3] Same as [2] where value_type is an alphabet but only if the alphabet is not an `unsigned int` (this condition has no test case?!)
https://github.com/seqan/seqan3/blob/6b681fb2eae5ab2997d293e99fc6a7f869a20316/include/seqan3/core/debug_stream/range.hpp#L138-L141
[4] Use this for all `std::tuple`-like types except if it is a `std::ranges::input_range` (what is a tuple and ranges at the same time?!) and an `seqan3::alphabet` (basically `seqan3::alphabet_tuple_base` derived types)
https://github.com/seqan/seqan3/blob/6b681fb2eae5ab2997d293e99fc6a7f869a20316/include/seqan3/core/debug_stream/tuple.hpp#L53-L56
[5] Use this for all `seqan3::alphabet`s except if it can be printed by `std::cout` (like `char`)
https://github.com/seqan/seqan3/blob/6b681fb2eae5ab2997d293e99fc6a7f869a20316/include/seqan3/alphabet/detail/debug_stream_alphabet.hpp#L30-L32
[6] Type must be `seqan3::semialphabet` and `seqan3::mask`
https://github.com/seqan/seqan3/blob/6b681fb2eae5ab2997d293e99fc6a7f869a20316/include/seqan3/alphabet/detail/debug_stream_alphabet.hpp#L46-L48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Epic needs refinement A story that was not discussed and/or estimated by the team yet but is planned for upcoming sprints.
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants