From d4ee7053742f9bbcbf97b004066dff0089e1cf60 Mon Sep 17 00:00:00 2001 From: Camille Scott Date: Fri, 1 Apr 2022 14:36:57 -0700 Subject: [PATCH 1/4] Introduce a kmer_iterator compliant with LegacyInputIterator, to deprecate KmerIterator --- include/goetia/hashing/canonical.hh | 22 ++++- include/goetia/hashing/kmeriterator.hh | 108 ++++++++++++++++++++++++- src/goetia/hashing/kmeriterator.cc | 18 +++++ tests/test_hashing.py | 17 +++- 4 files changed, 162 insertions(+), 3 deletions(-) diff --git a/include/goetia/hashing/canonical.hh b/include/goetia/hashing/canonical.hh index e68080a..0bde00a 100644 --- a/include/goetia/hashing/canonical.hh +++ b/include/goetia/hashing/canonical.hh @@ -53,6 +53,10 @@ struct Hash { return hash; } + const bool is_null() const { + return value() == std::numeric_limits::max(); + } + operator value_type() const { return value(); } @@ -125,6 +129,10 @@ struct Canonical { return fw_hash < rc_hash ? fw_hash : rc_hash; } + const bool is_null() const { + return (fw_hash == std::numeric_limits::max()) && (fw_hash == rc_hash); + } + operator value_type() const { return value(); } @@ -211,6 +219,10 @@ struct Wmer, MinimizerType> { return hash.value(); } + const bool is_null() const { + return hash.is_null(); + } + operator value_type() const { return hash.value(); } @@ -280,7 +292,7 @@ struct Kmer> { { } - Kmer() + Kmer() : hash(), kmer(0, ' ') { @@ -296,6 +308,10 @@ struct Kmer> { return hash.value(); } + const bool is_null() const { + return hash.is_null(); + } + operator value_type() const { return hash.value(); } @@ -387,6 +403,10 @@ struct Shift, Direction> { return hash.value(); } + const bool is_null() const { + return hash.is_null(); + } + operator value_type() const { return hash.value(); } diff --git a/include/goetia/hashing/kmeriterator.hh b/include/goetia/hashing/kmeriterator.hh index 6527786..8327fbf 100644 --- a/include/goetia/hashing/kmeriterator.hh +++ b/include/goetia/hashing/kmeriterator.hh @@ -17,7 +17,9 @@ #ifndef GOETIA_KMERITERATOR_HH #define GOETIA_KMERITERATOR_HH +#include #include +#include #include "goetia/goetia.hh" #include "goetia/hashing/hashshifter.hh" @@ -113,7 +115,7 @@ public: if (done()) { //throw InvalidCharacterException("past end of iterator"); - return shifter->get(); + return hash_type(); } auto ret = shifter->shift_right(_seq[index - 1], _seq[index + K - 1]); @@ -121,6 +123,18 @@ public: return ret; } + + hash_type get() { + if (!_initialized) { + return first(); + } + + if (done()) { + return hash_type(); + } + + return shifter->get(); + } __attribute__((visibility("default"))) bool done() const { @@ -141,6 +155,98 @@ public: }; +template +struct kmer_iterator { + using value_type = typename ShifterType::hash_type; + using iterator_category = std::input_iterator_tag; + using difference_type = int64_t; + using reference = const value_type; + + kmer_iterator(ShifterType * shifter, size_t start, const std::string_view sequence) + : shifter(shifter), sequence(sequence), index(start) + { + if (!_out_of_bounds()) { + shifter->hash_base(sequence.begin() + index, + sequence.begin() + index + shifter->K); + } + } + + reference operator*() const { + if (_out_of_bounds()) { + return value_type(); + } + return shifter->get(); + } + + kmer_iterator& operator++() { + index += 1; + if (!_out_of_bounds()) { + shifter->shift_right(sequence[index - 1], + sequence[index + shifter->K - 1]); + } + return *this; + } + + kmer_iterator operator++(int) { + auto tmp = *this; + ++(*this); + return tmp; + } + + friend bool operator==(const kmer_iterator& a, const kmer_iterator& b) { + return (*a == *b) && (a.shifter == b.shifter); + } + + friend bool operator!=(const kmer_iterator& a, const kmer_iterator& b) { + return (*a != *b) || (a.shifter != b.shifter); + } + +private: + ShifterType * shifter; + const std::string_view sequence; + size_t index; + + const bool _out_of_bounds() const { + return index + shifter->K > sequence.length(); + } +}; + + +template +struct kmer_iterator_wrapper { + const std::string_view sequence; + ShifterType shifter; + + kmer_iterator_wrapper(const std::string_view sequence, + uint16_t K, + Args&&... args) + : sequence(sequence), + shifter(K, std::forward(args)...) {} + + auto begin() { + return kmer_iterator(&shifter, 0, sequence); + } + + auto end() { + return kmer_iterator(&shifter, sequence.length() - shifter.K + 1, sequence); + } +}; + + +template +constexpr auto hash_sequence(const std::string_view sequence, + uint16_t K, + Args&&... args) { + + return kmer_iterator_wrapper{ sequence, K, std::forward(args)... }; +} + + +void test_kmer_iterator(); + + extern template class KmerIterator; extern template class KmerIterator; diff --git a/src/goetia/hashing/kmeriterator.cc b/src/goetia/hashing/kmeriterator.cc index c51029c..27991e1 100644 --- a/src/goetia/hashing/kmeriterator.cc +++ b/src/goetia/hashing/kmeriterator.cc @@ -17,4 +17,22 @@ namespace goetia { template class KmerIterator; template class KmerIterator; + + void test_kmer_iterator() { + std::string seq = "AAACCATTCTATATCTCTCTTAAAAACTTCTAATA"; + uint16_t K = 21; + + std::cout << "kmer_iterator" << std::endl; + for (const auto& h : hash_sequence(seq, 21)) { + std::cout << h << " "; + } std::cout << std::endl; + + std::cout << "legacy KmerIterator" << std::endl; + KmerIterator it(seq, K); + while (!it.done()) { + auto h = it.next(); + std::cout << h << " "; + } std::cout << std::endl; + } + } diff --git a/tests/test_hashing.py b/tests/test_hashing.py index adaf243..765a82b 100644 --- a/tests/test_hashing.py +++ b/tests/test_hashing.py @@ -284,7 +284,6 @@ def test_kmeriterator_from_proto(hasher, ksize, length, random_sequence): assert act == exp - @pytest.mark.parametrize('hasher_type', [FwdLemireShifter, CanLemireShifter], indirect=True) def test_kmeriterator_hashextender(hasher, ksize, length, random_sequence): s = random_sequence() @@ -300,6 +299,22 @@ def test_kmeriterator_hashextender(hasher, ksize, length, random_sequence): assert act == exp + +@pytest.mark.parametrize('hasher_type', [FwdLemireShifter, CanLemireShifter], indirect=True) +def test_stl_compat_kmer_iterator(hasher, ksize, length, random_sequence): + s = random_sequence() + + exp = [hasher.hash(kmer).value for kmer in kmers(s, ksize)] + + act = [] + for h in libgoetia.hash_sequence[type(hasher)](s, ksize): + act.append(h.value) + + print('\n', exp[:20], '\n', act[:20]) + + assert act == exp + + @using(length=30, ksize=27) def test_shift_right_left_right(hasher, ksize, length, random_sequence): s = random_sequence() From bc99f4fb6d36034ccb593cb4ebce1dd9a0cea051 Mon Sep 17 00:00:00 2001 From: Camille Scott Date: Fri, 1 Apr 2022 14:48:55 -0700 Subject: [PATCH 2/4] temporary: add exceptions back to lemire shifter --- include/goetia/hashing/rollinghashshifter.hh | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/include/goetia/hashing/rollinghashshifter.hh b/include/goetia/hashing/rollinghashshifter.hh index 013c9fc..dff5b8c 100644 --- a/include/goetia/hashing/rollinghashshifter.hh +++ b/include/goetia/hashing/rollinghashshifter.hh @@ -69,9 +69,9 @@ public: inline hash_type hash_base_impl(const char * sequence) { this->hasher.reset(); for (uint16_t i = 0; i < K; ++i) { - //if (sequence[i] == '\0') { - // throw SequenceLengthException("Encountered null terminator in k-mer!"); - //} + if (sequence[i] == '\0') { + throw InvalidSequenceException("Encountered null terminator in k-mer!"); + } assert(sequence[i] != '\0'); this->hasher.eat(sequence[i]); } @@ -185,9 +185,9 @@ LemireShifterPolicy> rc_hasher.reset(); for (uint16_t i = 0; i < K; ++i) { - //if (sequence[i] == '\0') { - // throw SequenceLengthException("Encountered null terminator in k-mer!"); - //} + if (sequence[i] == '\0') { + throw InvalidSequenceException("Encountered null terminator in k-mer!"); + } assert(sequence[i] != '\0'); hasher.eat(sequence[i]); rc_hasher.eat(alphabet::complement(sequence[K - i - 1])); From 01ab27e3d93b5722b6e6440b0fc111af43cd9011 Mon Sep 17 00:00:00 2001 From: Camille Scott Date: Mon, 4 Apr 2022 03:26:07 -0700 Subject: [PATCH 3/4] Alphabet: valid window functions --- include/goetia/sequences/alphabets.hh | 55 +++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/include/goetia/sequences/alphabets.hh b/include/goetia/sequences/alphabets.hh index 866705e..d3220a0 100644 --- a/include/goetia/sequences/alphabets.hh +++ b/include/goetia/sequences/alphabets.hh @@ -69,6 +69,61 @@ struct Alphabet { } } } + + template + static const bool is_valid(const Iterable& sequence) { + for (const auto& symbol : sequence) { + if (validate(symbol) == '\0') { + return false; + } + } + return true; + } + + static std::vector find_valid_windows(const std::string_view sequence, + size_t min_size = 1) { + + std::vector windows; + int prev_invalid = -1; + + for (const auto [i, symbol] : enumerate(sequence)) { + if (validate(symbol) == '\0') { + if ((i - prev_invalid) > min_size) { + windows.push_back(sequence.substr(prev_invalid + 1, i - prev_invalid - 1)); + } + prev_invalid = i; + } + } + if ((sequence.length() - prev_invalid) > min_size) { + windows.push_back(sequence.substr(prev_invalid + 1, min_size)); + } + + return windows; + } + + static std::vector find_and_sanitize_valid_windows(std::string& sequence, + size_t min_size = 1) { + + std::vector windows; + int prev_invalid = -1; + + for (const auto [i, symbol] : enumerate(sequence)) { + auto validated = validate(symbol); + if (validated == '\0') { + if ((i - prev_invalid) > min_size) { + windows.emplace_back(sequence.c_str() + prev_invalid + 1, i - prev_invalid - 1); + } + prev_invalid = i; + } else { + sequence[i] = validated; + } + } + if ((sequence.length() - prev_invalid) > min_size) { + windows.emplace_back(sequence.c_str() + prev_invalid + 1, sequence.length() - prev_invalid - 1); + } + + return windows; + } static const char complement(const char c) { return Derived::_complement(c); From 52bf77a96ba1993054ec93a56b76a56cc4bdeb1f Mon Sep 17 00:00:00 2001 From: Camille Scott Date: Tue, 5 Apr 2022 12:39:38 -0700 Subject: [PATCH 4/4] Centralize and improve exceptions --- goetia/storage.py | 8 +- include/goetia/cdbg/cdbg.hh | 5 +- include/goetia/cdbg/compactor.hh | 9 +- include/goetia/cdbg/saturating_compactor.hh | 6 +- include/goetia/dbg.hh | 17 +- include/goetia/errors.hh | 103 + include/goetia/goetia.hh | 68 +- include/goetia/hashing/hashextender.hh | 12 +- include/goetia/hashing/hashshifter.hh | 31 +- include/goetia/hashing/kmeriterator.hh | 10 +- include/goetia/hashing/rollinghashshifter.hh | 19 +- include/goetia/hashing/ukhs.hh | 7 +- include/goetia/interface.hh | 4 +- include/goetia/is_detected.hh | 1 - include/goetia/metrics.hh | 5 +- include/goetia/nonstd/expected.hpp | 2326 +++++++++++++++++ include/goetia/parsing/readers.hh | 57 +- include/goetia/processors.hh | 14 +- include/goetia/sequences/alphabets.hh | 31 +- include/goetia/sequences/exceptions.hh | 39 - include/goetia/sketches/unikmer_sketch.hh | 17 +- include/goetia/storage/partitioned_storage.hh | 17 +- include/goetia/storage/storage.hh | 3 +- manifest.cmake | 2 + src/goetia/cdbg/cdbg.cc | 5 +- src/goetia/cdbg/compactor.cc | 4 +- src/goetia/dbg.cc | 26 +- src/goetia/hashing/ukhs.cc | 3 +- src/goetia/parsing/readers.cc | 6 + src/goetia/storage/bitstorage.cc | 23 +- src/goetia/storage/bytestorage.cc | 51 +- src/goetia/storage/nibblestorage.cc | 18 +- src/goetia/storage/qfstorage.cc | 11 +- src/goetia/storage/sparseppstorage.cc | 5 +- src/goetia/traversal/unitig_walker.cc | 6 +- tests/test_signatures.py | 8 +- tests/test_solid_compactor.py | 3 +- 37 files changed, 2664 insertions(+), 316 deletions(-) create mode 100644 include/goetia/errors.hh create mode 100644 include/goetia/nonstd/expected.hpp delete mode 100644 include/goetia/sequences/exceptions.hh diff --git a/goetia/storage.py b/goetia/storage.py index 5fe8c09..54cedde 100644 --- a/goetia/storage.py +++ b/goetia/storage.py @@ -11,13 +11,11 @@ from goetia import libgoetia from goetia.utils import check_trait -typenames = [(t, t.__name__.replace(' ', '')) for t in [libgoetia.SparseppSetStorage, - libgoetia.PHMapStorage, +typenames = [(t, t.__name__.replace(' ', '')) for t in [libgoetia.PHMapStorage, libgoetia.BitStorage, libgoetia.ByteStorage, libgoetia.NibbleStorage, - libgoetia.QFStorage, - libgoetia.BTreeStorage]] + libgoetia.QFStorage]] types = [_type for _type, _name in typenames] @@ -29,7 +27,7 @@ StorageTraits = libgoetia.StorageTraits -def get_storage_args(parser, default='SparseppSetStorage', +def get_storage_args(parser, default='PHMapStorage', group_name='storage'): if 'storage' in [g.title for g in parser._action_groups]: return None diff --git a/include/goetia/cdbg/cdbg.hh b/include/goetia/cdbg/cdbg.hh index 71c99cc..8819eda 100644 --- a/include/goetia/cdbg/cdbg.hh +++ b/include/goetia/cdbg/cdbg.hh @@ -30,6 +30,7 @@ #pragma GCC diagnostic pop #include "goetia/goetia.hh" +#include "goetia/errors.hh" #include "goetia/metrics.hh" #include "goetia/traversal/unitig_walker.hh" #include "goetia/hashing/kmeriterator.hh" @@ -625,7 +626,7 @@ public: write_gfa1(out); break; default: - throw GoetiaException("Invalid cDBG format."); + throw std::invalid_argument("Invalid cDBG format."); }; } @@ -733,8 +734,10 @@ extern template class goetia::cDBG>; extern template class goetia::cDBG>; +/* extern template class goetia::cDBG>; extern template class goetia::cDBG>; +*/ } diff --git a/include/goetia/cdbg/compactor.hh b/include/goetia/cdbg/compactor.hh index c858ddf..bf2311c 100644 --- a/include/goetia/cdbg/compactor.hh +++ b/include/goetia/cdbg/compactor.hh @@ -12,6 +12,7 @@ #include #include +#include "goetia/errors.hh" #include "goetia/traversal/unitig_walker.hh" #include "goetia/hashing/kmeriterator.hh" #include "goetia/dbg.hh" @@ -1037,13 +1038,13 @@ struct StreamingCompactor> { try { compactor->insert_sequence(read.sequence); - } catch (InvalidCharacterException &e) { + } catch (InvalidSequence& e) { std::cerr << "WARNING: Bad sequence encountered at " << this->_n_reads << ": " << read.sequence << ", exception was " << e.what() << std::endl; return 0; - } catch (SequenceLengthException &e) { + } catch (SequenceTooShort& e) { std::cerr << "NOTE: Skipped sequence that was too short: read " << this->_n_reads << " with sequence " << read.sequence @@ -1159,14 +1160,14 @@ struct StreamingCompactor> { } -extern template class goetia::StreamingCompactor>; +//extern template class goetia::StreamingCompactor>; extern template class goetia::StreamingCompactor>; // extern template class goetia::StreamingCompactor>; // extern template class goetia::StreamingCompactor>; // extern template class goetia::StreamingCompactor>; // extern template class goetia::StreamingCompactor>; -extern template class std::deque>>; +//extern template class std::deque>>; extern template class std::deque>>; diff --git a/include/goetia/cdbg/saturating_compactor.hh b/include/goetia/cdbg/saturating_compactor.hh index 5e153fa..4e9112c 100644 --- a/include/goetia/cdbg/saturating_compactor.hh +++ b/include/goetia/cdbg/saturating_compactor.hh @@ -9,9 +9,9 @@ #ifndef GOETIA_SATURATING_CPTOR #define GOETIA_SATURATING_CPTOR +#include "goetia/errors.hh" #include "goetia/processors.hh" #include "goetia/parsing/readers.hh" -#include "goetia/sequences/exceptions.hh" namespace goetia { @@ -54,13 +54,13 @@ struct SaturatingCompactor { try { compactor->insert_sequence(read.sequence); signature->insert_sequence(read.sequence); - } catch (InvalidCharacterException &e) { + } catch (InvalidSequence& e) { std::cerr << "WARNING: Bad sequence encountered at " << this->_n_reads << ": " << read.sequence << ", exception was " << e.what() << std::endl; return 0; - } catch (SequenceLengthException &e) { + } catch (SequenceTooShort& e) { std::cerr << "NOTE: Skipped sequence that was too short: read " << this->_n_reads << " with sequence " << read.sequence diff --git a/include/goetia/dbg.hh b/include/goetia/dbg.hh index 309f1d7..57e68cd 100644 --- a/include/goetia/dbg.hh +++ b/include/goetia/dbg.hh @@ -10,13 +10,13 @@ #define GOETIA_DBG_HH #include "goetia/meta.hh" +#include "goetia/errors.hh" #include "goetia/hashing/kmeriterator.hh" #include "goetia/processors.hh" #include "goetia/storage/storage.hh" #include "goetia/storage/storage_types.hh" #include "goetia/hashing/rollinghashshifter.hh" #include "goetia/hashing/ukhs.hh" -#include "goetia/sequences/exceptions.hh" #include "goetia/traversal/unitig_walker.hh" #include @@ -513,10 +513,12 @@ extern template class goetia::dBG; extern template class goetia::dBG; extern template class goetia::dBG; +/* extern template class goetia::dBG; extern template class goetia::dBG; extern template class goetia::dBG; extern template class goetia::dBG; +*/ extern template class goetia::dBG; extern template class goetia::dBG; @@ -538,20 +540,24 @@ extern template class goetia::dBG; extern template class goetia::dBG; +/* extern template class goetia::dBG; extern template class goetia::dBG; extern template class goetia::dBG; extern template class goetia::dBG; +*/ extern template class goetia::UnitigWalker>; extern template class goetia::UnitigWalker>; extern template class goetia::UnitigWalker>; extern template class goetia::UnitigWalker>; +/* extern template class goetia::UnitigWalker>; extern template class goetia::UnitigWalker>; extern template class goetia::UnitigWalker>; extern template class goetia::UnitigWalker>; +*/ extern template class goetia::UnitigWalker>; extern template class goetia::UnitigWalker>; @@ -573,21 +579,24 @@ extern template class goetia::UnitigWalker>; extern template class goetia::UnitigWalker>; +/* extern template class goetia::UnitigWalker>; extern template class goetia::UnitigWalker>; extern template class goetia::UnitigWalker>; extern template class goetia::UnitigWalker>; +*/ extern template class goetia::KmerIterator>; extern template class goetia::KmerIterator>; extern template class goetia::KmerIterator>; extern template class goetia::KmerIterator>; +/* extern template class goetia::KmerIterator>; extern template class goetia::KmerIterator>; extern template class goetia::KmerIterator>; extern template class goetia::KmerIterator>; - +*/ extern template class goetia::KmerIterator>; extern template class goetia::KmerIterator>; extern template class goetia::KmerIterator>; @@ -608,11 +617,11 @@ extern template class goetia::KmerIterator>; extern template class goetia::KmerIterator>; +/* extern template class goetia::KmerIterator>; extern template class goetia::KmerIterator>; extern template class goetia::KmerIterator>; extern template class goetia::KmerIterator>; - - +*/ #endif diff --git a/include/goetia/errors.hh b/include/goetia/errors.hh new file mode 100644 index 0000000..d9a88d0 --- /dev/null +++ b/include/goetia/errors.hh @@ -0,0 +1,103 @@ +/** + * (c) Camille Scott, 2021 + * File : errors.hh + * License: MIT + * Author : Camille Scott + * Date : 04.04.2022 + */ + +#ifndef GOETIA_ERRORS_HH +#define GOETIA_ERRORS_HH + +#include +#include +#include + +struct GoetiaBaseException : public std::exception {}; + +struct InvalidSequence : public GoetiaBaseException { + size_t read_number; + const std::string sequence; + + InvalidSequence(size_t read_number, const std::string& sequence) + : read_number(read_number), sequence(sequence) + {} +}; + +struct InvalidRecord : public GoetiaBaseException { + size_t read_number; + const std::string sequence; + + InvalidRecord(size_t read_number, const std::string& sequence) + : read_number(read_number), sequence(sequence) + {} +}; + + +struct InvalidRecordPair : public GoetiaBaseException { + size_t pair_number; + const std::string name_left, name_right; + + InvalidRecordPair(size_t number, const std::string& name_left, const std::string& name_right) + : pair_number(number), name_left(name_left), name_right(name_right) + {} +}; + + +struct EndOfStream : public GoetiaBaseException {}; + +struct InvalidStream : public GoetiaBaseException { + const std::string message; + + InvalidStream(const std::string& message) + : message(message) + {} +}; + +struct InvalidPairedStream : public GoetiaBaseException {}; + +struct StreamReadError : public GoetiaBaseException { + size_t read_number; + const std::string filename; + + StreamReadError(size_t read_number, const std::string& filename) + : read_number(read_number), filename(filename) + {} +}; + + +struct SequenceTooShort : public GoetiaBaseException { + const std::string sequence; + SequenceTooShort(const std::string& sequence) + : sequence(sequence) + {} +}; + + +struct UninitializedShifter : public GoetiaBaseException {}; + +struct NotImplemented : public GoetiaBaseException {}; + +struct InvalidPartition : public GoetiaBaseException { + uint64_t partition; + InvalidPartition(uint64_t partition) + : partition(partition) + {} +}; + +struct DeserializationError : public GoetiaBaseException { + const std::string message; + DeserializationError(const std::string& message) + : message(message) + {} +}; + +struct SerializationError : public GoetiaBaseException { + const std::string message; + SerializationError(const std::string& message) + : message(message) + {} +}; + + +#endif diff --git a/include/goetia/goetia.hh b/include/goetia/goetia.hh index 3c52436..640a3d7 100644 --- a/include/goetia/goetia.hh +++ b/include/goetia/goetia.hh @@ -11,7 +11,6 @@ #define GOETIA_MISC_HH #include -#include #include #include #include @@ -110,6 +109,32 @@ bool contains(std::vector collection, return false; } +/* + * from https://www.reedbeta.com/blog/python-like-enumerate-in-cpp17/ + */ +template ())), + typename = decltype(std::end(std::declval()))> +constexpr auto enumerate(T && iterable) { + struct iterator + { + size_t i; + TIter iter; + bool operator != (const iterator & other) const { return iter != other.iter; } + void operator ++ () { ++i; ++iter; } + auto operator * () const { return std::tie(i, *iter); } + }; + + struct iterable_wrapper + { + T iterable; + auto begin() { return iterator{ 0, std::begin(iterable) }; } + auto end() { return iterator{ 0, std::end(iterable) }; } + }; + + return iterable_wrapper{ std::forward(iterable) }; +} + /* * Adapted from make_from_tuple: https://en.cppreference.com/w/cpp/utility/make_from_tuple @@ -137,47 +162,6 @@ auto make_tuple_from_tuple(std::tuple model_tuple, Args&&... args) { } -class GoetiaException : public std::exception { -public: - explicit GoetiaException(const std::string& msg = "Generic goetia exception.") - : _msg(msg) { } - - virtual ~GoetiaException() throw() { } - virtual const char* what() const throw () - { - return _msg.c_str(); - } - -protected: - const std::string _msg; -}; - -class GoetiaFileException: public GoetiaException { -public: - explicit GoetiaFileException(const std::string& msg = "Error reading or writing file.") - : GoetiaException(msg) { } -}; - - -class InvalidStream : public GoetiaFileException -{ -public: - InvalidStream() - : GoetiaFileException("Generic InvalidStream error") {} - explicit InvalidStream(const std::string& msg) - : GoetiaFileException(msg) {} -}; - -class StreamReadError : public GoetiaFileException -{ -public: - StreamReadError() - : GoetiaFileException("Generic StreamReadError error") {} - explicit StreamReadError(const std::string& msg) - : GoetiaFileException(msg) {} -}; - - template struct fail : std::false_type { diff --git a/include/goetia/hashing/hashextender.hh b/include/goetia/hashing/hashextender.hh index 3e4e74e..ec8265b 100644 --- a/include/goetia/hashing/hashextender.hh +++ b/include/goetia/hashing/hashextender.hh @@ -123,7 +123,7 @@ public: hash_type shift_left(const char& c) { if (!this->is_loaded()) { - throw UninitializedShifterException(); + throw UninitializedShifter(); } return this->shift_left_impl(c); @@ -144,7 +144,7 @@ public: std::vector left_extensions() { if (!this->is_loaded()) { - throw UninitializedShifterException(); + throw UninitializedShifter(); } return this->left_extensions_impl(); @@ -160,7 +160,7 @@ public: */ hash_type shift_right(const char& c){ if (!this->is_loaded()) { - throw UninitializedShifterException(); + throw UninitializedShifter(); } return this->shift_right_impl(c); @@ -182,7 +182,7 @@ public: std::vector right_extensions() { if (!this->is_loaded()) { - throw UninitializedShifterException(); + throw UninitializedShifter(); } return this->right_extensions_impl(); @@ -197,7 +197,7 @@ public: */ hash_type set_cursor(const std::string& sequence) { if (sequence.length() < K) { - throw InvalidSequenceException("Sequence must at least length K"); + throw SequenceTooShort(sequence); } return set_cursor(sequence.c_str()); } @@ -216,7 +216,7 @@ public: hash_type hash_base(const std::string& sequence) { if (sequence.length() < K) { - throw InvalidSequenceException("Sequence must at least length K"); + throw SequenceTooShort(sequence); } return set_cursor(sequence); diff --git a/include/goetia/hashing/hashshifter.hh b/include/goetia/hashing/hashshifter.hh index 4848ba2..4428931 100644 --- a/include/goetia/hashing/hashshifter.hh +++ b/include/goetia/hashing/hashshifter.hh @@ -20,9 +20,10 @@ #include "goetia/goetia.hh" #include "goetia/sequences/alphabets.hh" -#include "goetia/sequences/exceptions.hh" #include "goetia/meta.hh" +#include "goetia/errors.hh" +#include "goetia/nonstd/expected.hpp" #include "goetia/hashing/rollinghashshifter.hh" @@ -30,24 +31,6 @@ namespace goetia { -class HashShifterException : public GoetiaException { -public: - explicit HashShifterException(const std::string& msg = "HashShifter exception.") - : GoetiaException(msg) - { - } -}; - - -class UninitializedShifterException : public GoetiaException { -public: - explicit UninitializedShifterException(const std::string& msg = "Shifter used without hash_base being called.") - : GoetiaException(msg) - { - } -}; - - template struct has_minimizer { static const bool value = false; @@ -132,21 +115,21 @@ public: hash_type shift_right(const char& out, const char& in) { if (!initialized) { - throw UninitializedShifterException(); + throw UninitializedShifter(); } return this->shift_right_impl(out, in); } hash_type shift_left(const char& in, const char& out) { if (!initialized) { - throw UninitializedShifterException(); + throw UninitializedShifter(); } return this->shift_left_impl(in, out); } hash_type hash_base(const std::string& sequence) { if (sequence.length() < K) { - throw InvalidSequenceException("HashShifter::hash_base: Sequence must at least length K"); + throw SequenceTooShort(sequence); } hash_type h = hash_base(sequence.c_str()); @@ -156,7 +139,7 @@ public: template hash_type hash_base(It begin, It end) { if (std::distance(begin, end) != K) { - throw InvalidSequenceException("HashShifter::hash_base: Iterator distance must be length K"); + throw SequenceTooShort(std::string(begin, end)); } hash_type h = this->hash_base_impl(begin, end); initialized = true; @@ -179,7 +162,7 @@ public: const uint16_t K, ExtraArgs&&... args) { if (sequence.length() < K) { - throw InvalidSequenceException("HashShifter::hash: Sequence must at least length K"); + throw SequenceTooShort(sequence); } return hash(sequence.c_str(), K, std::forward(args)...); } diff --git a/include/goetia/hashing/kmeriterator.hh b/include/goetia/hashing/kmeriterator.hh index 8327fbf..176f0dc 100644 --- a/include/goetia/hashing/kmeriterator.hh +++ b/include/goetia/hashing/kmeriterator.hh @@ -23,7 +23,7 @@ #include "goetia/goetia.hh" #include "goetia/hashing/hashshifter.hh" -#include "goetia/sequences/exceptions.hh" +#include "goetia/errors.hh" #include "goetia/hashing/shifter_types.hh" @@ -57,7 +57,7 @@ public: { if (_seq.length() < K) { - throw SequenceLengthException("Sequence must have length >= K"); + throw SequenceTooShort(seq); } shifter = new ShifterType(seq, K, std::forward(args)...); } @@ -72,7 +72,7 @@ public: shifter(shifter) { if (_seq.length() < K) { - throw SequenceLengthException("Sequence must have length >= K"); + throw SequenceTooShort(seq); } shifter->hash_base(_seq); } @@ -87,7 +87,7 @@ public: { if (_seq.length() < K) { - throw SequenceLengthException("Sequence must have length >= K"); + throw SequenceTooShort(seq); } shifter = new ShifterType(shifter_proto); shifter->hash_base(_seq); @@ -157,7 +157,7 @@ public: template struct kmer_iterator { - using value_type = typename ShifterType::hash_type; + using value_type = typename ShifterType::hash_type; using iterator_category = std::input_iterator_tag; using difference_type = int64_t; using reference = const value_type; diff --git a/include/goetia/hashing/rollinghashshifter.hh b/include/goetia/hashing/rollinghashshifter.hh index dff5b8c..5719235 100644 --- a/include/goetia/hashing/rollinghashshifter.hh +++ b/include/goetia/hashing/rollinghashshifter.hh @@ -11,6 +11,7 @@ #include "goetia/goetia.hh" #include "goetia/meta.hh" +#include "goetia/errors.hh" #include "goetia/sequences/alphabets.hh" #include "goetia/hashing/canonical.hh" @@ -27,9 +28,8 @@ protected: CyclicHash hasher; explicit LemireShifterPolicyBase(uint16_t K) - : hasher(K) { - - //std::cout << "END RollingBase(K) ctor " << this << std::endl; + : hasher(K) + { } LemireShifterPolicyBase() = delete; @@ -70,9 +70,8 @@ public: this->hasher.reset(); for (uint16_t i = 0; i < K; ++i) { if (sequence[i] == '\0') { - throw InvalidSequenceException("Encountered null terminator in k-mer!"); + throw SequenceTooShort(sequence); } - assert(sequence[i] != '\0'); this->hasher.eat(sequence[i]); } return get_impl(); @@ -90,7 +89,6 @@ public: } hash_type get_impl() { - //std::cout << "RollingHash: get_impl " << this->hasher.hashvalue << std::endl;; return {this->hasher.hashvalue}; } @@ -111,13 +109,11 @@ protected: : LemireShifterPolicyBase(K), K(K) { - //std::cout << "END LemireShifterPolicy(K) ctor " << this << " / " << static_cast*>(this) << std::endl; } explicit LemireShifterPolicy(const LemireShifterPolicy& other) : LemireShifterPolicy(other.K) { - //std::cout << "END LemireShifterPolicy(other) ctor " << this << " / " << static_cast*>(this) << std::endl; } LemireShifterPolicy() = delete; @@ -163,13 +159,8 @@ LemireShifterPolicy> while (i < K) { hasher.eat(*(begin)); rc_hasher.eat(alphabet::complement(*(end))); - - //std::cout << "eat: " << *begin << " fwd, " << *end << " rc (" - // << alphabet::complement(*(end)) << ")" << std::endl; - ++begin; --end; - ++i; } @@ -186,7 +177,7 @@ LemireShifterPolicy> for (uint16_t i = 0; i < K; ++i) { if (sequence[i] == '\0') { - throw InvalidSequenceException("Encountered null terminator in k-mer!"); + throw SequenceTooShort(sequence); } assert(sequence[i] != '\0'); hasher.eat(sequence[i]); diff --git a/include/goetia/hashing/ukhs.hh b/include/goetia/hashing/ukhs.hh index 83df081..8dd2841 100644 --- a/include/goetia/hashing/ukhs.hh +++ b/include/goetia/hashing/ukhs.hh @@ -18,6 +18,7 @@ #include #include #include +#include #include #include "goetia/goetia.hh" @@ -249,7 +250,7 @@ protected: // throw GoetiaException("Window should contain unikmer."); //} if (window_unikmers.size() == 0) { - throw HashShifterException("No unikmers in window!"); + throw std::domain_error("No unikmers in window!"); } return *std::min_element(std::begin(window_unikmers), std::end(window_unikmers)); @@ -502,10 +503,10 @@ protected: ukhs_map (std::move(ukhs)) { if (ukhs_map->W != K) { - throw GoetiaException("Shifter K does not match UKHS::Map W."); + throw std::invalid_argument("Shifter K does not match UKHS::Map W."); } if (ukhs_map->K != unikmer_K) { - throw GoetiaException("Shifter unikmer_K does not match UKHS::Map K."); + throw std::invalid_argument("Shifter unikmer_K does not match UKHS::Map K."); } } diff --git a/include/goetia/interface.hh b/include/goetia/interface.hh index 2e011d5..34d1dd9 100644 --- a/include/goetia/interface.hh +++ b/include/goetia/interface.hh @@ -14,6 +14,7 @@ // it breaks conda builds with cppyy=1.5.5 #define _GLIBCXX_CONDITION_VARIABLE 1 +#include "goetia/errors.hh" #include "goetia/goetia.hh" #include "goetia/meta.hh" @@ -27,7 +28,7 @@ #include "goetia/storage/btreestorage.hh" #include "goetia/storage/phmapstorage.hh" #include "goetia/storage/sparsepp/spp.h" -// + #include "goetia/hashing/kmeriterator.hh" #include "goetia/hashing/kmer_span.hh" #include "goetia/hashing/hashshifter.hh" @@ -38,7 +39,6 @@ #include "goetia/hashing/canonical.hh" #include "goetia/sequences/alphabets.hh" -#include "goetia/sequences/exceptions.hh" #include "goetia/parsing/parsing.hh" #include "goetia/parsing/readers.hh" diff --git a/include/goetia/is_detected.hh b/include/goetia/is_detected.hh index 75c55ab..0b4fcc3 100644 --- a/include/goetia/is_detected.hh +++ b/include/goetia/is_detected.hh @@ -9,7 +9,6 @@ * */ - #ifndef GOETIA_IS_DETECTED_HH #define GOETIA_IS_DETECTED_HH diff --git a/include/goetia/metrics.hh b/include/goetia/metrics.hh index 8ae18a2..728908b 100644 --- a/include/goetia/metrics.hh +++ b/include/goetia/metrics.hh @@ -30,7 +30,7 @@ struct ReservoirSample { private: - std::default_random_engine gen; + std::default_random_engine gen; std::vector samples; size_t n_sampled; @@ -38,7 +38,7 @@ public: ReservoirSample(size_t sample_size) : gen (std::random_device()()), - samples (sample_size, 0), + samples (sample_size, 0), n_sampled (0) { @@ -73,7 +73,6 @@ public: std::fill(samples.begin(), samples.end(), 0); n_sampled = 0; } - }; diff --git a/include/goetia/nonstd/expected.hpp b/include/goetia/nonstd/expected.hpp new file mode 100644 index 0000000..31b130a --- /dev/null +++ b/include/goetia/nonstd/expected.hpp @@ -0,0 +1,2326 @@ +/// +// expected - An implementation of std::expected with extensions +// Written in 2017 by Simon Brand (simonrbrand@gmail.com, @TartanLlama) +// +// Documentation available at http://tl.tartanllama.xyz/ +// +// To the extent possible under law, the author(s) have dedicated all +// copyright and related and neighboring rights to this software to the +// public domain worldwide. This software is distributed without any warranty. +// +// You should have received a copy of the CC0 Public Domain Dedication +// along with this software. If not, see +// . +/// + +#ifndef TL_EXPECTED_HPP +#define TL_EXPECTED_HPP + +#define TL_EXPECTED_VERSION_MAJOR 1 +#define TL_EXPECTED_VERSION_MINOR 0 +#define TL_EXPECTED_VERSION_PATCH 1 + +#include +#include +#include +#include + +#if defined(__EXCEPTIONS) || defined(_CPPUNWIND) +#define TL_EXPECTED_EXCEPTIONS_ENABLED +#endif + +#if (defined(_MSC_VER) && _MSC_VER == 1900) +#define TL_EXPECTED_MSVC2015 +#define TL_EXPECTED_MSVC2015_CONSTEXPR +#else +#define TL_EXPECTED_MSVC2015_CONSTEXPR constexpr +#endif + +#if (defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ <= 9 && \ + !defined(__clang__)) +#define TL_EXPECTED_GCC49 +#endif + +#if (defined(__GNUC__) && __GNUC__ == 5 && __GNUC_MINOR__ <= 4 && \ + !defined(__clang__)) +#define TL_EXPECTED_GCC54 +#endif + +#if (defined(__GNUC__) && __GNUC__ == 5 && __GNUC_MINOR__ <= 5 && \ + !defined(__clang__)) +#define TL_EXPECTED_GCC55 +#endif + +#if (defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ <= 9 && \ + !defined(__clang__)) +// GCC < 5 doesn't support overloading on const&& for member functions + +#define TL_EXPECTED_NO_CONSTRR +// GCC < 5 doesn't support some standard C++11 type traits +#define TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T) \ + std::has_trivial_copy_constructor +#define TL_EXPECTED_IS_TRIVIALLY_COPY_ASSIGNABLE(T) \ + std::has_trivial_copy_assign + +// This one will be different for GCC 5.7 if it's ever supported +#define TL_EXPECTED_IS_TRIVIALLY_DESTRUCTIBLE(T) \ + std::is_trivially_destructible + +// GCC 5 < v < 8 has a bug in is_trivially_copy_constructible which breaks std::vector +// for non-copyable types +#elif (defined(__GNUC__) && __GNUC__ < 8 && \ + !defined(__clang__)) +#ifndef TL_GCC_LESS_8_TRIVIALLY_COPY_CONSTRUCTIBLE_MUTEX +#define TL_GCC_LESS_8_TRIVIALLY_COPY_CONSTRUCTIBLE_MUTEX +namespace tl { + namespace detail { + template + struct is_trivially_copy_constructible : std::is_trivially_copy_constructible{}; +#ifdef _GLIBCXX_VECTOR + template + struct is_trivially_copy_constructible> + : std::false_type{}; +#endif + } +} +#endif + +#define TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T) \ + tl::detail::is_trivially_copy_constructible +#define TL_EXPECTED_IS_TRIVIALLY_COPY_ASSIGNABLE(T) \ + std::is_trivially_copy_assignable +#define TL_EXPECTED_IS_TRIVIALLY_DESTRUCTIBLE(T) std::is_trivially_destructible +#else +#define TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T) \ + std::is_trivially_copy_constructible +#define TL_EXPECTED_IS_TRIVIALLY_COPY_ASSIGNABLE(T) \ + std::is_trivially_copy_assignable +#define TL_EXPECTED_IS_TRIVIALLY_DESTRUCTIBLE(T) \ + std::is_trivially_destructible +#endif + +#if __cplusplus > 201103L +#define TL_EXPECTED_CXX14 +#endif + +#ifdef TL_EXPECTED_GCC49 +#define TL_EXPECTED_GCC49_CONSTEXPR +#else +#define TL_EXPECTED_GCC49_CONSTEXPR constexpr +#endif + +#if (__cplusplus == 201103L || defined(TL_EXPECTED_MSVC2015) || \ + defined(TL_EXPECTED_GCC49)) +#define TL_EXPECTED_11_CONSTEXPR +#else +#define TL_EXPECTED_11_CONSTEXPR constexpr +#endif + +namespace tl { +template class expected; + +#ifndef TL_MONOSTATE_INPLACE_MUTEX +#define TL_MONOSTATE_INPLACE_MUTEX +class monostate {}; + +struct in_place_t { + explicit in_place_t() = default; +}; +static constexpr in_place_t in_place{}; +#endif + +template class unexpected { +public: + static_assert(!std::is_same::value, "E must not be void"); + + unexpected() = delete; + constexpr explicit unexpected(const E &e) : m_val(e) {} + + constexpr explicit unexpected(E &&e) : m_val(std::move(e)) {} + + constexpr const E &value() const & { return m_val; } + TL_EXPECTED_11_CONSTEXPR E &value() & { return m_val; } + TL_EXPECTED_11_CONSTEXPR E &&value() && { return std::move(m_val); } + constexpr const E &&value() const && { return std::move(m_val); } + +private: + E m_val; +}; + +template +constexpr bool operator==(const unexpected &lhs, const unexpected &rhs) { + return lhs.value() == rhs.value(); +} +template +constexpr bool operator!=(const unexpected &lhs, const unexpected &rhs) { + return lhs.value() != rhs.value(); +} +template +constexpr bool operator<(const unexpected &lhs, const unexpected &rhs) { + return lhs.value() < rhs.value(); +} +template +constexpr bool operator<=(const unexpected &lhs, const unexpected &rhs) { + return lhs.value() <= rhs.value(); +} +template +constexpr bool operator>(const unexpected &lhs, const unexpected &rhs) { + return lhs.value() > rhs.value(); +} +template +constexpr bool operator>=(const unexpected &lhs, const unexpected &rhs) { + return lhs.value() >= rhs.value(); +} + +template +unexpected::type> make_unexpected(E &&e) { + return unexpected::type>(std::forward(e)); +} + +struct unexpect_t { + unexpect_t() = default; +}; +static constexpr unexpect_t unexpect{}; + +namespace detail { +template +[[noreturn]] TL_EXPECTED_11_CONSTEXPR void throw_exception(E &&e) { +#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED + throw std::forward(e); +#else + #ifdef _MSC_VER + __assume(0); + #else + __builtin_unreachable(); + #endif +#endif +} + +#ifndef TL_TRAITS_MUTEX +#define TL_TRAITS_MUTEX +// C++14-style aliases for brevity +template using remove_const_t = typename std::remove_const::type; +template +using remove_reference_t = typename std::remove_reference::type; +template using decay_t = typename std::decay::type; +template +using enable_if_t = typename std::enable_if::type; +template +using conditional_t = typename std::conditional::type; + +// std::conjunction from C++17 +template struct conjunction : std::true_type {}; +template struct conjunction : B {}; +template +struct conjunction + : std::conditional, B>::type {}; + +#if defined(_LIBCPP_VERSION) && __cplusplus == 201103L +#define TL_TRAITS_LIBCXX_MEM_FN_WORKAROUND +#endif + +// In C++11 mode, there's an issue in libc++'s std::mem_fn +// which results in a hard-error when using it in a noexcept expression +// in some cases. This is a check to workaround the common failing case. +#ifdef TL_TRAITS_LIBCXX_MEM_FN_WORKAROUND +template struct is_pointer_to_non_const_member_func : std::false_type {}; +template +struct is_pointer_to_non_const_member_func : std::true_type {}; +template +struct is_pointer_to_non_const_member_func : std::true_type {}; +template +struct is_pointer_to_non_const_member_func : std::true_type {}; +template +struct is_pointer_to_non_const_member_func : std::true_type {}; +template +struct is_pointer_to_non_const_member_func : std::true_type {}; +template +struct is_pointer_to_non_const_member_func : std::true_type {}; + +template struct is_const_or_const_ref : std::false_type {}; +template struct is_const_or_const_ref : std::true_type {}; +template struct is_const_or_const_ref : std::true_type {}; +#endif + +// std::invoke from C++17 +// https://stackoverflow.com/questions/38288042/c11-14-invoke-workaround +template ::value + && is_const_or_const_ref::value)>, +#endif + typename = enable_if_t>::value>, + int = 0> + constexpr auto invoke(Fn && f, Args && ... args) noexcept( + noexcept(std::mem_fn(f)(std::forward(args)...))) + -> decltype(std::mem_fn(f)(std::forward(args)...)) { + return std::mem_fn(f)(std::forward(args)...); +} + +template >::value>> + constexpr auto invoke(Fn && f, Args && ... args) noexcept( + noexcept(std::forward(f)(std::forward(args)...))) + -> decltype(std::forward(f)(std::forward(args)...)) { + return std::forward(f)(std::forward(args)...); +} + +// std::invoke_result from C++17 +template struct invoke_result_impl; + +template +struct invoke_result_impl< + F, decltype(detail::invoke(std::declval(), std::declval()...), void()), + Us...> { + using type = decltype(detail::invoke(std::declval(), std::declval()...)); +}; + +template +using invoke_result = invoke_result_impl; + +template +using invoke_result_t = typename invoke_result::type; + +#if defined(_MSC_VER) && _MSC_VER <= 1900 +// TODO make a version which works with MSVC 2015 +template struct is_swappable : std::true_type {}; + +template struct is_nothrow_swappable : std::true_type {}; +#else +// https://stackoverflow.com/questions/26744589/what-is-a-proper-way-to-implement-is-swappable-to-test-for-the-swappable-concept +namespace swap_adl_tests { + // if swap ADL finds this then it would call std::swap otherwise (same + // signature) + struct tag {}; + + template tag swap(T&, T&); + template tag swap(T(&a)[N], T(&b)[N]); + + // helper functions to test if an unqualified swap is possible, and if it + // becomes std::swap + template std::false_type can_swap(...) noexcept(false); + template (), std::declval()))> + std::true_type can_swap(int) noexcept(noexcept(swap(std::declval(), + std::declval()))); + + template std::false_type uses_std(...); + template + std::is_same(), std::declval())), tag> + uses_std(int); + + template + struct is_std_swap_noexcept + : std::integral_constant::value&& + std::is_nothrow_move_assignable::value> {}; + + template + struct is_std_swap_noexcept : is_std_swap_noexcept {}; + + template + struct is_adl_swap_noexcept + : std::integral_constant(0))> {}; +} // namespace swap_adl_tests + +template +struct is_swappable + : std::integral_constant< + bool, + decltype(detail::swap_adl_tests::can_swap(0))::value && + (!decltype(detail::swap_adl_tests::uses_std(0))::value || + (std::is_move_assignable::value && + std::is_move_constructible::value))> {}; + +template +struct is_swappable + : std::integral_constant< + bool, + decltype(detail::swap_adl_tests::can_swap(0))::value && + (!decltype( + detail::swap_adl_tests::uses_std(0))::value || + is_swappable::value)> {}; + +template +struct is_nothrow_swappable + : std::integral_constant< + bool, + is_swappable::value && + ((decltype(detail::swap_adl_tests::uses_std(0))::value + && detail::swap_adl_tests::is_std_swap_noexcept::value) || + (!decltype(detail::swap_adl_tests::uses_std(0))::value && + detail::swap_adl_tests::is_adl_swap_noexcept::value))> { +}; +#endif +#endif + +// Trait for checking if a type is a tl::expected +template struct is_expected_impl : std::false_type {}; +template +struct is_expected_impl> : std::true_type {}; +template using is_expected = is_expected_impl>; + +template +using expected_enable_forward_value = detail::enable_if_t< + std::is_constructible::value && + !std::is_same, in_place_t>::value && + !std::is_same, detail::decay_t>::value && + !std::is_same, detail::decay_t>::value>; + +template +using expected_enable_from_other = detail::enable_if_t< + std::is_constructible::value && + std::is_constructible::value && + !std::is_constructible &>::value && + !std::is_constructible &&>::value && + !std::is_constructible &>::value && + !std::is_constructible &&>::value && + !std::is_convertible &, T>::value && + !std::is_convertible &&, T>::value && + !std::is_convertible &, T>::value && + !std::is_convertible &&, T>::value>; + +template +using is_void_or = conditional_t::value, std::true_type, U>; + +template +using is_copy_constructible_or_void = + is_void_or>; + +template +using is_move_constructible_or_void = + is_void_or>; + +template +using is_copy_assignable_or_void = + is_void_or>; + + +template +using is_move_assignable_or_void = + is_void_or>; + + +} // namespace detail + +namespace detail { +struct no_init_t {}; +static constexpr no_init_t no_init{}; + +// Implements the storage of the values, and ensures that the destructor is +// trivial if it can be. +// +// This specialization is for where neither `T` or `E` is trivially +// destructible, so the destructors must be called on destruction of the +// `expected` +template ::value, + bool = std::is_trivially_destructible::value> +struct expected_storage_base { + constexpr expected_storage_base() : m_val(T{}), m_has_val(true) {} + constexpr expected_storage_base(no_init_t) : m_no_init(), m_has_val(false) {} + + template ::value> * = + nullptr> + constexpr expected_storage_base(in_place_t, Args &&... args) + : m_val(std::forward(args)...), m_has_val(true) {} + + template &, Args &&...>::value> * = nullptr> + constexpr expected_storage_base(in_place_t, std::initializer_list il, + Args &&... args) + : m_val(il, std::forward(args)...), m_has_val(true) {} + template ::value> * = + nullptr> + constexpr explicit expected_storage_base(unexpect_t, Args &&... args) + : m_unexpect(std::forward(args)...), m_has_val(false) {} + + template &, Args &&...>::value> * = nullptr> + constexpr explicit expected_storage_base(unexpect_t, + std::initializer_list il, + Args &&... args) + : m_unexpect(il, std::forward(args)...), m_has_val(false) {} + + ~expected_storage_base() { + if (m_has_val) { + m_val.~T(); + } else { + m_unexpect.~unexpected(); + } + } + union { + T m_val; + unexpected m_unexpect; + char m_no_init; + }; + bool m_has_val; +}; + +// This specialization is for when both `T` and `E` are trivially-destructible, +// so the destructor of the `expected` can be trivial. +template struct expected_storage_base { + constexpr expected_storage_base() : m_val(T{}), m_has_val(true) {} + constexpr expected_storage_base(no_init_t) : m_no_init(), m_has_val(false) {} + + template ::value> * = + nullptr> + constexpr expected_storage_base(in_place_t, Args &&... args) + : m_val(std::forward(args)...), m_has_val(true) {} + + template &, Args &&...>::value> * = nullptr> + constexpr expected_storage_base(in_place_t, std::initializer_list il, + Args &&... args) + : m_val(il, std::forward(args)...), m_has_val(true) {} + template ::value> * = + nullptr> + constexpr explicit expected_storage_base(unexpect_t, Args &&... args) + : m_unexpect(std::forward(args)...), m_has_val(false) {} + + template &, Args &&...>::value> * = nullptr> + constexpr explicit expected_storage_base(unexpect_t, + std::initializer_list il, + Args &&... args) + : m_unexpect(il, std::forward(args)...), m_has_val(false) {} + + ~expected_storage_base() = default; + union { + T m_val; + unexpected m_unexpect; + char m_no_init; + }; + bool m_has_val; +}; + +// T is trivial, E is not. +template struct expected_storage_base { + constexpr expected_storage_base() : m_val(T{}), m_has_val(true) {} + TL_EXPECTED_MSVC2015_CONSTEXPR expected_storage_base(no_init_t) + : m_no_init(), m_has_val(false) {} + + template ::value> * = + nullptr> + constexpr expected_storage_base(in_place_t, Args &&... args) + : m_val(std::forward(args)...), m_has_val(true) {} + + template &, Args &&...>::value> * = nullptr> + constexpr expected_storage_base(in_place_t, std::initializer_list il, + Args &&... args) + : m_val(il, std::forward(args)...), m_has_val(true) {} + template ::value> * = + nullptr> + constexpr explicit expected_storage_base(unexpect_t, Args &&... args) + : m_unexpect(std::forward(args)...), m_has_val(false) {} + + template &, Args &&...>::value> * = nullptr> + constexpr explicit expected_storage_base(unexpect_t, + std::initializer_list il, + Args &&... args) + : m_unexpect(il, std::forward(args)...), m_has_val(false) {} + + ~expected_storage_base() { + if (!m_has_val) { + m_unexpect.~unexpected(); + } + } + + union { + T m_val; + unexpected m_unexpect; + char m_no_init; + }; + bool m_has_val; +}; + +// E is trivial, T is not. +template struct expected_storage_base { + constexpr expected_storage_base() : m_val(T{}), m_has_val(true) {} + constexpr expected_storage_base(no_init_t) : m_no_init(), m_has_val(false) {} + + template ::value> * = + nullptr> + constexpr expected_storage_base(in_place_t, Args &&... args) + : m_val(std::forward(args)...), m_has_val(true) {} + + template &, Args &&...>::value> * = nullptr> + constexpr expected_storage_base(in_place_t, std::initializer_list il, + Args &&... args) + : m_val(il, std::forward(args)...), m_has_val(true) {} + template ::value> * = + nullptr> + constexpr explicit expected_storage_base(unexpect_t, Args &&... args) + : m_unexpect(std::forward(args)...), m_has_val(false) {} + + template &, Args &&...>::value> * = nullptr> + constexpr explicit expected_storage_base(unexpect_t, + std::initializer_list il, + Args &&... args) + : m_unexpect(il, std::forward(args)...), m_has_val(false) {} + + ~expected_storage_base() { + if (m_has_val) { + m_val.~T(); + } + } + union { + T m_val; + unexpected m_unexpect; + char m_no_init; + }; + bool m_has_val; +}; + +// `T` is `void`, `E` is trivially-destructible +template struct expected_storage_base { + TL_EXPECTED_MSVC2015_CONSTEXPR expected_storage_base() : m_has_val(true) {} + constexpr expected_storage_base(no_init_t) : m_val(), m_has_val(false) {} + + constexpr expected_storage_base(in_place_t) : m_has_val(true) {} + + template ::value> * = + nullptr> + constexpr explicit expected_storage_base(unexpect_t, Args &&... args) + : m_unexpect(std::forward(args)...), m_has_val(false) {} + + template &, Args &&...>::value> * = nullptr> + constexpr explicit expected_storage_base(unexpect_t, + std::initializer_list il, + Args &&... args) + : m_unexpect(il, std::forward(args)...), m_has_val(false) {} + + ~expected_storage_base() = default; + struct dummy {}; + union { + unexpected m_unexpect; + dummy m_val; + }; + bool m_has_val; +}; + +// `T` is `void`, `E` is not trivially-destructible +template struct expected_storage_base { + constexpr expected_storage_base() : m_dummy(), m_has_val(true) {} + constexpr expected_storage_base(no_init_t) : m_dummy(), m_has_val(false) {} + + constexpr expected_storage_base(in_place_t) : m_dummy(), m_has_val(true) {} + + template ::value> * = + nullptr> + constexpr explicit expected_storage_base(unexpect_t, Args &&... args) + : m_unexpect(std::forward(args)...), m_has_val(false) {} + + template &, Args &&...>::value> * = nullptr> + constexpr explicit expected_storage_base(unexpect_t, + std::initializer_list il, + Args &&... args) + : m_unexpect(il, std::forward(args)...), m_has_val(false) {} + + ~expected_storage_base() { + if (!m_has_val) { + m_unexpect.~unexpected(); + } + } + + union { + unexpected m_unexpect; + char m_dummy; + }; + bool m_has_val; +}; + +// This base class provides some handy member functions which can be used in +// further derived classes +template +struct expected_operations_base : expected_storage_base { + using expected_storage_base::expected_storage_base; + + template void construct(Args &&... args) noexcept { + new (std::addressof(this->m_val)) T(std::forward(args)...); + this->m_has_val = true; + } + + template void construct_with(Rhs &&rhs) noexcept { + new (std::addressof(this->m_val)) T(std::forward(rhs).get()); + this->m_has_val = true; + } + + template void construct_error(Args &&... args) noexcept { + new (std::addressof(this->m_unexpect)) + unexpected(std::forward(args)...); + this->m_has_val = false; + } + + #ifdef TL_EXPECTED_EXCEPTIONS_ENABLED + + // These assign overloads ensure that the most efficient assignment + // implementation is used while maintaining the strong exception guarantee. + // The problematic case is where rhs has a value, but *this does not. + // + // This overload handles the case where we can just copy-construct `T` + // directly into place without throwing. + template ::value> + * = nullptr> + void assign(const expected_operations_base &rhs) noexcept { + if (!this->m_has_val && rhs.m_has_val) { + geterr().~unexpected(); + construct(rhs.get()); + } else { + assign_common(rhs); + } + } + + // This overload handles the case where we can attempt to create a copy of + // `T`, then no-throw move it into place if the copy was successful. + template ::value && + std::is_nothrow_move_constructible::value> + * = nullptr> + void assign(const expected_operations_base &rhs) noexcept { + if (!this->m_has_val && rhs.m_has_val) { + T tmp = rhs.get(); + geterr().~unexpected(); + construct(std::move(tmp)); + } else { + assign_common(rhs); + } + } + + // This overload is the worst-case, where we have to move-construct the + // unexpected value into temporary storage, then try to copy the T into place. + // If the construction succeeds, then everything is fine, but if it throws, + // then we move the old unexpected value back into place before rethrowing the + // exception. + template ::value && + !std::is_nothrow_move_constructible::value> + * = nullptr> + void assign(const expected_operations_base &rhs) { + if (!this->m_has_val && rhs.m_has_val) { + auto tmp = std::move(geterr()); + geterr().~unexpected(); + +#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED + try { + construct(rhs.get()); + } catch (...) { + geterr() = std::move(tmp); + throw; + } +#else + construct(rhs.get()); +#endif + } else { + assign_common(rhs); + } + } + + // These overloads do the same as above, but for rvalues + template ::value> + * = nullptr> + void assign(expected_operations_base &&rhs) noexcept { + if (!this->m_has_val && rhs.m_has_val) { + geterr().~unexpected(); + construct(std::move(rhs).get()); + } else { + assign_common(std::move(rhs)); + } + } + + template ::value> + * = nullptr> + void assign(expected_operations_base &&rhs) { + if (!this->m_has_val && rhs.m_has_val) { + auto tmp = std::move(geterr()); + geterr().~unexpected(); +#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED + try { + construct(std::move(rhs).get()); + } catch (...) { + geterr() = std::move(tmp); + throw; + } +#else + construct(std::move(rhs).get()); +#endif + } else { + assign_common(std::move(rhs)); + } + } + + #else + + // If exceptions are disabled then we can just copy-construct + void assign(const expected_operations_base &rhs) noexcept { + if (!this->m_has_val && rhs.m_has_val) { + geterr().~unexpected(); + construct(rhs.get()); + } else { + assign_common(rhs); + } + } + + void assign(expected_operations_base &&rhs) noexcept { + if (!this->m_has_val && rhs.m_has_val) { + geterr().~unexpected(); + construct(std::move(rhs).get()); + } else { + assign_common(rhs); + } + } + + #endif + + // The common part of move/copy assigning + template void assign_common(Rhs &&rhs) { + if (this->m_has_val) { + if (rhs.m_has_val) { + get() = std::forward(rhs).get(); + } else { + destroy_val(); + construct_error(std::forward(rhs).geterr()); + } + } else { + if (!rhs.m_has_val) { + geterr() = std::forward(rhs).geterr(); + } + } + } + + bool has_value() const { return this->m_has_val; } + + TL_EXPECTED_11_CONSTEXPR T &get() & { return this->m_val; } + constexpr const T &get() const & { return this->m_val; } + TL_EXPECTED_11_CONSTEXPR T &&get() && { return std::move(this->m_val); } +#ifndef TL_EXPECTED_NO_CONSTRR + constexpr const T &&get() const && { return std::move(this->m_val); } +#endif + + TL_EXPECTED_11_CONSTEXPR unexpected &geterr() & { + return this->m_unexpect; + } + constexpr const unexpected &geterr() const & { return this->m_unexpect; } + TL_EXPECTED_11_CONSTEXPR unexpected &&geterr() && { + return std::move(this->m_unexpect); + } +#ifndef TL_EXPECTED_NO_CONSTRR + constexpr const unexpected &&geterr() const && { + return std::move(this->m_unexpect); + } +#endif + + TL_EXPECTED_11_CONSTEXPR void destroy_val() { + get().~T(); + } +}; + +// This base class provides some handy member functions which can be used in +// further derived classes +template +struct expected_operations_base : expected_storage_base { + using expected_storage_base::expected_storage_base; + + template void construct() noexcept { this->m_has_val = true; } + + // This function doesn't use its argument, but needs it so that code in + // levels above this can work independently of whether T is void + template void construct_with(Rhs &&) noexcept { + this->m_has_val = true; + } + + template void construct_error(Args &&... args) noexcept { + new (std::addressof(this->m_unexpect)) + unexpected(std::forward(args)...); + this->m_has_val = false; + } + + template void assign(Rhs &&rhs) noexcept { + if (!this->m_has_val) { + if (rhs.m_has_val) { + geterr().~unexpected(); + construct(); + } else { + geterr() = std::forward(rhs).geterr(); + } + } else { + if (!rhs.m_has_val) { + construct_error(std::forward(rhs).geterr()); + } + } + } + + bool has_value() const { return this->m_has_val; } + + TL_EXPECTED_11_CONSTEXPR unexpected &geterr() & { + return this->m_unexpect; + } + constexpr const unexpected &geterr() const & { return this->m_unexpect; } + TL_EXPECTED_11_CONSTEXPR unexpected &&geterr() && { + return std::move(this->m_unexpect); + } +#ifndef TL_EXPECTED_NO_CONSTRR + constexpr const unexpected &&geterr() const && { + return std::move(this->m_unexpect); + } +#endif + + TL_EXPECTED_11_CONSTEXPR void destroy_val() { + //no-op + } +}; + +// This class manages conditionally having a trivial copy constructor +// This specialization is for when T and E are trivially copy constructible +template :: + value &&TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(E)::value> +struct expected_copy_base : expected_operations_base { + using expected_operations_base::expected_operations_base; +}; + +// This specialization is for when T or E are not trivially copy constructible +template +struct expected_copy_base : expected_operations_base { + using expected_operations_base::expected_operations_base; + + expected_copy_base() = default; + expected_copy_base(const expected_copy_base &rhs) + : expected_operations_base(no_init) { + if (rhs.has_value()) { + this->construct_with(rhs); + } else { + this->construct_error(rhs.geterr()); + } + } + + expected_copy_base(expected_copy_base &&rhs) = default; + expected_copy_base &operator=(const expected_copy_base &rhs) = default; + expected_copy_base &operator=(expected_copy_base &&rhs) = default; +}; + +// This class manages conditionally having a trivial move constructor +// Unfortunately there's no way to achieve this in GCC < 5 AFAIK, since it +// doesn't implement an analogue to std::is_trivially_move_constructible. We +// have to make do with a non-trivial move constructor even if T is trivially +// move constructible +#ifndef TL_EXPECTED_GCC49 +template >::value + &&std::is_trivially_move_constructible::value> +struct expected_move_base : expected_copy_base { + using expected_copy_base::expected_copy_base; +}; +#else +template struct expected_move_base; +#endif +template +struct expected_move_base : expected_copy_base { + using expected_copy_base::expected_copy_base; + + expected_move_base() = default; + expected_move_base(const expected_move_base &rhs) = default; + + expected_move_base(expected_move_base &&rhs) noexcept( + std::is_nothrow_move_constructible::value) + : expected_copy_base(no_init) { + if (rhs.has_value()) { + this->construct_with(std::move(rhs)); + } else { + this->construct_error(std::move(rhs.geterr())); + } + } + expected_move_base &operator=(const expected_move_base &rhs) = default; + expected_move_base &operator=(expected_move_base &&rhs) = default; +}; + +// This class manages conditionally having a trivial copy assignment operator +template >::value + &&TL_EXPECTED_IS_TRIVIALLY_COPY_ASSIGNABLE(E)::value + &&TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(E)::value + &&TL_EXPECTED_IS_TRIVIALLY_DESTRUCTIBLE(E)::value> +struct expected_copy_assign_base : expected_move_base { + using expected_move_base::expected_move_base; +}; + +template +struct expected_copy_assign_base : expected_move_base { + using expected_move_base::expected_move_base; + + expected_copy_assign_base() = default; + expected_copy_assign_base(const expected_copy_assign_base &rhs) = default; + + expected_copy_assign_base(expected_copy_assign_base &&rhs) = default; + expected_copy_assign_base &operator=(const expected_copy_assign_base &rhs) { + this->assign(rhs); + return *this; + } + expected_copy_assign_base & + operator=(expected_copy_assign_base &&rhs) = default; +}; + +// This class manages conditionally having a trivial move assignment operator +// Unfortunately there's no way to achieve this in GCC < 5 AFAIK, since it +// doesn't implement an analogue to std::is_trivially_move_assignable. We have +// to make do with a non-trivial move assignment operator even if T is trivially +// move assignable +#ifndef TL_EXPECTED_GCC49 +template , + std::is_trivially_move_constructible, + std::is_trivially_move_assignable>>:: + value &&std::is_trivially_destructible::value + &&std::is_trivially_move_constructible::value + &&std::is_trivially_move_assignable::value> +struct expected_move_assign_base : expected_copy_assign_base { + using expected_copy_assign_base::expected_copy_assign_base; +}; +#else +template struct expected_move_assign_base; +#endif + +template +struct expected_move_assign_base + : expected_copy_assign_base { + using expected_copy_assign_base::expected_copy_assign_base; + + expected_move_assign_base() = default; + expected_move_assign_base(const expected_move_assign_base &rhs) = default; + + expected_move_assign_base(expected_move_assign_base &&rhs) = default; + + expected_move_assign_base & + operator=(const expected_move_assign_base &rhs) = default; + + expected_move_assign_base & + operator=(expected_move_assign_base &&rhs) noexcept( + std::is_nothrow_move_constructible::value + &&std::is_nothrow_move_assignable::value) { + this->assign(std::move(rhs)); + return *this; + } +}; + +// expected_delete_ctor_base will conditionally delete copy and move +// constructors depending on whether T is copy/move constructible +template ::value && + std::is_copy_constructible::value), + bool EnableMove = (is_move_constructible_or_void::value && + std::is_move_constructible::value)> +struct expected_delete_ctor_base { + expected_delete_ctor_base() = default; + expected_delete_ctor_base(const expected_delete_ctor_base &) = default; + expected_delete_ctor_base(expected_delete_ctor_base &&) noexcept = default; + expected_delete_ctor_base & + operator=(const expected_delete_ctor_base &) = default; + expected_delete_ctor_base & + operator=(expected_delete_ctor_base &&) noexcept = default; +}; + +template +struct expected_delete_ctor_base { + expected_delete_ctor_base() = default; + expected_delete_ctor_base(const expected_delete_ctor_base &) = default; + expected_delete_ctor_base(expected_delete_ctor_base &&) noexcept = delete; + expected_delete_ctor_base & + operator=(const expected_delete_ctor_base &) = default; + expected_delete_ctor_base & + operator=(expected_delete_ctor_base &&) noexcept = default; +}; + +template +struct expected_delete_ctor_base { + expected_delete_ctor_base() = default; + expected_delete_ctor_base(const expected_delete_ctor_base &) = delete; + expected_delete_ctor_base(expected_delete_ctor_base &&) noexcept = default; + expected_delete_ctor_base & + operator=(const expected_delete_ctor_base &) = default; + expected_delete_ctor_base & + operator=(expected_delete_ctor_base &&) noexcept = default; +}; + +template +struct expected_delete_ctor_base { + expected_delete_ctor_base() = default; + expected_delete_ctor_base(const expected_delete_ctor_base &) = delete; + expected_delete_ctor_base(expected_delete_ctor_base &&) noexcept = delete; + expected_delete_ctor_base & + operator=(const expected_delete_ctor_base &) = default; + expected_delete_ctor_base & + operator=(expected_delete_ctor_base &&) noexcept = default; +}; + +// expected_delete_assign_base will conditionally delete copy and move +// constructors depending on whether T and E are copy/move constructible + +// assignable +template ::value && + std::is_copy_constructible::value && + is_copy_assignable_or_void::value && + std::is_copy_assignable::value), + bool EnableMove = (is_move_constructible_or_void::value && + std::is_move_constructible::value && + is_move_assignable_or_void::value && + std::is_move_assignable::value)> +struct expected_delete_assign_base { + expected_delete_assign_base() = default; + expected_delete_assign_base(const expected_delete_assign_base &) = default; + expected_delete_assign_base(expected_delete_assign_base &&) noexcept = + default; + expected_delete_assign_base & + operator=(const expected_delete_assign_base &) = default; + expected_delete_assign_base & + operator=(expected_delete_assign_base &&) noexcept = default; +}; + +template +struct expected_delete_assign_base { + expected_delete_assign_base() = default; + expected_delete_assign_base(const expected_delete_assign_base &) = default; + expected_delete_assign_base(expected_delete_assign_base &&) noexcept = + default; + expected_delete_assign_base & + operator=(const expected_delete_assign_base &) = default; + expected_delete_assign_base & + operator=(expected_delete_assign_base &&) noexcept = delete; +}; + +template +struct expected_delete_assign_base { + expected_delete_assign_base() = default; + expected_delete_assign_base(const expected_delete_assign_base &) = default; + expected_delete_assign_base(expected_delete_assign_base &&) noexcept = + default; + expected_delete_assign_base & + operator=(const expected_delete_assign_base &) = delete; + expected_delete_assign_base & + operator=(expected_delete_assign_base &&) noexcept = default; +}; + +template +struct expected_delete_assign_base { + expected_delete_assign_base() = default; + expected_delete_assign_base(const expected_delete_assign_base &) = default; + expected_delete_assign_base(expected_delete_assign_base &&) noexcept = + default; + expected_delete_assign_base & + operator=(const expected_delete_assign_base &) = delete; + expected_delete_assign_base & + operator=(expected_delete_assign_base &&) noexcept = delete; +}; + +// This is needed to be able to construct the expected_default_ctor_base which +// follows, while still conditionally deleting the default constructor. +struct default_constructor_tag { + explicit constexpr default_constructor_tag() = default; +}; + +// expected_default_ctor_base will ensure that expected has a deleted default +// consturctor if T is not default constructible. +// This specialization is for when T is default constructible +template ::value || std::is_void::value> +struct expected_default_ctor_base { + constexpr expected_default_ctor_base() noexcept = default; + constexpr expected_default_ctor_base( + expected_default_ctor_base const &) noexcept = default; + constexpr expected_default_ctor_base(expected_default_ctor_base &&) noexcept = + default; + expected_default_ctor_base & + operator=(expected_default_ctor_base const &) noexcept = default; + expected_default_ctor_base & + operator=(expected_default_ctor_base &&) noexcept = default; + + constexpr explicit expected_default_ctor_base(default_constructor_tag) {} +}; + +// This specialization is for when T is not default constructible +template struct expected_default_ctor_base { + constexpr expected_default_ctor_base() noexcept = delete; + constexpr expected_default_ctor_base( + expected_default_ctor_base const &) noexcept = default; + constexpr expected_default_ctor_base(expected_default_ctor_base &&) noexcept = + default; + expected_default_ctor_base & + operator=(expected_default_ctor_base const &) noexcept = default; + expected_default_ctor_base & + operator=(expected_default_ctor_base &&) noexcept = default; + + constexpr explicit expected_default_ctor_base(default_constructor_tag) {} +}; +} // namespace detail + +template class bad_expected_access : public std::exception { +public: + explicit bad_expected_access(E e) : m_val(std::move(e)) {} + + virtual const char *what() const noexcept override { + return "Bad expected access"; + } + + const E &error() const & { return m_val; } + E &error() & { return m_val; } + const E &&error() const && { return std::move(m_val); } + E &&error() && { return std::move(m_val); } + +private: + E m_val; +}; + +/// An `expected` object is an object that contains the storage for +/// another object and manages the lifetime of this contained object `T`. +/// Alternatively it could contain the storage for another unexpected object +/// `E`. The contained object may not be initialized after the expected object +/// has been initialized, and may not be destroyed before the expected object +/// has been destroyed. The initialization state of the contained object is +/// tracked by the expected object. +template +class expected : private detail::expected_move_assign_base, + private detail::expected_delete_ctor_base, + private detail::expected_delete_assign_base, + private detail::expected_default_ctor_base { + static_assert(!std::is_reference::value, "T must not be a reference"); + static_assert(!std::is_same::type>::value, + "T must not be in_place_t"); + static_assert(!std::is_same::type>::value, + "T must not be unexpect_t"); + static_assert(!std::is_same>::type>::value, + "T must not be unexpected"); + static_assert(!std::is_reference::value, "E must not be a reference"); + + T *valptr() { return std::addressof(this->m_val); } + const T *valptr() const { return std::addressof(this->m_val); } + unexpected *errptr() { return std::addressof(this->m_unexpect); } + const unexpected *errptr() const { return std::addressof(this->m_unexpect); } + + template ::value> * = nullptr> + TL_EXPECTED_11_CONSTEXPR U &val() { + return this->m_val; + } + TL_EXPECTED_11_CONSTEXPR unexpected &err() { return this->m_unexpect; } + + template ::value> * = nullptr> + constexpr const U &val() const { + return this->m_val; + } + constexpr const unexpected &err() const { return this->m_unexpect; } + + using impl_base = detail::expected_move_assign_base; + using ctor_base = detail::expected_default_ctor_base; + +public: + typedef T value_type; + typedef E error_type; + typedef unexpected unexpected_type; + +#if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && \ + !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55) + template TL_EXPECTED_11_CONSTEXPR auto and_then(F &&f) & { + return and_then_impl(*this, std::forward(f)); + } + template TL_EXPECTED_11_CONSTEXPR auto and_then(F &&f) && { + return and_then_impl(std::move(*this), std::forward(f)); + } + template constexpr auto and_then(F &&f) const & { + return and_then_impl(*this, std::forward(f)); + } + +#ifndef TL_EXPECTED_NO_CONSTRR + template constexpr auto and_then(F &&f) const && { + return and_then_impl(std::move(*this), std::forward(f)); + } +#endif + +#else + template + TL_EXPECTED_11_CONSTEXPR auto + and_then(F &&f) & -> decltype(and_then_impl(std::declval(), std::forward(f))) { + return and_then_impl(*this, std::forward(f)); + } + template + TL_EXPECTED_11_CONSTEXPR auto and_then(F &&f) && -> decltype( + and_then_impl(std::declval(), std::forward(f))) { + return and_then_impl(std::move(*this), std::forward(f)); + } + template + constexpr auto and_then(F &&f) const & -> decltype( + and_then_impl(std::declval(), std::forward(f))) { + return and_then_impl(*this, std::forward(f)); + } + +#ifndef TL_EXPECTED_NO_CONSTRR + template + constexpr auto and_then(F &&f) const && -> decltype( + and_then_impl(std::declval(), std::forward(f))) { + return and_then_impl(std::move(*this), std::forward(f)); + } +#endif +#endif + +#if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && \ + !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55) + template TL_EXPECTED_11_CONSTEXPR auto map(F &&f) & { + return expected_map_impl(*this, std::forward(f)); + } + template TL_EXPECTED_11_CONSTEXPR auto map(F &&f) && { + return expected_map_impl(std::move(*this), std::forward(f)); + } + template constexpr auto map(F &&f) const & { + return expected_map_impl(*this, std::forward(f)); + } + template constexpr auto map(F &&f) const && { + return expected_map_impl(std::move(*this), std::forward(f)); + } +#else + template + TL_EXPECTED_11_CONSTEXPR decltype( + expected_map_impl(std::declval(), std::declval())) + map(F &&f) & { + return expected_map_impl(*this, std::forward(f)); + } + template + TL_EXPECTED_11_CONSTEXPR decltype( + expected_map_impl(std::declval(), std::declval())) + map(F &&f) && { + return expected_map_impl(std::move(*this), std::forward(f)); + } + template + constexpr decltype(expected_map_impl(std::declval(), + std::declval())) + map(F &&f) const & { + return expected_map_impl(*this, std::forward(f)); + } + +#ifndef TL_EXPECTED_NO_CONSTRR + template + constexpr decltype(expected_map_impl(std::declval(), + std::declval())) + map(F &&f) const && { + return expected_map_impl(std::move(*this), std::forward(f)); + } +#endif +#endif + +#if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && \ + !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55) + template TL_EXPECTED_11_CONSTEXPR auto transform(F &&f) & { + return expected_map_impl(*this, std::forward(f)); + } + template TL_EXPECTED_11_CONSTEXPR auto transform(F &&f) && { + return expected_map_impl(std::move(*this), std::forward(f)); + } + template constexpr auto transform(F &&f) const & { + return expected_map_impl(*this, std::forward(f)); + } + template constexpr auto transform(F &&f) const && { + return expected_map_impl(std::move(*this), std::forward(f)); + } +#else + template + TL_EXPECTED_11_CONSTEXPR decltype( + expected_map_impl(std::declval(), std::declval())) + transform(F &&f) & { + return expected_map_impl(*this, std::forward(f)); + } + template + TL_EXPECTED_11_CONSTEXPR decltype( + expected_map_impl(std::declval(), std::declval())) + transform(F &&f) && { + return expected_map_impl(std::move(*this), std::forward(f)); + } + template + constexpr decltype(expected_map_impl(std::declval(), + std::declval())) + transform(F &&f) const & { + return expected_map_impl(*this, std::forward(f)); + } + +#ifndef TL_EXPECTED_NO_CONSTRR + template + constexpr decltype(expected_map_impl(std::declval(), + std::declval())) + transform(F &&f) const && { + return expected_map_impl(std::move(*this), std::forward(f)); + } +#endif +#endif + +#if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && \ + !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55) + template TL_EXPECTED_11_CONSTEXPR auto map_error(F &&f) & { + return map_error_impl(*this, std::forward(f)); + } + template TL_EXPECTED_11_CONSTEXPR auto map_error(F &&f) && { + return map_error_impl(std::move(*this), std::forward(f)); + } + template constexpr auto map_error(F &&f) const & { + return map_error_impl(*this, std::forward(f)); + } + template constexpr auto map_error(F &&f) const && { + return map_error_impl(std::move(*this), std::forward(f)); + } +#else + template + TL_EXPECTED_11_CONSTEXPR decltype(map_error_impl(std::declval(), + std::declval())) + map_error(F &&f) & { + return map_error_impl(*this, std::forward(f)); + } + template + TL_EXPECTED_11_CONSTEXPR decltype(map_error_impl(std::declval(), + std::declval())) + map_error(F &&f) && { + return map_error_impl(std::move(*this), std::forward(f)); + } + template + constexpr decltype(map_error_impl(std::declval(), + std::declval())) + map_error(F &&f) const & { + return map_error_impl(*this, std::forward(f)); + } + +#ifndef TL_EXPECTED_NO_CONSTRR + template + constexpr decltype(map_error_impl(std::declval(), + std::declval())) + map_error(F &&f) const && { + return map_error_impl(std::move(*this), std::forward(f)); + } +#endif +#endif + template expected TL_EXPECTED_11_CONSTEXPR or_else(F &&f) & { + return or_else_impl(*this, std::forward(f)); + } + + template expected TL_EXPECTED_11_CONSTEXPR or_else(F &&f) && { + return or_else_impl(std::move(*this), std::forward(f)); + } + + template expected constexpr or_else(F &&f) const & { + return or_else_impl(*this, std::forward(f)); + } + +#ifndef TL_EXPECTED_NO_CONSTRR + template expected constexpr or_else(F &&f) const && { + return or_else_impl(std::move(*this), std::forward(f)); + } +#endif + constexpr expected() = default; + constexpr expected(const expected &rhs) = default; + constexpr expected(expected &&rhs) = default; + expected &operator=(const expected &rhs) = default; + expected &operator=(expected &&rhs) = default; + + template ::value> * = + nullptr> + constexpr expected(in_place_t, Args &&... args) + : impl_base(in_place, std::forward(args)...), + ctor_base(detail::default_constructor_tag{}) {} + + template &, Args &&...>::value> * = nullptr> + constexpr expected(in_place_t, std::initializer_list il, Args &&... args) + : impl_base(in_place, il, std::forward(args)...), + ctor_base(detail::default_constructor_tag{}) {} + + template ::value> * = + nullptr, + detail::enable_if_t::value> * = + nullptr> + explicit constexpr expected(const unexpected &e) + : impl_base(unexpect, e.value()), + ctor_base(detail::default_constructor_tag{}) {} + + template < + class G = E, + detail::enable_if_t::value> * = + nullptr, + detail::enable_if_t::value> * = nullptr> + constexpr expected(unexpected const &e) + : impl_base(unexpect, e.value()), + ctor_base(detail::default_constructor_tag{}) {} + + template < + class G = E, + detail::enable_if_t::value> * = nullptr, + detail::enable_if_t::value> * = nullptr> + explicit constexpr expected(unexpected &&e) noexcept( + std::is_nothrow_constructible::value) + : impl_base(unexpect, std::move(e.value())), + ctor_base(detail::default_constructor_tag{}) {} + + template < + class G = E, + detail::enable_if_t::value> * = nullptr, + detail::enable_if_t::value> * = nullptr> + constexpr expected(unexpected &&e) noexcept( + std::is_nothrow_constructible::value) + : impl_base(unexpect, std::move(e.value())), + ctor_base(detail::default_constructor_tag{}) {} + + template ::value> * = + nullptr> + constexpr explicit expected(unexpect_t, Args &&... args) + : impl_base(unexpect, std::forward(args)...), + ctor_base(detail::default_constructor_tag{}) {} + + template &, Args &&...>::value> * = nullptr> + constexpr explicit expected(unexpect_t, std::initializer_list il, + Args &&... args) + : impl_base(unexpect, il, std::forward(args)...), + ctor_base(detail::default_constructor_tag{}) {} + + template ::value && + std::is_convertible::value)> * = + nullptr, + detail::expected_enable_from_other + * = nullptr> + explicit TL_EXPECTED_11_CONSTEXPR expected(const expected &rhs) + : ctor_base(detail::default_constructor_tag{}) { + if (rhs.has_value()) { + this->construct(*rhs); + } else { + this->construct_error(rhs.error()); + } + } + + template ::value && + std::is_convertible::value)> * = + nullptr, + detail::expected_enable_from_other + * = nullptr> + TL_EXPECTED_11_CONSTEXPR expected(const expected &rhs) + : ctor_base(detail::default_constructor_tag{}) { + if (rhs.has_value()) { + this->construct(*rhs); + } else { + this->construct_error(rhs.error()); + } + } + + template < + class U, class G, + detail::enable_if_t::value && + std::is_convertible::value)> * = nullptr, + detail::expected_enable_from_other * = nullptr> + explicit TL_EXPECTED_11_CONSTEXPR expected(expected &&rhs) + : ctor_base(detail::default_constructor_tag{}) { + if (rhs.has_value()) { + this->construct(std::move(*rhs)); + } else { + this->construct_error(std::move(rhs.error())); + } + } + + template < + class U, class G, + detail::enable_if_t<(std::is_convertible::value && + std::is_convertible::value)> * = nullptr, + detail::expected_enable_from_other * = nullptr> + TL_EXPECTED_11_CONSTEXPR expected(expected &&rhs) + : ctor_base(detail::default_constructor_tag{}) { + if (rhs.has_value()) { + this->construct(std::move(*rhs)); + } else { + this->construct_error(std::move(rhs.error())); + } + } + + template < + class U = T, + detail::enable_if_t::value> * = nullptr, + detail::expected_enable_forward_value * = nullptr> + explicit TL_EXPECTED_MSVC2015_CONSTEXPR expected(U &&v) + : expected(in_place, std::forward(v)) {} + + template < + class U = T, + detail::enable_if_t::value> * = nullptr, + detail::expected_enable_forward_value * = nullptr> + TL_EXPECTED_MSVC2015_CONSTEXPR expected(U &&v) + : expected(in_place, std::forward(v)) {} + + template < + class U = T, class G = T, + detail::enable_if_t::value> * = + nullptr, + detail::enable_if_t::value> * = nullptr, + detail::enable_if_t< + (!std::is_same, detail::decay_t>::value && + !detail::conjunction, + std::is_same>>::value && + std::is_constructible::value && + std::is_assignable::value && + std::is_nothrow_move_constructible::value)> * = nullptr> + expected &operator=(U &&v) { + if (has_value()) { + val() = std::forward(v); + } else { + err().~unexpected(); + ::new (valptr()) T(std::forward(v)); + this->m_has_val = true; + } + + return *this; + } + + template < + class U = T, class G = T, + detail::enable_if_t::value> * = + nullptr, + detail::enable_if_t::value> * = nullptr, + detail::enable_if_t< + (!std::is_same, detail::decay_t>::value && + !detail::conjunction, + std::is_same>>::value && + std::is_constructible::value && + std::is_assignable::value && + std::is_nothrow_move_constructible::value)> * = nullptr> + expected &operator=(U &&v) { + if (has_value()) { + val() = std::forward(v); + } else { + auto tmp = std::move(err()); + err().~unexpected(); + + #ifdef TL_EXPECTED_EXCEPTIONS_ENABLED + try { + ::new (valptr()) T(std::forward(v)); + this->m_has_val = true; + } catch (...) { + err() = std::move(tmp); + throw; + } + #else + ::new (valptr()) T(std::forward(v)); + this->m_has_val = true; + #endif + } + + return *this; + } + + template ::value && + std::is_assignable::value> * = nullptr> + expected &operator=(const unexpected &rhs) { + if (!has_value()) { + err() = rhs; + } else { + this->destroy_val(); + ::new (errptr()) unexpected(rhs); + this->m_has_val = false; + } + + return *this; + } + + template ::value && + std::is_move_assignable::value> * = nullptr> + expected &operator=(unexpected &&rhs) noexcept { + if (!has_value()) { + err() = std::move(rhs); + } else { + this->destroy_val(); + ::new (errptr()) unexpected(std::move(rhs)); + this->m_has_val = false; + } + + return *this; + } + + template ::value> * = nullptr> + void emplace(Args &&... args) { + if (has_value()) { + val() = T(std::forward(args)...); + } else { + err().~unexpected(); + ::new (valptr()) T(std::forward(args)...); + this->m_has_val = true; + } + } + + template ::value> * = nullptr> + void emplace(Args &&... args) { + if (has_value()) { + val() = T(std::forward(args)...); + } else { + auto tmp = std::move(err()); + err().~unexpected(); + + #ifdef TL_EXPECTED_EXCEPTIONS_ENABLED + try { + ::new (valptr()) T(std::forward(args)...); + this->m_has_val = true; + } catch (...) { + err() = std::move(tmp); + throw; + } + #else + ::new (valptr()) T(std::forward(args)...); + this->m_has_val = true; + #endif + } + } + + template &, Args &&...>::value> * = nullptr> + void emplace(std::initializer_list il, Args &&... args) { + if (has_value()) { + T t(il, std::forward(args)...); + val() = std::move(t); + } else { + err().~unexpected(); + ::new (valptr()) T(il, std::forward(args)...); + this->m_has_val = true; + } + } + + template &, Args &&...>::value> * = nullptr> + void emplace(std::initializer_list il, Args &&... args) { + if (has_value()) { + T t(il, std::forward(args)...); + val() = std::move(t); + } else { + auto tmp = std::move(err()); + err().~unexpected(); + + #ifdef TL_EXPECTED_EXCEPTIONS_ENABLED + try { + ::new (valptr()) T(il, std::forward(args)...); + this->m_has_val = true; + } catch (...) { + err() = std::move(tmp); + throw; + } + #else + ::new (valptr()) T(il, std::forward(args)...); + this->m_has_val = true; + #endif + } + } + +private: + using t_is_void = std::true_type; + using t_is_not_void = std::false_type; + using t_is_nothrow_move_constructible = std::true_type; + using move_constructing_t_can_throw = std::false_type; + using e_is_nothrow_move_constructible = std::true_type; + using move_constructing_e_can_throw = std::false_type; + + void swap_where_both_have_value(expected &/*rhs*/ , t_is_void) noexcept { + // swapping void is a no-op + } + + void swap_where_both_have_value(expected &rhs, t_is_not_void) { + using std::swap; + swap(val(), rhs.val()); + } + + void swap_where_only_one_has_value(expected &rhs, t_is_void) noexcept( + std::is_nothrow_move_constructible::value) { + ::new (errptr()) unexpected_type(std::move(rhs.err())); + rhs.err().~unexpected_type(); + std::swap(this->m_has_val, rhs.m_has_val); + } + + void swap_where_only_one_has_value(expected &rhs, t_is_not_void) { + swap_where_only_one_has_value_and_t_is_not_void( + rhs, typename std::is_nothrow_move_constructible::type{}, + typename std::is_nothrow_move_constructible::type{}); + } + + void swap_where_only_one_has_value_and_t_is_not_void( + expected &rhs, t_is_nothrow_move_constructible, + e_is_nothrow_move_constructible) noexcept { + auto temp = std::move(val()); + val().~T(); + ::new (errptr()) unexpected_type(std::move(rhs.err())); + rhs.err().~unexpected_type(); + ::new (rhs.valptr()) T(std::move(temp)); + std::swap(this->m_has_val, rhs.m_has_val); + } + + void swap_where_only_one_has_value_and_t_is_not_void( + expected &rhs, t_is_nothrow_move_constructible, + move_constructing_e_can_throw) { + auto temp = std::move(val()); + val().~T(); +#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED + try { + ::new (errptr()) unexpected_type(std::move(rhs.err())); + rhs.err().~unexpected_type(); + ::new (rhs.valptr()) T(std::move(temp)); + std::swap(this->m_has_val, rhs.m_has_val); + } catch (...) { + val() = std::move(temp); + throw; + } +#else + ::new (errptr()) unexpected_type(std::move(rhs.err())); + rhs.err().~unexpected_type(); + ::new (rhs.valptr()) T(std::move(temp)); + std::swap(this->m_has_val, rhs.m_has_val); +#endif + } + + void swap_where_only_one_has_value_and_t_is_not_void( + expected &rhs, move_constructing_t_can_throw, + t_is_nothrow_move_constructible) { + auto temp = std::move(rhs.err()); + rhs.err().~unexpected_type(); +#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED + try { + ::new (rhs.valptr()) T(val()); + val().~T(); + ::new (errptr()) unexpected_type(std::move(temp)); + std::swap(this->m_has_val, rhs.m_has_val); + } catch (...) { + rhs.err() = std::move(temp); + throw; + } +#else + ::new (rhs.valptr()) T(val()); + val().~T(); + ::new (errptr()) unexpected_type(std::move(temp)); + std::swap(this->m_has_val, rhs.m_has_val); +#endif + } + +public: + template + detail::enable_if_t::value && + detail::is_swappable::value && + (std::is_nothrow_move_constructible::value || + std::is_nothrow_move_constructible::value)> + swap(expected &rhs) noexcept( + std::is_nothrow_move_constructible::value + &&detail::is_nothrow_swappable::value + &&std::is_nothrow_move_constructible::value + &&detail::is_nothrow_swappable::value) { + if (has_value() && rhs.has_value()) { + swap_where_both_have_value(rhs, typename std::is_void::type{}); + } else if (!has_value() && rhs.has_value()) { + rhs.swap(*this); + } else if (has_value()) { + swap_where_only_one_has_value(rhs, typename std::is_void::type{}); + } else { + using std::swap; + swap(err(), rhs.err()); + } + } + + constexpr const T *operator->() const { return valptr(); } + TL_EXPECTED_11_CONSTEXPR T *operator->() { return valptr(); } + + template ::value> * = nullptr> + constexpr const U &operator*() const & { + return val(); + } + template ::value> * = nullptr> + TL_EXPECTED_11_CONSTEXPR U &operator*() & { + return val(); + } + template ::value> * = nullptr> + constexpr const U &&operator*() const && { + return std::move(val()); + } + template ::value> * = nullptr> + TL_EXPECTED_11_CONSTEXPR U &&operator*() && { + return std::move(val()); + } + + constexpr bool has_value() const noexcept { return this->m_has_val; } + constexpr explicit operator bool() const noexcept { return this->m_has_val; } + + template ::value> * = nullptr> + TL_EXPECTED_11_CONSTEXPR const U &value() const & { + if (!has_value()) + detail::throw_exception(bad_expected_access(err().value())); + return val(); + } + template ::value> * = nullptr> + TL_EXPECTED_11_CONSTEXPR U &value() & { + if (!has_value()) + detail::throw_exception(bad_expected_access(err().value())); + return val(); + } + template ::value> * = nullptr> + TL_EXPECTED_11_CONSTEXPR const U &&value() const && { + if (!has_value()) + detail::throw_exception(bad_expected_access(std::move(err()).value())); + return std::move(val()); + } + template ::value> * = nullptr> + TL_EXPECTED_11_CONSTEXPR U &&value() && { + if (!has_value()) + detail::throw_exception(bad_expected_access(std::move(err()).value())); + return std::move(val()); + } + + constexpr const E &error() const & { return err().value(); } + TL_EXPECTED_11_CONSTEXPR E &error() & { return err().value(); } + constexpr const E &&error() const && { return std::move(err().value()); } + TL_EXPECTED_11_CONSTEXPR E &&error() && { return std::move(err().value()); } + + template constexpr T value_or(U &&v) const & { + static_assert(std::is_copy_constructible::value && + std::is_convertible::value, + "T must be copy-constructible and convertible to from U&&"); + return bool(*this) ? **this : static_cast(std::forward(v)); + } + template TL_EXPECTED_11_CONSTEXPR T value_or(U &&v) && { + static_assert(std::is_move_constructible::value && + std::is_convertible::value, + "T must be move-constructible and convertible to from U&&"); + return bool(*this) ? std::move(**this) : static_cast(std::forward(v)); + } +}; + +namespace detail { +template using exp_t = typename detail::decay_t::value_type; +template using err_t = typename detail::decay_t::error_type; +template using ret_t = expected>; + +#ifdef TL_EXPECTED_CXX14 +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval(), + *std::declval()))> +constexpr auto and_then_impl(Exp &&exp, F &&f) { + static_assert(detail::is_expected::value, "F must return an expected"); + + return exp.has_value() + ? detail::invoke(std::forward(f), *std::forward(exp)) + : Ret(unexpect, std::forward(exp).error()); +} + +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval()))> +constexpr auto and_then_impl(Exp &&exp, F &&f) { + static_assert(detail::is_expected::value, "F must return an expected"); + + return exp.has_value() ? detail::invoke(std::forward(f)) + : Ret(unexpect, std::forward(exp).error()); +} +#else +template struct TC; +template (), + *std::declval())), + detail::enable_if_t>::value> * = nullptr> +auto and_then_impl(Exp &&exp, F &&f) -> Ret { + static_assert(detail::is_expected::value, "F must return an expected"); + + return exp.has_value() + ? detail::invoke(std::forward(f), *std::forward(exp)) + : Ret(unexpect, std::forward(exp).error()); +} + +template ())), + detail::enable_if_t>::value> * = nullptr> +constexpr auto and_then_impl(Exp &&exp, F &&f) -> Ret { + static_assert(detail::is_expected::value, "F must return an expected"); + + return exp.has_value() ? detail::invoke(std::forward(f)) + : Ret(unexpect, std::forward(exp).error()); +} +#endif + +#ifdef TL_EXPECTED_CXX14 +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval(), + *std::declval())), + detail::enable_if_t::value> * = nullptr> +constexpr auto expected_map_impl(Exp &&exp, F &&f) { + using result = ret_t>; + return exp.has_value() ? result(detail::invoke(std::forward(f), + *std::forward(exp))) + : result(unexpect, std::forward(exp).error()); +} + +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval(), + *std::declval())), + detail::enable_if_t::value> * = nullptr> +auto expected_map_impl(Exp &&exp, F &&f) { + using result = expected>; + if (exp.has_value()) { + detail::invoke(std::forward(f), *std::forward(exp)); + return result(); + } + + return result(unexpect, std::forward(exp).error()); +} + +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval())), + detail::enable_if_t::value> * = nullptr> +constexpr auto expected_map_impl(Exp &&exp, F &&f) { + using result = ret_t>; + return exp.has_value() ? result(detail::invoke(std::forward(f))) + : result(unexpect, std::forward(exp).error()); +} + +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval())), + detail::enable_if_t::value> * = nullptr> +auto expected_map_impl(Exp &&exp, F &&f) { + using result = expected>; + if (exp.has_value()) { + detail::invoke(std::forward(f)); + return result(); + } + + return result(unexpect, std::forward(exp).error()); +} +#else +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval(), + *std::declval())), + detail::enable_if_t::value> * = nullptr> + +constexpr auto expected_map_impl(Exp &&exp, F &&f) + -> ret_t> { + using result = ret_t>; + + return exp.has_value() ? result(detail::invoke(std::forward(f), + *std::forward(exp))) + : result(unexpect, std::forward(exp).error()); +} + +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval(), + *std::declval())), + detail::enable_if_t::value> * = nullptr> + +auto expected_map_impl(Exp &&exp, F &&f) -> expected> { + if (exp.has_value()) { + detail::invoke(std::forward(f), *std::forward(exp)); + return {}; + } + + return unexpected>(std::forward(exp).error()); +} + +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval())), + detail::enable_if_t::value> * = nullptr> + +constexpr auto expected_map_impl(Exp &&exp, F &&f) + -> ret_t> { + using result = ret_t>; + + return exp.has_value() ? result(detail::invoke(std::forward(f))) + : result(unexpect, std::forward(exp).error()); +} + +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval())), + detail::enable_if_t::value> * = nullptr> + +auto expected_map_impl(Exp &&exp, F &&f) -> expected> { + if (exp.has_value()) { + detail::invoke(std::forward(f)); + return {}; + } + + return unexpected>(std::forward(exp).error()); +} +#endif + +#if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && \ + !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55) +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval(), + std::declval().error())), + detail::enable_if_t::value> * = nullptr> +constexpr auto map_error_impl(Exp &&exp, F &&f) { + using result = expected, detail::decay_t>; + return exp.has_value() + ? result(*std::forward(exp)) + : result(unexpect, detail::invoke(std::forward(f), + std::forward(exp).error())); +} +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval(), + std::declval().error())), + detail::enable_if_t::value> * = nullptr> +auto map_error_impl(Exp &&exp, F &&f) { + using result = expected, monostate>; + if (exp.has_value()) { + return result(*std::forward(exp)); + } + + detail::invoke(std::forward(f), std::forward(exp).error()); + return result(unexpect, monostate{}); +} +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval(), + std::declval().error())), + detail::enable_if_t::value> * = nullptr> +constexpr auto map_error_impl(Exp &&exp, F &&f) { + using result = expected, detail::decay_t>; + return exp.has_value() + ? result() + : result(unexpect, detail::invoke(std::forward(f), + std::forward(exp).error())); +} +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval(), + std::declval().error())), + detail::enable_if_t::value> * = nullptr> +auto map_error_impl(Exp &&exp, F &&f) { + using result = expected, monostate>; + if (exp.has_value()) { + return result(); + } + + detail::invoke(std::forward(f), std::forward(exp).error()); + return result(unexpect, monostate{}); +} +#else +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval(), + std::declval().error())), + detail::enable_if_t::value> * = nullptr> +constexpr auto map_error_impl(Exp &&exp, F &&f) + -> expected, detail::decay_t> { + using result = expected, detail::decay_t>; + + return exp.has_value() + ? result(*std::forward(exp)) + : result(unexpect, detail::invoke(std::forward(f), + std::forward(exp).error())); +} + +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval(), + std::declval().error())), + detail::enable_if_t::value> * = nullptr> +auto map_error_impl(Exp &&exp, F &&f) -> expected, monostate> { + using result = expected, monostate>; + if (exp.has_value()) { + return result(*std::forward(exp)); + } + + detail::invoke(std::forward(f), std::forward(exp).error()); + return result(unexpect, monostate{}); +} + +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval(), + std::declval().error())), + detail::enable_if_t::value> * = nullptr> +constexpr auto map_error_impl(Exp &&exp, F &&f) + -> expected, detail::decay_t> { + using result = expected, detail::decay_t>; + + return exp.has_value() + ? result() + : result(unexpect, detail::invoke(std::forward(f), + std::forward(exp).error())); +} + +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval(), + std::declval().error())), + detail::enable_if_t::value> * = nullptr> +auto map_error_impl(Exp &&exp, F &&f) -> expected, monostate> { + using result = expected, monostate>; + if (exp.has_value()) { + return result(); + } + + detail::invoke(std::forward(f), std::forward(exp).error()); + return result(unexpect, monostate{}); +} +#endif + +#ifdef TL_EXPECTED_CXX14 +template (), + std::declval().error())), + detail::enable_if_t::value> * = nullptr> +constexpr auto or_else_impl(Exp &&exp, F &&f) { + static_assert(detail::is_expected::value, "F must return an expected"); + return exp.has_value() + ? std::forward(exp) + : detail::invoke(std::forward(f), std::forward(exp).error()); +} + +template (), + std::declval().error())), + detail::enable_if_t::value> * = nullptr> +detail::decay_t or_else_impl(Exp &&exp, F &&f) { + return exp.has_value() + ? std::forward(exp) + : (detail::invoke(std::forward(f), std::forward(exp).error()), + std::forward(exp)); +} +#else +template (), + std::declval().error())), + detail::enable_if_t::value> * = nullptr> +auto or_else_impl(Exp &&exp, F &&f) -> Ret { + static_assert(detail::is_expected::value, "F must return an expected"); + return exp.has_value() + ? std::forward(exp) + : detail::invoke(std::forward(f), std::forward(exp).error()); +} + +template (), + std::declval().error())), + detail::enable_if_t::value> * = nullptr> +detail::decay_t or_else_impl(Exp &&exp, F &&f) { + return exp.has_value() + ? std::forward(exp) + : (detail::invoke(std::forward(f), std::forward(exp).error()), + std::forward(exp)); +} +#endif +} // namespace detail + +template +constexpr bool operator==(const expected &lhs, + const expected &rhs) { + return (lhs.has_value() != rhs.has_value()) + ? false + : (!lhs.has_value() ? lhs.error() == rhs.error() : *lhs == *rhs); +} +template +constexpr bool operator!=(const expected &lhs, + const expected &rhs) { + return (lhs.has_value() != rhs.has_value()) + ? true + : (!lhs.has_value() ? lhs.error() != rhs.error() : *lhs != *rhs); +} + +template +constexpr bool operator==(const expected &x, const U &v) { + return x.has_value() ? *x == v : false; +} +template +constexpr bool operator==(const U &v, const expected &x) { + return x.has_value() ? *x == v : false; +} +template +constexpr bool operator!=(const expected &x, const U &v) { + return x.has_value() ? *x != v : true; +} +template +constexpr bool operator!=(const U &v, const expected &x) { + return x.has_value() ? *x != v : true; +} + +template +constexpr bool operator==(const expected &x, const unexpected &e) { + return x.has_value() ? false : x.error() == e.value(); +} +template +constexpr bool operator==(const unexpected &e, const expected &x) { + return x.has_value() ? false : x.error() == e.value(); +} +template +constexpr bool operator!=(const expected &x, const unexpected &e) { + return x.has_value() ? true : x.error() != e.value(); +} +template +constexpr bool operator!=(const unexpected &e, const expected &x) { + return x.has_value() ? true : x.error() != e.value(); +} + +template ::value || + std::is_move_constructible::value) && + detail::is_swappable::value && + std::is_move_constructible::value && + detail::is_swappable::value> * = nullptr> +void swap(expected &lhs, + expected &rhs) noexcept(noexcept(lhs.swap(rhs))) { + lhs.swap(rhs); +} +} // namespace tl + +#endif diff --git a/include/goetia/parsing/readers.hh b/include/goetia/parsing/readers.hh index 72468ff..44ecadd 100644 --- a/include/goetia/parsing/readers.hh +++ b/include/goetia/parsing/readers.hh @@ -60,6 +60,7 @@ #include "goetia/goetia.hh" #include "goetia/parsing/parsing.hh" #include "goetia/sequences/alphabets.hh" +#include "goetia/errors.hh" extern "C" { @@ -70,27 +71,6 @@ KSEQ_INIT(gzFile, gzread) namespace goetia { -struct NoMoreReadsAvailable : public GoetiaFileException { - explicit NoMoreReadsAvailable(const std::string& msg) : - GoetiaFileException(msg) {} - NoMoreReadsAvailable() : - GoetiaFileException("No more reads available in this stream.") {} -}; - -struct InvalidRead : public GoetiaException { - explicit InvalidRead(const std::string& msg) : - GoetiaException(msg) {} - InvalidRead() : - GoetiaException("Invalid FASTA/Q read") {} -}; - -struct InvalidReadPair : public GoetiaException { - explicit InvalidReadPair(const std::string& msg) : - GoetiaException(msg) {} - InvalidReadPair() : - GoetiaException("Invalid read pair detected.") {} -}; - template class FastxParser @@ -149,7 +129,7 @@ public: std::optional next() { if (is_complete()) { - throw NoMoreReadsAvailable(); + throw std::out_of_range("No more reads available."); } Record record; @@ -159,21 +139,15 @@ public: int stat = kseq_read(_kseq); if (stat >= 0) { - try { - Alphabet::validate(_kseq->seq.s, _kseq->seq.l); - } catch (InvalidCharacterException &e) { + + if (!Alphabet::sanitize(_kseq->seq.s, _kseq->seq.l)) { _spin_lock = 0; ++_n_skipped; if (_strict) { - throw e; + throw InvalidSequence(_n_parsed, _kseq->seq.s); } else { stat = -4; } - - } catch (std::exception& e) { - ++_n_skipped; - _spin_lock = 0; - throw e; } if (_kseq->seq.l < _min_length) { @@ -204,11 +178,11 @@ public: if (stat == -2) { ++_n_skipped; - throw InvalidRead("Sequence and quality lengths differ"); + throw InvalidRecord(_n_parsed, _kseq->seq.s); } if (stat == -3) { - throw GoetiaFileException("Error reading stream."); + throw StreamReadError(_n_parsed, _filename); } if (stat == -4) { @@ -271,7 +245,7 @@ public: bool is_complete() const { if (left_parser->is_complete() != right_parser->is_complete()) { - throw GoetiaException("Mismatched split paired files."); + throw InvalidPairedStream(); } return left_parser->is_complete(); } @@ -279,7 +253,7 @@ public: RecordPair next() { if (is_complete()) { - throw NoMoreReadsAvailable(); + throw std::out_of_range("No more reads available."); } std::optional left, right; @@ -287,17 +261,17 @@ public: try { left = this->left_parser->next(); - } catch (InvalidCharacterException) { + } catch (InvalidSequence) { left_exc_ptr = std::current_exception(); - } catch (InvalidRead) { + } catch (InvalidRecord) { left_exc_ptr = std::current_exception(); } try { right = this->right_parser->next(); - } catch (InvalidCharacterException) { + } catch (InvalidSequence) { right_exc_ptr = std::current_exception(); - } catch (InvalidRead) { + } catch (InvalidRecord) { right_exc_ptr = std::current_exception(); } @@ -313,7 +287,9 @@ public: if (this->_force_name_match && left && right) { if (!check_is_pair(left.value().name, right.value().name)) { if (_strict) { - throw GoetiaException("Unpaired reads"); + throw InvalidRecordPair(this->left_parser->n_parsed(), + left.value().name, + right.value().name); } else { _n_skipped += 2; return {{}, {}}; @@ -333,6 +309,7 @@ public: } }; + extern template class goetia::FastxParser; extern template class goetia::FastxParser; extern template class goetia::FastxParser; diff --git a/include/goetia/processors.hh b/include/goetia/processors.hh index 50a64cd..9ad0c91 100644 --- a/include/goetia/processors.hh +++ b/include/goetia/processors.hh @@ -22,10 +22,10 @@ #include #include "goetia/goetia.hh" +#include "goetia/errors.hh" #include "goetia/metrics.hh" #include "goetia/parsing/parsing.hh" #include "goetia/parsing/readers.hh" -#include "goetia/sequences/exceptions.hh" namespace goetia { @@ -151,7 +151,7 @@ public: try { auto record = reader.next(); return record; - } catch (InvalidCharacterException &e) { + } catch (InvalidSequence &e) { if (_verbose) { std::cerr << "WARNING: Bad sequence encountered at " << this->n_sequences() @@ -159,7 +159,7 @@ public: << e.what() << std::endl; } return {}; - } catch (InvalidRead& e) { + } catch (InvalidRecord& e) { if (_verbose) { std::cerr << "WARNING: Invalid sequence encountered at " << this->n_sequences() @@ -304,7 +304,7 @@ public: uint64_t process_sequence(const Record& sequence) { try { return inserter->insert_sequence(sequence.sequence); - } catch (SequenceLengthException &e) { + } catch (SequenceTooShort& e) { if (this->_verbose) { std::cerr << "WARNING: Skipped sequence that was too short: sequence number " << this->n_sequences() << " with sequence " @@ -313,7 +313,7 @@ public: << std::endl; } return 0; - } catch (InvalidCharacterException& e) { + } catch (InvalidSequence& e) { if (this->_verbose) { std::cerr << "WARNING: got an invalid character exception: sequence number " << this->n_sequences() << " with sequence " @@ -391,7 +391,7 @@ public: uint64_t time_taken = 0; try { std::tie(passed, time_taken) = filter->filter_sequence(sequence.sequence); - } catch (SequenceLengthException &e) { + } catch (SequenceTooShort& e) { if (this->_verbose) { std::cerr << "WARNING: Skipped sequence that was too short: read " << this->n_sequences() << " with sequence " @@ -399,7 +399,7 @@ public: << std::endl; } return 0; - } catch (InvalidCharacterException& e) { + } catch (InvalidSequence& e) { return 0; } catch (std::exception &e) { std::cerr << "ERROR: Exception thrown at " << this->n_sequences() diff --git a/include/goetia/sequences/alphabets.hh b/include/goetia/sequences/alphabets.hh index d3220a0..6f8fa0b 100644 --- a/include/goetia/sequences/alphabets.hh +++ b/include/goetia/sequences/alphabets.hh @@ -14,7 +14,8 @@ #include #include -#include "goetia/sequences/exceptions.hh" +#include "goetia/goetia.hh" + #define rc_tbl \ " "\ @@ -38,40 +39,34 @@ struct Alphabet { return Derived::_validate(c); } - static void validate(char * sequence, const size_t length) { + static const bool sanitize(char * sequence, const size_t length) { + bool is_valid = true; + for (size_t i = 0; i < length; ++i) { const char validated = validate(sequence[i]); if (validated == '\0') { - std::ostringstream os; - os << "Alphabet: Invalid symbol '" - << sequence[i] << "' in sequence " - << std::string(sequence, length) - << " (alphabet=" << SYMBOLS << ")."; - - throw InvalidCharacterException(os.str().c_str()); + is_valid = false; } else { sequence[i] = validated; } } + + return is_valid; } - static void validate(const char * sequence, const size_t length) { + static bool validate(const char * sequence, const size_t length) { for (size_t i = 0; i < length; ++i) { const char validated = validate(sequence[i]); if (validated == '\0') { - std::ostringstream os; - os << "Alphabet: Invalid symbol '" - << sequence[i] << "' in sequence " - << std::string(sequence, length) - << " (alphabet=" << SYMBOLS << ")."; - - throw InvalidCharacterException(os.str().c_str()); + return false; } } + + return true; } template - static const bool is_valid(const Iterable& sequence) { + static const bool validate(const Iterable& sequence) { for (const auto& symbol : sequence) { if (validate(symbol) == '\0') { return false; diff --git a/include/goetia/sequences/exceptions.hh b/include/goetia/sequences/exceptions.hh deleted file mode 100644 index 7d26f66..0000000 --- a/include/goetia/sequences/exceptions.hh +++ /dev/null @@ -1,39 +0,0 @@ -/** - * (c) Camille Scott, 2019 - * File : exceptions.hh - * License: MIT - * Author : Camille Scott - * Date : 05.08.2019 - */ - -#ifndef GOETIA_SEQUENCES_EXCEPTIONS_HH -#define GOETIA_SEQUENCES_EXCEPTIONS_HH - -#include "goetia/goetia.hh" - -namespace goetia { - -class InvalidCharacterException : public GoetiaException { -public: - explicit InvalidCharacterException(const std::string& msg = "Invalid character encountered.") - : GoetiaException(msg) { } -}; - - -class SequenceLengthException: public GoetiaException { -public: - explicit SequenceLengthException(const std::string& msg = "Sequence was too short.") - : GoetiaException(msg) { } - -}; - - -class InvalidSequenceException: public GoetiaException { -public: - explicit InvalidSequenceException(const std::string& msg = "Got invalid sequence (too short or malformed).") - : GoetiaException(msg) { } - -}; - -} -#endif diff --git a/include/goetia/sketches/unikmer_sketch.hh b/include/goetia/sketches/unikmer_sketch.hh index d46a0a0..f886b32 100644 --- a/include/goetia/sketches/unikmer_sketch.hh +++ b/include/goetia/sketches/unikmer_sketch.hh @@ -33,13 +33,6 @@ namespace goetia { -class IncompatibleSketch : public GoetiaException { -public: - explicit IncompatibleSketch(const std::string& msg = "Incompatible sketches.") - : GoetiaException(msg) { } -}; - - template struct UnikmerSketch { @@ -86,15 +79,15 @@ struct UnikmerSketch { } static std::shared_ptr build(uint16_t W, - uint16_t K, - std::shared_ptr ukhs_map) { + uint16_t K, + std::shared_ptr ukhs_map) { return std::make_shared(W, K, ukhs_map, storage_traits::default_params); } static std::shared_ptr build(uint16_t W, - uint16_t K, - std::shared_ptr ukhs_map, - const typename storage_traits::params_type& storage_params) { + uint16_t K, + std::shared_ptr ukhs_map, + const typename storage_traits::params_type& storage_params) { return std::make_shared(W, K, ukhs_map, storage_params); } diff --git a/include/goetia/storage/partitioned_storage.hh b/include/goetia/storage/partitioned_storage.hh index 1e1a98f..cdc9867 100644 --- a/include/goetia/storage/partitioned_storage.hh +++ b/include/goetia/storage/partitioned_storage.hh @@ -5,18 +5,11 @@ * Author : Camille Scott * Date : 30.08.2019 */ -/* partitioned_storage.hh -- storage classes for the goetia dbg - * - * Copyright (C) 2018 Camille Scott - * All rights reserved. - * - * This software may be modified and distributed under the terms - * of the MIT license. See the LICENSE file for details. - */ #ifndef GOETIA_PARTITIONEDSTORAGE_HH #define GOETIA_PARTITIONEDSTORAGE_HH +#include "goetia/errors.hh" #include "goetia/goetia.hh" #include "goetia/storage/storage.hh" #include "goetia/storage/storage_types.hh" @@ -150,7 +143,7 @@ public: if (partition < n_partitions) { return partitions[partition].get(); } else { - throw GoetiaException("Invalid storage partition: " + std::to_string(partition)); + throw InvalidPartition(partition); } } @@ -160,15 +153,15 @@ public: const bool insert(value_type khash ) { - throw GoetiaException("Method not available!"); + throw NotImplemented(); } const count_t insert_and_query(value_type khash) { - throw GoetiaException("Method not available!"); + throw NotImplemented(); } const count_t query(value_type khash) const { - throw GoetiaException("Method not available!"); + throw NotImplemented(); } std::vector get_partition_counts() { diff --git a/include/goetia/storage/storage.hh b/include/goetia/storage/storage.hh index dbd5d6a..c0aa34b 100644 --- a/include/goetia/storage/storage.hh +++ b/include/goetia/storage/storage.hh @@ -55,6 +55,7 @@ #include #include #include +#include #include "goetia/goetia.hh" @@ -129,7 +130,7 @@ public: void set_use_bigcount(bool b) { if (!_supports_bigcount) { - throw GoetiaException("bigcount is not supported for this storage."); + throw std::invalid_argument("bigcount is not supported for this storage."); } _use_bigcount = b; } diff --git a/manifest.cmake b/manifest.cmake index 523714b..8810be6 100644 --- a/manifest.cmake +++ b/manifest.cmake @@ -1,5 +1,6 @@ set(_headers include/goetia/benchmarks/bench_storage.hh + include/goetia/errors.hh include/goetia/goetia.hh include/goetia/cdbg/cdbg.hh include/goetia/cdbg/cdbg_types.hh @@ -106,6 +107,7 @@ set(_sources ) set(_interface_headers + include/goetia/errors.hh include/goetia/goetia.hh include/goetia/cdbg/cdbg.hh include/goetia/cdbg/cdbg_types.hh diff --git a/src/goetia/cdbg/cdbg.cc b/src/goetia/cdbg/cdbg.cc index a9a026f..366b97d 100644 --- a/src/goetia/cdbg/cdbg.cc +++ b/src/goetia/cdbg/cdbg.cc @@ -826,12 +826,15 @@ ::compute_unitig_fragmentation(std::shared_ptr cdbg, template class cDBG>; template class cDBG>; +/* template class cDBG>; template class cDBG>; +*/ template class cDBG>; template class cDBG>; +/* template class cDBG>; template class cDBG>; @@ -843,5 +846,5 @@ template class cDBG>; template class cDBG>; template class cDBG>; - +*/ } diff --git a/src/goetia/cdbg/compactor.cc b/src/goetia/cdbg/compactor.cc index ba1ffd0..90301b0 100644 --- a/src/goetia/cdbg/compactor.cc +++ b/src/goetia/cdbg/compactor.cc @@ -66,7 +66,7 @@ Compactor::_induce_decision_nodes(std::deque& induced_decision_kme } if (n_attempts > max_attempts) { - throw GoetiaException("Stuck in split attempt loop, failing."); + throw std::runtime_error("Stuck in split attempt loop, failing."); } } } @@ -392,7 +392,7 @@ Compactor::_try_split_unode_right(kmer_type root, } -template class goetia::StreamingCompactor>; +//template class goetia::StreamingCompactor>; template class goetia::StreamingCompactor>; // template class goetia::StreamingCompactor>; // template class goetia::StreamingCompactor>; diff --git a/src/goetia/dbg.cc b/src/goetia/dbg.cc index 7290d6c..42d45ed 100644 --- a/src/goetia/dbg.cc +++ b/src/goetia/dbg.cc @@ -20,10 +20,12 @@ namespace goetia { template class dBG; template class dBG; + /* template class dBG; template class dBG; template class dBG; template class dBG; + */ template class dBG; template class dBG; @@ -40,26 +42,29 @@ namespace goetia { template class dBG; template class dBG; - template class goetia::dBG; - template class goetia::dBG; - template class goetia::dBG; - template class goetia::dBG; + template class dBG; + template class dBG; + template class dBG; + template class dBG; - template class goetia::dBG; - template class goetia::dBG; - template class goetia::dBG; - template class goetia::dBG; - + /* + template class dBG; + template class dBG; + template class dBG; + template class dBG; + */ template class KmerIterator>; template class KmerIterator>; template class KmerIterator>; template class KmerIterator>; + /* template class KmerIterator>; template class KmerIterator>; template class KmerIterator>; template class KmerIterator>; + */ template class KmerIterator>; template class KmerIterator>; @@ -81,10 +86,11 @@ namespace goetia { template class goetia::KmerIterator>; template class goetia::KmerIterator>; + /* template class goetia::KmerIterator>; template class goetia::KmerIterator>; template class goetia::KmerIterator>; template class goetia::KmerIterator>; - + */ } diff --git a/src/goetia/hashing/ukhs.cc b/src/goetia/hashing/ukhs.cc index 4554119..ca3fe81 100644 --- a/src/goetia/hashing/ukhs.cc +++ b/src/goetia/hashing/ukhs.cc @@ -7,6 +7,7 @@ */ #include +#include #include "goetia/hashing/rollinghashshifter.hh" #include "goetia/hashing/hashshifter.hh" @@ -22,7 +23,7 @@ namespace goetia { W (W) { if (unikmers.front().size() != K) { - throw GoetiaException("K does not match k-mer size from provided UKHS"); + throw std::invalid_argument("K does not match k-mer size from provided UKHS"); } uint64_t pid = 0; diff --git a/src/goetia/parsing/readers.cc b/src/goetia/parsing/readers.cc index 2d3e82d..5f02473 100644 --- a/src/goetia/parsing/readers.cc +++ b/src/goetia/parsing/readers.cc @@ -6,9 +6,12 @@ * Date : 21.10.2019 */ +#include + #include "goetia/parsing/readers.hh" #include "goetia/sequences/alphabets.hh" + namespace goetia { @@ -26,6 +29,9 @@ FastxParser::FastxParser(const std::string& infile, _min_length(min_length) { _fp = gzopen(_filename.c_str(), "r"); + if (_fp == Z_NULL) { + throw InvalidStream(strerror(errno)); + } _kseq = kseq_init(_fp); __asm__ __volatile__ ("" ::: "memory"); diff --git a/src/goetia/storage/bitstorage.cc b/src/goetia/storage/bitstorage.cc index bfb461a..643b744 100644 --- a/src/goetia/storage/bitstorage.cc +++ b/src/goetia/storage/bitstorage.cc @@ -51,13 +51,16 @@ * Contact: khmer-project@idyll.org */ +#include "goetia/errors.hh" #include "goetia/storage/bitstorage.hh" #include #include // IWYU pragma: keep #include #include +#include +#include "goetia/errors.hh" #include "goetia/goetia.hh" #include "zlib.h" @@ -104,7 +107,7 @@ void BitStorage::update_from(const BitStorage& other) { if (_tablesizes != other._tablesizes) { - throw GoetiaException("both nodegraphs must have same table sizes"); + throw std::invalid_argument("both nodegraphs must have same table sizes"); } byte_t tmp = 0; @@ -152,7 +155,7 @@ void BitStorage::save(std::string outfilename, uint16_t ksize) { if (!_counts[0]) { - throw GoetiaException(); + throw SerializationError("No value for counts."); } unsigned int save_ksize = ksize; @@ -183,7 +186,7 @@ BitStorage::save(std::string outfilename, uint16_t ksize) outfile.write((const char *) _counts[i], tablebytes); } if (outfile.fail()) { - throw GoetiaFileException(strerror(errno)); + throw SerializationError(strerror(errno)); } outfile.close(); } @@ -210,13 +213,13 @@ BitStorage::load(std::string infilename, uint16_t &ksize) } else { err = "Unknown error in opening file: " + infilename; } - throw GoetiaFileException(err); + throw DeserializationError(err); } catch (const std::exception &e) { // Catching std::exception is a stopgap for // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=66145 std::string err = "Unknown error opening file: " + infilename + " " + strerror(errno); - throw GoetiaFileException(err); + throw DeserializationError(err); } if (_counts) { @@ -247,18 +250,18 @@ BitStorage::load(std::string infilename, uint16_t &ksize) err << std::hex << (int) signature[i]; } err << " Should be: " << SAVED_SIGNATURE; - throw GoetiaFileException(err.str()); + throw DeserializationError(err.str()); } else if (!(version == SAVED_FORMAT_VERSION)) { std::ostringstream err; err << "Incorrect file format version " << (int) version << " while reading k-mer graph from " << infilename << "; should be " << (int) SAVED_FORMAT_VERSION; - throw GoetiaFileException(err.str()); + throw DeserializationError(err.str()); } else if (!(ht_type == SAVED_HASHBITS)) { std::ostringstream err; err << "Incorrect file format type " << (int) ht_type << " while reading k-mer graph from " << infilename; - throw GoetiaFileException(err.str()); + throw DeserializationError(err.str()); } infile.read((char *) &save_ksize, sizeof(save_ksize)); @@ -296,12 +299,12 @@ BitStorage::load(std::string infilename, uint16_t &ksize) } else { err = "Error reading from k-mer graph file: " + infilename; } - throw GoetiaFileException(err); + throw DeserializationError(err); } catch (const std::exception &e) { // Catching std::exception is a stopgap for // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=66145 std::string err = "Unknown error opening file: " + infilename + " " + strerror(errno); - throw GoetiaFileException(err); + throw DeserializationError(err); } } diff --git a/src/goetia/storage/bytestorage.cc b/src/goetia/storage/bytestorage.cc index 011a3cf..53e3cf9 100644 --- a/src/goetia/storage/bytestorage.cc +++ b/src/goetia/storage/bytestorage.cc @@ -43,6 +43,7 @@ * Contact: khmer-project@idyll.org */ +#include "goetia/errors.hh" #include "goetia/storage/bytestorage.hh" #include @@ -186,11 +187,11 @@ ByteStorageFileReader::ByteStorageFileReader( } else { err = "Unknown error in opening file: " + infilename; } - throw GoetiaFileException(err + " " + strerror(errno)); + throw InvalidStream(err + " " + strerror(errno)); } catch (const std::exception &e) { std::string err = "Unknown error opening file: " + infilename + " " + strerror(errno); - throw GoetiaFileException(err); + throw InvalidStream(err); } if (store._counts) { @@ -221,18 +222,18 @@ ByteStorageFileReader::ByteStorageFileReader( err << std::hex << (int) signature[i]; } err << " Should be: " << SAVED_SIGNATURE; - throw GoetiaFileException(err.str()); + throw DeserializationError(err.str()); } else if (!(version == SAVED_FORMAT_VERSION)) { std::ostringstream err; err << "Incorrect file format version " << (int) version << " while reading k-mer count file from " << infilename << "; should be " << (int) SAVED_FORMAT_VERSION; - throw GoetiaFileException(err.str()); + throw DeserializationError(err.str()); } else if (!(ht_type == SAVED_COUNTING_HT)) { std::ostringstream err; err << "Incorrect file format type " << (int) ht_type << " while reading k-mer count file from " << infilename; - throw GoetiaFileException(err.str()); + throw DeserializationError(err.str()); } infile.read((char *) &use_bigcount, 1); @@ -293,11 +294,11 @@ ByteStorageFileReader::ByteStorageFileReader( err = "Error reading from k-mer count file: " + infilename + " " + strerror(errno); } - throw GoetiaFileException(err); + throw DeserializationError(err); } catch (const std::exception &e) { std::string err = "Error reading from k-mer count file: " + infilename + " " + strerror(errno); - throw GoetiaFileException(err); + throw DeserializationError(err); } } @@ -309,7 +310,7 @@ ByteStorageGzFileReader::ByteStorageGzFileReader( gzFile infile = gzopen(infilename.c_str(), "rb"); if (infile == Z_NULL) { std::string err = "Cannot open k-mer count file: " + infilename; - throw GoetiaFileException(err); + throw DeserializationError(err); } if (store._counts) { @@ -336,13 +337,13 @@ ByteStorageGzFileReader::ByteStorageGzFileReader( std::string err = "K-mer count file read error: " + infilename + " " + strerror(errno); gzclose(infile); - throw GoetiaFileException(err); + throw DeserializationError(err); } else if (!(std::string(signature, 4) == SAVED_SIGNATURE)) { std::ostringstream err; err << "Does not start with signature for a oxli " << "file: " << signature << " Should be: " << SAVED_SIGNATURE; - throw GoetiaFileException(err.str()); + throw DeserializationError(err.str()); } else if (!(version == SAVED_FORMAT_VERSION) || !(ht_type == SAVED_COUNTING_HT)) { if (!(version == SAVED_FORMAT_VERSION)) { @@ -351,13 +352,13 @@ ByteStorageGzFileReader::ByteStorageGzFileReader( << " while reading k-mer count file from " << infilename << "; should be " << (int) SAVED_FORMAT_VERSION; gzclose(infile); - throw GoetiaFileException(err.str()); + throw DeserializationError(err.str()); } else if (!(ht_type == SAVED_COUNTING_HT)) { std::ostringstream err; err << "Incorrect file format type " << (int) ht_type << " while reading k-mer count file from " << infilename; gzclose(infile); - throw GoetiaFileException(err.str()); + throw DeserializationError(err.str()); } } @@ -372,7 +373,7 @@ ByteStorageGzFileReader::ByteStorageGzFileReader( std::string err = "K-mer count file header read error: " + infilename + " " + strerror(errno); gzclose(infile); - throw GoetiaFileException(err); + throw DeserializationError(err); } ksize = (uint16_t) save_ksize; @@ -398,7 +399,7 @@ ByteStorageGzFileReader::ByteStorageGzFileReader( err = err + " " + gzerr; } gzclose(infile); - throw GoetiaFileException(err); + throw DeserializationError(err); } tablesize = save_tablesize; @@ -427,7 +428,7 @@ ByteStorageGzFileReader::ByteStorageGzFileReader( err = err + " " + gzerr; } gzclose(infile); - throw GoetiaFileException(err); + throw DeserializationError(err); } loaded += read_b; @@ -445,7 +446,7 @@ ByteStorageGzFileReader::ByteStorageGzFileReader( err = err + " " + gzerr; } gzclose(infile); - throw GoetiaFileException(err); + throw DeserializationError(err); } if (n_counts) { @@ -467,7 +468,7 @@ ByteStorageGzFileReader::ByteStorageGzFileReader( err = err + " " + gzerr; } gzclose(infile); - throw GoetiaFileException(err); + throw DeserializationError(err); } store._bigcounts[kmer] = count; @@ -483,7 +484,7 @@ ByteStorageFileWriter::ByteStorageFileWriter( const ByteStorage& store) { if (!store._counts[0]) { - throw GoetiaException(); + throw SerializationError("No value for counts."); } unsigned int save_ksize = ksize; @@ -530,7 +531,7 @@ ByteStorageFileWriter::ByteStorageFileWriter( } } if (outfile.fail()) { - throw GoetiaFileException(strerror(errno)); + throw SerializationError(strerror(errno)); } outfile.close(); } @@ -541,7 +542,7 @@ ByteStorageGzFileWriter::ByteStorageGzFileWriter( const ByteStorage &store) { if (!store._counts[0]) { - throw GoetiaException(); + throw SerializationError("No value for counts."); } int errnum = 0; @@ -554,9 +555,9 @@ ByteStorageGzFileWriter::ByteStorageGzFileWriter( if (outfile == NULL) { const char * error = gzerror(outfile, &errnum); if (errnum == Z_ERRNO) { - throw GoetiaFileException(strerror(errno)); + throw SerializationError(strerror(errno)); } else { - throw GoetiaFileException(error); + throw SerializationError(error); } } @@ -614,7 +615,7 @@ ByteStorageGzFileWriter::ByteStorageGzFileWriter( msg << strerror(errno); } gzclose(outfile); - throw GoetiaFileException(msg.str()); + throw SerializationError(msg.str()); } written += gz_result; } @@ -633,9 +634,9 @@ ByteStorageGzFileWriter::ByteStorageGzFileWriter( } const char * error = gzerror(outfile, &errnum); if (errnum == Z_ERRNO) { - throw GoetiaFileException(strerror(errno)); + throw SerializationError(strerror(errno)); } else if (errnum != Z_OK) { - throw GoetiaFileException(error); + throw SerializationError(error); } gzclose(outfile); } diff --git a/src/goetia/storage/nibblestorage.cc b/src/goetia/storage/nibblestorage.cc index 6c56d2c..afa3e83 100644 --- a/src/goetia/storage/nibblestorage.cc +++ b/src/goetia/storage/nibblestorage.cc @@ -50,7 +50,9 @@ #include // IWYU pragma: keep #include #include +#include +#include "goetia/errors.hh" #include "goetia/goetia.hh" using namespace std; @@ -133,7 +135,7 @@ void NibbleStorage::save(std::string outfilename, uint16_t ksize) { if (!_counts[0]) { - throw GoetiaException(); + throw SerializationError("No value for counts."); } unsigned int save_ksize = ksize; @@ -180,11 +182,11 @@ NibbleStorage::load(std::string infilename, uint16_t& ksize) } else { err = "Unknown error in opening file: " + infilename; } - throw GoetiaFileException(err + " " + strerror(errno)); + throw DeserializationError(err + " " + strerror(errno)); } catch (const std::exception &e) { std::string err = "Unknown error opening file: " + infilename + " " + strerror(errno); - throw GoetiaFileException(err); + throw DeserializationError(err); } if (_counts) { @@ -215,18 +217,18 @@ NibbleStorage::load(std::string infilename, uint16_t& ksize) err << std::hex << (int) signature[i]; } err << " Should be: " << SAVED_SIGNATURE; - throw GoetiaFileException(err.str()); + throw DeserializationError(err.str()); } else if (!(version == SAVED_FORMAT_VERSION)) { std::ostringstream err; err << "Incorrect file format version " << (int) version << " while reading k-mer count file from " << infilename << "; should be " << (int) SAVED_FORMAT_VERSION; - throw GoetiaFileException(err.str()); + throw DeserializationError(err.str()); } else if (!(ht_type == SAVED_SMALLCOUNT)) { std::ostringstream err; err << "Incorrect file format type " << (int) ht_type << " while reading k-mer count file from " << infilename; - throw GoetiaFileException(err.str()); + throw DeserializationError(err.str()); } infile.read((char *) &save_ksize, sizeof(save_ksize)); @@ -269,11 +271,11 @@ NibbleStorage::load(std::string infilename, uint16_t& ksize) err = "Error reading from k-mer count file: " + infilename + " " + strerror(errno); } - throw GoetiaFileException(err); + throw DeserializationError(err); } catch (const std::exception &e) { std::string err = "Error reading from k-mer count file: " + infilename + " " + strerror(errno); - throw GoetiaFileException(err); + throw DeserializationError(err); } } diff --git a/src/goetia/storage/qfstorage.cc b/src/goetia/storage/qfstorage.cc index f20b7f5..b2041b7 100644 --- a/src/goetia/storage/qfstorage.cc +++ b/src/goetia/storage/qfstorage.cc @@ -52,6 +52,7 @@ #include #include +#include "goetia/errors.hh" #include "goetia/goetia.hh" #include "goetia/storage/cqf/gqf.h" @@ -184,11 +185,11 @@ QFStorage::load(std::string infilename, uint16_t &ksize) } else { err = "Unknown error in opening file: " + infilename; } - throw GoetiaFileException(err + " " + strerror(errno)); + throw DeserializationError(err + " " + strerror(errno)); } catch (const std::exception &e) { std::string err = "Unknown error opening file: " + infilename + " " + strerror(errno); - throw GoetiaFileException(err); + throw DeserializationError(err); } uint16_t save_ksize = 0; char signature [4]; @@ -206,19 +207,19 @@ QFStorage::load(std::string infilename, uint16_t &ksize) err << std::hex << (int) signature[i]; } err << " Should be: " << SAVED_SIGNATURE; - throw GoetiaFileException(err.str()); + throw DeserializationError(err.str()); } else if (!(version == SAVED_FORMAT_VERSION)) { std::ostringstream err; err << "Incorrect file format version " << (int) version << " while reading k-mer count file from " << infilename << "; should be " << (int) SAVED_FORMAT_VERSION; - throw GoetiaFileException(err.str()); + throw DeserializationError(err.str()); } else if (!(ht_type == SAVED_QFCOUNT)) { std::ostringstream err; err << "Incorrect file format type " << (int) ht_type << " expected " << (int) SAVED_QFCOUNT << " while reading k-mer count file from " << infilename; - throw GoetiaFileException(err.str()); + throw DeserializationError(err.str()); } infile.read((char *) &save_ksize, sizeof(save_ksize)); diff --git a/src/goetia/storage/sparseppstorage.cc b/src/goetia/storage/sparseppstorage.cc index 24fd1c8..5018b25 100644 --- a/src/goetia/storage/sparseppstorage.cc +++ b/src/goetia/storage/sparseppstorage.cc @@ -6,6 +6,7 @@ * Date : 30.08.2019 */ +#include "goetia/errors.hh" #include "goetia/goetia.hh" #include "goetia/storage/sparseppstorage.hh" #include "goetia/storage/sparsepp/spp.h" @@ -79,14 +80,14 @@ SparseppSetStorage::deserialize(std::ifstream& in) { << name << ", should be " << Tagged::NAME; - throw GoetiaFileException(err.str()); + throw DeserializationError(err.str()); } else if (version != Tagged::OBJECT_ABI_VERSION) { std::ostringstream err; err << "File has wrong binary version: found " << std::to_string(version) << ", expected " << std::to_string(Tagged::OBJECT_ABI_VERSION); - throw GoetiaFileException(err.str()); + throw DeserializationError(err.str()); } diff --git a/src/goetia/traversal/unitig_walker.cc b/src/goetia/traversal/unitig_walker.cc index 2652b1e..91bad19 100644 --- a/src/goetia/traversal/unitig_walker.cc +++ b/src/goetia/traversal/unitig_walker.cc @@ -19,10 +19,12 @@ namespace goetia { template class UnitigWalker>; template class UnitigWalker>; + /* template class UnitigWalker>; template class UnitigWalker>; template class UnitigWalker>; template class UnitigWalker>; + */ template class UnitigWalker>; template class UnitigWalker>; @@ -44,11 +46,11 @@ namespace goetia { template class goetia::UnitigWalker>; template class goetia::UnitigWalker>; + /* template class goetia::UnitigWalker>; template class goetia::UnitigWalker>; template class goetia::UnitigWalker>; template class goetia::UnitigWalker>; - - + */ }; diff --git a/tests/test_signatures.py b/tests/test_signatures.py index 4c35b74..33fbdc5 100644 --- a/tests/test_signatures.py +++ b/tests/test_signatures.py @@ -11,8 +11,8 @@ from goetia.hashing import Canonical, StrandAware from goetia.parsing import read_fastx from goetia.sketches import SourmashSketch, UnikmerSketch -from goetia.storage import SparseppSetStorage -from goetia.storage import HLLStorage +from goetia.storage import PHMapStorage +#from goetia.storage import HLLStorage from .utils import * @@ -62,7 +62,7 @@ def test_sourmash_scaled(datadir, ksize): def test_draff_to_numpy(datadir): rfile = datadir('random-20-a.fa') - sketch_t = UnikmerSketch[SparseppSetStorage, StrandAware] + sketch_t = UnikmerSketch[PHMapStorage, StrandAware] sketch = sketch_t.Sketch.build(31, 7) processor = sketch_t.Processor.build(sketch) processor.process(rfile) @@ -77,6 +77,7 @@ def test_draff_to_numpy(datadir): assert np_val == py_val +''' def test_hllcounter(): ints = set(np.random.randint(0, 100000, 100000)) e = 0.01 @@ -94,6 +95,7 @@ def test_hllcounter(): print(est) assert (act - margin) < est < (act + margin) +''' def test_sourmash_stream(tmpdir, datadir): diff --git a/tests/test_solid_compactor.py b/tests/test_solid_compactor.py index 36a8a36..e830f7e 100644 --- a/tests/test_solid_compactor.py +++ b/tests/test_solid_compactor.py @@ -11,6 +11,7 @@ import pytest from goetia import libgoetia +from goetia.storage import PHMapStorage from .utils import * from .test_cdbg import compactor, compactor_type @@ -31,7 +32,7 @@ def solid_compactor(graph, compactor, compactor_type, min_abund): return _solid_compactor -@using(hasher_type=libgoetia.FwdLemireShifter) +@using(hasher_type=libgoetia.FwdLemireShifter, storage_type=PHMapStorage) class TestFindSolidSegments: @using(ksize=21, length=100, min_abund=2)