diff --git a/.github/workflows/test-android.yml b/.github/workflows/test-android.yml new file mode 100644 index 00000000..380d47a9 --- /dev/null +++ b/.github/workflows/test-android.yml @@ -0,0 +1,31 @@ +name: Android + +on: + push: + branches: + - '**' + - '!releases' + +jobs: + build: + runs-on: [ self-hosted, linux, android-ndk ] + strategy: + matrix: + BUILD_TYPE: [Release, Debug] + fail-fast: false + + steps: + - uses: actions/checkout@v2 + + - name: Configure CMake + run: > + cmake -B build + -DCMAKE_TOOLCHAIN_FILE=$ANDROID_HOME/ndk/21.1.6352462/build/cmake/android.toolchain.cmake + -DCMAKE_BUILD_TYPE=${{ matrix.BUILD_TYPE }} + -DVCCC_USE_OPENCV_FEATURES=OFF + -DVCCC_RUN_TEST=ON + + - name: Build + run: cmake --build build --config ${{ matrix.BUILD_TYPE }} -- -j + + # CTest not available diff --git a/.github/workflows/test-emscripten.yml b/.github/workflows/test-emscripten.yml new file mode 100644 index 00000000..d017aad0 --- /dev/null +++ b/.github/workflows/test-emscripten.yml @@ -0,0 +1,42 @@ +name: WebAssembly + +on: + push: + branches: + - '**' + - '!releases' + +env: + BUILD_TYPE: Release + EMSDK_VERSION: 3.1.34 + +jobs: + build: + runs-on: [ self-hosted, unix ] + + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-node@v3 + with: + node-version: 16 + + - name: Install Emscripten + run: | + git clone https://github.com/emscripten-core/emsdk.git + cd emsdk + ./emsdk install ${{ env.EMSDK_VERSION }} + ./emsdk activate ${{ env.EMSDK_VERSION }} + + - name: Configure CMake + run: | + source emsdk/emsdk_env.sh + emcmake cmake -B build -DCMAKE_CROSSCOMPILING_EMULATOR=node -DCMAKE_BUILD_TYPE=${{ env.BUILD_TYPE }} -DVCCC_USE_OPENCV_FEATURES=OFF -DVCCC_RUN_TEST=ON + + - name: Build + run: | + source emsdk/emsdk_env.sh + emmake cmake --build build --config ${{ env.BUILD_TYPE }} -- -j 4 + + - name: Test + working-directory: build + run: ctest -C ${{ env.BUILD_TYPE }} --extra-verbose --output-on-failure diff --git a/CMakeLists.txt b/CMakeLists.txt index 1949f7ae..3bea5d42 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -27,7 +27,11 @@ message("VCCC_USE_OPENCV_FEATURES: ${VCCC_USE_OPENCV_FEATURES}") MACRO(VCCC_TEST_ONE name target) add_test(NAME ${name} COMMAND ${name}) add_executable(${name} ${target}) - target_link_libraries(${name} PUBLIC ${ARGN}) + if (EMSCRIPTEN) + target_link_libraries(${name} PUBLIC ${ARGN} -sEXIT_RUNTIME=1 -fexceptions) + else() + target_link_libraries(${name} PUBLIC ${ARGN}) + endif() ENDMACRO() MACRO(VCCC_TEST_ONE_CXX name target cxx) diff --git a/README.md b/README.md index e54e2731..61e1fc13 100644 --- a/README.md +++ b/README.md @@ -5,15 +5,24 @@ ![develop](https://github.com/visualcamp/vccc/actions/workflows/test-macos.yml/badge.svg?branch=main) ![develop](https://github.com/visualcamp/vccc/actions/workflows/test-ubuntu.yml/badge.svg?branch=main) ![develop](https://github.com/visualcamp/vccc/actions/workflows/test-windows.yml/badge.svg?branch=main) +![develop](https://github.com/visualcamp/vccc/actions/workflows/test-android.yml/badge.svg?branch=main) +![develop](https://github.com/visualcamp/vccc/actions/workflows/test-emscripten.yml/badge.svg?branch=main) [![Deploy](https://github.com/visualcamp/vccc/actions/workflows/deploy-docs.yml/badge.svg)](https://github.com/visualcamp/vccc/actions/workflows/deploy-docs.yml) # VisualCamp Common C++ library ## Features -* C++14 concepts library (up to C++23 standard) -* C++14 ranges library (up to C++26 standard) -* C++14 variant, optional, span, ... (up to C++26 standard) -* Compatible with existing STL +* algorithm library implemented in C++14 (up to C++23 standard) +* concepts library implemented in C++14 (up to C++23 standard) +* ranges library implemented in C++14 (up to C++26 standard) +* utility library implemented in c++14 (up to C++26 standard) + * `expected` + * `optional` + * `span` + * `variant` +* Other ~ C++26 libraries + +#### All features are compatible with existing STL ## Examples ```c++ diff --git a/include/log/include/vccc/__log/global_stream_wrapper_settings.hpp b/include/log/include/vccc/__log/global_stream_wrapper_settings.hpp new file mode 100644 index 00000000..a7d35cc3 --- /dev/null +++ b/include/log/include/vccc/__log/global_stream_wrapper_settings.hpp @@ -0,0 +1,63 @@ +// +// Created by YongGyu Lee on 3/22/24. +// + +#ifndef VCCC_LOG_GLOBAL_STREAM_WRAPPER_SETTINGS_HPP_ +#define VCCC_LOG_GLOBAL_STREAM_WRAPPER_SETTINGS_HPP_ + +namespace vccc { + +/// @addtogroup log +/// @{ + +class GlobalStreamWrapperSettings { + public: + GlobalStreamWrapperSettings(const GlobalStreamWrapperSettings&) = delete; + GlobalStreamWrapperSettings(GlobalStreamWrapperSettings&&) = delete; + GlobalStreamWrapperSettings& operator=(const GlobalStreamWrapperSettings&) = delete; + GlobalStreamWrapperSettings& operator=(GlobalStreamWrapperSettings&&) = delete; + + // Add quotation to strings (string and string_view) + static bool quote_string() { + return GetInstance().quote_string_; + } + + static void quote_string(bool new_value) { + GetInstance().quote_string_ = new_value; + } + + // Expand aggregate types (non-empty aggregate types) + static bool expand_aggregate() { + return GetInstance().expand_aggregate_; + } + + static void expand_aggregate(bool new_value) { + GetInstance().expand_aggregate_ = new_value; + } + + static bool expand_array() { + return GetInstance().expand_array_; + } + + static void expand_array(bool new_value) { + GetInstance().expand_array_ = new_value; + } + + private: + GlobalStreamWrapperSettings() = default; + + static GlobalStreamWrapperSettings& GetInstance() { + static auto inst = new GlobalStreamWrapperSettings(); + return *inst; + } + + bool quote_string_ = false; + bool expand_aggregate_ = false; + bool expand_array_ = false; +}; + +/// @} + +} // namespace vccc + +#endif // VCCC_LOG_GLOBAL_STREAM_WRAPPER_SETTINGS_HPP_ diff --git a/include/log/include/vccc/__log/iomanip.hpp b/include/log/include/vccc/__log/iomanip.hpp index e4880505..60eed5e0 100644 --- a/include/log/include/vccc/__log/iomanip.hpp +++ b/include/log/include/vccc/__log/iomanip.hpp @@ -38,6 +38,62 @@ class Separator : public StreamManipulator { std::string sep_; }; +/// @brief Manipulator for adding quotation to strings(only to string and string_view) +class Quoted : public StreamManipulator { + public: + Quoted() : value_(true) {} + + explicit Quoted(bool value) : value_(value) {} + + + template + BasicStreamWrapper& operator()(BasicStreamWrapper& stream) const { + stream.quote_string(value_); + return stream; + } + + private: + bool value_; +}; + +/// @brief Manipulator for expanding aggregate types +/// +/// Note: empty aggregate will not be expanded +class ExpandAggregate : public StreamManipulator { + public: + ExpandAggregate() : value_(true) {} + + explicit ExpandAggregate(bool value) : value_(value) {} + + template + BasicStreamWrapper& operator()(BasicStreamWrapper& stream) const { + stream.expand_aggregate(value_); + return stream; + } + + private: + bool value_; +}; + +/// @brief Expand array instead of printing its value +/// +/// Note: non-default-printable array is expanded by default +class ExpandArray : public StreamManipulator { + public: + ExpandArray() : value_(true) {} + + explicit ExpandArray(bool value) : value_(value) {} + + template + BasicStreamWrapper& operator()(BasicStreamWrapper& stream) const { + stream.expand_array(value_); + return stream; + } + + private: + bool value_; +}; + //! @} log } // namespace vccc diff --git a/include/log/include/vccc/__log/stream_wrapper.hpp b/include/log/include/vccc/__log/stream_wrapper.hpp index 3fa59939..35068e86 100644 --- a/include/log/include/vccc/__log/stream_wrapper.hpp +++ b/include/log/include/vccc/__log/stream_wrapper.hpp @@ -9,6 +9,7 @@ # include # include # include +# include # include # include # include @@ -17,17 +18,62 @@ # include # include # +# include "vccc/__core/nodiscard.hpp" +# include "vccc/__log/global_stream_wrapper_settings.hpp" +# include "vccc/__log/ios_flags_saver.hpp" +# include "vccc/__memory/addressof.hpp" # include "vccc/optional.hpp" +# include "vccc/__ranges/range.hpp" +# include "vccc/__ranges/range_value_t.hpp" +# include "vccc/__ranges/subrange.hpp" +# include "vccc/string_view.hpp" # include "vccc/type_traits.hpp" -# include "vccc/__log/ios_flags_saver.hpp" +# include "vccc/variant.hpp" # # if __cplusplus >= 201703 # include # include +# include +# include # include "boost/pfr.hpp" # endif namespace vccc { +namespace detail { + +template +struct has_typename_key_type : std::false_type {}; + +template +struct has_typename_key_type> : std::true_type {}; + +template +struct has_typename_mapped_type : std::false_type {}; + +template +struct has_typename_mapped_type> : std::true_type {}; + +template::value> +struct is_mapped_range : std::false_type {}; + +template +struct is_mapped_range + : conjunction< + has_typename_key_type, + has_typename_mapped_type, + is_specialization, std::pair> + > {}; + +#if __cplusplus >= 201703L +template::value /* false */> +struct empty_aggregate : std::false_type {}; + +template +struct empty_aggregate : bool_constant::value == 0> {}; +#else +#endif + +} // namespace detail //! @addtogroup log //! @{ @@ -82,10 +128,37 @@ class StreamWrapperBase { return prev; } + VCCC_NODISCARD bool quote_string() const noexcept { + return quote_string_; + } + + void quote_string(bool new_value) noexcept { + quote_string_ = new_value; + } + + VCCC_NODISCARD bool expand_aggregate() const noexcept { + return expand_aggregate_; + } + + void expand_aggregate(bool new_value) noexcept { + expand_aggregate_ = new_value; + } + + VCCC_NODISCARD bool expand_array() { + return expand_array_; + } + + void expand_array(bool new_value) { + expand_array_ = new_value; + } + protected: struct { string_type separator_ = global_separator(); bool first_ = true; + bool quote_string_ = GlobalStreamWrapperSettings::quote_string(); + bool expand_aggregate_ = GlobalStreamWrapperSettings::expand_aggregate(); + bool expand_array_ = GlobalStreamWrapperSettings::expand_array(); }; }; @@ -123,31 +196,31 @@ class BasicStreamWrapper : public StreamWrapperBase { explicit BasicStreamWrapper(stream_type&& stream) : stream_(std::move(stream)) {} // for std::endl - BasicStreamWrapper& operator << (std::ostream& (*pf)(std::ostream&)) { + BasicStreamWrapper& operator<<(std::ostream& (*pf)(std::ostream&)) { pf(stream_); return *this; } // I/O manipulators - BasicStreamWrapper& operator << (std::ios_base& (&io_manip)(std::ios_base&)) { + BasicStreamWrapper& operator<<(std::ios_base& (&io_manip)(std::ios_base&)) { stream_ << io_manip; return *this; } - template>::value, - int> = 0> - BasicStreamWrapper& operator << (const T& manipulator) { + // Custom I/O manipulators + template> + ::value, int> = 0> + BasicStreamWrapper& operator<<(const T& manipulator) { manipulator(*this); return *this; } - template>::value, - int> = 0> - BasicStreamWrapper& operator << (const T& value) { + // All printable types + template> + ::value, int> = 0> + BasicStreamWrapper& operator<<(const T& value) { if (base::first_) { base::first_ = false; } else { @@ -182,26 +255,81 @@ class BasicStreamWrapper : public StreamWrapperBase { using default_printable_t = std::true_type; using not_default_printable_t = std::false_type; - bool use_key_value_separator = true; + // Default-printable types + template + void try_write(default_printable_t, const T& value) { + stream_ << value; + } - using key_value_container_t = std::true_type; - using not_key_value_container_t = std::false_type; + // Raw string + template + void try_write(default_printable_t, const char(&str)[N]) { + stream_ << str; + } - template - using is_key_value_container_t - = typename is_specialization().begin())>, std::pair>::type; + // Array types + template + void try_write(default_printable_t, const T(&arr)[N]) { + if (!base::expand_array() && is_printable::value) { + stream_ << arr; + return; + } - template - inline void try_write(default_printable_t, const T& value) { stream_ << value;} + if (N == 0) { + stream_ << "{}"; + return; + } + + stream_ << "{ "; + *this << *arr; + for (std::size_t i = 1; i < N; ++i) { + stream_ << ", "; + *this << *(arr + i); + } + stream_ << " }"; + } + + // Strings + void try_write(default_printable_t, const std::string& s) { + if (base::quote_string_) + stream_ << "\"" << s << "\""; + else + stream_ << s; + } + + void try_write(default_printable_t, const string_view& sv) { + if (base::quote_string_) + stream_ << "\"" << sv << "\""; + else + stream_ << sv; + } + +#if __cplusplus >= 201703L + void try_write(default_printable_t, const std::string_view& sv) { + if (base::quote_string_) + stream_ << "\"" << sv << "\""; + else + stream_ << sv; + } +#endif template - constexpr void try_write(not_default_printable_t, const T& value) { write(value); } + constexpr void try_write(not_default_printable_t, const T& value) { + write(value); + } #if __cplusplus >= 201703 // any aggregate type - template::value && std::is_aggregate_v, int> = 0> + template>, + std::is_aggregate, + negation> + >::value, int> = 0> void write(const T& aggr) { - writeAggregate(aggr, std::make_index_sequence>{}); + if (base::expand_aggregate_) + writeAggregate(aggr, std::make_index_sequence>{}); + else + writeAddress(aggr); } template @@ -225,25 +353,63 @@ class BasicStreamWrapper : public StreamWrapperBase { } #endif - // container types - template::value, int> = 0> - inline void write(const T& value) { - use_key_value_separator? - writeContainer(is_key_value_container_t{}, value): - writeContainer(not_key_value_container_t{}, value); +#if __cplusplus < 201703L + template::value, int> = 0> +#else + template>, + disjunction< + negation>, + detail::empty_aggregate + > + >::value, int> = 0> +#endif + void write(const T& value) { + writeAddress(value); } template - inline void writeContainer(key_value_container_t, const T& value) - { writeIterator(value.begin(), value.end(), [this](const auto& p){write(p, ": ");}); } + void writeAddress(const T& value) { + stream_ << '@' << vccc::addressof(value); + } + + // ranges + template::value, int> = 0> + void write(const T& value) { + writeRange(value); + } template - inline void writeContainer(not_key_value_container_t, const T& value) - { writeIterator(value.begin(), value.end(), [this](const auto& elem){*this << elem;}); } + void writeRange(const T& value) { + auto first = value.begin(); + auto last = value.end(); + + if (first == last) { + stream_ << "{}"; + return; + } + + stream_ << "{ "; + writeRangeElement(*first, detail::is_mapped_range::value); + for (++first; first != last; ++first) { + stream_ << ", "; + writeRangeElement(*first, detail::is_mapped_range::value); + } + stream_ << " }"; + } - template - void writeIterator(InputIterator first, InputIterator last, Func f); + template + void writeRangeElement(const T& value, bool) { + (*this) << value; + } + template + void writeRangeElement(const std::pair& value, bool as_map) { + if (as_map) + (*this) << value.first << " => " << value.second; + else + (*this) << "{" << value.first << ", " << value.second << "}"; + } // chrono types template @@ -253,57 +419,114 @@ class BasicStreamWrapper : public StreamWrapperBase { void write(const std::chrono::time_point& time_point); template - inline void write(const std::chrono::time_point& time_point) { + void write(const std::chrono::time_point& time_point) { stream_ << "+"; write(time_point.time_since_epoch()); } void write(const std::time_t* tt); - - // tuple-like types + // Tuple-like types template - inline void write(const std::tuple& value) { - writeTuple(value, std::index_sequence_for{}); + void write(const std::tuple& value) { + writeTupleLike(value, std::index_sequence_for{}); } - template - inline void writeTuple(const std::tuple& value, std::index_sequence<>) { + template + void write(const std::pair& value) { + writeTupleLike(value, std::index_sequence_for{}); + } + + template + void writeTupleLike(const T&, std::index_sequence<>) { stream_ << "{}"; } - template - inline void writeTuple(const std::tuple& value, std::index_sequence<0>) { - stream_ << "{ " << std::get<0>(value) << " }"; + template + void writeTupleLike(const T& value, std::index_sequence<0>) { + stream_ << "{ "; + (*this) << std::get<0>(value); + stream_ << " }"; + (*this) << "{ " << std::get<0>(value) << " }"; } - template - void writeTuple(const std::tuple& value, std::index_sequence<0, I...>); + template + void writeTupleLike(const T& value, std::index_sequence<0, I...>) { + stream_ << "{ "; + (*this) << std::get<0>(value); - template - void write(const std::pair& value, const std::string& sep = ", " /* use " : " for key-value */); + int dummy[sizeof...(I)] = { + (((*this) << ", " << std::get(value)), 0)... + }; + stream_ << " }"; + } // integer sequence template - inline void write(const std::integer_sequence&) { - write(std::make_tuple(v...)); + void write(const std::integer_sequence&) { + writeTupleLike(std::make_tuple(v...), std::make_index_sequence{}); } + // optional template - void write(const vccc::optional& op) { + void write(const optional& op) { if (op) *this << *op; else stream_ << "nullopt"; } + // variant + template + void write(const variant& v) { + if (v.valueless_by_exception()) { + stream_ << "valueless_by_exception"; + return; + } + v.visit([this](const auto& elem) { + *this << elem; + }); + } + + template + void write(const std::integral_constant& t) { + stream_ << (v ? "true_type" : "false_type"); + } + + // unique_ptr + template + inline void write(const std::unique_ptr& p) { + stream_ << "std::unique_ptr -> "; + writePointer(p.get()); + } + + // shared_ptr + template + inline void write(const std::shared_ptr& p) { + stream_ << "std::shared_ptr -> "; + writePointer(p.get()); + } + + // weak_ptr + template + inline void write(const std::weak_ptr& p) { + stream_ << "std::weak_ptr -> "; + writePointer(p.get()); + } + + template + void writePointer(const T* ptr) { + stream_ << ptr; + } + // directory_entry(Apple Clang defects) # if __cplusplus >= 201703L void write(const std::filesystem::directory_entry& d) { stream_ << d.path(); } + // std::optional template void write(const std::optional& op) { if (op) @@ -311,26 +534,23 @@ class BasicStreamWrapper : public StreamWrapperBase { else stream_ << "nullopt"; } + + // std::variant + template + void write(const std::variant& v) { + if (v.valueless_by_exception()) { + stream_ << "valueless_by_exception"; + return; + } + + std::visit([this](const auto& elem) { + *this << elem; + }, v); + } # endif }; //! @cond ignored -template -template -void BasicStreamWrapper::writeIterator(InputIterator first, InputIterator last, Func f) { - stream_ << '{'; - if (first != last) { - stream_ << ' '; - f(*first); - ++first; - for(; first != last; ++first) { - stream_ << ", "; - f(*first); - } - stream_ << ' '; - } - stream_ << '}'; -} template template @@ -432,26 +652,6 @@ void BasicStreamWrapper::write(const std::time_t *tt) { stream_ << std::put_time(tm_obj, "%Y-%m-%d %H:%M:%S."); } -template -template -void BasicStreamWrapper::writeTuple(const std::tuple& value, std::index_sequence<0, I...>) { - stream_ << "{ "; - *this << std::get<0>(value); - int dummy[sizeof...(I)] = { - (*this << ", " << std::get(value), 0)... - }; - stream_ << " }"; -} - -template -template -void BasicStreamWrapper::write(const std::pair& value, const std::string& sep) { - stream_ << "{ "; - *this << value.first; - stream_ << sep; - *this << value.second; - stream_ << " }"; -} //! @endcond ignored using StreamWrapper = BasicStreamWrapper; diff --git a/include/log/include/vccc/log.hpp b/include/log/include/vccc/log.hpp index 029d56bd..56373da4 100644 --- a/include/log/include/vccc/log.hpp +++ b/include/log/include/vccc/log.hpp @@ -5,10 +5,13 @@ # ifndef VCCC_LOG_HPP # define VCCC_LOG_HPP # -# include "vccc/__log/pwd.hpp" -# include "vccc/__log/logger.hpp" # include "vccc/__log/function_macro.hpp" +# include "vccc/__log/global_stream_wrapper_settings.hpp" # include "vccc/__log/iomanip.hpp" +# include "vccc/__log/ios_flags_saver.hpp" +# include "vccc/__log/logger.hpp" +# include "vccc/__log/pwd.hpp" +# include "vccc/__log/stream_wrapper.hpp" /** @defgroup log log diff --git a/include/signal/CMakeLists.txt b/include/signal/CMakeLists.txt index af1120c0..820fc2af 100644 --- a/include/signal/CMakeLists.txt +++ b/include/signal/CMakeLists.txt @@ -6,7 +6,10 @@ add_library(VCCC::signal ALIAS vccc_signal) target_include_directories(vccc_signal INTERFACE include) -if (UNIX AND NOT APPLE) +if (EMSCRIPTEN) + target_compile_options(vccc_signal INTERFACE -pthread) + target_link_libraries(vccc_signal INTERFACE vccc_stl -pthread) +elseif (UNIX AND NOT APPLE) set(THREADS_PREFER_PTHREAD_FLAG ON) find_package(Threads REQUIRED) target_link_libraries(vccc_signal INTERFACE vccc_stl Threads::Threads) diff --git a/include/vccc/__algorithm/ranges/set_intersection.hpp b/include/vccc/__algorithm/ranges/set_intersection.hpp new file mode 100644 index 00000000..e2ceecc8 --- /dev/null +++ b/include/vccc/__algorithm/ranges/set_intersection.hpp @@ -0,0 +1,109 @@ +// +// Created by YongGyu Lee on 3/19/24. +// + +#ifndef VCCC_ALGORITHM_RANGES_SET_INTERSECTION_HPP_ +#define VCCC_ALGORITHM_RANGES_SET_INTERSECTION_HPP_ + +#include + +#include "vccc/__algorithm/ranges/in_in_out_result.hpp" +#include "vccc/__core/inline_or_static.hpp" +#include "vccc/__functional/less.hpp" +#include "vccc/__functional/identity.hpp" +#include "vccc/__functional/invoke.hpp" +#include "vccc/__iterator/input_iterator.hpp" +#include "vccc/__iterator/mergeable.hpp" +#include "vccc/__iterator/next.hpp" +#include "vccc/__iterator/sentinel_for.hpp" +#include "vccc/__iterator/weakly_incrementable.hpp" +#include "vccc/__ranges/borrowed_iterator_t.hpp" +#include "vccc/__ranges/input_range.hpp" +#include "vccc/__type_traits/conjunction.hpp" + +namespace vccc { +namespace ranges { + +/// @addtogroup ranges +/// @{ + +template< class I1, class I2, class O > +using set_intersection_result = ranges::in_in_out_result; + +namespace detail { + +struct set_intersection_niebloid { + private: + template, input_range>::value /* false */> + struct check_range : std::false_type {}; + template + struct check_range + : conjunction< + weakly_incrementable, + mergeable, iterator_t, O, Comp, Proj1, Proj2> + > {}; + + public: + template< + typename I1, typename S1, + typename I2, typename S2, + typename O, + typename Comp = ranges::less, + typename Proj1 = identity, + typename Proj2 = identity, + std::enable_if_t, sentinel_for, + input_iterator, sentinel_for, + weakly_incrementable, + mergeable + >::value, int> = 0> + constexpr set_intersection_result + operator()(I1 first1, S1 last1, I2 first2, S2 last2, O result, Comp comp = {}, Proj1 proj1 = {}, Proj2 proj2 = {}) const { + while (first1 != last1 && first2 != last2) { + if (vccc::invoke(comp, vccc::invoke(proj1, *first1), vccc::invoke(proj2, *first2))) { + ++first1; + } else if (vccc::invoke(comp, vccc::invoke(proj2, *first2), vccc::invoke(proj1, *first1))) { + ++first2; + } else { + *result = *first1; + ++first1; + ++first2; + ++result; + } + } + return { + ranges::next(std::move(first1), std::move(last1)), + ranges::next(std::move(first2), std::move(last2)), + std::move(result) + }; + } + + template< + typename R1, typename R2, + typename O, + typename Comp = ranges::less, + typename Proj1 = identity, + typename Proj2 = identity, + std::enable_if_t< + check_range + ::value, int> = 0> + constexpr set_intersection_result, borrowed_iterator_t, O> + operator()(R1&& r1, R2&& r2, O result, Comp comp = {}, Proj1 proj1 = {}, Proj2 proj2 = {}) const { + return (*this)(ranges::begin(r1), ranges::end(r1), + ranges::begin(r2), ranges::end(r2), + std::move(result), std::move(comp), + std::move(proj1), std::move(proj2)); + } +}; + +} // namespace detail + +VCCC_INLINE_OR_STATIC constexpr detail::set_intersection_niebloid set_intersection{}; + +/// @} + +} // namespace ranges +} // namespace vccc + +#endif // VCCC_ALGORITHM_RANGES_SET_INTERSECTION_HPP_ diff --git a/include/vccc/__concepts/different_from.hpp b/include/vccc/__concepts/different_from.hpp index 1a7f357e..e192c375 100644 --- a/include/vccc/__concepts/different_from.hpp +++ b/include/vccc/__concepts/different_from.hpp @@ -17,6 +17,12 @@ namespace vccc { template struct different_from : negation< same_as, remove_cvref_t> > {}; +template +struct different_from_this : std::true_type {}; + +template +struct different_from_this : different_from {}; + /// @} } // namespace vccc diff --git a/include/vccc/__expected/bad_expected_access.hpp b/include/vccc/__expected/bad_expected_access.hpp new file mode 100644 index 00000000..093bcc41 --- /dev/null +++ b/include/vccc/__expected/bad_expected_access.hpp @@ -0,0 +1,48 @@ +// +// Created by YongGyu Lee on 3/20/24. +// + +#ifndef VCCC_EXPECTED_BAD_EXPECTED_ACCESS_HPP_ +#define VCCC_EXPECTED_BAD_EXPECTED_ACCESS_HPP_ + +#include +#include + +namespace vccc { + +template +class bad_expected_access; + +template<> +class bad_expected_access : public std::exception { + public: + const char* what() const noexcept override { + return "vccc::bad_expected_access"; + } + + protected: + bad_expected_access() noexcept = default; + bad_expected_access(const bad_expected_access&) = default; + bad_expected_access(bad_expected_access&&) = default; + bad_expected_access& operator=(const bad_expected_access&) = default; + bad_expected_access& operator=(bad_expected_access&&) = default; +}; + +template +class bad_expected_access : public bad_expected_access { + public: + explicit bad_expected_access(E e) + : error_(std::move(e)) {} + + const E& error() const& noexcept { return error_; } + E& error() & noexcept { return error_; } + const E&& error() const&& noexcept { return std::move(error_); } + E&& error() && noexcept { return std::move(error_); } + + private: + E error_; +}; + +} // namespace vccc + +#endif // VCCC_EXPECTED_BAD_EXPECTED_ACCESS_HPP_ diff --git a/include/vccc/__expected/expected.hpp b/include/vccc/__expected/expected.hpp new file mode 100644 index 00000000..cc659f20 --- /dev/null +++ b/include/vccc/__expected/expected.hpp @@ -0,0 +1,1091 @@ +// +// Created by YongGyu Lee on 3/14/24. +// + +#ifndef VCCC_EXPECTED_EXPECTED_HPP_ +#define VCCC_EXPECTED_EXPECTED_HPP_ + +#include +#include +#include + +#include "vccc/__concepts/convertible_to.hpp" +#include "vccc/__concepts/copy_constructible.hpp" +#include "vccc/__concepts/move_constructible.hpp" +#include "vccc/__core/constexpr.hpp" +#include "vccc/__core/nodiscard.hpp" +#include "vccc/__expected/bad_expected_access.hpp" +#include "vccc/__expected/monadic.hpp" +#include "vccc/__expected/unexpected.hpp" +#include "vccc/__expected/unexpect.hpp" +#include "vccc/__functional/invoke.hpp" +#include "vccc/__memory/addressof.hpp" +#include "vccc/__memory/construct_at.hpp" +#include "vccc/__memory/destroy_at.hpp" +#include "vccc/__type_traits/has_typename_value_type.hpp" +#include "vccc/__type_traits/is_specialization.hpp" +#include "vccc/__type_traits/is_swappable.hpp" +#include "vccc/__type_traits/remove_cvref.hpp" +#include "vccc/__utility/as_const.hpp" +#include "vccc/__utility/cxx20_rel_ops.hpp" +#include "vccc/__utility/in_place.hpp" +#include "vccc/variant.hpp" + +namespace vccc { + +template +class expected; + +namespace detail { + +struct void_placeholder_t {}; + +template +using void_placdholder_or_t = std::conditional_t::value, void_placeholder_t, T>; + +template +VCCC_CONSTEXPR_AFTER_CXX20 +void reinit_expected_impl(std::false_type, std::false_type, + NewType& new_val, OldType& old_val, Args&&... args) { + OldType temp(std::move(old_val)); + vccc::destroy_at(vccc::addressof(old_val)); + try { + vccc::construct_at(vccc::addressof(new_val), std::forward(args)...); + } catch (...) { + vccc::construct_at(vccc::addressof(old_val), std::move(temp)); + throw; + } +} + +template +constexpr void reinit_expected_impl(std::false_type, std::true_type, + NewType& new_val, OldType& old_val, Args&&... args) { + NewType temp(std::forward(args)...); + vccc::destroy_at(vccc::addressof(old_val)); + vccc::construct_at(vccc::addressof(new_val), std::move(temp)); +} + +template +constexpr void reinit_expected_impl(std::true_type, Any, + NewType& new_val, OldType& old_val, Args&&... args) { + vccc::destroy_at(vccc::addressof(old_val)); + vccc::construct_at(vccc::addressof(new_val), std::forward(args)...); +} + +template +VCCC_CONSTEXPR_AFTER_CXX20 +void reinit_expected(NewType& new_val, OldType& old_val, Args&&... args) { + reinit_expected_impl( + std::is_nothrow_constructible{}, + std::is_nothrow_move_constructible{}, + new_val, old_val, std::forward(args)...); +} + +template +struct expected_storage_t {}; + +template +using expected_storage = expected_storage_t< + conjunction, std::is_trivially_destructible>::value, T, E>; + +template +struct expected_storage_t { + // Resolution handled in expected + constexpr expected_storage_t() {} + + constexpr explicit expected_storage_t(bool has_value) + : has_value_(has_value) {} + + constexpr explicit expected_storage_t(in_place_index_t<0>, void_placeholder_t) noexcept + : has_value_(true) {} + + template + constexpr explicit expected_storage_t(in_place_index_t<0>, Args&&... args) + noexcept(std::is_nothrow_constructible::value) + : has_value_(true) + , value_(std::forward(args)...) {} + + template + constexpr explicit expected_storage_t(in_place_index_t<0>, std::initializer_list il, Args&&... args) + noexcept(std::is_nothrow_constructible&, Args...>::value) + : has_value_(true) + , value_(il, std::forward(args)...) {} + + template + constexpr explicit expected_storage_t(in_place_index_t<1>, Args&&... args) + noexcept(std::is_nothrow_constructible::value) + : has_value_(false) + , error_(std::forward(args)...) {} + + template + constexpr explicit expected_storage_t(in_place_index_t<1>, std::initializer_list il, Args&&... args) + noexcept(std::is_nothrow_constructible&, Args...>::value) + : has_value_(false) + , error_(il, std::forward(args)...) {} + + bool has_value_; + union { + T value_; + E error_; + }; +}; + +template +struct expected_storage_t { + // Resolution handled in expected + constexpr expected_storage_t() {} + + constexpr explicit expected_storage_t(bool has_value) + : has_value_(has_value) {} + + template + constexpr explicit expected_storage_t(in_place_index_t<0>, Args&&... args) + noexcept(std::is_nothrow_constructible::value) + : has_value_(true) + , value_(std::forward(args)...) {} + + template + constexpr explicit expected_storage_t(in_place_index_t<0>, std::initializer_list il, Args&&... args) + noexcept(std::is_nothrow_constructible&, Args...>::value) + : has_value_(true) + , value_(il, std::forward(args)...) {} + + template + constexpr explicit expected_storage_t(in_place_index_t<1>, Args&&... args) + noexcept(std::is_nothrow_constructible::value) + : has_value_(false) + , error_(std::forward(args)...) {} + + template + constexpr explicit expected_storage_t(in_place_index_t<1>, std::initializer_list il, Args&&... args) + noexcept(std::is_nothrow_constructible&, Args...>::value) + : has_value_(false) + , error_(il, std::forward(args)...) {} + + VCCC_CONSTEXPR_AFTER_CXX20 ~expected_storage_t() noexcept {} + + constexpr expected_storage_t(const expected_storage_t&) = default; + constexpr expected_storage_t(expected_storage_t&&) = default; + constexpr expected_storage_t& operator=(const expected_storage_t&) = default; + constexpr expected_storage_t& operator=(expected_storage_t&&) = default; + + bool has_value_; + union { + T value_; + E error_; + }; +}; + +// T is never void. void_placeholder_t is used instead. +template +struct expected_base { + static_assert(!std::is_void::value, "T should be void_placeholder_t"); + + // Resolution handled in expected + constexpr expected_base() = default; + + constexpr explicit expected_base(bool has_value) + : storage(has_value) {} + + template + constexpr explicit expected_base(in_place_index_t, Args&&... args) + : storage(in_place_index, std::forward(args)...) {} + + template + constexpr explicit expected_base(in_place_index_t, std::initializer_list il, Args&&... args) + : storage(in_place_index, il, std::forward(args)...) {} + + constexpr void construct_from(const expected_base& other) + noexcept(conjunction< + std::is_nothrow_copy_constructible, + std::is_nothrow_copy_constructible + >::value) + { + storage.has_value_ = other.has_value(); + if (other.has_value()) { + construct_value_from(other); + } else { + construct_error_from(other); + } + } + + constexpr void construct_from(expected_base&& other) + noexcept(conjunction< + std::is_nothrow_move_constructible, + std::is_nothrow_move_constructible + >::value) + { + storage.has_value_ = other.has_value(); + if (other.has_value()) { + construct_value_from(std::move(other)); + } else { + construct_error_from(std::move(other)); + } + } + + constexpr void assign_from(const expected_base& other) { + if (has_value() == other.has_value()) { + if (has_value()) { + assign_value_from(other); + } else { + storage.error_ = other.error(); + } + } else { + if (has_value()) { + reinit_error_and_destroy_value(other.error()); + } else { + reinit_value_and_destroy_error_from(other); + } + storage.has_value_ = other.has_value(); + } + } + + constexpr void assign_from(expected_base&& other) { + if (has_value() == other.has_value()) { + if (has_value()) + assign_value_from(std::move(other)); + else + storage.error_ = std::move(other).error(); + } else { + if (has_value()) { + reinit_error_and_destroy_value(std::move(other.error())); + storage.has_value_ = false; + } else { // other.has_value() + reinit_value_and_destroy_error_from(std::move(other)); + storage.has_value_ = true; + } + } + } + + template::value, int> = 0> + constexpr void reinit_value_and_destroy_error() { + destroy_error(); + } + + template::value, int> = 0> + VCCC_CONSTEXPR_AFTER_CXX20 void reinit_value_and_destroy_error(Args&&... args) { + reinit_expected(value(), error(), std::forward(args)...); + } + + template::value, int> = 0> + constexpr void reinit_value_and_destroy_error_from(Other&&) { + destroy_error(); + } + + template::value, int> = 0> + VCCC_CONSTEXPR_AFTER_CXX20 void reinit_value_and_destroy_error_from(Other&& other) { + reinit_value_and_destroy_error(std::forward(other).value()); + } + + template::value, int> = 0> + constexpr void reinit_error_and_destroy_value(Args&&... args) { + construct_error(std::forward(args)...); + } + + template::value, int> = 0> + VCCC_CONSTEXPR_AFTER_CXX20 void reinit_error_and_destroy_value(Args&&... args) { + reinit_expected(error(), value(), std::forward(args)...); + } + + template::value, int> = 0> + constexpr void construct_value(Args&&...) noexcept {} + + template::value, int> = 0> + constexpr void construct_value(Args&&... args) { + vccc::construct_at(vccc::addressof(storage.value_), std::forward(args)...); + } + + template::value, int> = 0> + constexpr void construct_value_from(Other&&) noexcept {} + + template::value, int> = 0> + constexpr void construct_value_from(Other&& other) + noexcept(std::is_nothrow_constructible(other).value())>::value) { + construct_value(std::forward(other).value()); + } + + template::value, int> = 0> + constexpr void assign_value_from(Other&&) noexcept {} + + template::value, int> = 0> + constexpr void assign_value_from(Other&& other) { + storage.value_ = std::forward(other).value(); + } + + template + constexpr void construct_error(G&& e) { + vccc::construct_at(vccc::addressof(storage.error_), std::forward(e)); + } + + template + constexpr void construct_error_from(Other&& other) { + construct_error(std::forward(other).error()); + } + + VCCC_CONSTEXPR_AFTER_CXX20 void destroy() { + has_value() ? destroy_value() : destroy_error(); + } + + template::value, int> = 0> + constexpr void destroy_value() {} + template::value, int> = 0> + VCCC_CONSTEXPR_AFTER_CXX20 void destroy_value() { + value().~T(); + } + + template::value, int> = 0> + constexpr void destroy_error() {} + template::value, int> = 0> + VCCC_CONSTEXPR_AFTER_CXX20 void destroy_error() { + error().~E(); + } + + VCCC_NODISCARD constexpr bool has_value() const noexcept { + return storage.has_value_; + } + + constexpr void has_value(bool new_value) noexcept { + storage.has_value_ = new_value; + } + + constexpr T& value() & noexcept { return storage.value_; } + constexpr const T& value() const& noexcept { return storage.value_; } + constexpr T&& value() && noexcept { return std::move(storage.value_); } + constexpr const T&& value() const&& noexcept { return std::move(storage.value_); } + + constexpr E& error() & noexcept { return storage.error_; } + constexpr const E& error() const& noexcept { return storage.error_; } + constexpr E&& error() && noexcept { return std::move(storage.error_); } + constexpr const E&& error() const&& noexcept { return std::move(storage.error_); } + + expected_storage storage; +}; + +template +struct expected_nontrivial_dtor : expected_base { + using base = expected_base; + using base::base; + + VCCC_CONSTEXPR_AFTER_CXX20 ~expected_nontrivial_dtor() { + base::destroy(); + } + + expected_nontrivial_dtor() = default; + expected_nontrivial_dtor(const expected_nontrivial_dtor&) = default; + expected_nontrivial_dtor(expected_nontrivial_dtor&&) = default; + expected_nontrivial_dtor& operator=(const expected_nontrivial_dtor&) = default; + expected_nontrivial_dtor& operator=(expected_nontrivial_dtor&&) = default; +}; + +template +using expected_control_smf = + std::conditional_t< + conjunction, std::is_trivially_destructible>::value, + control_special, T, E>, + control_special, T, E> + >; + + + +} // namespace detail + +template +class expected : private detail::expected_control_smf, E> { + using base = detail::expected_control_smf, E>; + + // monadic functors + using and_then_t = detail::expected_and_then_t; + using transform_t = detail::expected_transform_t; + using or_else_t = detail::expected_or_else_t; + using transform_error_t = detail::expected_transform_error_t; + + // swap exception + using value_nothrow_swappable = conjunction< + detail::void_or, + detail::void_or>; + using error_nothrow_swappable = conjunction< + std::is_nothrow_move_constructible, + is_nothrow_swappable>; + using nothrow_swappable = conjunction; + + public: + static_assert(!std::is_reference::value, "Constraints not satisfied"); + static_assert(!std::is_array::value, "Constraints not satisfied"); + static_assert(!std::is_function::value, "Constraints not satisfied"); + static_assert(!is_specialization::value, "Constraints not satisfied"); + static_assert(!std::is_same::value, "Constraints not satisfied"); + static_assert(!std::is_same::value, "Constraints not satisfied"); + + using value_type = T; + using error_type = E; + using unexpected_type = unexpected; + + template + using rebind = expected; + + template, + detail::void_or + >::value, int> = 0> + constexpr expected() : base(in_place_index<0>) {} + + constexpr expected(const expected& other) = default; + constexpr expected(expected&& other) = default; + + template, T>, + std::is_convertible>>, + disjunction< + conjunction, std::is_void>, + std::is_constructible> + >, + std::is_constructible + >::value, int> = 0> + constexpr explicit expected(const expected& other) + : base(other.has_value()) + { + if (other.has_value()) { + base::construct_value_from(other); + } else { + base::construct_error_from(other); + } + } + + template, T>, + std::is_convertible>, + disjunction< + conjunction, std::is_void>, + std::is_constructible> + >, + std::is_constructible + >::value, int> = 0> + constexpr expected(const expected& other) + : base(other.has_value()) + { + if (other.has_value()) { + base::construct_value_from(other); + } else { + base::construct_error_from(other); + } + } + + template, + std::is_convertible>>, + disjunction< + conjunction, std::is_void>, + std::is_constructible + >, + std::is_constructible + >::value, int> = 0> + constexpr explicit expected(expected&& other) + : base(other.has_value()) + { + if (other.has_value()) { + base::construct_value_from(std::move(other)); + } else { + base::construct_error_from(std::move(other)); + } + } + + template, + std::is_convertible>, + disjunction< + conjunction, std::is_void>, + std::is_constructible + >, + std::is_constructible + >::value, int> = 0> + constexpr expected(expected&& other) + : base(other.has_value()) + { + if (other.has_value()) { + base::construct_value_from(std::move(other)); + } else { + base::construct_error_from(std::move(other)); + } + } + + template>, + negation, in_place_t>>, + negation>>, + std::is_constructible, + negation, unexpected>>, + disjunction< + negation>>, + negation, expected>> + >, + negation> + >::value, int> = 0> + constexpr explicit expected(U&& v) + : base(in_place_index<0>, std::forward(v)) {} + + template>, + negation, in_place_t>>, + negation>>, + std::is_constructible, + negation, unexpected>>, + disjunction< + negation>>, + negation, expected>> + >, + std::is_convertible + >::value, int> = 0> + constexpr expected(U&& v) + : base(in_place_index<0>, std::forward(v)) {} + + template, + negation> + >::value, int> = 0> + constexpr explicit expected(const unexpected& e) + : base(in_place_index<1>, e.error()) {} + + template, + std::is_convertible + >::value, int> = 0> + constexpr expected(const unexpected& e) + : base(in_place_index<1>, e.error()) {} + + template, + negation> + >::value, int> = 0> + constexpr explicit expected(unexpected&& e) + : base(in_place_index<1>, std::move(e.error())) {} + + template, + std::is_convertible + >::value, int> = 0> + constexpr expected(unexpected&& e) + : base(in_place_index<1>, std::move(e.error())) {} + + template>, + std::is_constructible + >::value, int> = 0> + constexpr explicit expected(in_place_t, Args&&... args) + : base(in_place_index<0>, std::forward(args)...) {} + + template>, + std::is_constructible&, Args...> + >::value, int> = 0> + constexpr explicit expected(in_place_t, std::initializer_list il, Args&&... args) + : base(in_place_index<0>, il, std::forward(args)...) {} + + template::value, int> = 0> + constexpr explicit expected(in_place_t) noexcept + : base(in_place_index<0>) {} + + template + ::value, int> = 0> + constexpr explicit expected(unexpect_t, Args&&... args) + : base(in_place_index<1>, std::forward(args)...) {} + + template&, Args...> + ::value, int> = 0> + constexpr explicit expected(unexpect_t, std::initializer_list il, Args&&... args) + : base(in_place_index<1>, il, std::forward(args)...) {} + + constexpr expected& operator=(const expected&) = default; + constexpr expected& operator=(expected&&) = default; + + template>, + negation>>, + negation, unexpected>>, + std::is_constructible, + std::is_assignable, U>, + disjunction< + std::is_nothrow_constructible, + std::is_nothrow_move_constructible, + std::is_nothrow_move_constructible + > + >::value, int> = 0> + constexpr expected& operator=(U&& v) { + if (has_value()) { + **this = std::forward(v); + } else { + base::reinit_value_and_destroy_error(std::forward(v)); + base::has_value(true); + } + return *this; + } + + template, + std::is_assignable, + disjunction< + std::is_void, + std::is_nothrow_constructible, + std::is_nothrow_move_constructible, + std::is_nothrow_move_constructible + > + >::value, int> = 0> + constexpr expected& operator=(const unexpected& e) { + if (has_value()) { + base::reinit_error_and_destroy_value(e.error()); + base::has_value(false); + } else { + error() = e.error(); + } + + return *this; + } + + template, + std::is_assignable, + disjunction< + std::is_void, + std::is_nothrow_constructible, + std::is_nothrow_move_constructible, + std::is_nothrow_move_constructible + > + >::value, int> = 0> + constexpr expected& operator=(unexpected&& e) { + if (has_value()) { + base::reinit_error_and_destroy_value(std::move(e.error())); + base::has_value(false); + } else { + error() = std::move(e.error()); + } + + return *this; + } + + template::value, int> = 0> + constexpr const T* operator->() const noexcept { + return vccc::addressof(base::value()); + } + + template::value, int> = 0> + constexpr T* operator->() noexcept { + return vccc::addressof(base::value()); + } + + template::value, int> = 0> + constexpr const U& operator*() const& noexcept { + return base::value(); + } + + template::value, int> = 0> + constexpr U& operator*() & noexcept { + return base::value(); + } + + template::value, int> = 0> + constexpr const U&& operator*() const&& noexcept { + return std::move(base::value()); + } + + template::value, int> = 0> + constexpr U&& operator*() && noexcept { + return std::move(base::value()); + } + + template::value, int> = 0> + constexpr void operator*() const noexcept {} + + VCCC_NODISCARD bool has_value() const noexcept { + return base::has_value(); + } + + constexpr explicit operator bool() const noexcept { + return has_value(); + } + + template::value, int> = 0> + constexpr U& value() & { + check_throw(); + return **this; + } + + template::value, int> = 0> + constexpr const U& value() const & { + check_throw(); + return **this; + } + + template::value, int> = 0> + constexpr U&& value() && { + check_throw(); + return std::move(**this); + } + + template::value, int> = 0> + constexpr const U&& value() const && { + check_throw(); + return std::move(**this); + } + + template::value, int> = 0> + constexpr void value() const & { + check_throw(); + } + + template::value, int> = 0> + constexpr void value() && { + check_throw(); + } + + constexpr const E& error() const& noexcept { return base::error(); } + constexpr E& error() & noexcept { return base::error(); } + constexpr const E&& error() const&& noexcept { return std::move(base::error()); } + constexpr E&& error() && noexcept { return std::move(base::error()); } + + template>, + copy_constructible, + convertible_to + >::value, int> = 0> + constexpr T value_or(U&& default_value) const & { + return bool(*this) ? **this : static_cast(std::forward(default_value)); + } + + template>, + move_constructible, + convertible_to + >::value, int> = 0> + constexpr T value_or(U&& default_value) && { + return bool(*this) ? std::move(**this) : static_cast(std::forward(default_value)); + } + + template, + is_invocable + >::value, int> = 0> + constexpr auto and_then(F&& f) & { + return and_then_t{}(std::forward(f), *this); + } + + template, + is_invocable + >::value, int> = 0> + constexpr auto and_then(F&& f) const & { + return and_then_t{}(std::forward(f), *this); + } + + template, + is_invocable + >::value, int> = 0> + constexpr auto and_then(F&& f) && { + return and_then_t{}(std::forward(f), std::move(*this)); + } + + template, + is_invocable + >::value, int> = 0> + constexpr auto and_then(F&& f) const && { + return and_then_t{}(std::forward(f), std::move(*this)); + } + + template, + is_invocable + >::value, int> = 0> + constexpr auto transform(F&& f) & { + return transform_t{}(std::forward(f), *this); + } + + template, + is_invocable + >::value, int> = 0> + constexpr auto transform(F&& f) const & { + return transform_t{}(std::forward(f), *this); + } + + template, + is_invocable + >::value, int> = 0> + constexpr auto transform(F&& f) && { + return transform_t{}(std::forward(f), std::move(*this)); + } + + template, + is_invocable + >::value, int> = 0> + constexpr auto transform(F&& f) const && { + return transform_t{}(std::forward(f), std::move(*this)); + } + + template>, + is_invocable + >::value, int> = 0> + constexpr auto or_else(F&& f) & { + return or_else_t{}(std::forward(f), *this); + } + + template>>, + is_invocable + >::value, int> = 0> + constexpr auto or_else(F&& f) const & { + return or_else_t{}(std::forward(f), *this); + } + + template>, + is_invocable + >::value, int> = 0> + constexpr auto or_else(F&& f) && { + return or_else_t{}(std::forward(f), std::move(*this)); + } + + template>>, + is_invocable + >::value, int> = 0> + constexpr auto or_else(F&& f) const && { + return or_else_t{}(std::forward(f), std::move(*this)); + } + + template>, + is_invocable + >::value, int> = 0> + constexpr auto transform_error(F&& f) & { + return transform_error_t{}(std::forward(f), *this); + } + + template>>, + is_invocable + >::value, int> = 0> + constexpr auto transform_error(F&& f) const & { + return transform_error_t{}(std::forward(f), *this); + } + + template>, + is_invocable + >::value, int> = 0> + constexpr auto transform_error(F&& f) && { + return transform_error_t{}(std::forward(f), std::move(*this)); + } + + template>>, + is_invocable + >::value, int> = 0> + constexpr auto transform_error(F&& f) const && { + return transform_error_t{}(std::forward(f), std::move(*this)); + } + + template>, + std::is_nothrow_constructible + >::value, int> = 0> + constexpr std::add_lvalue_reference_t emplace(Args&&... args) noexcept { + if (has_value()) { + detail::reinit_expected(value(), value(), std::forward(args)...); + } else { + base::reinit_value_and_destroy_error(std::forward(args)...); + } + return value(); + } + + template>, + std::is_nothrow_constructible&, Args...> + >::value, int> = 0> + constexpr std::add_lvalue_reference_t emplace(std::initializer_list il, Args&&... args) noexcept { + if (has_value()) { + detail::reinit_expected(value(), value(), il, std::forward(args)...); + } else { + base::reinit_value_and_destroy_error(il, std::forward(args)...); + } + return value(); + } + + template, + std::is_void + >::value, int> = 0> + constexpr void emplace() noexcept { + if(has_value()) + base::destroy_value(); + } + + template, + detail::void_or, + is_swappable, + detail::void_or, + std::is_move_constructible, + disjunction< + detail::void_or, + std::is_nothrow_move_constructible + > + >::value, int> = 0> + constexpr void swap(expected& other) noexcept(nothrow_swappable::value) { + if (has_value() == other.has_value()) { + using std::swap; + using vccc::swap; + has_value() ? swap_value(**this, *other) : swap(error(), other.error()); + } else { + has_value() ? swap_value_with_error(*this, other) : swap_value_with_error(other, *this); + } + } + + template>, + negation>, + rel_ops::is_equality_comparable< + std::add_lvalue_reference_t>, + std::add_lvalue_reference_t>>, + rel_ops::is_equality_comparable + >::value, int> = 0> + friend constexpr bool operator==(const expected& lhs, const expected& rhs) { + if (lhs.has_value() != rhs.has_value()) + return false; + using namespace rel_ops; + return lhs.has_value() ? *lhs == *rhs : lhs.error() == rhs.error(); + } + + template, + std::is_void, + rel_ops::is_equality_comparable + >::value, int> = 0> + friend constexpr bool operator==(const expected& lhs, const expected& rhs) { + if (lhs.has_value() != rhs.has_value()) + return false; + using namespace rel_ops; + return lhs.has_value() ? true : lhs.error() == rhs.error(); + } + + template&>::value, int> = 0> + friend constexpr bool operator!=(const expected& lhs, const expected& rhs) { + return !(lhs == rhs); + } + + template>, + rel_ops::is_equality_comparable>, const T2&> + >::value, int> = 0> + friend constexpr bool operator==(const expected& x, const T2& y) { + using namespace rel_ops; + return x.has_value() && static_cast(*x == y); + } + + template::value, int> = 0> + friend constexpr bool operator!=(const expected& x, const T2& y) { + return !(x == y); + } + + template::value, int> = 0> + friend constexpr bool operator==(const T2& y, const expected& x) { + return x == y; + } + + template::value, int> = 0> + friend constexpr bool operator!=(const T2& y, const expected& x) { + return !(x == y); + } + + template::value, int> = 0> + friend constexpr bool operator==(const expected& x, const unexpected& e) { + using namespace rel_ops; + return !x.has_value() && static_cast(x.error() == e.error()); + } + + template::value, int> = 0> + friend constexpr bool operator!=(const expected& x, const unexpected& e) { + return !(x == e); + } + + template::value, int> = 0> + friend constexpr bool operator==(const unexpected& e, const expected& x) { + return x == e; + } + + template::value, int> = 0> + friend constexpr bool operator!=(const unexpected& e, const expected& x) { + return !(x == e); + } + + private: + static constexpr + void swap_value(expected&, expected&, std::true_type /* void */) noexcept {} + + static constexpr + void swap_value(expected& thiz, expected& other, std::false_type /* void */) noexcept(value_nothrow_swappable::value) { + using std::swap; + using vccc::swap; + swap(*thiz, *other); + } + + static VCCC_CONSTEXPR_AFTER_CXX17 + void swap_value_with_error(expected& thiz, expected& other) { + expected::swap_value_with_error(thiz, other, std::is_void{}, std::is_nothrow_move_constructible{}); + } + + template + static VCCC_CONSTEXPR_AFTER_CXX17 + void swap_value_with_error(expected& thiz, expected& other, std::true_type /* void */, Any) noexcept(error_nothrow_swappable::value) { + vccc::construct_at(std::addressof(thiz.error()), std::move(other.error())); + vccc::destroy_at(std::addressof(other.error())); + } + + static VCCC_CONSTEXPR_AFTER_CXX23 + void swap_value_with_error(expected& thiz, expected& other, std::false_type /* void */, std::true_type /* nothrow */) noexcept(nothrow_swappable::value) { + E temp(std::move(other.error())); + vccc::destroy_at(std::addressof(other.error())); + try { + vccc::construct_at(std::addressof(other.value()), std::move(thiz.value())); + vccc::destroy_at(std::addressof(thiz.value())); + vccc::construct_at(std::addressof(thiz.error()), std::move(temp)); + } catch (...) { + vccc::construct_at(std::addressof(other.error()), std::move(temp)); + throw; + } + } + + static VCCC_CONSTEXPR_AFTER_CXX23 + void swap_value_with_error(expected& thiz, expected& other, std::false_type /* void */, std::false_type /* nothrow */) noexcept(nothrow_swappable::value) { + T temp(std::move(thiz.value())); + vccc::destroy_at(std::addressof(thiz.value())); + try { + vccc::construct_at(std::addressof(thiz.error()), std::move(other.error())); + vccc::destroy_at(std::addressof(other.error())); + vccc::construct_at(std::addressof(other.value()), std::move(temp)); + } catch (...) { + vccc::construct_at(std::addressof(thiz.value()), std::move(temp)); + throw; + } + } + + constexpr void check_throw() & { + if (!has_value()) + throw bad_expected_access>(vccc::as_const(error())); + } + constexpr void check_throw() && { + if (!has_value()) + throw bad_expected_access>(std::move(error())); + } +}; + +} // namespace vccc + +#endif // VCCC_EXPECTED_EXPECTED_HPP_ diff --git a/include/vccc/__expected/monadic.hpp b/include/vccc/__expected/monadic.hpp new file mode 100644 index 00000000..c65aba97 --- /dev/null +++ b/include/vccc/__expected/monadic.hpp @@ -0,0 +1,230 @@ +// +// Created by YongGyu Lee on 3/28/24. +// + +#ifndef VCCC_EXPECTED_MONADIC_HPP_ +#define VCCC_EXPECTED_MONADIC_HPP_ + +#include +#include + +#include "vccc/__expected/unexpect.hpp" +#include "vccc/__expected/unexpected.hpp" +#include "vccc/__functional/invoke.hpp" +#include "vccc/__type_traits/conjunction.hpp" +#include "vccc/__type_traits/is_invocable.hpp" +#include "vccc/__type_traits/is_specialization.hpp" +#include "vccc/__type_traits/negation.hpp" +#include "vccc/__type_traits/remove_cvref.hpp" +#include "vccc/__utility/in_place.hpp" + +namespace vccc { +namespace detail { + +template class Test, typename... U> +struct void_or_impl : std::true_type {}; + +template class Test, typename... U> +struct void_or_impl : Test {}; + +template class Test, typename... U> +using void_or = void_or_impl::value, T, Test, U...>; + +template +struct has_typename_error_type : std::false_type {}; + +template +struct has_typename_error_type> : std::true_type {}; + +template +struct expected_valid_value_type + : conjunction< + negation>, + negation>, + negation>, + negation>, + negation, in_place_t>>, + negation, unexpect_t>> + > {}; + +template +struct expected_valid_error_type + : conjunction< + std::is_object, + negation>, + negation>, + negation>, + negation> + >{}; + +template +struct expected_is_void : std::is_void::value_type> {}; + +template::value /* true */> +struct expected_value_invocable + : is_invocable {}; + +template +struct expected_value_invocable + : is_invocable())> {}; + +template +struct expected_error_invocable + : is_invocable().error())>{}; + +template::value /* true */> +struct expected_value_invoke_result { + using type = invoke_result_t; +}; + +template +struct expected_value_invoke_result { + using type = invoke_result_t())>; +}; + +template +using expected_value_invoke_result_t = typename expected_value_invoke_result::type; + +template +using expected_error_invoke_result_t = invoke_result_t().error())>; + +template::value == true, int> = 0> +constexpr decltype(auto) expected_value_invoke(F&& f, Expected&&) { + return vccc::invoke(std::forward(f)); +} + +template::value == false, int> = 0> +constexpr decltype(auto) expected_value_invoke(F&& f, Expected&& e) { + return vccc::invoke(std::forward(f), *std::forward(e)); +} + +template +class expected_and_then_t; + +template class Expected, typename T, typename E> +class expected_and_then_t> { + template + using U = remove_cvref_t>; + + public: + template, + is_specialization, Expected>, + has_typename_error_type>, + std::is_same::error_type, E> + >::value, int> = 0> + constexpr auto operator()(F&& f, Self&& self) const { + if (self.has_value()) { + return expected_value_invoke(std::forward(f), std::forward(self)); + } else { + return U(unexpect, std::forward(self).error()); + } + } +}; + +template +class expected_transform_t; + +template class Expected, typename T, typename E> +class expected_transform_t> { + template + using U = std::remove_cv_t>; + + template + constexpr auto valued(F&& f, Self&& self, std::true_type /* void */) const { + expected_value_invoke(std::forward(f), std::forward(self)); + return Expected, E>(); + } + + template + constexpr auto valued(F&& f, Self&& self, std::false_type /* void */) const { + return Expected, E>(expected_value_invoke(std::forward(f), std::forward(self))); + } + + public: + template, + expected_valid_value_type>, + void_or, expected_value_invoke_result_t> + >::value, int> = 0> + constexpr auto operator()(F&& f, Self&& self) const { + if (self.has_value()) { + return valued(std::forward(f), std::forward(self), std::is_void{}); + } else { + return Expected, E>(unexpect, std::forward(self).error()); + } + } +}; + +template +class expected_or_else_t; + +template class Expected, typename T, typename E> +class expected_or_else_t> { + template + using G = remove_cvref_t>; + + template + constexpr auto valued(F&&, Self&&, std::true_type /* void */) const { + return G(); + } + + template + constexpr auto valued(F&&, Self&& self, std::false_type /* void */) const { + return G(in_place, *std::forward(self)); + } + + public: + template, + is_specialization, Expected>, + std::is_same::value_type, T> + >::value, int> = 0> + constexpr auto operator()(F&& f, Self&& self) const { + if (self.has_value()) { + return valued(std::forward(f), std::forward(self), std::is_void{}); + } else { + return vccc::invoke(std::forward(f), std::forward(self).error()); + } + } +}; + +template +class expected_transform_error_t; + +template class Expected, typename T, typename E> +class expected_transform_error_t> { + template + using G = std::remove_cv_t>; + + template + constexpr auto valued(F&&, Self&&, std::true_type) const { + return Expected>(); + } + + template + constexpr auto valued(F&&, Self&& e, std::false_type) const { + return Expected>(in_place, *std::forward(e)); + } + + public: + template, + expected_valid_error_type>, + std::is_constructible, expected_error_invoke_result_t> + >::value, int> = 0> + constexpr auto operator()(F&& f, Self&& self) const { + if (self.has_value()) { + return valued(std::forward(f), std::forward(self), std::is_void{}); + } else { + return Expected>(unexpect, vccc::invoke(std::forward(f), std::forward(self).error())); + } + } +}; + +} // namespace detail +} // namespace vccc + +#endif // VCCC_EXPECTED_MONADIC_HPP_ diff --git a/include/vccc/__expected/unexpect.hpp b/include/vccc/__expected/unexpect.hpp new file mode 100644 index 00000000..75e8fced --- /dev/null +++ b/include/vccc/__expected/unexpect.hpp @@ -0,0 +1,37 @@ +// +// Created by YongGyu Lee on 3/14/24. +// + +#ifndef VCCC_EXPECTED_UNEXPECT_HPP_ +#define VCCC_EXPECTED_UNEXPECT_HPP_ + +#if __cplusplus >= 202302L + +#include + +#else + +#include "vccc/__core/inline_or_static.hpp" + +#endif + +namespace vccc { + +#if __cplusplus >= 202302L + +using unexpect_t = std::unexpect_t; +inline constexpr unexpect_t unexpect{}; + +#else + +struct unexpect_t { + explicit unexpect_t() = default; +}; + +VCCC_INLINE_OR_STATIC constexpr unexpect_t unexpect{}; + +#endif + +} // namespace vccc + +#endif // VCCC_EXPECTED_UNEXPECT_HPP_ diff --git a/include/vccc/__expected/unexpected.hpp b/include/vccc/__expected/unexpected.hpp new file mode 100644 index 00000000..4c7db6ee --- /dev/null +++ b/include/vccc/__expected/unexpected.hpp @@ -0,0 +1,100 @@ +// +// Created by YongGyu Lee on 3/14/24. +// + +#ifndef VCCC_EXPECTED_UNEXPECTED_HPP_ +#define VCCC_EXPECTED_UNEXPECTED_HPP_ + +#include +#include + +#include "vccc/__concepts/equality_comparable.hpp" +#include "vccc/__type_traits/conjunction.hpp" +#include "vccc/__type_traits/is_swappable.hpp" +#include "vccc/__type_traits/is_specialization.hpp" +#include "vccc/__type_traits/negation.hpp" +#include "vccc/__type_traits/remove_cvref.hpp" +#include "vccc/__utility/in_place.hpp" + +namespace vccc { + +template +class unexpected { + public: + static_assert(std::is_object::value, "Constraints not satisfied"); + static_assert(!std::is_array::value, "Constraints not satisfied"); + static_assert(!std::is_const::value, "Constraints not satisfied"); + static_assert(!std::is_volatile::value, "Constraints not satisfied"); + static_assert(!is_specialization::value, "Constraints not satisfied"); + + constexpr unexpected(const unexpected&) = default; + constexpr unexpected(unexpected&&) = default; + + template, unexpected>>, + negation, in_place_t>>, + std::is_constructible + >::value, int> = 0> + constexpr explicit unexpected(Err&& e) + : error_(std::forward(e)) {} + + template + ::value, int> = 0> + constexpr explicit unexpected(in_place_t, Args&&... args) + : error_(std::forward(args)...) {} + + template&, Args...> + ::value, int> = 0> + constexpr explicit unexpected(in_place_t, std::initializer_list il, Args&&... args) + : error_(il, std::forward(args)...) {} + + constexpr unexpected& operator=(const unexpected&) = default; + constexpr unexpected& operator=(unexpected&&) = default; + + constexpr const E& error() const& noexcept { + return error_; + } + constexpr E& error() & noexcept { + return error_; + } + constexpr const E&& error() const&& noexcept { + return std::move(error_); + } + constexpr E&& error() && noexcept { + return std::move(error_); + } + + template::value, int> = 0> + constexpr void swap(unexpected& other) noexcept(is_nothrow_swappable::value) { + using std::swap; + swap(error(), other.error()); + } + + template::value, int> = 0> + friend constexpr bool operator==(unexpected& x, unexpected& y) { + return x.error() == y.error(); + } + + template::value, int> = 0> + friend constexpr bool operator!=(unexpected& x, unexpected& y) { + return !(x == y); + } + + // TODO: Implement friend swap + + private: + E error_; +}; + +#if __cplusplus >= 201703L + +template +unexpected(E) -> unexpected; + +#endif + +} // namespace vccc + +#endif // VCCC_EXPECTED_UNEXPECTED_HPP_ diff --git a/include/vccc/__memory/construct_at.hpp b/include/vccc/__memory/construct_at.hpp new file mode 100644 index 00000000..101ebb66 --- /dev/null +++ b/include/vccc/__memory/construct_at.hpp @@ -0,0 +1,42 @@ +// +// Created by YongGyu Lee on 3/20/24. +// + +#ifndef VCCC_MEMORY_CONSTRUCT_AT_HPP_ +#define VCCC_MEMORY_CONSTRUCT_AT_HPP_ + +#include +#include + +#include "vccc/__core/constexpr.hpp" +#include "vccc/__utility/type_sequence.hpp" + +namespace vccc { +namespace detail { + +template +struct in_place_constructible_impl : std::false_type {}; + +template +struct in_place_constructible_impl, + void_t()) T(std::declval()...))>> : std::true_type {}; + +template +struct in_place_constructible + : in_place_constructible_impl> {}; + +} // namespace detail + +/// @addtogroup memory +/// @{ + +template::value, int> = 0> +constexpr T* construct_at(T* p, Args&&... args) noexcept(std::is_nothrow_constructible::value) { + return ::new (static_cast(p)) T(std::forward(args)...); +} + +/// @} + +} // namespace vccc + +#endif // VCCC_MEMORY_CONSTRUCT_AT_HPP_ diff --git a/include/vccc/__memory/destroy.hpp b/include/vccc/__memory/destroy.hpp new file mode 100644 index 00000000..ae00eb69 --- /dev/null +++ b/include/vccc/__memory/destroy.hpp @@ -0,0 +1,24 @@ +// +// Created by YongGyu Lee on 3/20/24. +// + +#ifndef VCCC_MEMORY_DESTROY_HPP_ +#define VCCC_MEMORY_DESTROY_HPP_ + +#include "vccc/__core/constexpr.hpp" +#include "vccc/__memory/addressof.hpp" +#include "vccc/__memory/destroy_at.hpp" + +namespace vccc { + +template +VCCC_CONSTEXPR_AFTER_CXX20 void destroy(ForwardIt first, ForwardIt last) { + for (; first != last; ++first) { + vccc::destroy_at(vccc::addressof(*first)); + } +} + + +} // namespace vccc + +#endif // VCCC_MEMORY_DESTROY_HPP_ diff --git a/include/vccc/__memory/destroy_at.hpp b/include/vccc/__memory/destroy_at.hpp new file mode 100644 index 00000000..7ccc9582 --- /dev/null +++ b/include/vccc/__memory/destroy_at.hpp @@ -0,0 +1,32 @@ +// +// Created by YongGyu Lee on 3/20/24. +// + +#ifndef VCCC_MEMORY_DESTROY_AT_HPP_ +#define VCCC_MEMORY_DESTROY_AT_HPP_ + +#include + +#include "vccc/__core/constexpr.hpp" +#include "vccc/__memory/addressof.hpp" + +namespace vccc { + +template ::value, int> = 0> +VCCC_CONSTEXPR_AFTER_CXX20 void destroy_at(T* p) { + p->~T(); +} + +template ::value, int> = 0> +VCCC_CONSTEXPR_AFTER_CXX20 void destroy_at(T* p) { + auto first = std::begin(*p); + auto last = std::end(*p); + + for (; first != last; ++first) { + vccc::destroy_at(vccc::addressof(*first)); + } +} + +} // namespace vccc + +#endif // VCCC_MEMORY_DESTROY_AT_HPP_ diff --git a/include/vccc/__memory/pointer_traits.hpp b/include/vccc/__memory/pointer_traits.hpp new file mode 100644 index 00000000..1e856ca6 --- /dev/null +++ b/include/vccc/__memory/pointer_traits.hpp @@ -0,0 +1,105 @@ +// +// Created by YongGyu Lee on 3/7/24. +// + +#ifndef VCCC_MEMORY_POINTER_TRAITS_HPP +#define VCCC_MEMORY_POINTER_TRAITS_HPP + +#include + +#include "vccc/__core/constexpr.hpp" +#include "vccc/__memory/addressof.hpp" +#include "vccc/__type_traits/has_typename_difference_type.hpp" +#include "vccc/__type_traits/has_typename_element_type.hpp" +#include "vccc/__type_traits/has_typename_type.hpp" +#include "vccc/__type_traits/void_t.hpp" + +namespace vccc { +namespace detail { + +template::value> +struct pointer_traits_element_type {}; + +template +struct pointer_traits_element_type { + using type = typename Ptr::element_type; +}; + +template class Ptr, typename T, typename... Args> +struct pointer_traits_element_type, false> { + using type = T; +}; + +template::value /* true */> +struct pointer_traits_difference_type { + using type = typename Ptr::difference_type; +}; + +template +struct pointer_traits_difference_type { + using type = std::ptrdiff_t; +}; + +template +struct has_rebind : std::false_type {}; +template +struct has_rebind>> : std::true_type {}; + +template::value> +struct pointer_traits_rebind; + +template +struct pointer_traits_rebind { + using type = typename Ptr::template rebind; +}; + +template class Ptr, typename U, typename T, typename... Args> +struct pointer_traits_rebind, U, false> { + using type = Ptr; +}; + +template>::value /* true */> +struct pointer_traits_impl { + using pointer = Ptr; + using element_type = typename pointer_traits_element_type::type; + using difference_type = typename pointer_traits_difference_type::type; + + template + using rebind = typename pointer_traits_rebind::type; + + static pointer pointer_to(element_type& r) { + return Ptr::pointer_to(r); + } +}; + +template +struct pointer_traits_impl {}; + +} // namespace detail + +/// @addtogroup memory +/// @{ + +template +struct pointer_traits + : detail::pointer_traits_impl {}; + +template +struct pointer_traits { + using pointer = T*; + using element_type = T; + using difference_type = std::ptrdiff_t; + + template + using rebind = U*; + + static VCCC_CONSTEXPR_AFTER_CXX17 pointer pointer_to(element_type& r) noexcept { + return vccc::addressof(r); + } +}; + +/// @} + +} // namespace vccc + +#endif // VCCC_MEMORY_POINTER_TRAITS_HPP diff --git a/include/vccc/__memory/to_address.hpp b/include/vccc/__memory/to_address.hpp index 1dc1962d..34b6b1d8 100644 --- a/include/vccc/__memory/to_address.hpp +++ b/include/vccc/__memory/to_address.hpp @@ -8,6 +8,7 @@ #include #include +#include "vccc/__memory/pointer_traits.hpp" #include "vccc/__type_traits/has_operator_arrow.hpp" #include "vccc/__type_traits/void_t.hpp" @@ -18,11 +19,11 @@ template struct has_to_address : std::false_type {}; template -struct has_to_address::to_address(std::declval()))>> : std::true_type {}; +struct has_to_address::to_address(std::declval()))>> : std::true_type {}; template constexpr auto to_address_fancy(const T& p, std::true_type /* has_to_address */ ) noexcept { - return std::pointer_traits::to_address(p); + return vccc::pointer_traits::to_address(p); } template constexpr auto to_address_fancy(const T& p, std::false_type /* has_to_address */ ) noexcept; diff --git a/include/vccc/__ranges/__forward_declare.hpp b/include/vccc/__ranges/__forward_declare.hpp index 4bc83c70..2d1529b8 100644 --- a/include/vccc/__ranges/__forward_declare.hpp +++ b/include/vccc/__ranges/__forward_declare.hpp @@ -13,7 +13,7 @@ template struct borrowed_range; template struct sized_range; template struct view; template struct input_range; -template struct output_range; +template struct output_range; template struct forward_range; template struct bidirectional_range; template struct random_access_range; diff --git a/include/vccc/__ranges/as_const_pointer.hpp b/include/vccc/__ranges/as_const_pointer.hpp new file mode 100644 index 00000000..a2127e2f --- /dev/null +++ b/include/vccc/__ranges/as_const_pointer.hpp @@ -0,0 +1,19 @@ +// +// Created by YongGyu Lee on 3/27/24. +// + +#ifndef VCCC_RANGES_AS_CONST_POINTER_HPP_ +#define VCCC_RANGES_AS_CONST_POINTER_HPP_ + +namespace vccc { +namespace ranges { + +template +constexpr auto as_const_pointer(const T* p) noexcept { + return p; +} + +} // namespace ranges +} // namespace vccc + +#endif // VCCC_RANGES_AS_CONST_POINTER_HPP_ diff --git a/include/vccc/__ranges/cbegin.hpp b/include/vccc/__ranges/cbegin.hpp index 46f87372..b27366fe 100644 --- a/include/vccc/__ranges/cbegin.hpp +++ b/include/vccc/__ranges/cbegin.hpp @@ -10,11 +10,9 @@ #include "vccc/__core/inline_or_static.hpp" #include "vccc/__iterator/basic_const_iterator.hpp" #include "vccc/__ranges/begin.hpp" -#include "vccc/__ranges/constant_range.hpp" #include "vccc/__ranges/enable_borrowed_range.hpp" -#include "vccc/__type_traits/detail/tag.hpp" +#include "vccc/__ranges/possibly_const_range.hpp" #include "vccc/__type_traits/disjunction.hpp" -#include "vccc/__utility/as_const.hpp" namespace vccc { namespace ranges { @@ -26,22 +24,7 @@ struct cbegin_niebloid { enable_borrowed_range> >::value, int> = 0> constexpr auto operator()(T&& t) const { - using tag = conditional_tag, constant_range>; - return this->call(std::forward(t), tag{}); - } - - private: - template - constexpr auto call(T&& t, vccc::detail::tag_1) const { - return ranges::begin(t); - } - template - constexpr auto call(T&& t, vccc::detail::tag_2) const { - return ranges::begin(vccc::as_const(t)); - } - template - constexpr auto call(T&& t, vccc::detail::tag_else) const { - return make_const_iterator(ranges::begin(t)); + return vccc::const_iterator(ranges::begin(vccc::ranges::possibly_const_range(t))); } }; diff --git a/include/vccc/__ranges/cdata.hpp b/include/vccc/__ranges/cdata.hpp new file mode 100644 index 00000000..68665996 --- /dev/null +++ b/include/vccc/__ranges/cdata.hpp @@ -0,0 +1,49 @@ +// +// Created by YongGyu Lee on 3/27/24. +// + +#ifndef VCCC_RANGES_CDATA_HPP_ +#define VCCC_RANGES_CDATA_HPP_ + +#include + +#include "vccc/__core/inline_or_static.hpp" +#include "vccc/__iterator/basic_const_iterator.hpp" +#include "vccc/__ranges/as_const_pointer.hpp" +#include "vccc/__ranges/data.hpp" +#include "vccc/__ranges/enable_borrowed_range.hpp" +#include "vccc/__ranges/possibly_const_range.hpp" +#include "vccc/__ranges/range_const_reference_t.hpp" +#include "vccc/__type_traits/disjunction.hpp" + +namespace vccc { +namespace ranges { +namespace detail { + +struct cdata_niebloid { + template, + enable_borrowed_range> + >::value, int> = 0> + constexpr std::remove_reference_t>* operator()(T&& t) const { + return vccc::ranges::as_const_pointer(ranges::data(possibly_const_range(t))); + } +}; + +} // namespace detail + +inline namespace niebloid { + +/// @addtogroup ranges +/// @{ + +VCCC_INLINE_OR_STATIC constexpr detail::cdata_niebloid cdata{}; + +/// @} + +} // inline namespace niebloid + +} // namespace ranges +} // namespace vccc + +#endif // VCCC_RANGES_CDATA_HPP_ diff --git a/include/vccc/__ranges/cend.hpp b/include/vccc/__ranges/cend.hpp index 4b3343d2..bcdd7570 100644 --- a/include/vccc/__ranges/cend.hpp +++ b/include/vccc/__ranges/cend.hpp @@ -9,12 +9,10 @@ #include "vccc/__core/inline_or_static.hpp" #include "vccc/__iterator/basic_const_iterator.hpp" -#include "vccc/__ranges/constant_range.hpp" #include "vccc/__ranges/enable_borrowed_range.hpp" #include "vccc/__ranges/end.hpp" -#include "vccc/__type_traits/detail/tag.hpp" +#include "vccc/__ranges/possibly_const_range.hpp" #include "vccc/__type_traits/disjunction.hpp" -#include "vccc/__utility/as_const.hpp" namespace vccc { namespace ranges { @@ -26,22 +24,7 @@ struct cend_niebloid { enable_borrowed_range> >::value, int> = 0> constexpr auto operator()(T&& t) const { - using tag = conditional_tag, constant_range>; - return this->call(std::forward(t), tag{}); - } - - private: - template - constexpr auto call(T&& t, vccc::detail::tag_1) const { - return ranges::end(t); - } - template - constexpr auto call(T&& t, vccc::detail::tag_2) const { - return ranges::end(vccc::as_const(t)); - } - template - constexpr auto call(T&& t, vccc::detail::tag_else) const { - return make_const_sentinel(ranges::end(t)); + return vccc::const_sentinel(ranges::end(possibly_const_range(t))); } }; diff --git a/include/vccc/__ranges/crbegin.hpp b/include/vccc/__ranges/crbegin.hpp new file mode 100644 index 00000000..939d3e21 --- /dev/null +++ b/include/vccc/__ranges/crbegin.hpp @@ -0,0 +1,47 @@ +// +// Created by YongGyu Lee on 3/27/24. +// + +#ifndef VCCC_RANGES_CRBEGIN_HPP_ +#define VCCC_RANGES_CRBEGIN_HPP_ + +#include + +#include "vccc/__core/inline_or_static.hpp" +#include "vccc/__iterator/basic_const_iterator.hpp" +#include "vccc/__ranges/enable_borrowed_range.hpp" +#include "vccc/__ranges/possibly_const_range.hpp" +#include "vccc/__ranges/rbegin.hpp" +#include "vccc/__type_traits/disjunction.hpp" + +namespace vccc { +namespace ranges { +namespace detail { + +struct crbegin_niebloid { + template, + enable_borrowed_range> + >::value, int> = 0> + constexpr auto operator()(T&& t) const { + return vccc::const_iterator(ranges::rbegin(possibly_const_range(t))); + } +}; + +} // namespace detail + +inline namespace niebloid { + +/// @addtogroup ranges +/// @{ + +VCCC_INLINE_OR_STATIC constexpr detail::crbegin_niebloid crbegin{}; + +/// @} + +} // inline namespace niebloid + +} // namespace ranges +} // namespace vccc + +#endif // VCCC_RANGES_CRBEGIN_HPP_ diff --git a/include/vccc/__ranges/crend.hpp b/include/vccc/__ranges/crend.hpp new file mode 100644 index 00000000..8e0c7997 --- /dev/null +++ b/include/vccc/__ranges/crend.hpp @@ -0,0 +1,47 @@ +// +// Created by YongGyu Lee on 3/27/24. +// + +#ifndef VCCC_RANGES_CREND_HPP_ +#define VCCC_RANGES_CREND_HPP_ + +#include + +#include "vccc/__core/inline_or_static.hpp" +#include "vccc/__iterator/basic_const_iterator.hpp" +#include "vccc/__ranges/enable_borrowed_range.hpp" +#include "vccc/__ranges/possibly_const_range.hpp" +#include "vccc/__ranges/rend.hpp" +#include "vccc/__type_traits/disjunction.hpp" + +namespace vccc { +namespace ranges { +namespace detail { + +struct crend_niebloid { + template, + enable_borrowed_range> + >::value, int> = 0> + constexpr auto operator()(T&& t) const { + return vccc::const_sentinel(ranges::rend(possibly_const_range(t))); + } +}; + +} // namespace detail + +inline namespace niebloid { + +/// @addtogroup ranges +/// @{ + +VCCC_INLINE_OR_STATIC constexpr detail::crend_niebloid crend{}; + +/// @} + +} // inline namespace niebloid + +} // namespace ranges +} // namespace vccc + +#endif // VCCC_RANGES_CREND_HPP_ diff --git a/include/vccc/__ranges/distance.hpp b/include/vccc/__ranges/distance.hpp index 15143f0f..b2d9e226 100644 --- a/include/vccc/__ranges/distance.hpp +++ b/include/vccc/__ranges/distance.hpp @@ -16,6 +16,7 @@ #include "vccc/__ranges/range.hpp" #include "vccc/__ranges/range_difference_t.hpp" #include "vccc/__ranges/size.hpp" +#include "vccc/__ranges/sized_range.hpp" #include "vccc/__type_traits/conjunction.hpp" #include "vccc/__type_traits/negation.hpp" #include "vccc/__type_traits/remove_cvref.hpp" diff --git a/include/vccc/__ranges/output_range.hpp b/include/vccc/__ranges/output_range.hpp new file mode 100644 index 00000000..427ef7cd --- /dev/null +++ b/include/vccc/__ranges/output_range.hpp @@ -0,0 +1,31 @@ +// +// Created by YongGyu Lee on 3/27/24. +// + +#ifndef VCCC_RANGES_OUTPUT_RANGE_HPP_ +#define VCCC_RANGES_OUTPUT_RANGE_HPP_ + +#include "vccc/__ranges/iterator_t.hpp" +#include "vccc/__iterator/output_iterator.hpp" +#include "vccc/__ranges/range.hpp" +#include "vccc/__type_traits/conjunction.hpp" + +namespace vccc { +namespace ranges { +namespace detail { + +template::value /* true */> +struct output_range_impl : std::false_type {}; + +template +struct output_range_impl : output_iterator, T> {}; + +} // namespace detail + +template +struct output_range : detail::output_range_impl {}; + +} // namespace ranges +} // namespace vccc + +#endif // VCCC_RANGES_OUTPUT_RANGE_HPP_ diff --git a/include/vccc/__ranges/possibly_const_range.hpp b/include/vccc/__ranges/possibly_const_range.hpp new file mode 100644 index 00000000..aedacefb --- /dev/null +++ b/include/vccc/__ranges/possibly_const_range.hpp @@ -0,0 +1,39 @@ +// +// Created by YongGyu Lee on 3/27/24. +// + +#ifndef VCCC_RANGES_POSSIBLY_CONST_RANGE_HPP_ +#define VCCC_RANGES_POSSIBLY_CONST_RANGE_HPP_ + +#include + +#include "vccc/__ranges/constant_range.hpp" +#include "vccc/__ranges/input_range.hpp" +#include "vccc/__type_traits/conjunction.hpp" +#include "vccc/__type_traits/negation.hpp" + +namespace vccc { +namespace ranges { +namespace detail { + +template +constexpr auto& possibly_const_range_impl(R& r, std::true_type) noexcept { + return const_cast(r); +} + +template +constexpr auto& possibly_const_range_impl(R& r, std::false_type) noexcept { + return r; +} + +} // namespace detail + +template::value, int> = 0> +constexpr auto& possibly_const_range(R& r) noexcept { + return detail::possibly_const_range_impl(r, conjunction, negation>>{}); +} + +} // namespace ranges +} // namespace vccc + +#endif // VCCC_RANGES_POSSIBLY_CONST_RANGE_HPP_ diff --git a/include/vccc/__ranges/range_adaptor.hpp b/include/vccc/__ranges/range_adaptor.hpp new file mode 100644 index 00000000..db4724f2 --- /dev/null +++ b/include/vccc/__ranges/range_adaptor.hpp @@ -0,0 +1,67 @@ +// +// Created by YongGyu Lee on 3/25/24. +// + +#ifndef VCCC_RANGES_RANGE_ADAPTOR_HPP_ +#define VCCC_RANGES_RANGE_ADAPTOR_HPP_ + +#include +#include +#include + +#include "vccc/__concepts/different_from.hpp" +#include "vccc/__ranges/range_adaptor_closure.hpp" +#include "vccc/__type_traits/bool_constant.hpp" +#include "vccc/__type_traits/conjunction.hpp" +#include "vccc/__type_traits/is_invocable.hpp" + +namespace vccc { +namespace ranges { + +template +class range_adaptor : public range_adaptor_closure> { + public: + static_assert(std::is_default_constructible::value, "Constraints not satisfied"); + static_assert(std::is_empty::value, "Constraints not satisfied"); + + template, + bool_constant<(sizeof...(Args) == sizeof...(T))> + >::value, int> = 0> + constexpr explicit range_adaptor(T&&... args) + noexcept(conjunction...>::value) + : args_(std::forward(args)...) {} + + template::value, int> = 0> + constexpr decltype(auto) operator()(R&& r) & { + return call(*this, std::forward(r), std::index_sequence_for{}); + } + + template::value, int> = 0> + constexpr decltype(auto) operator()(R&& r) const & { + return call(*this, std::forward(r), std::index_sequence_for{}); + } + + template::value, int> = 0> + constexpr decltype(auto) operator()(R&& r) && { + return call(std::move(*this), std::forward(r), std::index_sequence_for{}); + } + + template::value, int> = 0> + constexpr decltype(auto) operator()(R&& r) const && { + return call(std::move(*this), std::forward(r), std::index_sequence_for{}); + } + + private: + template + static constexpr decltype(auto) call(This&& thiz, R&& r, std::index_sequence) { + return Niebloid{}(std::forward(r), std::get(std::forward(thiz).args_)...); + } + + std::tuple args_; +}; + +} // namespace ranges +} // namespace vccc + +#endif // VCCC_RANGES_RANGE_ADAPTOR_HPP_ diff --git a/include/vccc/__ranges/detail/simple_view.hpp b/include/vccc/__ranges/simple_view.hpp similarity index 80% rename from include/vccc/__ranges/detail/simple_view.hpp rename to include/vccc/__ranges/simple_view.hpp index 136f7681..64f7952e 100644 --- a/include/vccc/__ranges/detail/simple_view.hpp +++ b/include/vccc/__ranges/simple_view.hpp @@ -2,8 +2,8 @@ // Created by yonggyulee on 2024/01/03. // -#ifndef VCCC_RANGES_DETAIL_SIMPLE_VIEW_HPP -#define VCCC_RANGES_DETAIL_SIMPLE_VIEW_HPP +#ifndef VCCC_RANGES_SIMPLE_VIEW_HPP +#define VCCC_RANGES_SIMPLE_VIEW_HPP #include @@ -16,7 +16,6 @@ namespace vccc { namespace ranges { -namespace detail { template, range>::value /* true */> struct simple_view @@ -28,8 +27,7 @@ struct simple_view template struct simple_view : std::false_type {}; -} // namespace detail } // namespace vccc } // namespace ranges -#endif // VCCC_RANGES_DETAIL_SIMPLE_VIEW_HPP +#endif // VCCC_RANGES_SIMPLE_VIEW_HPP diff --git a/include/vccc/__ranges/views/as_const_view.hpp b/include/vccc/__ranges/views/as_const_view.hpp index 9bc3d55f..4a3c4e6f 100644 --- a/include/vccc/__ranges/views/as_const_view.hpp +++ b/include/vccc/__ranges/views/as_const_view.hpp @@ -9,7 +9,7 @@ #include "vccc/__concepts/copy_constructible.hpp" #include "vccc/__ranges/cbegin.hpp" -#include "vccc/__ranges/detail/simple_view.hpp" +#include "vccc/__ranges/simple_view.hpp" #include "vccc/__ranges/enable_borrowed_range.hpp" #include "vccc/__ranges/input_range.hpp" #include "vccc/__ranges/range.hpp" @@ -42,7 +42,7 @@ class as_const_view : public view_interface> { return std::move(base_); } - template::value == false, int> = 0> + template::value == false, int> = 0> constexpr auto begin() { return vccc::ranges::cbegin(base_); } @@ -52,7 +52,7 @@ class as_const_view : public view_interface> { return vccc::ranges::cbegin(base_); } - template::value == false, int> = 0> + template::value == false, int> = 0> constexpr auto end() { return vccc::ranges::cend(base_); } diff --git a/include/vccc/__ranges/views/cartesian_product_view.hpp b/include/vccc/__ranges/views/cartesian_product_view.hpp index d219eb24..de20b02a 100644 --- a/include/vccc/__ranges/views/cartesian_product_view.hpp +++ b/include/vccc/__ranges/views/cartesian_product_view.hpp @@ -21,7 +21,7 @@ #include "vccc/__ranges/begin.hpp" #include "vccc/__ranges/bidirectional_range.hpp" #include "vccc/__ranges/common_range.hpp" -#include "vccc/__ranges/detail/simple_view.hpp" +#include "vccc/__ranges/simple_view.hpp" #include "vccc/__ranges/distance.hpp" #include "vccc/__ranges/end.hpp" #include "vccc/__ranges/forward_range.hpp" @@ -498,8 +498,8 @@ class cartesian_product_view : public view_interface >, - negation< detail::simple_view >... + negation< simple_view >, + negation< simple_view >... >::value, int> = 0> constexpr iterator begin() { return iterator( @@ -517,8 +517,8 @@ class cartesian_product_view : public view_interface >, - negation< detail::simple_view >...>, + negation< simple_view >, + negation< simple_view >...>, detail::cartesian_product_is_common >::value, int> = 0> constexpr iterator end() { diff --git a/include/vccc/__ranges/views/concat_view.hpp b/include/vccc/__ranges/views/concat_view.hpp index 8da57d18..a2a3bdc2 100644 --- a/include/vccc/__ranges/views/concat_view.hpp +++ b/include/vccc/__ranges/views/concat_view.hpp @@ -13,7 +13,7 @@ #include "vccc/__core/inline_or_static.hpp" #include "vccc/__iterator/next.hpp" #include "vccc/__ranges/common_range.hpp" -#include "vccc/__ranges/detail/simple_view.hpp" +#include "vccc/__ranges/simple_view.hpp" #include "vccc/__ranges/distance.hpp" #include "vccc/__ranges/end.hpp" #include "vccc/__ranges/range_difference_t.hpp" @@ -386,7 +386,7 @@ struct concat_view : view_interface> { : bases_{std::move(rngs)...} {} constexpr auto begin() { - using Const = conjunction...>; + using Const = conjunction...>; return iterator{this, in_place_index<0>, ranges::begin(std::get<0>(bases_))}; } @@ -403,7 +403,7 @@ struct concat_view : view_interface> { common_range... >::value, int> = 0> constexpr auto end() { - using Const = conjunction...>; + using Const = conjunction...>; return iterator{this, in_place_index, ranges::end(std::get(bases_))}; } @@ -412,7 +412,7 @@ struct concat_view : view_interface> { negation>... >::value, int> = 0> constexpr auto end() { - using Const = conjunction...>; + using Const = conjunction...>; return sentinel{*this}; } diff --git a/include/vccc/__ranges/views/concat_with_view.hpp b/include/vccc/__ranges/views/concat_with_view.hpp index 72e9afb8..7d063ceb 100644 --- a/include/vccc/__ranges/views/concat_with_view.hpp +++ b/include/vccc/__ranges/views/concat_with_view.hpp @@ -13,19 +13,19 @@ #include "vccc/__core/inline_or_static.hpp" #include "vccc/__iterator/next.hpp" #include "vccc/__ranges/common_range.hpp" -#include "vccc/__ranges/detail/simple_view.hpp" +#include "vccc/__ranges/simple_view.hpp" #include "vccc/__ranges/distance.hpp" #include "vccc/__ranges/end.hpp" #include "vccc/__ranges/range_difference_t.hpp" #include "vccc/__ranges/sized_range.hpp" #include "vccc/__ranges/views/all.hpp" -#include "vccc/__ranges/views/maybe_const.hpp" #include "vccc/__tuple/tuple_fold.hpp" #include "vccc/__tuple/tuple_transform.hpp" #include "vccc/__type_traits/conjunction.hpp" #include "vccc/__type_traits/common_type.hpp" #include "vccc/__type_traits/common_reference.hpp" #include "vccc/__type_traits/has_typename_type.hpp" +#include "vccc/__type_traits/maybe_const.hpp" #include "vccc/__utility/type_sequence.hpp" #include "vccc/variant.hpp" @@ -387,7 +387,7 @@ struct concat_with_view : view_interface> { : bases_{std::move(rngs)...} {} constexpr auto begin() { - using Const = conjunction...>; + using Const = conjunction...>; return iterator{this, in_place_index<0>, ranges::begin(std::get<0>(bases_))}; } @@ -404,7 +404,7 @@ struct concat_with_view : view_interface> { common_range... >::value, int> = 0> constexpr auto end() { - using Const = conjunction...>; + using Const = conjunction...>; return iterator{this, in_place_index, ranges::end(std::get(bases_))}; } @@ -413,7 +413,7 @@ struct concat_with_view : view_interface> { negation>... >::value, int> = 0> constexpr auto end() { - using Const = conjunction...>; + using Const = conjunction...>; return sentinel{*this}; } diff --git a/include/vccc/__ranges/views/drop.hpp b/include/vccc/__ranges/views/drop.hpp index 7bd7b826..db72ac85 100644 --- a/include/vccc/__ranges/views/drop.hpp +++ b/include/vccc/__ranges/views/drop.hpp @@ -12,6 +12,7 @@ #include "vccc/__concepts/different_from.hpp" #include "vccc/__core/decay_copy.hpp" #include "vccc/__ranges/range.hpp" +#include "vccc/__ranges/range_adaptor.hpp" #include "vccc/__ranges/range_adaptor_closure.hpp" #include "vccc/__ranges/subrange.hpp" #include "vccc/__ranges/views/all.hpp" @@ -31,24 +32,6 @@ namespace ranges { namespace views { namespace detail { -template -struct drop_adaptor_closure : range_adaptor_closure> { - template, - std::is_constructible - >::value, int> = 0> - constexpr explicit drop_adaptor_closure(U&& count) : count_(std::forward(count)) {} - - template::value, int> = 0> - constexpr drop_view> - operator()(R&& r) const { - return drop_view>(std::forward(r), count_); - } - - private: - D count_; -}; - using vccc::detail::return_category; struct drop_niebloid { @@ -183,9 +166,8 @@ struct drop_niebloid { } template - constexpr drop_adaptor_closure> - operator()(DifferenceType&& count) const { - return drop_adaptor_closure>(std::forward(count)); + constexpr auto operator()(DifferenceType&& count) const { + return range_adaptor>(std::forward(count)); } }; diff --git a/include/vccc/__ranges/views/drop_view.hpp b/include/vccc/__ranges/views/drop_view.hpp index 9023b7e5..149eb770 100644 --- a/include/vccc/__ranges/views/drop_view.hpp +++ b/include/vccc/__ranges/views/drop_view.hpp @@ -12,7 +12,7 @@ #include "vccc/__concepts/copy_constructible.hpp" #include "vccc/__iterator/next.hpp" #include "vccc/optional.hpp" -#include "vccc/__ranges/detail/simple_view.hpp" +#include "vccc/__ranges/simple_view.hpp" #include "vccc/__ranges/begin.hpp" #include "vccc/__ranges/enable_borrowed_range.hpp" #include "vccc/__ranges/iterator_t.hpp" @@ -82,7 +82,7 @@ class drop_view } template, + simple_view, sized_range, random_access_range >::value == false, int> = 0> @@ -98,7 +98,7 @@ class drop_view return begin_base::begin(base_, count_); } - template::value == false, int> = 0> + template::value == false, int> = 0> constexpr auto end() { return ranges::end(base_); } diff --git a/include/vccc/__ranges/views/drop_while.hpp b/include/vccc/__ranges/views/drop_while.hpp index e573e2eb..26a4cbb3 100644 --- a/include/vccc/__ranges/views/drop_while.hpp +++ b/include/vccc/__ranges/views/drop_while.hpp @@ -5,36 +5,19 @@ #ifndef VCCC_RANGES_VIEWS_DROP_WHILE_HPP #define VCCC_RANGES_VIEWS_DROP_WHILE_HPP -#include -#include #include #include +#include "vccc/__ranges/range_adaptor.hpp" #include "vccc/__ranges/viewable_range.hpp" #include "vccc/__ranges/views/all.hpp" #include "vccc/__ranges/views/drop_while_view.hpp" -#include "vccc/__type_traits/remove_cvref.hpp" namespace vccc { namespace ranges { namespace views { namespace detail { -template -class drop_while_adaptor_closure : public range_adaptor_closure> { - public: - explicit drop_while_adaptor_closure(F func) : func_(func) {} - - template::value, int> = 0> - constexpr drop_while_view, F> - operator()(R&& r) const { - return drop_while_view, F>(std::forward(r), *func_); - } - - private: - movable_box func_; -}; - struct drop_while_niebloid { template::value, int> = 0> constexpr auto operator()(R&& r, Pred&& pred) const { @@ -43,7 +26,7 @@ struct drop_while_niebloid { template>::value, int> = 0> constexpr auto operator()(Pred&& pred) const { - return drop_while_adaptor_closure>(std::forward(pred)); + return range_adaptor>(std::forward(pred)); } }; diff --git a/include/vccc/__ranges/views/elements_view.hpp b/include/vccc/__ranges/views/elements_view.hpp index b8f3eb3a..a7c5ed01 100644 --- a/include/vccc/__ranges/views/elements_view.hpp +++ b/include/vccc/__ranges/views/elements_view.hpp @@ -19,7 +19,7 @@ #include "vccc/__ranges/begin.hpp" #include "vccc/__ranges/bidirectional_range.hpp" #include "vccc/__ranges/common_range.hpp" -#include "vccc/__ranges/detail/simple_view.hpp" +#include "vccc/__ranges/simple_view.hpp" #include "vccc/__ranges/enable_borrowed_range.hpp" #include "vccc/__ranges/end.hpp" #include "vccc/__ranges/forward_range.hpp" @@ -362,7 +362,7 @@ class elements_view : public view_interface> { return std::move(base_); } - template::value == false, int> = 0> + template::value == false, int> = 0> constexpr auto begin() { return iterator{ranges::begin(base_)}; } @@ -373,7 +373,7 @@ class elements_view : public view_interface> { } template >, + negation< simple_view >, negation< common_range > >::value, int> = 0> constexpr auto end() { @@ -381,7 +381,7 @@ class elements_view : public view_interface> { } template >, + negation< simple_view >, common_range >::value, int> = 0> constexpr auto end() { diff --git a/include/vccc/__ranges/views/enumerate_view.hpp b/include/vccc/__ranges/views/enumerate_view.hpp index 77118fe4..ebd49a55 100644 --- a/include/vccc/__ranges/views/enumerate_view.hpp +++ b/include/vccc/__ranges/views/enumerate_view.hpp @@ -16,7 +16,7 @@ #include "vccc/__ranges/begin.hpp" #include "vccc/__ranges/bidirectional_range.hpp" #include "vccc/__ranges/common_range.hpp" -#include "vccc/__ranges/detail/simple_view.hpp" +#include "vccc/__ranges/simple_view.hpp" #include "vccc/__ranges/distance.hpp" #include "vccc/__ranges/enable_borrowed_range.hpp" #include "vccc/__ranges/end.hpp" @@ -317,7 +317,7 @@ class enumerate_view : public view_interface> { return std::move(base_); } - template::valule == false, int> = 0> + template::valule == false, int> = 0> constexpr iterator begin() { return iterator(ranges::begin(base_), 0); } @@ -328,7 +328,7 @@ class enumerate_view : public view_interface> { return iterator(ranges::begin(base_), 0); } - template::value == false, int> = 0> + template::value == false, int> = 0> constexpr auto end() { return end_nonconst(conjunction, sized_range>{}); } diff --git a/include/vccc/__ranges/views/filter.hpp b/include/vccc/__ranges/views/filter.hpp index 8ba0bd82..356951a7 100644 --- a/include/vccc/__ranges/views/filter.hpp +++ b/include/vccc/__ranges/views/filter.hpp @@ -8,50 +8,28 @@ #include #include "vccc/__core/inline_or_static.hpp" +#include "vccc/__ranges/range_adaptor.hpp" +#include "vccc/__ranges/range_adaptor_closure.hpp" #include "vccc/__ranges/views/filter_view.hpp" #include "vccc/__ranges/viewable_range.hpp" -#include "vccc/__ranges/range_adaptor_closure.hpp" #include "vccc/__type_traits/conjunction.hpp" +#include "vccc/__type_traits/remove_cvref.hpp" namespace vccc { namespace ranges { namespace views { namespace detail { -template -class filter_adaptor_closure : public range_adaptor_closure>, movable_box { - using pred = movable_box; - public: - explicit filter_adaptor_closure(Pred f) : pred(f) {} - - template::value, int> = 0> - constexpr filter_view, Pred> - operator()(R&& r) const { - return filter_view, Pred>(std::forward(r), *static_cast(*this)); - } -}; - -template::value /* false */> -struct check_filter_call : std::false_type {}; -template -struct check_filter_call - : conjunction< - input_range, - indirect_unary_predicate>, - std::is_object - > {}; - -struct filter_adaptor { - template::value, int> = 0> +struct filter_niebloid { + template::value, int> = 0> constexpr auto operator()(R&& r, Pred&& pred) const { - return filter_view, Pred>(std::forward(r), std::forward(pred)); + return filter_view, remove_cvref_t>(std::forward(r), std::forward(pred)); } template - constexpr filter_adaptor_closure> operator()(Pred&& pred) const { - return filter_adaptor_closure>(std::forward(pred)); + constexpr auto operator()(Pred&& pred) const { + return range_adaptor>{std::forward(pred)}; } - }; } // namespace detail @@ -59,7 +37,7 @@ struct filter_adaptor { /// @addtogroup ranges /// @{ -VCCC_INLINE_OR_STATIC constexpr detail::filter_adaptor filter{}; +VCCC_INLINE_OR_STATIC constexpr detail::filter_niebloid filter{}; /// @} diff --git a/include/vccc/__ranges/views/filter_view.hpp b/include/vccc/__ranges/views/filter_view.hpp index e93d4d89..98c3c2a4 100644 --- a/include/vccc/__ranges/views/filter_view.hpp +++ b/include/vccc/__ranges/views/filter_view.hpp @@ -111,9 +111,9 @@ struct has_arrow template class filter_view : public view_interface>, detail::filter_view_cache { - V base_; - movable_box pred_; - using cache_base = detail::filter_view_cache; + V base_; + movable_box pred_; + using cache_base = detail::filter_view_cache; public: static_assert(input_range::value, "Constraints not satisfied"); diff --git a/include/vccc/__ranges/views/iota_view.hpp b/include/vccc/__ranges/views/iota_view.hpp index f51a9a76..064961dd 100644 --- a/include/vccc/__ranges/views/iota_view.hpp +++ b/include/vccc/__ranges/views/iota_view.hpp @@ -387,7 +387,12 @@ class iota_view : public view_interface> { return *x == y.bound_; } friend constexpr bool operator!=(const iterator& x, const sentinel& y) { - using namespace vccc::rel_ops; + return !(x == y); + } + friend constexpr bool operator==(const sentinel& y, const iterator& x) { + return x == y; + } + friend constexpr bool operator!=(const sentinel& y, const iterator& x) { return !(x == y); } diff --git a/include/vccc/__ranges/views/join_view.hpp b/include/vccc/__ranges/views/join_view.hpp index 014bf0fb..6373155c 100644 --- a/include/vccc/__ranges/views/join_view.hpp +++ b/include/vccc/__ranges/views/join_view.hpp @@ -17,7 +17,7 @@ #include "vccc/__memory/addressof.hpp" #include "vccc/__ranges/bidirectional_range.hpp" #include "vccc/__ranges/common_range.hpp" -#include "vccc/__ranges/detail/simple_view.hpp" +#include "vccc/__ranges/simple_view.hpp" #include "vccc/__ranges/forward_range.hpp" #include "vccc/__ranges/input_range.hpp" #include "vccc/__ranges/movable_box.hpp" @@ -365,7 +365,7 @@ class join_view : public view_interface> { } constexpr auto begin() { - using B = conjunction, std::is_reference>>; + using B = conjunction, std::is_reference>>; return iterator{*this, ranges::begin(base_)}; } @@ -403,10 +403,10 @@ class join_view : public view_interface> { private: constexpr auto end_impl(std::true_type) { - return iterator::value>{*this, ranges::end(base_)}; + return iterator::value>{*this, ranges::end(base_)}; } constexpr auto end_impl(std::false_type) { - return sentinel::value>{*this}; + return sentinel::value>{*this}; } constexpr auto end_impl(std::true_type) const { diff --git a/include/vccc/__ranges/views/join_with.hpp b/include/vccc/__ranges/views/join_with.hpp index b3801f05..797dd84d 100644 --- a/include/vccc/__ranges/views/join_with.hpp +++ b/include/vccc/__ranges/views/join_with.hpp @@ -8,46 +8,91 @@ #include #include +#include "vccc/__concepts/derived_from_single_crtp.hpp" #include "vccc/__concepts/different_from.hpp" #include "vccc/__ranges/range.hpp" #include "vccc/__ranges/range_adaptor_closure.hpp" #include "vccc/__ranges/range_reference_t.hpp" #include "vccc/__ranges/range_value_t.hpp" +#include "vccc/__ranges/view.hpp" +#include "vccc/__ranges/view_interface.hpp" #include "vccc/__ranges/viewable_range.hpp" #include "vccc/__ranges/views/all.hpp" #include "vccc/__ranges/views/join_with_view.hpp" #include "vccc/__ranges/views/single.hpp" +#include "vccc/__type_traits/is_implicitly_constructible.hpp" namespace vccc { namespace ranges { namespace views { namespace detail { +template +struct first_template_arg; +template class Class, typename T1, typename... Ts> +struct first_template_arg> { + static_assert(derived_from_single_crtp, view_interface>::value, "Class must be view"); + using type = T1; +}; + +template +using first_template_arg_t = typename first_template_arg::type; + +// Using its own implementation to support lazy pattern evaluation template -class join_with_adaptor_object : public range_adaptor_closure> { +class join_with_adaptor : public range_adaptor_closure> { + using prv = first_template_arg_t; + public: - template::value, int> = 0> - constexpr explicit join_with_adaptor_object(U&& pattern) + template::value, int> = 0> + constexpr explicit join_with_adaptor(U&& pattern) : pattern_(std::forward(pattern)) {} - template::value, int> = 0> + template::value, int> = 0> + constexpr auto operator()(R&& r) & { + return call(*this, std::forward(r), is_implicitly_constructible>, prv&>{}); + } + + template::value, int> = 0> constexpr auto operator()(R&& r) const& { - return join_with_view, all_t>{std::forward(r), pattern_}; + return call(*this, std::forward(r), is_implicitly_constructible>, const prv&>{}); } - template::value, int> = 0> + template::value, int> = 0> constexpr auto operator()(R&& r) && { - return join_with_view, all_t>{std::forward(r), std::move(pattern_)}; + return call(std::move(*this), std::forward(r), is_implicitly_constructible>, prv&&>{}); + } + + template::value, int> = 0> + constexpr auto operator()(R&& r) const&& { + return call(std::move(*this), std::forward(r), is_implicitly_constructible>, const prv&&>{}); } private: + constexpr decltype(auto) value_ref() & { return *pattern_.begin(); } + constexpr decltype(auto) value_ref() const& { return *pattern_.begin(); } + constexpr decltype(auto) value_ref() && { return std::move(*pattern_.begin()); } + constexpr decltype(auto) value_ref() const&& { return std::move(*pattern_.begin()); } + + template + constexpr static auto call(This&& thiz, R&& r, std::true_type) { + return join_with_view, single_view>>>( + std::forward(r), std::forward(thiz).value_ref()); + } + + template + constexpr static auto call(This&& thiz, R&& r, std::false_type) { + return join_with_view, Pattern>{std::forward(r), std::forward(thiz).pattern_}; + } + Pattern pattern_; }; struct join_with_niebloid { template, - different_from>> + has_typename_type>, + negation>, Pattern>> >::value, int> = 0> constexpr auto operator()(R&& r, Pattern&& pattern) const { return join_with_view, all_t>{std::forward(r), std::forward(pattern)}; @@ -63,7 +108,18 @@ struct join_with_niebloid { template constexpr auto operator()(Pattern&& pattern) const { - return join_with_adaptor_object>{std::forward(pattern)}; + return create_closure(std::forward(pattern), has_typename_type>{}); + } + + private: + template + constexpr auto create_closure(Pattern&& pattern, std::true_type /* all */) const { + return join_with_adaptor>(std::forward(pattern)); + } + + template + constexpr auto create_closure(Pattern&& pattern, std::false_type /* all */) const { + return join_with_adaptor(pattern)))>(std::forward(pattern)); } }; diff --git a/include/vccc/__ranges/views/join_with_view.hpp b/include/vccc/__ranges/views/join_with_view.hpp index 9639f34a..83d7ce25 100644 --- a/include/vccc/__ranges/views/join_with_view.hpp +++ b/include/vccc/__ranges/views/join_with_view.hpp @@ -19,7 +19,7 @@ #include "vccc/__ranges/begin.hpp" #include "vccc/__ranges/bidirectional_range.hpp" #include "vccc/__ranges/common_range.hpp" -#include "vccc/__ranges/detail/simple_view.hpp" +#include "vccc/__ranges/simple_view.hpp" #include "vccc/__ranges/end.hpp" #include "vccc/__ranges/forward_range.hpp" #include "vccc/__ranges/input_range.hpp" @@ -67,48 +67,57 @@ class join_with_view_base : public view_interface { }; template -class join_with_view_base : public view_interface { +class join_with_view_base : public join_with_view_base { protected: - non_propagating_cache>> inner_base_{}; non_propagating_cache> outer_it_{}; }; template -using join_with_view_iterator_concept = - std::conditional_t< - conjunction< - std::is_reference>, - bidirectional_range, - bidirectional_range, - bidirectional_range, - common_range, - common_range - >::value, bidirectional_iterator_tag, - std::conditional_t< - conjunction< - std::is_reference>, - forward_range, - forward_range - >::value, forward_iterator_tag, - input_iterator_tag - >>; +struct join_with_view_iterator_concept { + using iterator_concept = + std::conditional_t< + conjunction< + has_typename_type>, + std::is_reference>, + bidirectional_range, + bidirectional_range, + bidirectional_range, + common_range, + common_range + >::value, bidirectional_iterator_tag, + std::conditional_t< + conjunction< + has_typename_type>, + std::is_reference>, + forward_range, + forward_range + >::value, forward_iterator_tag, + input_iterator_tag + >>; +}; template>::iterator_category, - typename InnerC = typename cxx20_iterator_traits>::iterator_category, - typename PatternC = typename cxx20_iterator_traits>::iterator_category, typename IterConcept = join_with_view_iterator_concept> -struct join_with_view_iterator_category { - using iterator_concept = IterConcept; +struct join_with_view_iterator_category + : join_with_view_iterator_concept +{ + // iterator_category is defined iif IterConcept denotes forward_iterator_tag #if __cplusplus < 202002L using iterator_category = iterator_ignore; #endif }; -template -struct join_with_view_iterator_category { - using iterator_concept = forward_iterator_tag; +template +struct join_with_view_iterator_category + : join_with_view_iterator_concept +{ + private: + using OuterC = typename cxx20_iterator_traits>::iterator_category; + using InnerC = typename cxx20_iterator_traits>::iterator_category; + using PatternC = typename cxx20_iterator_traits>::iterator_category; + + public: using iterator_category = std::conditional_t< negation, derived_from, - derived_from + derived_from, + common_range>, + common_range >::value, bidirectional_iterator_tag, std::conditional_t< @@ -154,7 +165,7 @@ class join_with_view_iterator_base /// @addtogroup ranges /// @{ -// Implementation taken from MSVC +// Part of implementations are taken from MSVC template class join_with_view : public detail::join_with_view_base> { public: @@ -478,7 +489,7 @@ class join_with_view : public detail::join_with_view_base& x) { + constexpr bool equal(const iterator& x) const { using namespace vccc::rel_ops; return x.get_outer() == end_; } @@ -511,8 +522,8 @@ class join_with_view : public detail::join_with_view_base::value, int> = 0> constexpr auto begin() { using Const = conjunction< - detail::simple_view, - detail::simple_view, + simple_view, + simple_view, std::is_reference> >; return iterator{*this, ranges::begin(base_)}; @@ -527,7 +538,7 @@ class join_with_view : public detail::join_with_view_base, forward_range, - std::is_reference> + std::is_reference> >::value, int> = 0> constexpr iterator begin() const { return iterator{*this, ranges::begin(base_)}; @@ -535,32 +546,32 @@ class join_with_view : public detail::join_with_view_base, - std::is_reference>, - forward_range>, - common_range, - common_range> + std::is_reference>, + forward_range>, + common_range, + common_range> >::value, int> = 0> constexpr auto end() { - using simple = conjunction, detail::simple_view>; + using simple = conjunction, simple_view>; return iterator{*this, ranges::end(base_)}; } template, - std::is_reference>, - forward_range>, - common_range, - common_range> + std::is_reference>, + forward_range>, + common_range, + common_range> >::value == false, int> = 0> constexpr auto end() { - using simple = conjunction, detail::simple_view>; + using simple = conjunction, simple_view>; return sentinel{*this}; } template, forward_range, - std::is_reference> + std::is_reference> >::value, int> = 0> constexpr auto end() const { return end_impl(conjunction< diff --git a/include/vccc/__ranges/views/split.hpp b/include/vccc/__ranges/views/split.hpp index 12796900..53b45dd5 100644 --- a/include/vccc/__ranges/views/split.hpp +++ b/include/vccc/__ranges/views/split.hpp @@ -44,6 +44,11 @@ class split_adapter_closure : public range_adaptor_closure(pattern)) {} + template::value, int> = 0> + constexpr auto operator()(R&& r) & { + return split_view, pattern_type>(std::forward(r), *pattern_); + } + template::value, int> = 0> constexpr auto operator()(R&& r) const& { return split_view, pattern_type>(std::forward(r), *pattern_); @@ -53,6 +58,11 @@ class split_adapter_closure : public range_adaptor_closure, pattern_type>(std::forward(r), std::move(*pattern_)); } + + template::value, int> = 0> + constexpr auto operator()(R&& r) const && { + return split_view, pattern_type>(std::forward(r), std::move(*pattern_)); + } }; struct split_niebloid { diff --git a/include/vccc/__ranges/views/take.hpp b/include/vccc/__ranges/views/take.hpp index a2f774c9..a3b947a2 100644 --- a/include/vccc/__ranges/views/take.hpp +++ b/include/vccc/__ranges/views/take.hpp @@ -11,12 +11,18 @@ #include "vccc/__core/decay_copy.hpp" #include "vccc/__concepts/convertible_to.hpp" +#include "vccc/__ranges/begin.hpp" +#include "vccc/__ranges/end.hpp" #include "vccc/__ranges/subrange.hpp" #include "vccc/__ranges/range.hpp" +#include "vccc/__ranges/range_adaptor.hpp" #include "vccc/__ranges/range_adaptor_closure.hpp" +#include "vccc/__ranges/range_difference_t.hpp" #include "vccc/__ranges/views/all.hpp" #include "vccc/__ranges/views/empty_view.hpp" #include "vccc/__ranges/views/iota_view.hpp" +#include "vccc/__ranges/views/repeat.hpp" +#include "vccc/__ranges/views/repeat_view.hpp" #include "vccc/__ranges/views/take_view.hpp" #include "vccc/span.hpp" #include "vccc/string_view.hpp" @@ -31,24 +37,6 @@ namespace ranges { namespace views { namespace detail { -template -struct take_adaptor_closure : public range_adaptor_closure> { - template> >, - std::is_constructible - >::value, int> = 0> - constexpr explicit take_adaptor_closure(U&& count) : count_(std::forward(count)) {} - - template::value, int> = 0> - constexpr take_view> - operator()(R&& r) const { - return take_view>(std::forward(r), count_); - } - - private: - D count_; -}; - using vccc::detail::return_category; struct take_niebloid { @@ -107,7 +95,7 @@ struct take_niebloid { ); } - template< typename T, bool = is_specialization::value /* true */> + template, random_access_range, sized_range>::value /* true */> struct return_category_iota_view : std::true_type { using category = return_category<3, T>; }; @@ -118,14 +106,30 @@ struct take_niebloid { template constexpr IV operator()(R&& e, ranges::range_difference_t f, return_category<3, IV>) const { using D = ranges::range_difference_t; - return IV( + return views::iota( *ranges::begin(e), *(ranges::begin(e) + (std::min)(ranges::distance(e), f)) ); } - // TODO: Add repeat_view - + template::value /* true */ > + struct return_category_repeat_view : std::true_type { + using category = return_category<4, bool_constant::value>>; + }; + template + struct return_category_repeat_view : std::false_type { + using category = return_category<0>; + }; + template + constexpr auto operator()(R&& e, ranges::range_difference_t f, return_category<4, std::true_type /* sized_range */>) const { + using D = ranges::range_difference_t; + return views::repeat(*(e.begin()), (std::min)(ranges::distance(e), f)); + } + template + constexpr auto operator()(R&& e, ranges::range_difference_t f, return_category<4, std::false_type /* sized_range */>) const { + using D = ranges::range_difference_t; + return views::repeat(*(e.begin()), static_cast(f)); + } template constexpr TakeView operator()(R&& r, ranges::range_difference_t f, return_category<5, TakeView>) const { @@ -144,7 +148,9 @@ struct take_niebloid { return_category_subrange::value, typename return_category_subrange::category, // 2 std::conditional_t< return_category_iota_view::value, typename return_category_iota_view::category, // 3 - return_category<5, take_view>> + std::conditional_t< + return_category_repeat_view::value, typename return_category_repeat_view::category, // 4 + return_category<5, take_view>>> // 5 >>>>>; @@ -159,9 +165,8 @@ struct take_niebloid { } template - constexpr take_adaptor_closure> - operator()(DifferenceType&& count) const { - return take_adaptor_closure>(std::forward(count)); + constexpr auto operator()(DifferenceType&& count) const { + return range_adaptor>(std::forward(count)); } }; } // namespace detail diff --git a/include/vccc/__ranges/views/take_view.hpp b/include/vccc/__ranges/views/take_view.hpp index 1a0fd1b3..a4f99d5f 100644 --- a/include/vccc/__ranges/views/take_view.hpp +++ b/include/vccc/__ranges/views/take_view.hpp @@ -13,7 +13,7 @@ #include "vccc/__concepts/copy_constructible.hpp" #include "vccc/__iterator/counted_iterator.hpp" #include "vccc/__iterator/sentinel_for.hpp" -#include "vccc/__ranges/detail/simple_view.hpp" +#include "vccc/__ranges/simple_view.hpp" #include "vccc/__ranges/begin.hpp" #include "vccc/__ranges/enable_borrowed_range.hpp" #include "vccc/__ranges/iterator_t.hpp" @@ -118,7 +118,7 @@ class take_view : public view_interface> { template >, + negation< simple_view >, sized_range, random_access_range >::value, int> = 0> @@ -127,7 +127,7 @@ class take_view : public view_interface> { } template >, + negation< simple_view >, sized_range, negation< random_access_range > >::value, int> = 0> @@ -137,7 +137,7 @@ class take_view : public view_interface> { } template >, + negation< simple_view >, negation< sized_range > >::value, int> = 0> constexpr auto begin() { @@ -175,7 +175,7 @@ class take_view : public view_interface> { template >, + negation< simple_view >, sized_range, random_access_range >::value, int> = 0> @@ -184,7 +184,7 @@ class take_view : public view_interface> { } template >, + negation< simple_view >, sized_range, negation< random_access_range > >::value, int> = 0> @@ -193,7 +193,7 @@ class take_view : public view_interface> { } template >, + negation< simple_view >, negation< sized_range > >::value, int> = 0> constexpr sentinel end() { diff --git a/include/vccc/__ranges/views/take_while.hpp b/include/vccc/__ranges/views/take_while.hpp new file mode 100644 index 00000000..ce0beae2 --- /dev/null +++ b/include/vccc/__ranges/views/take_while.hpp @@ -0,0 +1,59 @@ +// +// Created by YongGyu Lee on 3/27/24. +// + +#ifndef VCCC_RANGES_VIEWS_TAKE_WHILE_HPP_ +#define VCCC_RANGES_VIEWS_TAKE_WHILE_HPP_ + +#include + +#include "vccc/__core/inline_or_static.hpp" +#include "vccc/__iterator/indirect_unary_predicate.hpp" +#include "vccc/__ranges/input_range.hpp" +#include "vccc/__ranges/iterator_t.hpp" +#include "vccc/__ranges/range_adaptor.hpp" +#include "vccc/__ranges/view.hpp" +#include "vccc/__ranges/viewable_range.hpp" +#include "vccc/__ranges/views/all.hpp" +#include "vccc/__ranges/views/take_while_view.hpp" +#include "vccc/__type_traits/conjunction.hpp" +#include "vccc/__type_traits/has_typename_type.hpp" +#include "vccc/__type_traits/remove_cvref.hpp" + +namespace vccc { +namespace ranges { +namespace views { +namespace detail { + +struct take_while_niebloid { + template>, + view>, + input_range>, + std::is_object>, + indirect_unary_predicate, iterator_t>> + >::value, int> = 0> + constexpr auto operator()(R&& r, Pred&& pred) const { + return take_while_view, std::decay_t>(std::forward(r), std::forward(pred)); + } + + template + constexpr auto operator()(Pred&& pred) const { + return range_adaptor>(std::forward(pred)); + } +}; + +} // namespace detail + +/// @addtogroup ranges +/// @{ + +VCCC_INLINE_OR_STATIC constexpr detail::take_while_niebloid take_while{}; + +/// @} + +} // namespace views +} // namespace ranges +} // namespace vccc + +#endif // VCCC_RANGES_VIEWS_TAKE_WHILE_HPP_ diff --git a/include/vccc/__ranges/views/take_while_view.hpp b/include/vccc/__ranges/views/take_while_view.hpp new file mode 100644 index 00000000..f38566ed --- /dev/null +++ b/include/vccc/__ranges/views/take_while_view.hpp @@ -0,0 +1,159 @@ +// +// Created by YongGyu Lee on 3/27/24. +// + +#ifndef VCCC_RANGES_VIEWS_TAKE_WHILE_VIEW_HPP_ +#define VCCC_RANGES_VIEWS_TAKE_WHILE_VIEW_HPP_ + +#include +#include +#include + +#include "vccc/__core/constexpr.hpp" +#include "vccc/__concepts/convertible_to.hpp" +#include "vccc/__concepts/copy_constructible.hpp" +#include "vccc/__functional/invoke.hpp" +#include "vccc/__iterator/indirect_unary_predicate.hpp" +#include "vccc/__ranges/begin.hpp" +#include "vccc/__ranges/input_range.hpp" +#include "vccc/__ranges/iterator_t.hpp" +#include "vccc/__ranges/movable_box.hpp" +#include "vccc/__ranges/range.hpp" +#include "vccc/__ranges/sentinel_t.hpp" +#include "vccc/__ranges/simple_view.hpp" +#include "vccc/__ranges/view.hpp" +#include "vccc/__ranges/viewable_range.hpp" +#include "vccc/__ranges/view_interface.hpp" +#include "vccc/__ranges/views/all.hpp" +#include "vccc/__type_traits/bool_constant.hpp" +#include "vccc/__type_traits/conjunction.hpp" +#include "vccc/__type_traits/maybe_const.hpp" +#include "vccc/__type_traits/negation.hpp" +#include "vccc/__utility/cxx20_rel_ops.hpp" + +namespace vccc { +namespace ranges { + +/// @addtogroup ranges +/// @{ + +template +class take_while_view { + public: + static_assert(ranges::view::value, "Constraints not satisfied"); + static_assert(ranges::input_range::value, "Constraints not satisfied"); + static_assert(std::is_object::value, "Constraints not satisfied"); + static_assert(indirect_unary_predicate>::value, "Constraints not satisfied"); + + template + class sentinel { + using Base = maybe_const; + friend class sentinel; + + public: + sentinel() = default; + + constexpr explicit sentinel(sentinel_t end, const Pred* pred) + : end_(std::move(end)), pred_(pred) {} + + template, + convertible_to, sentinel_t> + >::value, int> = 0> + constexpr sentinel(sentinel that) + : end_(std::move(that.end_)), pred_(that.pred_) {} + + constexpr sentinel_t base() const { + return end_; + } + + friend constexpr bool operator==(const iterator_t& x, const sentinel& y) { + using namespace vccc::rel_ops; + return x == y.end_ || !vccc::invoke(*y.pred_, *x); + } + + friend constexpr bool operator!=(const iterator_t& x, const sentinel& y) { + return !(x == y); + } + + friend constexpr bool operator==(const sentinel& y, const iterator_t& x) { + return x == y; + } + + friend constexpr bool operator!=(const sentinel& y, const iterator_t& x) { + return !(x == y); + } + + private: + sentinel_t end_; + const Pred* pred_; + }; + + take_while_view() = default; + + constexpr explicit take_while_view(V base, Pred pred) + : base_(std::move(base)), pred_(std::move(pred)) {} + + template, + copy_constructible + >::value, int> = 0> + constexpr V base() const& { + return base_; + } + + constexpr V base() && { + return std::move(base_); + } + + constexpr const Pred& pred() const { + return *pred_; + } + + template, + negation> + >::value, int> = 0> + constexpr auto begin() { + return ranges::begin(base_); + } + + template, + range, + indirect_unary_predicate> + >::value, int> = 0> + constexpr auto begin() const { + return ranges::begin(base_); + } + + template, + negation> + >::value, int> = 0> + VCCC_CONSTEXPR_AFTER_CXX17 auto end() { + return sentinel{ranges::end(base_), pred_.operator->()}; + } + + template, + range, + indirect_unary_predicate> + >::value, int> = 0> + VCCC_CONSTEXPR_AFTER_CXX17 auto end() const { + return sentinel{ranges::end(base_), pred_.operator->()}; + } + + private: + V base_{}; + movable_box pred_{}; +}; + +#if __cplusplus >= 201703L + +template +take_while_view(R&&, Pred) -> take_while_view, Pred>; + +#endif + +/// @} + +} // namespace ranges +} // namespace vccc + +#endif // VCCC_RANGES_VIEWS_TAKE_WHILE_VIEW_HPP_ diff --git a/include/vccc/__ranges/views/transform.hpp b/include/vccc/__ranges/views/transform.hpp index ea43ab6e..703eb559 100644 --- a/include/vccc/__ranges/views/transform.hpp +++ b/include/vccc/__ranges/views/transform.hpp @@ -12,8 +12,7 @@ #include "vccc/__concepts/copy_constructible.hpp" #include "vccc/__concepts/invocable.hpp" #include "vccc/__ranges/input_range.hpp" -#include "vccc/__ranges/movable_box.hpp" -#include "vccc/__ranges/range_adaptor_closure.hpp" +#include "vccc/__ranges/range_adaptor.hpp" #include "vccc/__ranges/viewable_range.hpp" #include "vccc/__ranges/views/all.hpp" #include "vccc/__ranges/views/transform_view.hpp" @@ -24,21 +23,6 @@ namespace ranges { namespace views { namespace detail { -template -class transform_adaptor_closure : public range_adaptor_closure> { - public: - explicit transform_adaptor_closure(F func) : func_(func) {} - - template::value, int> = 0> - constexpr transform_view, F> - operator()(R&& r) const { - return transform_view, F>(std::forward(r), *func_); - } - - private: - movable_box func_; -}; - struct transform_niebloid { private: template, copy_constructible>::value /* false */> @@ -64,9 +48,8 @@ struct transform_niebloid { copy_constructible>, std::is_object> >::value, int> = 0> - constexpr transform_adaptor_closure> - operator()(F&& fun) const { - return transform_adaptor_closure>(std::forward(fun)); + constexpr auto operator()(F&& fun) const { + return range_adaptor>(std::forward(fun)); } }; diff --git a/include/vccc/__ranges/views/views.hpp b/include/vccc/__ranges/views/views.hpp index d43fbc5f..b84d7f64 100644 --- a/include/vccc/__ranges/views/views.hpp +++ b/include/vccc/__ranges/views/views.hpp @@ -15,6 +15,7 @@ #include "vccc/__ranges/views/common.hpp" #include "vccc/__ranges/views/common_view.hpp" #include "vccc/__ranges/views/concat_view.hpp" +//#include "vccc/__ranges/views/concat_with_view.hpp" #include "vccc/__ranges/views/counted.hpp" #include "vccc/__ranges/views/drop.hpp" #include "vccc/__ranges/views/drop_view.hpp" @@ -42,6 +43,8 @@ #include "vccc/__ranges/views/split_view.hpp" #include "vccc/__ranges/views/take.hpp" #include "vccc/__ranges/views/take_view.hpp" +#include "vccc/__ranges/views/take_while.hpp" +#include "vccc/__ranges/views/take_while_view.hpp" #include "vccc/__ranges/views/transform.hpp" #include "vccc/__ranges/views/transform_view.hpp" #include "vccc/__ranges/views/values_view.hpp" diff --git a/include/vccc/__tuple/tuple_fold.hpp b/include/vccc/__tuple/tuple_fold.hpp index 1c76bcd6..6e386575 100644 --- a/include/vccc/__tuple/tuple_fold.hpp +++ b/include/vccc/__tuple/tuple_fold.hpp @@ -26,7 +26,7 @@ constexpr auto tuple_fold_left_impl(Tuple&& tuple, T&& x, F&& f, std::integral_c ); } -template +template = 0> constexpr auto tuple_fold_left_impl(Tuple&& tuple, T&& x, F&& f, std::integral_constant) { return tuple_fold_left_impl( std::forward(tuple), diff --git a/include/vccc/algorithm.hpp b/include/vccc/algorithm.hpp index b5277471..9acaf636 100644 --- a/include/vccc/algorithm.hpp +++ b/include/vccc/algorithm.hpp @@ -37,6 +37,7 @@ #include "vccc/__algorithm/ranges/none_of.hpp" #include "vccc/__algorithm/ranges/out_value_result.hpp" #include "vccc/__algorithm/ranges/search.hpp" +#include "vccc/__algorithm/ranges/set_intersection.hpp" #include "vccc/__algorithm/ranges/swap_ranges.hpp" /// @defgroup algorithm diff --git a/include/vccc/expected.hpp b/include/vccc/expected.hpp new file mode 100644 index 00000000..99b2dcda --- /dev/null +++ b/include/vccc/expected.hpp @@ -0,0 +1,10 @@ +// +// Created by YongGyu Lee on 3/14/24. +// + +#ifndef VCCC_EXPECTED_HPP_ +#define VCCC_EXPECTED_HPP_ + +#include "vccc/__expected/expected.hpp" + +#endif // VCCC_EXPECTED_HPP_ diff --git a/include/vccc/memory.hpp b/include/vccc/memory.hpp index 7b66cb50..76b6e9f9 100644 --- a/include/vccc/memory.hpp +++ b/include/vccc/memory.hpp @@ -6,6 +6,10 @@ #define VCCC_MEMORY_HPP_ #include "vccc/__memory/addressof.hpp" +#include "vccc/__memory/construct_at.hpp" +#include "vccc/__memory/destroy.hpp" +#include "vccc/__memory/destroy_at.hpp" +#include "vccc/__memory/pointer_traits.hpp" #include "vccc/__memory/to_address.hpp" #endif // VCCC_MEMORY_HPP_ diff --git a/include/vccc/ranges.hpp b/include/vccc/ranges.hpp index 932b4c3b..f616d451 100644 --- a/include/vccc/ranges.hpp +++ b/include/vccc/ranges.hpp @@ -11,12 +11,15 @@ #include "vccc/__ranges/borrowed_range.hpp" #include "vccc/__ranges/borrowed_subrange_t.hpp" #include "vccc/__ranges/cbegin.hpp" +#include "vccc/__ranges/cdata.hpp" #include "vccc/__ranges/cend.hpp" #include "vccc/__ranges/common_range.hpp" #include "vccc/__ranges/const_iterator_t.hpp" #include "vccc/__ranges/const_sentinel_t.hpp" #include "vccc/__ranges/constant_range.hpp" #include "vccc/__ranges/contiguous_range.hpp" +#include "vccc/__ranges/crbegin.hpp" +#include "vccc/__ranges/crend.hpp" #include "vccc/__ranges/dangling.hpp" #include "vccc/__ranges/data.hpp" #include "vccc/__ranges/disabled_sized_range.hpp" @@ -31,6 +34,7 @@ #include "vccc/__ranges/iterator_t.hpp" #include "vccc/__ranges/movable_box.hpp" #include "vccc/__ranges/non_propagating_cache.hpp" +#include "vccc/__ranges/output_range.hpp" #include "vccc/__ranges/owning_view.hpp" #include "vccc/__ranges/random_access_range.hpp" #include "vccc/__ranges/range.hpp" diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index f4473427..702b7546 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -1,8 +1,13 @@ cmake_minimum_required(VERSION 3.5) +if (EMSCRIPTEN) + add_compile_options(-fexceptions) +endif () + VCCC_TEST(algorithm_test algorithm_test.cpp VCCC) VCCC_TEST(concepts_test concepts_test.cpp VCCC) VCCC_TEST(core_test core_test.cpp VCCC) +VCCC_TEST(expected_test expected_test.cpp VCCC) VCCC_TEST(functional_test functional_test.cpp VCCC) VCCC_TEST(iterator_test iterator_test.cpp VCCC) VCCC_TEST(numeric_test numeric_test.cpp VCCC) @@ -14,7 +19,9 @@ VCCC_TEST(tuple_test tuple_test.cpp VCCC) VCCC_TEST(type_traits_test type_traits_test.cpp VCCC) VCCC_TEST(variant_test variant_test.cpp VCCC) -VCCC_TEST_ONE_CXX(directory_test directory_test.cc 17 VCCC) +if (NOT ANDROID AND NOT EMSCRIPTEN) + VCCC_TEST_ONE_CXX(directory_test directory_test.cc 17 VCCC) +endif() VCCC_TEST(log_test log_test.cpp VCCC) VCCC_TEST(math_test math_test.cpp VCCC) VCCC_TEST(signal_test signal_test.cc VCCC) diff --git a/test/algorithm_test.cpp b/test/algorithm_test.cpp index ba4b6e3c..a256a7b6 100644 --- a/test/algorithm_test.cpp +++ b/test/algorithm_test.cpp @@ -166,6 +166,26 @@ int Test() { TEST_ENSURES(ranges::lexicographical_compare("acdb"_sv, "cdab"_sv) == true); } + { // ranges::set_intersection + const auto in1 = {1, 2, 2, 3, 4, 5, 6}; + const auto in2 = {2, 2, 3, 3, 5, 7}; + std::vector out {}; + + ranges::set_intersection(in1, in2, std::back_inserter(out)); + + TEST_ENSURES(ranges::equal(out, {2, 2, 3, 5})); + + std::vector v1{7, 2, 3, 4, 5, 6, 7, 8}; + std::vector v2{5, 7, 9, 7}; + std::sort(v1.begin(), v1.end()); + std::sort(v2.begin(), v2.end()); + + std::vector v_intersection; + ranges::set_intersection(v1.begin(), v1.end(), v2.begin(), v2.end(), + std::back_inserter(v_intersection)); + TEST_ENSURES(ranges::equal(v_intersection, {5, 7, 7})); + } + return TEST_RETURN_RESULT; } diff --git a/test/expected_test.cpp b/test/expected_test.cpp new file mode 100644 index 00000000..99b1f390 --- /dev/null +++ b/test/expected_test.cpp @@ -0,0 +1,114 @@ +// +// Created by YongGyu Lee on 2021/02/03. +// + +#include +#include +#include +#include + +#include "vccc/expected.hpp" +#include "vccc/string_view.hpp" +#include "vccc/type_traits.hpp" +#include "test_core.hpp" + +namespace vccc { +namespace { + + +int Test() { + INIT_TEST("vccc::expected") + + struct foo { + foo(int x) noexcept : x(x) { + std::cout << x << " constructed." << std::endl; + } + foo(const foo& f) : x(f.x) { + std::cout << x << " copy constructed" << std::endl; + } + foo(foo&& f) : x(f.x) { + if (f.x == 1) + throw 1; + std::cout << x << " move constructed" << std::endl; + } + foo& operator=(const foo& f) { + x = f.x; + std::cout << x << " copy assigned" << std::endl; + return *this; + } + foo& operator=(foo&& f) { + x = f.x; + std::cout << x << " move assigned" << std::endl; + return *this; + } + ~foo() { + std::cout << x << " destroyed." << std::endl; + } + int x; + }; + + { + expected e; + e.and_then([]() { return expected(); }); + e.transform([]() { /* no-op */ }); + e.or_else([](int) { return expected{}; }); + e.transform_error([](int) { return ""; }); + e.emplace(); + } + + { + expected e = 1; + e.and_then([](const foo&) { return expected(); }); + e.transform([](const foo&) { return expected(); }); + e.or_else([](const std::string&) { return expected{1}; }); + e.transform_error([](const std::string&) { return 123123; }); + e.emplace(2); + } + + { + enum class parse_error { + invalid_input, + overflow + }; + + auto parse_number = [](vccc::string_view str) -> vccc::expected { + const char* begin = str.data(); + char* end; + double retval = std::strtod(begin, &end); + + if (begin == end) + return vccc::unexpected(parse_error::invalid_input); + else if (std::isinf(retval)) + return vccc::unexpected(parse_error::overflow); + + str.remove_prefix(end - begin); + return retval; + }; + + TEST_ENSURES(*parse_number("42") == 42); + TEST_ENSURES(parse_number("42abc") == 42); + TEST_ENSURES(parse_number("meow").error() == parse_error::invalid_input); + TEST_ENSURES(parse_number("inf").error() == parse_error::overflow); + } + + expected e1 = 1; + expected e2{unexpected{foo{2}}}; + + std::cout << "Start" << std::endl; + try { + e2 = std::move(e1); + } catch (...) { + std::cout << std::boolalpha << e2.has_value() << std::endl; + } + std::cout << "End" << std::endl; + + + return TEST_RETURN_RESULT; +} + +} // namespace +} // namespace vccc + +int main() { + return ::vccc::Test(); +} diff --git a/test/log_test.cpp b/test/log_test.cpp index 90a21de8..3c7035e6 100644 --- a/test/log_test.cpp +++ b/test/log_test.cpp @@ -72,8 +72,8 @@ int main() { TEST_ENSURES(vccc::Logger{}.to_string(std::array{-1,-2,-3}) == "{ -1, -2, -3 }"); TEST_ENSURES(vccc::Logger{}.to_string(std::list{0,1,0}) == "{ 0, 1, 0 }"); - TEST_ENSURES(vccc::Logger{}.to_string(std::map{{1,"one"},{2,"two"}}) - == "{ { 1: one }, { 2: two } }"); + TEST_ENSURES(vccc::Logger{}.to_string(vccc::Quoted{}, std::map{{1,"one"},{2,"two"}}) + == "{ 1 => \"one\", 2 => \"two\" }"); TEST_ENSURES(vccc::Logger{}.to_string(bar{1,2,3}) == "{ 1, 2, 3 }"); // test tuples @@ -106,37 +106,61 @@ int main() { LOGD(std::chrono::milliseconds(123)); LOGD(std::chrono::time_point(std::chrono::hours(24)*365*1)); + auto address_to_string = [](const void* p) { + std::stringstream ss; + ss << p; + return ss.str(); + }; + + // test non-printable + struct bar {}; + bar b; + TEST_ENSURES(vccc::Logger{}.to_string(b) == '@' + address_to_string(std::addressof(b))); + LOGD("THIS", "IS", "DEBUG"); LOGI("THIS", "IS", "INFO"); LOGW("THIS", "IS", "WARN"); LOGE("THIS", "IS", "ERROR"); -// vccc::StreamWrapper sw; -// -// sw << std::vector>{{1, 2, 3}, {}}; -// LOGD(sw.stream().str()); sw.stream().str(""); -// sw << std::map{{"key", "value"}}; -// LOGD(sw.stream().str()); sw.stream().str(""); -// sw << std::make_tuple(); -// LOGD(sw.stream().str()); sw.stream().str(""); -// sw << std::make_index_sequence<0>(); -// LOGD(sw.stream().str()); sw.stream().str(""); -// sw << std::make_index_sequence<3>(); -// LOGD(sw.stream().str()); sw.stream().str(""); -// -// sw << std::chrono::system_clock::now(); -// LOGD(sw.stream().str()); sw.stream().str(""); -// -// auto now = std::chrono::steady_clock::now(); -// for(int i=0; i<3600*3; ++i) { -// sw << now; -// std::cout << sw.stream().str() << '\n'; -// sw.stream().str(""); -// now += std::chrono::milliseconds (7007); -// } -// -// vccc::IOSFlagsSaver saver(std::cout); + // test non-empty aggregate types + + struct agg { + int x; + std::string s; + }; + agg a{1, "foo"}; +#if __cplusplus < 201703L + TEST_ENSURES(vccc::Logger{}.to_string(a) == '@' + address_to_string(std::addressof(a))); +#else + TEST_ENSURES(vccc::Logger{}.to_string(vccc::ExpandAggregate{}, a) == "{ 1, foo }"); +#endif + + vccc::Logger l; + using complex_type = std::map>; + complex_type c = { + {"first", {{1, "one"}, {}}}, + {"second", {{2, "two"}, {}}} + }; + + auto str = l.to_string(vccc::Quoted{}, vccc::ExpandAggregate{}, c); + +#if __cplusplus < 201703L + TEST_ENSURES(str == + "{ " + "\"first\"" " => { " "@" + address_to_string(std::addressof(c["first" ].first)) + ", " "@" + address_to_string(std::addressof(c["first" ].second)) + " }" ", " + "\"second\"" " => { " "@" + address_to_string(std::addressof(c["second"].first)) + ", " "@" + address_to_string(std::addressof(c["second"].second)) + " }" + " }"); +#else + TEST_ENSURES(str == + "{ " + "\"first\"" " => { { 1, \"one\" }, " "@" + address_to_string(std::addressof(c["first" ].second)) + " }" ", " + "\"second\"" " => { { 2, \"two\" }, " "@" + address_to_string(std::addressof(c["second"].second)) + " }" + " }"); +#endif + int arr[] = {1,2,3,4,5}; + TEST_ENSURES(l.to_string(arr) == address_to_string(std::addressof(arr))); + TEST_ENSURES(l.to_string(vccc::ExpandArray{}, arr) == "{ 1, 2, 3, 4, 5 }"); return TEST_RETURN_RESULT; } diff --git a/test/ranges_test.cpp b/test/ranges_test.cpp index 19da7591..ffc13163 100644 --- a/test/ranges_test.cpp +++ b/test/ranges_test.cpp @@ -4,6 +4,7 @@ #include #include +#include #include #include #include @@ -402,6 +403,12 @@ int main() { std::cout << '\n'; } + { // take_while + using namespace vccc; + + TEST_ENSURES(ranges::equal(views::iota(2020) | views::take_while([](int y) { return y <= 2023; }), views::iota(2020, 2024))); + } + { // transform_view std::string s = "The length of this string is 42 characters"; auto tv = vccc::ranges::make_transform_view(s, [](char c) -> char {