SeqAn3  3.0.0
The Modern C++ library for sequence analysis.
seqan3::view::deep< underlying_adaptor_t > Class Template Reference

A wrapper type around an existing view adaptor that enables "deep view" behaviour for that view. More...

#include <seqan3/range/view/deep.hpp>

+ Inheritance diagram for seqan3::view::deep< underlying_adaptor_t >:

Related Functions

(Note that these are not member functions.)

Template argument deduction guides.
template<typename underlying_adaptor_t >
 deep (underlying_adaptor_t &&inner) -> deep< underlying_adaptor_t >
 Template argument deduction helper that preserves lvalue references and turns rvalue references into values. More...
 

Detailed Description

template<typename underlying_adaptor_t>
class seqan3::view::deep< underlying_adaptor_t >

A wrapper type around an existing view adaptor that enables "deep view" behaviour for that view.

Template Parameters
underlying_adaptor_tThe type of the adaptor being wrapped.

Deep views

If you pass a range to a view that view performs some transformation on that range. If the range passed is multi-dimensional (i.e. a range-of-ranges) that transformation happens on the outermost range. So if you call std::view::reverse on a range-of-dna-ranges, it will revert the order of the dna-ranges, but leave the dna-ranges themselves unchanged.

In some cases this is not desirable or even possible, i.e. seqan3::view::complement performs it's operation on nucleotide-ranges and it would be logical to do so, even it is passed a range-of-nucleotide-ranges (it obviously cannot transform the outer range). We call these views "deep views" as they always perform their operation on the innermost ranges of a multi-dimensional range; in case the input is a one-dimensional range, deepness does not modify the behaviour.

Using view::deep

Strictly speaking, seqan3::view::deep is a view adaptor adaptor, i.e. it gets passed another adaptor when being constructed (not via the pipe!) and returns an adaptor that behaves like the underlying one, except being deep.

You can use it mostly like any other view (adaptor) with some subtle differences, illustrated in the examples below.

View properties

The returned view has the same requirements and guarantees as those of the underlying adaptor type, except that it is also deep, i.e. if the underlying range is range-of-ranges, all transformations apply to the innermost ranges and conversely the requirements also apply to the innermost ranges of the underlying range and guarantees apply to the innermost ranges of the returned range.

For the higher dimensions (all except the innermost ranges) the following properties hold:

range concepts and reference_t urng_t (underlying range type) rrng_t (returned range type)
std::ranges::InputRange required preserved
std::ranges::ForwardRange preserved
std::ranges::BidirectionalRange preserved
std::ranges::RandomAccessRange preserved
std::ranges::ContiguousRange lost
std::ranges::ViewableRange required guaranteed
std::ranges::View guaranteed
std::ranges::SizedRange preserved
std::ranges::CommonRange preserved
std::ranges::OutputRange lost
seqan3::ConstIterableRange preserved
seqan3::reference_t std::ranges::InputRange std::ranges::InputRange + std::ranges::View

Examples

Wrapping an adaptor that takes no parameters ("range adaptor <i>closure</i> object"):

std::vector<dna5_vector> foo{"AAATTT"_dna5, "CCCGGG"_dna5};
auto r = foo | std::view::reverse; // == [ [C,C,C,G,G,G], [A,A,A,T,T,T] ]
auto d = foo | view::deep{std::view::reverse}; // == [ [T,T,T,A,A,A], [G,G,G,C,C,C] ]
// You can also create a permanent alias:
namespace view
{
inline auto const deep_reverse = deep{std::view::reverse};
}
auto e = foo | view::deep_reverse; // == [ [T,T,T,A,A,A], [G,G,G,C,C,C] ]

Wrapping an adaptor that takes parameters:

std::vector<dna5_vector> foo{"AAATTT"_dna5, "CCCGGG"_dna5};
auto t = foo | std::view::take(1); // == [ [A,A,A,T,T,T] ]
auto d = foo | view::deep{ranges::view::take}(1); // == [ [A], [C] ]
// constructor arguments passed via {} and arguments to underlying view passed via ()
// In this case especially, an alias improves readability:
namespace view
{
inline auto const deep_take = deep{ranges::view::take};
}
auto e = foo | view::deep_take(1); // == [ [A], [C] ]

The above example illustrates that view::deep has two sets of arguments, the arguments to construct this adaptor, and the arguments passed to the underlying adaptor when calling this adaptor. You can use () for both, but we highly recommend to use {} to not confuse these; or just use an alias.

Attention
Note that in the case of parameter handling the arguments to view::deep are copied to each invocation of the underlying adaptor if they are temporaries. This is no problem for small objects like the integer above, but might be expensive for larger ones. To avoid this, pass in references to external objects instead of temporaries:
int i = 7;
auto f = foo | view::deep_take(i);

Wrapping an adaptor including its arguments:

std::vector<dna5_vector> foo{"AAATTT"_dna5, "CCCGGG"_dna5};
auto t = foo | std::view::take(1); // == [ [A,A,A,T,T,T] ]
auto d = foo | view::deep{ranges::view::take(1)}; // == [ [A], [C] ]
// constructor arguments passed via {} and arguments to underlying view hardcoded inside
// Or with an alias
namespace view
{
inline auto const deep_take1 = deep{ranges::view::take(1)};
}
auto e = foo | view::deep_take1; // == [ [A], [C] ]

In the above example the argument to the underlying adaptor is hardcoded and can't be changed at the call-site. It is less flexible, but does not require workarounds for arguments that are expensive (or impossible) to copy.

Friends And Related Function Documentation

◆ deep()

template<typename underlying_adaptor_t >
deep ( underlying_adaptor_t &&  inner) -> deep< underlying_adaptor_t >
related

Template argument deduction helper that preserves lvalue references and turns rvalue references into values.


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