SeqAn3 3.4.0-rc.1
The Modern C++ library for sequence analysis.
Loading...
Searching...
No Matches
validators.hpp
Go to the documentation of this file.
1// SPDX-FileCopyrightText: 2006-2024 Knut Reinert & Freie Universität Berlin
2// SPDX-FileCopyrightText: 2016-2024 Knut Reinert & MPI für molekulare Genetik
3// SPDX-License-Identifier: BSD-3-Clause
4
10#pragma once
11
12#include <algorithm>
13#include <concepts>
14#include <filesystem>
15#include <fstream>
16#include <ranges>
17#include <regex>
18#include <sstream>
19
30
31namespace seqan3
32{
33
93template <typename validator_type>
94concept validator =
95 std::copyable<std::remove_cvref_t<validator_type>>
96 && requires (validator_type validator, typename std::remove_reference_t<validator_type>::option_value_type value) {
98
99 {
100 validator(value)
101 } -> std::same_as<void>;
102 {
104 } -> std::same_as<std::string>;
105 };
107
123template <arithmetic option_value_t>
125{
126public:
128 using option_value_type = option_value_t;
129
135 {}
136
141 void operator()(option_value_type const & cmp) const
142 {
143 if (!((cmp <= max) && (cmp >= min)))
144 throw validation_error{detail::to_string("Value ", cmp, " is not in range [", min, ",", max, "].")};
145 }
146
153 template <std::ranges::forward_range range_type>
155 void operator()(range_type const & range) const
156 {
157 std::for_each(range.begin(),
158 range.end(),
159 [&](auto cmp)
160 {
161 (*this)(cmp);
162 });
163 }
164
167 {
168 return detail::to_string("Value must be in range [", min, ",", max, "].");
169 }
170
171private:
174
177};
178
198template <typename option_value_t>
200{
201public:
203 using option_value_type = option_value_t;
204
214
220 template <std::ranges::forward_range range_type>
221 requires std::constructible_from<option_value_type, std::ranges::range_rvalue_reference_t<range_type>>
222 value_list_validator(range_type rng)
223 {
224 values.clear();
226 }
227
233 template <typename... option_types>
234 requires ((std::constructible_from<option_value_type, option_types> && ...))
235 value_list_validator(option_types &&... opts)
236 {
237 (values.emplace_back(std::forward<option_types>(opts)), ...);
238 }
240
245 void operator()(option_value_type const & cmp) const
246 {
247 if (!(std::find(values.begin(), values.end(), cmp) != values.end()))
248 throw validation_error{detail::to_string("Value ", cmp, " is not one of ", std::views::all(values), ".")};
249 }
250
256 template <std::ranges::forward_range range_type>
257 requires std::convertible_to<std::ranges::range_value_t<range_type>, option_value_type>
258 void operator()(range_type const & range) const
259 {
261 std::ranges::end(range),
262 [&](auto cmp)
263 {
264 (*this)(cmp);
265 });
266 }
267
270 {
271 return detail::to_string("Value must be one of ", std::views::all(values), ".");
272 }
273
274private:
277};
278
284template <typename option_type, typename... option_types>
285 requires (std::constructible_from<std::string, std::decay_t<option_types>> && ...
286 && std::constructible_from<std::string, std::decay_t<option_type>>)
288
290template <typename range_type>
291 requires (std::ranges::forward_range<std::decay_t<range_type>>
292 && std::constructible_from<std::string, std::ranges::range_value_t<range_type>>)
294
296template <typename option_type, typename... option_types>
298
300template <typename range_type>
301 requires (std::ranges::forward_range<std::decay_t<range_type>>)
304
320{
321public:
324
333 virtual ~file_validator_base() = default;
335
343 virtual void operator()(std::filesystem::path const & path) const = 0;
344
352 template <std::ranges::forward_range range_type>
353 requires (std::convertible_to<std::ranges::range_value_t<range_type>, std::filesystem::path const &>
354 && !std::convertible_to<range_type, std::filesystem::path const &>)
355 void operator()(range_type const & v) const
356 {
357 std::for_each(v.begin(),
358 v.end(),
359 [&](auto cmp)
360 {
361 this->operator()(cmp);
362 });
363 }
364
365protected:
372 {
373 // If no valid extensions are given we can safely return here.
374 if (extensions.empty())
375 return;
376
377 // Check if extension is available.
378 if (!path.has_extension())
379 throw validation_error{detail::to_string("The given filename ",
380 path.string(),
381 " has no extension. Expected"
382 " one of the following valid extensions:",
384 "!")};
385
386 std::string file_path{path.filename().string()};
387
388 // Leading dot indicates a hidden file is not part of the extension.
389 if (file_path.front() == '.')
390 file_path.erase(0, 1);
391
392 // Store a string_view containing all extensions for a better error message.
393 std::string const all_extensions{file_path.substr(file_path.find(".") + 1)};
394
395 // Compares the extensions in lower case.
396 auto case_insensitive_ends_with = [&](std::string const & ext)
397 {
398 return case_insensitive_string_ends_with(file_path, ext);
399 };
400
401 // Check if requested extension is present.
402 if (std::ranges::find_if(extensions, case_insensitive_ends_with) == extensions.end())
403 {
404 throw validation_error{detail::to_string("Expected one of the following valid extensions: ",
406 "! Got ",
407 all_extensions,
408 " instead!")};
409 }
410 }
411
418 {
419 // Check if input directory is readable.
421 {
422 std::error_code ec{};
423 std::filesystem::directory_iterator{path, ec}; // if directory iterator cannot be created, ec will be set.
424 if (static_cast<bool>(ec))
425 throw validation_error{detail::to_string("Cannot read the directory ", path, "!")};
426 }
427 else
428 {
429 // Must be a regular file.
431 throw validation_error{detail::to_string("Expected a regular file ", path, "!")};
432
433 std::ifstream file{path};
434 if (!file.is_open() || !file.good())
435 throw validation_error{detail::to_string("Cannot read the file ", path, "!")};
436 }
437 }
438
445 {
446 std::ofstream file{path};
447 detail::safe_filesystem_entry file_guard{path};
448
449 bool is_open = file.is_open();
450 bool is_good = file.good();
451 file.close();
452
453 if (!is_good || !is_open)
454 throw validation_error{detail::to_string("Cannot write ", path, "!")};
455
456 file_guard.remove();
457 }
458
461 {
462 if (extensions.empty())
463 return "";
464 else
465 return detail::to_string(" Valid file extensions are: [",
467 "].");
468 }
469
476 {
477 size_t const suffix_length{suffix.size()};
478 size_t const str_length{str.size()};
479 return suffix_length > str_length ? false
480 : std::ranges::equal(str.substr(str_length - suffix_length),
481 suffix,
482 [](char const chr1, char const chr2)
483 {
484 return std::tolower(chr1) == std::tolower(chr2);
485 });
486 }
487
490};
491
516template <typename file_t = void>
518{
519public:
520 static_assert(std::same_as<file_t, void> || detail::has_type_valid_formats<file_t>,
521 "Expected either a template type with a static member called valid_formats (a file type) or void.");
522
523 // Import from base class.
525
539 {
540 if constexpr (!std::same_as<file_t, void>)
541 file_validator_base::extensions = detail::valid_file_extensions<typename file_t::valid_formats>();
542 }
543
548 virtual ~input_file_validator() = default;
549
559 requires std::same_as<file_t, void>
561 {
563 }
564
565 // Import base class constructor.
568
569 // Import the base::operator()
570 using file_validator_base::operator();
571
577 virtual void operator()(std::filesystem::path const & file) const override
578 {
579 try
580 {
581 if (!std::filesystem::exists(file))
582 throw validation_error{detail::to_string("The file ", file, " does not exist!")};
583
584 // Check if file is regular and can be opened for reading.
586
587 // Check extension.
588 validate_filename(file);
589 }
590 // LCOV_EXCL_START
592 {
593 std::throw_with_nested(validation_error{"Unhandled filesystem error!"});
594 }
595 // LCOV_EXCL_STOP
596 catch (...)
597 {
599 }
600 }
601
604 {
605 return "The input file must exist and read permissions must be granted." + valid_extensions_help_page_message();
606 }
607};
608
617
646template <typename file_t = void>
648{
649public:
650 static_assert(std::same_as<file_t, void> || detail::has_type_valid_formats<file_t>,
651 "Expected either a template type with a static member called valid_formats (a file type) or void.");
652
653 // Import from base class.
655
663
668 virtual ~output_file_validator() = default;
669
683
684 // Import base constructor.
687
697 {
698 if constexpr (!std::same_as<file_t, void>)
699 return detail::valid_file_extensions<typename file_t::valid_formats>();
700 return {};
701 }
702
703 // Import the base::operator()
704 using file_validator_base::operator();
705
711 virtual void operator()(std::filesystem::path const & file) const override
712 {
713 try
714 {
716 throw validation_error{detail::to_string("The file ", file, " already exists!")};
717
718 // Check if file has any write permissions.
720
721 validate_filename(file);
722 }
723 // LCOV_EXCL_START
725 {
726 std::throw_with_nested(validation_error{"Unhandled filesystem error!"});
727 }
728 // LCOV_EXCL_STOP
729 catch (...)
730 {
732 }
733 }
734
737 {
739 return "Write permissions must be granted." + valid_extensions_help_page_message();
740 else // mode == create_new
741 return "The output file must not exist already and write permissions must be granted."
743 }
744
745private:
748};
749
767{
768public:
769 // Import from base class.
771
780 virtual ~input_directory_validator() = default;
781
782 // Import base constructor.
785
786 // Import the base::operator()
787 using file_validator_base::operator();
788
794 virtual void operator()(std::filesystem::path const & dir) const override
795 {
796 try
797 {
798 if (!std::filesystem::exists(dir))
799 throw validation_error{detail::to_string("The directory ", dir, " does not exists!")};
800
802 throw validation_error{detail::to_string("The path ", dir, " is not a directory!")};
803
804 // Check if directory has any read permissions.
806 }
807 // LCOV_EXCL_START
809 {
810 std::throw_with_nested(validation_error{"Unhandled filesystem error!"});
811 }
812 // LCOV_EXCL_STOP
813 catch (...)
814 {
816 }
817 }
818
821 {
822 return detail::to_string("An existing, readable path for the input directory.");
823 }
824};
825
843{
844public:
845 // Imported from base class.
847
856 virtual ~output_directory_validator() = default;
857
858 // Import base constructor.
861
862 // Import the base::operator().
863 using file_validator_base::operator();
864
870 virtual void operator()(std::filesystem::path const & dir) const override
871 {
872 bool dir_exists = std::filesystem::exists(dir);
873 // Make sure the created dir is deleted after we are done.
875 std::filesystem::create_directory(dir, ec); // does nothing and is not treated as error if path already exists.
876 // if error code was set or if dummy.txt could not be created within the output dir, throw an error.
877 if (static_cast<bool>(ec))
878 throw validation_error{detail::to_string("Cannot create directory: ", dir, "!")};
879
880 try
881 {
882 if (!dir_exists)
883 {
884 detail::safe_filesystem_entry dir_guard{dir};
885 validate_writeability(dir / "dummy.txt");
886 dir_guard.remove_all();
887 }
888 else
889 {
890 validate_writeability(dir / "dummy.txt");
891 }
892 }
893 // LCOV_EXCL_START
895 {
896 std::throw_with_nested(validation_error{"Unhandled filesystem error!"});
897 }
898 // LCOV_EXCL_STOP
899 catch (...)
900 {
902 }
903 }
904
907 {
908 return detail::to_string("A valid path for the output directory.");
909 }
910};
911
932{
933public:
936
940 regex_validator(std::string const & pattern_) : pattern{pattern_}
941 {}
942
947 void operator()(option_value_type const & cmp) const
948 {
949 std::regex rgx(pattern);
950 if (!std::regex_match(cmp, rgx))
951 throw validation_error{detail::to_string("Value ", cmp, " did not match the pattern ", pattern, ".")};
952 }
953
960 template <std::ranges::forward_range range_type>
961 requires std::convertible_to<std::ranges::range_reference_t<range_type>, option_value_type const &>
962 void operator()(range_type const & v) const
963 {
964 for (auto && file_name : v)
965 {
966 // note: we explicitly copy/construct any reference type other than `std::string &`
967 (*this)(static_cast<option_value_type const &>(file_name));
968 }
969 }
970
973 {
974 return detail::to_string("Value must match the pattern '", pattern, "'.");
975 }
976
977private:
980};
981
982namespace detail
983{
984
997template <typename option_value_t>
999{
1001 using option_value_type = option_value_t;
1002
1004 void operator()(option_value_t const & /*cmp*/) const noexcept
1005 {}
1006
1009 {
1010 return "";
1011 }
1012};
1013
1027template <validator validator1_type, validator validator2_type>
1028 requires std::common_with<typename validator1_type::option_value_type, typename validator2_type::option_value_type>
1030{
1031public:
1035
1044
1049 validator_chain_adaptor(validator1_type vali1_, validator2_type vali2_) :
1050 vali1{std::move(vali1_)},
1051 vali2{std::move(vali2_)}
1052 {}
1053
1057
1066 template <typename cmp_type>
1067 requires std::invocable<validator1_type, cmp_type const> && std::invocable<validator2_type, cmp_type const>
1068 void operator()(cmp_type const & cmp) const
1069 {
1070 vali1(cmp);
1071 vali2(cmp);
1072 }
1073
1076 {
1077 return detail::to_string(vali1.get_help_page_message(), " ", vali2.get_help_page_message());
1078 }
1079
1080private:
1082 validator1_type vali1;
1084 validator2_type vali2;
1085};
1086
1087} // namespace detail
1088
1118template <validator validator1_type, validator validator2_type>
1119 requires std::common_with<typename std::remove_reference_t<validator1_type>::option_value_type,
1121auto operator|(validator1_type && vali1, validator2_type && vali2)
1122{
1123 return detail::validator_chain_adaptor{std::forward<validator1_type>(vali1), std::forward<validator2_type>(vali2)};
1124}
1125
1126} // namespace seqan3
T back_inserter(T... args)
Provides various type traits on generic types.
T begin(T... args)
A validator that checks whether a number is inside a given range.
Definition validators.hpp:125
void operator()(option_value_type const &cmp) const
Tests whether cmp lies inside [min, max].
Definition validators.hpp:141
option_value_type max
Maximum of the range to test.
Definition validators.hpp:176
option_value_t option_value_type
The type of value that this validator invoked upon.
Definition validators.hpp:128
void operator()(range_type const &range) const
Tests whether every element in range lies inside [min, max].
Definition validators.hpp:155
option_value_type min
Minimum of the range to test.
Definition validators.hpp:173
std::string get_help_page_message() const
Returns a message that can be appended to the (positional) options help page info.
Definition validators.hpp:166
arithmetic_range_validator(option_value_type const min_, option_value_type const max_)
The constructor.
Definition validators.hpp:134
A safe guard to manage a filesystem entry, e.g. a file or a directory.
Definition safe_filesystem_entry.hpp:35
A helper struct to chain validators recursively via the pipe operator.
Definition validators.hpp:1030
validator_chain_adaptor(validator_chain_adaptor &&)=default
Defaulted.
validator_chain_adaptor & operator=(validator_chain_adaptor &&)=default
Defaulted.
validator_chain_adaptor(validator1_type vali1_, validator2_type vali2_)
Constructing from two validators.
Definition validators.hpp:1049
validator2_type vali2
The second validator in the chain.
Definition validators.hpp:1084
~validator_chain_adaptor()=default
The destructor.
validator_chain_adaptor & operator=(validator_chain_adaptor const &pf)=default
Defaulted.
validator_chain_adaptor(validator_chain_adaptor const &pf)=default
Defaulted.
std::string get_help_page_message() const
Returns a message that can be appended to the (positional) options help page info.
Definition validators.hpp:1075
validator1_type vali1
The first validator in the chain.
Definition validators.hpp:1082
void operator()(cmp_type const &cmp) const
Calls the operator() of each validator on the value cmp.
Definition validators.hpp:1068
An abstract base class for the file and directory validators.
Definition validators.hpp:320
bool case_insensitive_string_ends_with(std::string_view str, std::string_view suffix) const
Helper function that checks if a string is a suffix of another string. Case insensitive.
Definition validators.hpp:475
void validate_filename(std::filesystem::path const &path) const
Validates the given filename path based on the specified extensions.
Definition validators.hpp:371
std::string valid_extensions_help_page_message() const
Returns the information of valid file extensions.
Definition validators.hpp:460
virtual void operator()(std::filesystem::path const &path) const =0
Tests if the given path is a valid input, respectively output, file or directory.
std::string option_value_type
Type of values that are tested by validator.
Definition validators.hpp:323
file_validator_base(file_validator_base &&)=default
Defaulted.
file_validator_base & operator=(file_validator_base &&)=default
Defaulted.
void validate_readability(std::filesystem::path const &path) const
Checks if the given path is readable.
Definition validators.hpp:417
file_validator_base()=default
Defaulted.
file_validator_base(file_validator_base const &)=default
Defaulted.
std::vector< std::string > extensions
Stores the extensions.
Definition validators.hpp:489
virtual ~file_validator_base()=default
file_validator_base & operator=(file_validator_base const &)=default
Defaulted.
void validate_writeability(std::filesystem::path const &path) const
Checks if the given path is writable.
Definition validators.hpp:444
A validator that checks if a given path is a valid input directory.
Definition validators.hpp:767
input_directory_validator(input_directory_validator &&)=default
Defaulted.
std::string get_help_page_message() const
Returns a message that can be appended to the (positional) options help page info.
Definition validators.hpp:820
input_directory_validator()=default
Defaulted.
input_directory_validator(input_directory_validator const &)=default
Defaulted.
input_directory_validator & operator=(input_directory_validator &&)=default
Defaulted.
virtual void operator()(std::filesystem::path const &dir) const override
Tests whether path is an existing directory and is readable.
Definition validators.hpp:794
input_directory_validator & operator=(input_directory_validator const &)=default
Defaulted.
virtual ~input_directory_validator()=default
Virtual Destructor.
A validator that checks if a given path is a valid input file.
Definition validators.hpp:518
input_file_validator(input_file_validator const &)=default
Defaulted.
virtual ~input_file_validator()=default
Virtual destructor.
input_file_validator & operator=(input_file_validator &&)=default
Defaulted.
std::string get_help_page_message() const
Returns a message that can be appended to the (positional) options help page info.
Definition validators.hpp:603
input_file_validator(input_file_validator &&)=default
Defaulted.
input_file_validator(std::vector< std::string > extensions)
Constructs from a given collection of valid extensions.
Definition validators.hpp:558
virtual void operator()(std::filesystem::path const &file) const override
Tests whether path is an existing regular file and is readable.
Definition validators.hpp:577
input_file_validator()
Default constructor.
Definition validators.hpp:538
input_file_validator & operator=(input_file_validator const &)=default
Defaulted.
A validator that checks if a given path is a valid output directory.
Definition validators.hpp:843
output_directory_validator()=default
Defaulted.
output_directory_validator & operator=(output_directory_validator const &)=default
Defaulted.
virtual ~output_directory_validator()=default
Virtual Destructor.
output_directory_validator(output_directory_validator &&)=default
Defaulted.
output_directory_validator(output_directory_validator const &)=default
Defaulted.
virtual void operator()(std::filesystem::path const &dir) const override
Tests whether path is writable.
Definition validators.hpp:870
std::string get_help_page_message() const
Returns a message that can be appended to the (positional) options help page info.
Definition validators.hpp:906
output_directory_validator & operator=(output_directory_validator &&)=default
Defaulted.
A validator that checks if a given path is a valid output file.
Definition validators.hpp:648
static std::vector< std::string > default_extensions()
The default extensions of file_t.
Definition validators.hpp:696
output_file_validator(output_file_validator &&)=default
Defaulted.
output_file_validator(output_file_validator const &)=default
Defaulted.
virtual void operator()(std::filesystem::path const &file) const override
Tests whether path is does not already exists and is writable.
Definition validators.hpp:711
output_file_validator()
Default constructor.
Definition validators.hpp:661
output_file_validator & operator=(output_file_validator const &)=default
Defaulted.
output_file_validator & operator=(output_file_validator &&)=default
Defaulted.
output_file_open_options mode
Stores the current mode of whether it is valid to overwrite the output file.
Definition validators.hpp:747
std::string get_help_page_message() const
Returns a message that can be appended to the (positional) options help page info.
Definition validators.hpp:736
virtual ~output_file_validator()=default
Virtual Destructor.
output_file_validator(output_file_open_options const mode, std::vector< std::string > extensions=default_extensions())
Constructs from a given overwrite mode and a list of valid extensions.
Definition validators.hpp:676
A validator that checks if a matches a regular expression pattern.
Definition validators.hpp:932
std::string get_help_page_message() const
Returns a message that can be appended to the (positional) options help page info.
Definition validators.hpp:972
std::string pattern
The pattern to match.
Definition validators.hpp:979
void operator()(option_value_type const &cmp) const
Tests whether cmp lies inside values.
Definition validators.hpp:947
std::string option_value_type
Type of values that are tested by validator.
Definition validators.hpp:935
void operator()(range_type const &v) const
Tests whether every filename in list v matches the pattern.
Definition validators.hpp:962
regex_validator(std::string const &pattern_)
Constructing from a vector.
Definition validators.hpp:940
Argument parser exception thrown when an argument could not be casted to the according type.
Definition exceptions.hpp:128
A validator that checks whether a value is inside a list of valid values.
Definition validators.hpp:200
value_list_validator(option_type, option_types...) -> value_list_validator< option_type >
Deduction guide for a parameter pack.
value_list_validator()=default
Defaulted.
void operator()(option_value_type const &cmp) const
Tests whether cmp lies inside values.
Definition validators.hpp:245
value_list_validator(value_list_validator const &)=default
Defaulted.
value_list_validator & operator=(value_list_validator const &)=default
Defaulted.
option_value_t option_value_type
Type of values that are tested by validator.
Definition validators.hpp:203
void operator()(range_type const &range) const
Tests whether every element in range lies inside values.
Definition validators.hpp:258
std::string get_help_page_message() const
Returns a message that can be appended to the (positional) options help page info.
Definition validators.hpp:269
std::vector< option_value_type > values
Minimum of the range to test.
Definition validators.hpp:276
value_list_validator(value_list_validator &&)=default
Defaulted.
~value_list_validator()=default
Defaulted.
value_list_validator(range_type &&rng) -> value_list_validator< std::string >
Deduction guide for ranges over a value type convertible to std::string.
value_list_validator(option_type, option_types...) -> value_list_validator< std::string >
Type deduction guides.
value_list_validator & operator=(value_list_validator &&)=default
Defaulted.
value_list_validator(range_type rng)
Constructing from a range.
Definition validators.hpp:222
value_list_validator(range_type &&rng) -> value_list_validator< std::ranges::range_value_t< range_type > >
Deduction guide for ranges.
value_list_validator(option_types &&... opts)
Constructing from a parameter pack.
Definition validators.hpp:235
T clear(T... args)
T create_directory(T... args)
T current_exception(T... args)
T emplace_back(T... args)
T empty(T... args)
T end(T... args)
T equal(T... args)
Provides parser related exceptions.
T exists(T... args)
T filename(T... args)
T find(T... args)
T for_each(T... args)
auto operator|(validator1_type &&vali1, validator2_type &&vali2)
Enables the chaining of validators.
Definition validators.hpp:1121
seqan::stl::views::join_with join_with
A view adaptor that represents view consisting of the sequence obtained from flattening a view of ran...
Definition join_with.hpp:25
T has_extension(T... args)
A type that satisfies std::is_arithmetic_v<t>.
The concept for option validators passed to add_option/positional_option.
std::string get_help_page_message() const
Returns a message that can be appended to the (positional) options help page info.
Provides various utility functions.
T is_directory(T... args)
T is_regular_file(T... args)
Provides seqan3::views::join_with.
T move(T... args)
std::string to_string(value_type &&... values)
Streams all parameters via the seqan3::debug_stream and returns a concatenated string.
Definition to_string.hpp:26
The main SeqAn3 namespace.
Definition aligned_sequence_concept.hpp:26
output_file_open_options
Mode of an output file: Determines whether an existing file can be (silently) overwritten.
Definition validators.hpp:611
@ create_new
Forbid overwriting the output file.
@ open_or_create
Allow to overwrite the output file.
SeqAn specific customisations in the standard namespace.
Provides seqan3::debug_stream and related types.
T regex_match(T... args)
T rethrow_exception(T... args)
Provides seqan3::detail::safe_filesystem_entry.
T size(T... args)
Validator that always returns true.
Definition validators.hpp:999
void operator()(option_value_t const &) const noexcept
Value cmp always passes validation because the operator never throws.
Definition validators.hpp:1004
option_value_t option_value_type
Type of values that are tested by validator.
Definition validators.hpp:1001
std::string get_help_page_message() const
Since no validation is happening the help message is empty.
Definition validators.hpp:1008
T substr(T... args)
T throw_with_nested(T... args)
Auxiliary for pretty printing of exception messages.
Provides traits for seqan3::type_list.
Provides various traits for template packs.
Provides concepts that do not have equivalents in C++20.
Hide me