BioC++ core-0.7.0
The Modern C++ libraries for Bioinformatics.
 
Loading...
Searching...
No Matches
variant.hpp
Go to the documentation of this file.
1// -----------------------------------------------------------------------------------------------------
2// Copyright (c) 2022 deCODE Genetics
3// Copyright (c) 2006-2021, Knut Reinert & Freie Universität Berlin
4// Copyright (c) 2016-2021, Knut Reinert & MPI für molekulare Genetik
5// This file may be used, modified and/or redistributed under the terms of the 3-clause BSD-License
6// shipped with this file and also available at: https://github.com/biocpp/biocpp-core/blob/main/LICENSE.md
7// -----------------------------------------------------------------------------------------------------
8
16#pragma once
17
18#include <algorithm>
19#include <array>
20#include <cassert>
21#include <utility>
22#include <variant>
23
24#include <bio/alphabet/base.hpp>
27
28namespace bio::alphabet::detail
29{
30
32template <typename other_t, typename... alternative_types>
33concept variant_general_guard =
34 !meta::one_of<other_t, variant<alternative_types...>, alternative_types...> &&
35 !meta::list_traits::contains<variant<alternative_types...>, recursive_required_types_t<other_t>>;
36
37} // namespace bio::alphabet::detail
38
39namespace bio::alphabet
40{
41
100template <typename... alternative_types>
101 requires((detail::writable_constexpr_alphabet<alternative_types> && ...) &&
102 (std::regular<alternative_types> && ...) && (sizeof...(alternative_types) >= 2))
103class variant : public base<variant<alternative_types...>, (static_cast<size_t>(size<alternative_types>) + ...), char>
104{
105private:
107 using base_t = base<variant<alternative_types...>, (static_cast<size_t>(size<alternative_types>) + ...), char>;
108
109 static_assert((std::is_same_v<char_t<alternative_types>, char> && ...),
110 "The variant is currently only tested for alphabets with char_type char. "
111 "Contact us on GitHub if you have a different use case: https://github.com/biocpp/biocpp-core .");
112
114 friend base_t;
115
117 using alternatives = meta::type_list<alternative_types...>;
118
119 static_assert(((meta::list_traits::count<alternative_types, alternatives> == 1) && ... && true),
120 "All types in a variant must be distinct.");
121
122 using typename base_t::char_type;
123 using typename base_t::rank_type;
124
125public:
126 using base_t::alphabet_size;
127 using base_t::assign_rank;
128 using base_t::to_char;
129 using base_t::to_rank;
130
135 using biocpp_required_types = meta::type_list<alternative_types...>;
140 using biocpp_recursive_required_types = meta::list_traits::concat<
143
150 template <typename alternative_t>
151 static constexpr bool is_alternative() noexcept
152 {
153 return meta::detail::pack_traits::contains<alternative_t, alternative_types...>;
154 }
155
159 constexpr variant() noexcept = default;
160 constexpr variant(variant const &) noexcept = default;
161 constexpr variant(variant &&) noexcept = default;
162 constexpr variant & operator=(variant const &) noexcept = default;
163 constexpr variant & operator=(variant &&) noexcept = default;
164 ~variant() noexcept = default;
165
173 template <typename alternative_t>
174 requires(is_alternative<alternative_t>())
175 constexpr variant(alternative_t const alternative) noexcept
176 {
177 assign_rank(rank_by_type_(alternative));
178 }
179
197 template <typename indirect_alternative_t>
198 requires(detail::variant_general_guard<indirect_alternative_t, alternative_types...> &&
199 (std::is_convertible_v<indirect_alternative_t, alternative_types> || ...))
200 constexpr variant(indirect_alternative_t const rhs) noexcept
201 {
202 using alternative_predicate = detail::implicitly_convertible_from<indirect_alternative_t>;
203 constexpr auto alternative_position =
204 meta::list_traits::find_if<alternative_predicate::template invoke, alternatives>;
206 assign_rank(rank_by_type_(alternative_t(rhs)));
207 }
208
224 template <typename indirect_alternative_t>
225 requires(detail::variant_general_guard<indirect_alternative_t, alternative_types...> &&
226 !(std::is_convertible_v<indirect_alternative_t, alternative_types> || ...) &&
227 (std::is_constructible_v<alternative_types, indirect_alternative_t> || ...))
228 constexpr explicit variant(indirect_alternative_t const rhs) noexcept
229 {
230 using alternative_predicate = detail::constructible_from<indirect_alternative_t>;
231 constexpr auto alternative_position =
232 meta::list_traits::find_if<alternative_predicate::template invoke, alternatives>;
234 assign_rank(rank_by_type_(alternative_t(rhs)));
235 }
236
247 template <typename indirect_alternative_t>
248 requires(detail::variant_general_guard<indirect_alternative_t, alternative_types...> &&
250 constexpr variant & operator=(indirect_alternative_t const & rhs) noexcept
251 {
252 using alternative_predicate = detail::assignable_from<indirect_alternative_t>;
253 constexpr auto alternative_position =
254 meta::list_traits::find_if<alternative_predicate::template invoke, alternatives>;
256 alternative_t alternative{};
257 alternative = rhs;
258 assign_rank(rank_by_type_(alternative));
259 return *this;
260 }
262
271 template <size_t index>
272 constexpr bool holds_alternative() const noexcept
273 {
274 static_assert(index < alphabet_size, "The variant contains less alternatives than you are checking.");
275 return (to_rank() >= partial_sum_sizes[index]) && (to_rank() < partial_sum_sizes[index + 1]);
276 }
277
283 template <size_t index>
284 constexpr auto convert_to() const
285 {
286 return convert_impl<index, true>();
287 }
288
293 template <size_t index>
294 constexpr auto convert_unsafely_to() const noexcept
295 {
296 return convert_impl<index, false>();
297 }
299
307 template <typename alternative_t>
308 constexpr bool holds_alternative() const noexcept
309 requires(is_alternative<alternative_t>())
310 {
311 constexpr size_t index = meta::list_traits::find<alternative_t, alternatives>;
312 return holds_alternative<index>();
313 }
314
320 template <typename alternative_t>
321 constexpr alternative_t convert_to() const
322 requires(is_alternative<alternative_t>())
323 {
324 constexpr size_t index = meta::list_traits::find<alternative_t, alternatives>;
325 return convert_impl<index, true>();
326 }
327
332 template <typename alternative_t>
333 constexpr alternative_t convert_unsafely_to() const noexcept
334 requires(is_alternative<alternative_t>())
335 {
336 constexpr size_t index = meta::list_traits::find<alternative_t, alternatives>;
337 return convert_impl<index, false>();
338 }
340
362 template <std::same_as<variant> variant_t, typename indirect_alternative_type>
363 requires((detail::variant_general_guard<indirect_alternative_type, alternative_types...>) &&
365 friend constexpr bool operator==(variant_t const lhs, indirect_alternative_type const rhs) noexcept
366 {
367 using alternative_predicate = detail::weakly_equality_comparable_with_<indirect_alternative_type>;
368 constexpr auto alternative_position =
369 meta::list_traits::find_if<alternative_predicate::template invoke, alternatives>;
371 return lhs.template holds_alternative<alternative_t>() &&
372 (lhs.template convert_unsafely_to<alternative_t>() == rhs);
373 }
375
379 static constexpr bool char_is_valid(char_type const chr) noexcept
380 {
381 using index_t = std::make_unsigned_t<char_type>;
382 return first_valid_char_table[static_cast<index_t>(chr)] < sizeof...(alternative_types);
383 }
384
385protected:
387
392 template <size_t index, bool throws>
393 constexpr auto convert_impl() const noexcept(!throws) -> meta::list_traits::at<index, alternatives>
394 {
395 static_assert(index < alphabet_size, "The variant contains less alternatives than you are checking.");
396 using alternative_t = meta::list_traits::at<index, alternatives>;
397
398 if constexpr (throws)
399 {
400 if (!holds_alternative<index>()) // [[unlikely]]
401 {
403 }
404 }
405
406 return bio::alphabet::assign_rank_to(to_rank() - partial_sum_sizes[index], alternative_t{});
407 }
408
416 static constexpr std::array<rank_type, sizeof...(alternative_types) + 1> partial_sum_sizes = []() constexpr
417 {
418 constexpr size_t N = sizeof...(alternative_types) + 1;
419
420 std::array<rank_type, N> partial_sum{0, bio::alphabet::size<alternative_types>...};
421 for (size_t i = 1u; i < N; ++i)
422 partial_sum[i] += partial_sum[i - 1];
423
424 return partial_sum;
425 }();
426
433 static constexpr std::array<char_type, alphabet_size> rank_to_char = []() constexpr
434 {
435 // Explicitly writing assign_rank_to_char within assign_rank_to_char
436 // causes this bug (g++-7 and g++-8):
437 // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=84684
438 auto assign_rank_to_char = [](auto alternative, size_t rank) constexpr
439 { return bio::alphabet::to_char(bio::alphabet::assign_rank_to(rank, alternative)); };
440
441 auto assign_value_to_char =
442 [assign_rank_to_char](auto alternative, auto & value_to_char, auto & value) constexpr
443 {
444 using alternative_t = std::decay_t<decltype(alternative)>;
445 for (size_t i = 0u; i < bio::alphabet::size<alternative_t>; ++i, ++value)
446 value_to_char[value] = assign_rank_to_char(alternative, i);
447 };
448
449 unsigned value = 0u;
451
452 // initializer lists guarantee sequencing;
453 // the following expression behaves as:
454 // for(auto alternative: alternative_types)
455 // assign_rank_to_char(alternative, rank_to_char, value);
456 ((assign_value_to_char(alternative_types{}, value_to_char, value)), ...);
457
458 return value_to_char;
459 }();
460
465 template <size_t index, typename alternative_t>
466 requires(is_alternative<alternative_t>())
467 static constexpr rank_type rank_by_index_(alternative_t const & alternative) noexcept
468 {
469 return partial_sum_sizes[index] + static_cast<rank_type>(bio::alphabet::to_rank(alternative));
470 }
471
476 template <typename alternative_t>
477 requires(is_alternative<alternative_t>())
478 static constexpr rank_type rank_by_type_(alternative_t const & alternative) noexcept
479 {
480 constexpr size_t index = meta::list_traits::find<alternative_t, alternatives>;
481 return rank_by_index_<index>(alternative);
482 }
483
487 static constexpr auto first_valid_char_table = []() constexpr
488 {
489 constexpr size_t alternative_size = sizeof...(alternative_types);
490 constexpr size_t table_size = meta::detail::size_in_values_v<char_type>;
491 using first_alphabet_t = meta::detail::min_viable_uint_t<alternative_size>;
492
494
495 for (size_t i = 0u; i < table_size; ++i)
496 {
497 char_type chr = static_cast<char_type>(i);
498
499 std::array<bool, alternative_size> valid_chars{char_is_valid_for<alternative_types>(chr)...};
500
501 auto found_it = std::find(valid_chars.begin(), valid_chars.end(), true);
502 lookup_table[i] = found_it - valid_chars.begin();
503 }
504
505 return lookup_table;
506 }();
507
514 static constexpr std::array<rank_type, meta::detail::size_in_values_v<char_type>> char_to_rank = []() constexpr
515 {
516 constexpr size_t alternative_size = sizeof...(alternative_types);
517 constexpr size_t table_size = meta::detail::size_in_values_v<char_type>;
518
520
521 for (size_t i = 0u; i < table_size; ++i)
522 {
523 char_type chr = static_cast<char_type>(i);
524
525 std::array<rank_type, alternative_size> ranks{rank_by_type_(assign_char_to(chr, alternative_types{}))...};
526
527 // if no char_is_valid_for any alternative use the rank of the first alternative
528 char_to_rank[i] = first_valid_char_table[i] < alternative_size ? ranks[first_valid_char_table[i]] : 0;
529 }
530
531 return char_to_rank;
532 }();
533};
534
535} // namespace bio::alphabet
Provides implementation detail for bio::alphabet::variant and bio::alphabet::tuple_base.
Provides bio::alphabet::base.
A CRTP-base that makes defining a custom alphabet easier.
Definition: base.hpp:55
A combined alphabet that can hold values of either of its alternatives..
Definition: variant.hpp:104
static constexpr bool is_alternative() noexcept
Returns true if alternative_t is one of the given alternative types.
Definition: variant.hpp:151
constexpr alternative_t convert_unsafely_to() const noexcept
Convert to the specified alphabet (undefined behaviour if holds_alternative() would be false).
Definition: variant.hpp:333
constexpr auto convert_unsafely_to() const noexcept
Convert to the specified alphabet (undefined behaviour if holds_alternative() would be false).
Definition: variant.hpp:294
constexpr variant() noexcept=default
Defaulted.
constexpr variant(indirect_alternative_t const rhs) noexcept
Constructor for arguments implicitly convertible to an alternative.
Definition: variant.hpp:200
static constexpr bool char_is_valid(char_type const chr) noexcept
Validate whether a character is valid in the combined alphabet.
Definition: variant.hpp:379
constexpr bool holds_alternative() const noexcept
Whether the variant alphabet currently holds a value of the given alternative.
Definition: variant.hpp:308
constexpr variant(indirect_alternative_t const rhs) noexcept
Constructor for arguments explicitly (but not implicitly) convertible to an alternative.
Definition: variant.hpp:228
constexpr bool holds_alternative() const noexcept
Whether the variant alphabet currently holds a value of the given alternative.
Definition: variant.hpp:272
constexpr auto convert_to() const
Convert to the specified alphabet (throws if holds_alternative() would be false).
Definition: variant.hpp:284
constexpr alternative_t convert_to() const
Convert to the specified alphabet (throws if holds_alternative() would be false).
Definition: variant.hpp:321
Resolves to std::is_assignable_v<t>.
Definition: core_language.hpp:128
Requires the two operands to be comparable with == and != in both directions.
Definition: core_language.hpp:33
T find(T... args)
constexpr auto to_char
Return the char representation of an alphabet object.
Definition: concept.hpp:192
constexpr auto assign_char_to
Assign a char to an alphabet object.
Definition: concept.hpp:260
constexpr auto to_rank
Return the rank representation of a (semi-)alphabet object.
Definition: concept.hpp:70
constexpr auto assign_rank_to
Assign a rank to an alphabet object.
Definition: concept.hpp:138
decltype(detail::concat(lists_t{}...)) concat
Join two meta::type_list s into one.
Definition: traits.hpp:372
typename decltype(detail::at< idx >(list_t{}))::type at
Return the type at given index from the type list.
Definition: traits.hpp:309
constexpr bool contains
Whether a type occurs in a type list or not.
Definition: traits.hpp:282
typename transformation_trait_or< type_t, default_t >::type transformation_trait_or_t
Helper type of bio::meta::transformation_trait_or (transformation_trait shortcut).
Definition: transformation_trait_or.hpp:53
The alphabet module's namespace.
Definition: aa10li.hpp:23
T partial_sum(T... args)
Type that contains multiple types.
Definition: type_list.hpp:30
Provides traits for meta::type_list.