├── .gitattributes ├── test ├── iter.cpp ├── range.cpp ├── result.cpp ├── test.cpp ├── slice.cpp ├── option.cpp ├── raii_checker.hpp ├── function.cpp ├── cmp.cpp ├── enum.cpp └── tuple.cpp ├── README.md ├── .gitignore ├── CMakeLists.txt.in ├── include └── crust │ ├── fmt │ └── mod.hpp │ ├── ops │ ├── mod.hpp │ ├── range.hpp │ └── function.hpp │ ├── clone.hpp │ ├── iter │ └── mod.hpp │ ├── slice.hpp │ ├── option.hpp │ ├── result.hpp │ ├── helper │ ├── repeat_macro.hpp │ └── types.hpp │ ├── enum.hpp │ ├── option_decl.hpp │ ├── num │ └── mod.hpp │ ├── cmp_decl.hpp │ ├── tuple.hpp │ ├── cmp.hpp │ ├── tuple_decl.hpp │ ├── utility.hpp │ └── enum_decl.hpp ├── .github └── workflows │ ├── test-clang.yml │ ├── test-gcc.yml │ └── test-msvc.yml ├── .clang-tidy ├── LICENSE ├── CMakeLists.txt └── .clang-format /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /test/iter.cpp: -------------------------------------------------------------------------------- 1 | #include "gtest/gtest.h" 2 | 3 | #include "crust/iter/mod.hpp" 4 | 5 | using namespace crust; 6 | 7 | 8 | GTEST_TEST(iter, iter) {} 9 | -------------------------------------------------------------------------------- /test/range.cpp: -------------------------------------------------------------------------------- 1 | #include "gtest/gtest.h" 2 | 3 | #include "crust/ops/range.hpp" 4 | 5 | using namespace crust; 6 | 7 | 8 | GTEST_TEST(range, range) {} 9 | -------------------------------------------------------------------------------- /test/result.cpp: -------------------------------------------------------------------------------- 1 | #include "gtest/gtest.h" 2 | 3 | #include "crust/result.hpp" 4 | 5 | using namespace crust; 6 | 7 | 8 | GTEST_TEST(result, result) {} 9 | -------------------------------------------------------------------------------- /test/test.cpp: -------------------------------------------------------------------------------- 1 | #include "gtest/gtest.h" 2 | 3 | 4 | int main(int argc, char **argv) { 5 | ::testing::InitGoogleTest(&argc, argv); 6 | return RUN_ALL_TESTS(); 7 | } 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # crust 2 | 3 | A Rust style C++ library. 4 | 5 | ![](https://github.com/Cytosine2020/crust/actions/workflows/test-gcc.yml/badge.svg) 6 | ![](https://github.com/Cytosine2020/crust/actions/workflows/test-clang.yml/badge.svg) 7 | ![](https://github.com/Cytosine2020/crust/actions/workflows/test-msvc.yml/badge.svg) -------------------------------------------------------------------------------- /test/slice.cpp: -------------------------------------------------------------------------------- 1 | #include "gtest/gtest.h" 2 | 3 | #include "crust/slice.hpp" 4 | #include "crust/utility.hpp" 5 | 6 | 7 | using namespace crust; 8 | 9 | 10 | GTEST_TEST(slice, slice) { 11 | constexpr usize BUFFER_SIZE = 10; 12 | 13 | u8 buffer[BUFFER_SIZE]{}; 14 | 15 | auto slice = Slice::from_raw_parts(buffer, BUFFER_SIZE); 16 | 17 | EXPECT_EQ(slice[1], 0); 18 | } 19 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | *.d 3 | 4 | # Compiled Object files 5 | *.slo 6 | *.lo 7 | *.o 8 | *.obj 9 | 10 | # Precompiled Headers 11 | *.gch 12 | *.pch 13 | 14 | # Compiled Dynamic libraries 15 | *.so 16 | *.dylib 17 | *.dll 18 | 19 | # Fortran module files 20 | *.mod 21 | *.smod 22 | 23 | # Compiled Static libraries 24 | *.lai 25 | *.la 26 | *.a 27 | *.lib 28 | 29 | # Executables 30 | *.exe 31 | *.out 32 | *.app 33 | 34 | .idea/ 35 | .vscode/ 36 | .vs/ 37 | 38 | build/ 39 | cmake-build-*/ 40 | out/ 41 | -------------------------------------------------------------------------------- /CMakeLists.txt.in: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.8.12) 2 | 3 | project(googletest-download NONE) 4 | 5 | include(ExternalProject) 6 | ExternalProject_Add(googletest 7 | GIT_REPOSITORY https://github.com/google/googletest.git 8 | GIT_TAG main 9 | SOURCE_DIR "${CMAKE_CURRENT_BINARY_DIR}/googletest-src" 10 | BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}/googletest-build" 11 | CONFIGURE_COMMAND "" 12 | BUILD_COMMAND "" 13 | INSTALL_COMMAND "" 14 | TEST_COMMAND "" 15 | ) 16 | -------------------------------------------------------------------------------- /include/crust/fmt/mod.hpp: -------------------------------------------------------------------------------- 1 | #ifndef CRUST_FMT_MOD_HPP 2 | #define CRUST_FMT_MOD_HPP 3 | 4 | 5 | #include "crust/utility.hpp" 6 | 7 | 8 | namespace crust { 9 | namespace fmt { 10 | struct Formatter {}; 11 | 12 | CRUST_TRAIT(Debug) { 13 | CRUST_TRAIT_USE_SELF(Debug); 14 | 15 | bool fmt_debug(Formatter & fmt); 16 | }; 17 | 18 | CRUST_TRAIT(Display) { 19 | CRUST_TRAIT_USE_SELF(Display); 20 | 21 | bool fmt_display(Formatter & fmt); 22 | }; 23 | } // namespace fmt 24 | } // namespace crust 25 | 26 | 27 | #endif // CRUST_FMT_MOD_HPP 28 | -------------------------------------------------------------------------------- /include/crust/ops/mod.hpp: -------------------------------------------------------------------------------- 1 | #ifndef CRUST_OPS_MOD_HPP 2 | #define CRUST_OPS_MOD_HPP 3 | 4 | 5 | #include "crust/utility.hpp" 6 | 7 | 8 | namespace crust { 9 | namespace index { 10 | CRUST_TRAIT(Index, class Idx, class T) { 11 | CRUST_TRAIT_USE_SELF(Index); 12 | 13 | const T &index(Idx idx) const; 14 | 15 | T &index_mut(Idx idx); 16 | 17 | const T &operator[](Idx idx) const { return self().index(idx); } 18 | 19 | T &operator[](Idx idx) { return self().index_mut(idx); } 20 | }; 21 | } // namespace index 22 | } // namespace crust 23 | 24 | 25 | #endif // CRUST_OPS_MOD_HPP 26 | -------------------------------------------------------------------------------- /include/crust/clone.hpp: -------------------------------------------------------------------------------- 1 | #ifndef CRUST_CLONE_HPP 2 | #define CRUST_CLONE_HPP 3 | 4 | 5 | #include "crust/utility.hpp" 6 | 7 | 8 | namespace crust { 9 | 10 | namespace clone { 11 | template 12 | T clone(const T &self) { 13 | return self.clone(); 14 | } 15 | 16 | CRUST_TRAIT(Clone) { 17 | CRUST_TRAIT_USE_SELF(Clone); 18 | 19 | Self clone() const { return *static_cast(this); } 20 | 21 | void clone_from(const Self &other) { 22 | *static_cast(this) = clone::clone(other); 23 | } 24 | }; 25 | } // namespace clone 26 | } // namespace crust 27 | 28 | 29 | #endif // CRUST_CLONE_HPP 30 | -------------------------------------------------------------------------------- /include/crust/ops/range.hpp: -------------------------------------------------------------------------------- 1 | #ifndef CRUST_OPS_RANGE_HPP 2 | #define CRUST_OPS_RANGE_HPP 3 | 4 | 5 | #include "crust/enum.hpp" 6 | #include "crust/utility.hpp" 7 | 8 | 9 | namespace crust { 10 | namespace range { 11 | struct RangeFull {}; 12 | 13 | template 14 | struct Range { 15 | T start; 16 | T end; 17 | 18 | constexpr Range(T start, T end) : start{start}, end{end} {} 19 | 20 | constexpr bool is_empty() const { return start >= end; } 21 | }; 22 | 23 | template 24 | struct RangeFrom { 25 | T start; 26 | 27 | explicit constexpr RangeFrom(T start) : start{start} {} 28 | }; 29 | 30 | template 31 | struct RangeTo { 32 | T end; 33 | 34 | explicit constexpr RangeTo(T end) : end{end} {} 35 | }; 36 | } // namespace range 37 | } // namespace crust 38 | 39 | 40 | #endif // CRUST_OPS_RANGE_HPP 41 | -------------------------------------------------------------------------------- /.github/workflows/test-clang.yml: -------------------------------------------------------------------------------- 1 | name: test-clang 2 | 3 | on: push 4 | 5 | jobs: 6 | test: 7 | runs-on: macos-latest 8 | 9 | strategy: 10 | fail-fast: false 11 | matrix: 12 | build: [Debug, Release] 13 | standard: [cxx11, cxx14, cxx17, cxx20] 14 | 15 | defaults: 16 | run: 17 | shell: bash 18 | 19 | name: "${{matrix.standard}}-${{matrix.build}}" 20 | steps: 21 | - uses: actions/checkout@v2 22 | 23 | - name: configure cmake 24 | run: cmake -S $GITHUB_WORKSPACE -B ${{runner.workspace}}/build -DCMAKE_BUILD_TYPE=${{matrix.build}} 25 | 26 | - name: build 27 | working-directory: ${{runner.workspace}}/build 28 | run: cmake --build . --config ${{matrix.build}} --target test-${{matrix.standard}} 29 | 30 | - name: test 31 | working-directory: ${{runner.workspace}}/build 32 | run: ./test-${{matrix.standard}} -V 33 | -------------------------------------------------------------------------------- /.github/workflows/test-gcc.yml: -------------------------------------------------------------------------------- 1 | name: test-gcc 2 | 3 | on: push 4 | 5 | jobs: 6 | test: 7 | runs-on: ubuntu-latest 8 | 9 | strategy: 10 | fail-fast: false 11 | matrix: 12 | build: [Debug, Release] 13 | standard: [cxx11, cxx14, cxx17, cxx20] 14 | 15 | defaults: 16 | run: 17 | shell: bash 18 | 19 | name: "${{matrix.standard}}-${{matrix.build}}" 20 | steps: 21 | - uses: actions/checkout@v2 22 | 23 | - name: configure cmake 24 | run: cmake -S $GITHUB_WORKSPACE -B ${{runner.workspace}}/build -DCMAKE_BUILD_TYPE=${{matrix.build}} 25 | 26 | - name: build 27 | working-directory: ${{runner.workspace}}/build 28 | run: cmake --build . --config ${{matrix.build}} --target test-${{matrix.standard}} 29 | 30 | - name: test 31 | working-directory: ${{runner.workspace}}/build 32 | run: ./test-${{matrix.standard}} -V 33 | -------------------------------------------------------------------------------- /include/crust/iter/mod.hpp: -------------------------------------------------------------------------------- 1 | #ifndef CRUST_ITER_MOD_HPP 2 | #define CRUST_ITER_MOD_HPP 3 | 4 | 5 | #include "crust/enum.hpp" 6 | #include "crust/option.hpp" 7 | #include "crust/tuple.hpp" 8 | #include "crust/utility.hpp" 9 | 10 | 11 | namespace crust { 12 | namespace iter { 13 | CRUST_TRAIT(Iterator, class Item) { 14 | CRUST_TRAIT_USE_SELF(Iterator); 15 | 16 | Option next(); 17 | 18 | constexpr Tuple> size_hint() const { 19 | return tuple>(0, None{}); 20 | } 21 | 22 | template 23 | B fold(B && init, ops::Fn f) { 24 | B accum = forward(init); 25 | 26 | Item x; 27 | while ((let>(x) = self().next())) { 28 | accum = f(move(accum), move(x)); 29 | } 30 | 31 | return accum; 32 | } 33 | }; 34 | } // namespace iter 35 | } // namespace crust 36 | 37 | 38 | #endif // CRUST_ITER_MOD_HPP 39 | -------------------------------------------------------------------------------- /.github/workflows/test-msvc.yml: -------------------------------------------------------------------------------- 1 | name: test-msvc 2 | 3 | on: push 4 | 5 | jobs: 6 | test: 7 | runs-on: windows-2019 8 | 9 | strategy: 10 | fail-fast: false 11 | matrix: 12 | build: [Debug, Release] 13 | standard: [cxx11, cxx14, cxx17, cxx20] 14 | 15 | defaults: 16 | run: 17 | shell: cmd 18 | 19 | name: "${{matrix.standard}}-${{matrix.build}}" 20 | steps: 21 | - uses: actions/checkout@v2 22 | 23 | - name: configure cmake 24 | run: cmake -S %GITHUB_WORKSPACE% -B ${{runner.workspace}}\build -DCMAKE_BUILD_TYPE=${{matrix.build}} 25 | 26 | - name: build 27 | working-directory: ${{runner.workspace}}\build 28 | run: cmake --build . --config ${{matrix.build}} --target test-${{matrix.standard}} 29 | 30 | - name: test 31 | working-directory: ${{runner.workspace}}\build 32 | run: .\${{matrix.build}}\test-${{matrix.standard}}.exe -V 33 | -------------------------------------------------------------------------------- /test/option.cpp: -------------------------------------------------------------------------------- 1 | #include "gtest/gtest.h" 2 | 3 | #include "crust/cmp.hpp" 4 | #include "crust/option.hpp" 5 | #include "crust/utility.hpp" 6 | 7 | 8 | using namespace crust; 9 | using ops::bind; 10 | 11 | 12 | GTEST_TEST(option, option) { 13 | EXPECT_TRUE(make_some(0) == make_some(0)); 14 | EXPECT_FALSE(make_some(0) == make_some(1)); 15 | EXPECT_TRUE(make_some(0) != make_some(1)); 16 | EXPECT_FALSE(make_some(0) != make_some(0)); 17 | EXPECT_TRUE(make_some(0) != None{}); 18 | EXPECT_FALSE(make_some(0) == None{}); 19 | 20 | EXPECT_TRUE(make_some(0).is_some()); 21 | EXPECT_FALSE(make_some(0).is_none()); 22 | EXPECT_TRUE(make_none().is_none()); 23 | EXPECT_FALSE(make_none().is_some()); 24 | 25 | EXPECT_TRUE(*make_some(1234).as_ptr().unwrap_or(0) == 1234); 26 | EXPECT_TRUE( 27 | *make_some(1234) 28 | .map(bind([](const i32 &value) { return &value; })) 29 | .unwrap_or(0) == 1234); 30 | } 31 | -------------------------------------------------------------------------------- /.clang-tidy: -------------------------------------------------------------------------------- 1 | Checks: '-*,clang-diagnostic-*,llvm-*,misc-*,-llvm-header-guard,-llvm-else-after-return,-misc-non-private-member-variables-in-classes,-misc-unconventional-assign-operator' 2 | 3 | CheckOptions: 4 | - key: readability-identifier-naming.ClassCase 5 | value: CamelCase 6 | - key: readability-identifier-naming.EnumCase 7 | value: CamelCase 8 | - key: readability-identifier-naming.FunctionCase 9 | value: lower_case 10 | - key: readability-identifier-naming.MemberCase 11 | value: lower_case 12 | - key: readability-identifier-naming.ParameterCase 13 | value: lower_case 14 | - key: readability-identifier-naming.UnionCase 15 | value: lower_case 16 | - key: readability-identifier-naming.VariableCase 17 | value: lower_case 18 | - key: readability-identifier-naming.StaticConstantCase 19 | value: UPPER_CASE 20 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Cytosine2020 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /include/crust/slice.hpp: -------------------------------------------------------------------------------- 1 | #ifndef CRUST_SLICE_HPP 2 | #define CRUST_SLICE_HPP 3 | 4 | 5 | #include "crust/ops/mod.hpp" 6 | #include "crust/utility.hpp" 7 | 8 | 9 | namespace crust { 10 | template 11 | struct crust_ebco Slice : Impl, Trait> { 12 | private: 13 | T *inner; 14 | usize size; 15 | 16 | Slice() : inner{nullptr}, size{0} {} 17 | 18 | Slice(T *inner, usize size) : inner{inner}, size{size} {} 19 | 20 | public: 21 | static Slice from_ptr(T *inner) { return Slice{inner, 1}; } 22 | 23 | static Slice from_raw_parts(T *inner, usize size) { 24 | return Slice{inner, size}; 25 | } 26 | 27 | usize len() const { return size; } 28 | 29 | bool is_empty() const { return len() == 0; } 30 | 31 | const T *as_ptr() const { return inner; } 32 | 33 | T *as_ptr() { return inner; } 34 | }; 35 | 36 | template 37 | CRUST_IMPL_FOR(CRUST_MACRO(index::Index, usize, T>)) { 38 | CRUST_IMPL_USE_SELF(Slice); 39 | 40 | const T &index(usize index) const { 41 | if (index >= self().len()) { 42 | crust_panic("index out of boundary!"); 43 | } 44 | return self().as_ptr()[index]; 45 | } 46 | 47 | T &index_mut(usize index) { 48 | if (index >= self().len()) { 49 | crust_panic("index_mut out of boundary!"); 50 | } 51 | return self().as_ptr()[index]; 52 | } 53 | }; 54 | } // namespace crust 55 | 56 | 57 | #endif // CRUST_SLICE_HPP 58 | -------------------------------------------------------------------------------- /include/crust/option.hpp: -------------------------------------------------------------------------------- 1 | #ifndef CRUST_OPTION_HPP 2 | #define CRUST_OPTION_HPP 3 | 4 | 5 | #include "crust/option_decl.hpp" 6 | 7 | 8 | namespace crust { 9 | namespace option { 10 | struct crust_ebco None : 11 | TupleStruct<>, 12 | Derive< 13 | None, 14 | Trait, 15 | Trait, 16 | Trait, 17 | Trait, 18 | Trait, 19 | Trait> { 20 | CRUST_USE_BASE_CONSTRUCTORS(None, TupleStruct<>); 21 | }; 22 | 23 | template 24 | struct crust_ebco Some : 25 | TupleStruct, 26 | Derive< 27 | Some, 28 | Trait, 29 | Trait, 30 | Trait, 31 | Trait, 32 | Trait, 33 | Trait> { 34 | CRUST_USE_BASE_CONSTRUCTORS(Some, TupleStruct); 35 | }; 36 | 37 | template 38 | crust_always_inline constexpr Option::Result> 39 | make_some(T &&value) { 40 | return Some::Result>{forward(value)}; 41 | } 42 | 43 | template 44 | crust_always_inline constexpr Option::Result> 45 | make_none() { 46 | return None{}; 47 | } 48 | 49 | template 50 | template 51 | constexpr Option Option::map(ops::Fn f) const { 52 | return this->template visit>( 53 | [&](const Some &value) { 54 | return make_some(f(value.template get<0>())); 55 | }, 56 | [](const None &) { return make_none(); }); 57 | } 58 | 59 | template 60 | crust_cxx14_constexpr Option Option::take() { 61 | auto tmp = this->template move_variant>(); 62 | *this = None{}; 63 | return tmp; 64 | } 65 | } // namespace option 66 | 67 | using option::make_none; 68 | using option::make_some; 69 | } // namespace crust 70 | 71 | 72 | #endif // CRUST_OPTION_HPP 73 | -------------------------------------------------------------------------------- /test/raii_checker.hpp: -------------------------------------------------------------------------------- 1 | #ifndef CRUST_TEST_RAII_CHECKER_HPP 2 | #define CRUST_TEST_RAII_CHECKER_HPP 3 | 4 | 5 | #include 6 | #include 7 | 8 | #include "crust/utility.hpp" 9 | 10 | 11 | namespace test { 12 | struct RAIITypeInfo {}; 13 | 14 | struct RAIIRecorder { 15 | private: 16 | struct Record { 17 | const RAIITypeInfo *type_info; 18 | }; 19 | 20 | std::unordered_map record; 21 | 22 | public: 23 | RAIIRecorder() : record{} {} 24 | 25 | void construct(const RAIITypeInfo *type_info, void *self) { 26 | crust_assert(record.find(self) == record.end()); 27 | record.emplace(self, Record{type_info}); 28 | } 29 | 30 | void deconstruct(const RAIITypeInfo *type_info, void *self) { 31 | auto ptr = record.find(self); 32 | crust_assert(ptr != record.end() && ptr->second.type_info == type_info); 33 | record.erase(ptr); 34 | } 35 | 36 | ~RAIIRecorder() { crust_assert(record.empty()); } 37 | }; 38 | 39 | template 40 | struct RAIIChecker { 41 | private: 42 | static const RAIITypeInfo TYPE_INFO; 43 | 44 | std::shared_ptr recorder; 45 | 46 | public: 47 | explicit RAIIChecker(std::shared_ptr recorder) : 48 | recorder{std::move(recorder)} { 49 | this->recorder->construct(&TYPE_INFO, this); 50 | } 51 | 52 | RAIIChecker(const RAIIChecker &other) : recorder{other.recorder} { 53 | this->recorder->construct(&TYPE_INFO, this); 54 | } 55 | 56 | RAIIChecker(RAIIChecker &&other) noexcept : recorder{other.recorder} { 57 | this->recorder->construct(&TYPE_INFO, this); 58 | } 59 | 60 | RAIIChecker &operator=(const RAIIChecker &other) { 61 | crust_assert(recorder == other.recorder); 62 | return *this; 63 | } 64 | 65 | RAIIChecker &operator=(RAIIChecker &&other) noexcept { 66 | crust_assert(recorder == other.recorder); 67 | return *this; 68 | } 69 | 70 | ~RAIIChecker() { recorder->deconstruct(&TYPE_INFO, this); } 71 | }; 72 | 73 | template 74 | const RAIITypeInfo RAIIChecker::TYPE_INFO{}; 75 | } // namespace test 76 | 77 | 78 | #endif // CRUST_TEST_RAII_CHECKER_HPP 79 | -------------------------------------------------------------------------------- /include/crust/result.hpp: -------------------------------------------------------------------------------- 1 | #ifndef CRUST_RESULT_HPP 2 | #define CRUST_RESULT_HPP 3 | 4 | 5 | #include "crust/enum.hpp" 6 | #include "crust/option.hpp" 7 | #include "crust/utility.hpp" 8 | 9 | 10 | namespace crust { 11 | namespace result { 12 | template 13 | struct Ok; 14 | 15 | template 16 | struct Err; 17 | 18 | template 19 | struct Result; 20 | } // namespace result 21 | 22 | using result::Err; 23 | using result::Ok; 24 | using result::Result; 25 | 26 | template 27 | struct BluePrint> : TmplType> {}; 28 | 29 | template 30 | struct BluePrint> : TmplType> {}; 31 | 32 | template 33 | struct BluePrint> : TmplType, Err>> {}; 34 | 35 | namespace result { 36 | template 37 | CRUST_ENUM_TUPLE_VARIANT(Ok, Ok, T); 38 | 39 | template 40 | CRUST_ENUM_TUPLE_VARIANT(Err, Err, E); 41 | 42 | template 43 | struct crust_ebco Result : 44 | Enum, Err>, 45 | Derive< 46 | Result, 47 | Trait, 48 | Trait, 49 | Trait, 50 | Trait> { 51 | CRUST_ENUM_USE_BASE(Result, Enum, Err>); 52 | 53 | constexpr bool is_ok() const { return this->template is_variant>(); } 54 | 55 | constexpr bool is_err() const { return this->template is_variant>(); } 56 | 57 | constexpr bool contains(const T &other) const { 58 | return this->template visit( 59 | [&](const Ok &value) { return value.template get<0>() == other; }, 60 | [](const Err &) { return false; }); 61 | } 62 | 63 | constexpr bool contains_err(const E &other) const { 64 | return this->template visit( 65 | [](const Ok &) { return false; }, 66 | [&](const Err &value) { return value.template get<0>() == other; }); 67 | } 68 | 69 | crust_cxx14_constexpr Option ok() { 70 | return this->template move_variant>(); 71 | } 72 | 73 | crust_cxx14_constexpr Option err() { 74 | return this->template move_variant>(); 75 | } 76 | }; 77 | } // namespace result 78 | } // namespace crust 79 | 80 | 81 | #endif // CRUST_RESULT_HPP 82 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.1) 2 | 3 | project(crust) 4 | 5 | add_compile_definitions(GTEST_DONT_DEFINE_TEST) 6 | 7 | # Download and unpack googletest at configure time 8 | configure_file(CMakeLists.txt.in googletest-download/CMakeLists.txt) 9 | execute_process(COMMAND ${CMAKE_COMMAND} -G "${CMAKE_GENERATOR}" . 10 | RESULT_VARIABLE result 11 | WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/googletest-download) 12 | if (result) 13 | message(FATAL_ERROR "CMake step for googletest failed: ${result}") 14 | endif () 15 | execute_process(COMMAND ${CMAKE_COMMAND} --build . 16 | RESULT_VARIABLE result 17 | WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/googletest-download) 18 | if (result) 19 | message(FATAL_ERROR "Build step for googletest failed: ${result}") 20 | endif () 21 | 22 | # Prevent overriding the parent project's compiler/linker 23 | # settings on Windows 24 | set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) 25 | 26 | # Add googletest directly to our build. This defines 27 | # the gtest and gtest_main targets. 28 | add_subdirectory(${CMAKE_CURRENT_BINARY_DIR}/googletest-src 29 | ${CMAKE_CURRENT_BINARY_DIR}/googletest-build 30 | EXCLUDE_FROM_ALL) 31 | 32 | if (NOT MSVC) 33 | add_compile_options(-Wall -Wextra -fno-exceptions -fno-rtti) 34 | set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} \ 35 | -ggdb -g3 -fno-omit-frame-pointer -fprofile-arcs -ftest-coverage") 36 | else () 37 | add_compile_options(/W 3 /sdl /EHr /GR-) 38 | set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /Z7") 39 | endif () 40 | 41 | IF(CMAKE_BUILD_TYPE MATCHES RELEASE) 42 | set(CMAKE_INTERPROCEDURAL_OPTIMIZATION TRUE) 43 | ENDIF(CMAKE_BUILD_TYPE MATCHES RELEASE) 44 | 45 | include_directories(include) 46 | 47 | file(GLOB TEST_SRC "test/*.hpp" "test/*.cpp") 48 | 49 | add_executable(test-cxx11 ${TEST_SRC}) 50 | target_compile_features(test-cxx11 PUBLIC cxx_std_11) 51 | target_link_libraries(test-cxx11 gtest_main) 52 | 53 | add_executable(test-cxx14 ${TEST_SRC}) 54 | target_compile_features(test-cxx14 PUBLIC cxx_std_14) 55 | target_link_libraries(test-cxx14 gtest_main) 56 | 57 | add_executable(test-cxx17 ${TEST_SRC}) 58 | target_compile_features(test-cxx17 PUBLIC cxx_std_17) 59 | target_link_libraries(test-cxx17 gtest_main) 60 | 61 | add_executable(test-cxx20 ${TEST_SRC}) 62 | target_compile_features(test-cxx20 PUBLIC cxx_std_20) 63 | target_link_libraries(test-cxx20 gtest_main) 64 | -------------------------------------------------------------------------------- /test/function.cpp: -------------------------------------------------------------------------------- 1 | #include "gtest/gtest.h" 2 | 3 | #include "crust/ops/function.hpp" 4 | #include "crust/utility.hpp" 5 | 6 | #include "raii_checker.hpp" 7 | 8 | 9 | using namespace crust; 10 | using ops::bind; 11 | using ops::bind_mut; 12 | 13 | 14 | namespace { 15 | struct A : test::RAIIChecker { 16 | CRUST_USE_BASE_CONSTRUCTORS(A, test::RAIIChecker); 17 | 18 | i32 operator()() { return 1; } 19 | 20 | i32 operator()() const { return 2; } 21 | 22 | i32 fn_a() { return 3; } 23 | 24 | i32 fn_b() const { return 4; } 25 | }; 26 | 27 | i32 fn_c() { return 5; } 28 | 29 | i32 fn_d(i32 a) { return a; } 30 | 31 | i32 fn_e(const i32 &a) { return a; } 32 | 33 | i32 fn_f(i32 &a) { return a; } 34 | 35 | i32 fn_g(i32 &&a) { return a; } 36 | 37 | template 38 | i32 test_fn(ops::Fn fn) { 39 | return fn(); 40 | } 41 | 42 | template 43 | i32 test_fn_mut(ops::FnMut fn) { 44 | return fn(); 45 | } 46 | 47 | i32 test_dyn_fn(ops::DynFn fn) { return fn(); } 48 | 49 | i32 test_dyn_fn_mut(ops::DynFnMut fn) { return fn(); } 50 | } // namespace 51 | 52 | GTEST_TEST(function, raii) { 53 | auto recorder = std::make_shared(); 54 | 55 | GTEST_ASSERT_EQ(bind(crust_tmpl_val(&fn_d))(11), 11); 56 | GTEST_ASSERT_EQ(bind(crust_tmpl_val(&fn_e))(12), 12); 57 | GTEST_ASSERT_EQ(bind(crust_tmpl_val(&fn_g))(13), 13); 58 | 59 | i32 i = 14; 60 | GTEST_ASSERT_EQ(bind(crust_tmpl_val(&fn_d))(i), 14); 61 | GTEST_ASSERT_EQ(bind(crust_tmpl_val(&fn_e))(i), 14); 62 | GTEST_ASSERT_EQ(bind(crust_tmpl_val(&fn_f))(i), 14); 63 | 64 | A a{recorder}; 65 | GTEST_ASSERT_EQ(bind(crust_tmpl_val(&A::fn_b))(a), 4); 66 | GTEST_ASSERT_EQ(bind_mut(crust_tmpl_val(&A::fn_a))(a), 3); 67 | 68 | GTEST_ASSERT_EQ(test_fn(bind(crust_tmpl_val(&fn_c))), 5); 69 | GTEST_ASSERT_EQ(test_fn(bind(A{recorder})), 2); 70 | GTEST_ASSERT_EQ(test_fn(bind([]() { return 7; })), 7); 71 | 72 | GTEST_ASSERT_EQ(test_fn_mut(bind_mut(crust_tmpl_val(&fn_c))), 5); 73 | GTEST_ASSERT_EQ(test_fn_mut(bind_mut(A{recorder})), 1); 74 | GTEST_ASSERT_EQ(test_fn_mut(bind_mut([]() mutable { return 6; })), 6); 75 | 76 | GTEST_ASSERT_EQ(test_dyn_fn(crust_tmpl_val(&fn_c)), 5); 77 | GTEST_ASSERT_EQ(test_dyn_fn(A{recorder}), 2); 78 | GTEST_ASSERT_EQ(test_dyn_fn([]() { return 9; }), 9); 79 | 80 | GTEST_ASSERT_EQ(test_dyn_fn_mut(crust_tmpl_val(&fn_c)), 5); 81 | GTEST_ASSERT_EQ(test_dyn_fn_mut(A{recorder}), 1); 82 | GTEST_ASSERT_EQ(test_dyn_fn_mut([]() mutable { return 8; }), 8); 83 | } 84 | -------------------------------------------------------------------------------- /include/crust/helper/repeat_macro.hpp: -------------------------------------------------------------------------------- 1 | #ifndef CRUST_HELPER_REPEAT_MACRO_HPP 2 | #define CRUST_HELPER_REPEAT_MACRO_HPP 3 | 4 | 5 | #define CRUST_MACRO_REPEAT_1(FN) FN(0) 6 | #define CRUST_MACRO_REPEAT_2(FN) \ 7 | CRUST_MACRO_REPEAT_1(FN) \ 8 | FN(1) 9 | #define CRUST_MACRO_REPEAT_3(FN) \ 10 | CRUST_MACRO_REPEAT_2(FN) \ 11 | FN(2) 12 | #define CRUST_MACRO_REPEAT_4(FN) \ 13 | CRUST_MACRO_REPEAT_3(FN) \ 14 | FN(3) 15 | #define CRUST_MACRO_REPEAT_5(FN) \ 16 | CRUST_MACRO_REPEAT_4(FN) \ 17 | FN(4) 18 | #define CRUST_MACRO_REPEAT_6(FN) \ 19 | CRUST_MACRO_REPEAT_5(FN) \ 20 | FN(5) 21 | #define CRUST_MACRO_REPEAT_7(FN) \ 22 | CRUST_MACRO_REPEAT_6(FN) \ 23 | FN(6) 24 | #define CRUST_MACRO_REPEAT_8(FN) \ 25 | CRUST_MACRO_REPEAT_7(FN) \ 26 | FN(7) 27 | #define CRUST_MACRO_REPEAT_9(FN) \ 28 | CRUST_MACRO_REPEAT_8(FN) \ 29 | FN(8) 30 | #define CRUST_MACRO_REPEAT_10(FN) \ 31 | CRUST_MACRO_REPEAT_9(FN) \ 32 | FN(9) 33 | #define CRUST_MACRO_REPEAT_11(FN) \ 34 | CRUST_MACRO_REPEAT_10(FN) \ 35 | FN(10) 36 | #define CRUST_MACRO_REPEAT_12(FN) \ 37 | CRUST_MACRO_REPEAT_11(FN) \ 38 | FN(11) 39 | #define CRUST_MACRO_REPEAT_13(FN) \ 40 | CRUST_MACRO_REPEAT_12(FN) \ 41 | FN(12) 42 | #define CRUST_MACRO_REPEAT_14(FN) \ 43 | CRUST_MACRO_REPEAT_13(FN) \ 44 | FN(13) 45 | #define CRUST_MACRO_REPEAT_15(FN) \ 46 | CRUST_MACRO_REPEAT_14(FN) \ 47 | FN(14) 48 | #define CRUST_MACRO_REPEAT_16(FN) \ 49 | CRUST_MACRO_REPEAT_15(FN) \ 50 | FN(15) 51 | #define CRUST_MACRO_REPEAT(N, FN) CRUST_MACRO_REPEAT_##N(FN) 52 | 53 | 54 | #endif // CRUST_HELPER_REPEAT_MACRO_HPP 55 | -------------------------------------------------------------------------------- /include/crust/enum.hpp: -------------------------------------------------------------------------------- 1 | #ifndef CRUST_ENUM_HPP 2 | #define CRUST_ENUM_HPP 3 | 4 | 5 | #include "crust/enum_decl.hpp" 6 | 7 | #include "crust/cmp.hpp" 8 | #include "crust/option.hpp" 9 | #include "crust/utility.hpp" 10 | 11 | 12 | namespace crust { 13 | namespace _impl_enum { 14 | template 15 | template 16 | constexpr Option 17 | EnumTagUnion::PartialCmp::operator()(const T &value) const { 18 | return operator_partial_cmp(value, other->unsafe_get_variant()); 19 | } 20 | 21 | template 22 | template 23 | constexpr cmp::Ordering 24 | EnumTagUnion::Cmp::operator()(const T &value) const { 25 | return operator_cmp(value, other->unsafe_get_variant()); 26 | } 27 | 28 | template 29 | constexpr Option 30 | EnumTagUnion::partial_cmp(const EnumTagUnion &other) const { 31 | return make_some(operator_cmp(*this, other)); // FIXME: 32 | } 33 | 34 | template 35 | constexpr cmp::Ordering 36 | EnumTagUnion::cmp(const EnumTagUnion &other) const { 37 | return operator_cmp(index, other.index) 38 | .then(visit(Cmp{&other})); 39 | } 40 | 41 | template 42 | constexpr Option 43 | EnumTagOnly::partial_cmp(const EnumTagOnly &other) const { 44 | return operator_partial_cmp(index, other.index); 45 | } 46 | 47 | template 48 | constexpr cmp::Ordering 49 | EnumTagOnly::cmp(const EnumTagOnly &other) const { 50 | return operator_cmp(index, other.index); 51 | } 52 | 53 | template 54 | template 55 | crust_cxx14_constexpr Option Enum::move_variant() && { 56 | return is_variant() ? 57 | make_some(move(inner.template unsafe_get_variant())) : 58 | None{}; 59 | } 60 | 61 | template 62 | struct LetEnum { 63 | private: 64 | T &ref; 65 | 66 | public: 67 | explicit constexpr LetEnum(T &ref) : ref{ref} {} 68 | 69 | template 70 | crust_cxx14_constexpr bool operator=(Enum &&other) { 71 | return other.inner.template let_helper(ref); 72 | } 73 | }; 74 | } // namespace _impl_enum 75 | 76 | template 77 | crust_cxx14_constexpr _impl_enum::LetEnum let(T &ref) { 78 | return _impl_enum::LetEnum{ref}; 79 | } 80 | 81 | template 82 | constexpr Option ImplFor< 83 | cmp::PartialOrd, 84 | EnableIf<_impl_derive::ImplForEnum>>:: 85 | partial_cmp(const Self &other) const { 86 | return operator_partial_cmp(self().inner, other.inner); 87 | } 88 | 89 | template 90 | constexpr cmp::Ordering 91 | ImplFor, EnableIf<_impl_derive::ImplForEnum>>::cmp( 92 | const Self &other) const { 93 | return operator_cmp(self().inner, other.inner); 94 | } 95 | } // namespace crust 96 | 97 | 98 | #endif // CRUST_ENUM_HPP 99 | -------------------------------------------------------------------------------- /include/crust/option_decl.hpp: -------------------------------------------------------------------------------- 1 | #ifndef CRUST_OPTION_DECL_HPP 2 | #define CRUST_OPTION_DECL_HPP 3 | 4 | 5 | #include "crust/enum_decl.hpp" 6 | #include "crust/ops/function.hpp" 7 | #include "crust/utility.hpp" 8 | 9 | 10 | namespace crust { 11 | namespace option { 12 | struct None; 13 | 14 | template 15 | struct Some; 16 | 17 | template 18 | struct Option; 19 | } // namespace option 20 | 21 | using option::None; 22 | using option::Option; 23 | using option::Some; 24 | 25 | template <> 26 | struct BluePrint : TmplType> {}; 27 | 28 | template 29 | struct BluePrint> : TmplType> {}; 30 | 31 | template 32 | struct BluePrint> : TmplType>> {}; 33 | 34 | namespace option { 35 | template 36 | struct crust_ebco Option : 37 | Enum>, 38 | Derive< 39 | Option, 40 | Trait, 41 | Trait, 42 | Trait, 43 | Trait> { 44 | CRUST_ENUM_USE_BASE(Option, Enum>) 45 | 46 | constexpr bool is_some() const { 47 | return this->template is_variant>(); 48 | } 49 | 50 | constexpr bool is_none() const { return this->template is_variant(); } 51 | 52 | constexpr bool contains(const T &other) const { 53 | return this->template eq_variant>(other); 54 | } 55 | 56 | constexpr Option as_ptr() const { 57 | return map(ops::bind([](const T &value) { return &value; })); 58 | } 59 | 60 | crust_cxx14_constexpr Option as_mut_ptr() { 61 | return map(ops::bind([](T &value) { return &value; })); 62 | } 63 | 64 | crust_cxx14_constexpr T unwrap() && { 65 | return this->template visit( 66 | [](Some &value) { return move(value.template get<0>()); }, 67 | [](None &) { 68 | crust_panic("called `Option::unwrap()` on a `None` value"); 69 | }); 70 | } 71 | 72 | crust_cxx14_constexpr T unwrap_or(T &&d) && { 73 | return this->template visit( 74 | [](Some &value) { return move(value.template get<0>()); }, 75 | [&](None &) { return d; }); 76 | } 77 | 78 | template 79 | constexpr Option map(ops::Fn f) const; 80 | 81 | template 82 | constexpr U map_or(U &&d, ops::Fn f) const { 83 | return this->template visit( 84 | [&](const Some &value) { return f(value.template get<0>()); }, 85 | [&](const None &) { return move(d); }); 86 | } 87 | 88 | template 89 | constexpr U map_or_else(ops::Fn d, ops::Fn f) const { 90 | return this->template visit( 91 | [&](const Some &value) { return f(value.template get<0>()); }, 92 | [&](const None &) { return d(); }); 93 | } 94 | 95 | crust_cxx14_constexpr Option take(); 96 | }; 97 | } // namespace option 98 | } // namespace crust 99 | 100 | 101 | #endif // CRUST_OPTION_DECL_HPP 102 | -------------------------------------------------------------------------------- /include/crust/num/mod.hpp: -------------------------------------------------------------------------------- 1 | #ifndef CRUST_NUM_MOD_HPP 2 | #define CRUST_NUM_MOD_HPP 3 | 4 | 5 | #include "crust/utility.hpp" 6 | 7 | 8 | namespace crust { 9 | namespace num { 10 | namespace _impl_num { 11 | template 12 | struct Int { 13 | static constexpr Type BITS = static_cast(8 * sizeof(Type)); 14 | static constexpr Type MIN = min; 15 | static constexpr Type MAX = max; 16 | }; 17 | } // namespace _impl_num 18 | 19 | template 20 | struct Int; 21 | 22 | template <> 23 | struct Int : _impl_num::Int {}; 24 | 25 | template <> 26 | struct Int : _impl_num::Int {}; 27 | 28 | template <> 29 | struct Int : _impl_num::Int {}; 30 | 31 | template <> 32 | struct Int : _impl_num::Int {}; 33 | 34 | template <> 35 | struct Int : _impl_num::Int {}; 36 | 37 | template <> 38 | struct Int : _impl_num::Int {}; 39 | 40 | template <> 41 | struct Int : _impl_num::Int {}; 42 | 43 | template <> 44 | struct Int : 45 | _impl_num::Int< 46 | i64, 47 | static_cast(0x8000000000000000ULL), 48 | 0x7FFFFFFFFFFFFFFFLL> {}; 49 | } // namespace num 50 | 51 | template 52 | struct AddVal : TmplVal { 53 | crust_static_assert(IsSame::result); 54 | crust_static_assert( 55 | A::result <= num::Int::MAX - B::result); 56 | }; 57 | 58 | template 59 | struct SumVal; 60 | 61 | template 62 | struct SumVal : AddVal> {}; 63 | 64 | template 65 | struct SumVal : TmplVal {}; 66 | 67 | template 68 | struct SubVal : TmplVal { 69 | crust_static_assert(IsSame::result); 70 | crust_static_assert( 71 | A::result >= num::Int::MIN + B::result); 72 | }; 73 | 74 | template 75 | struct IncVal : AddVal> {}; 76 | 77 | template 78 | struct DecVal : SubVal> {}; 79 | 80 | template 81 | struct EQVal : BoolVal<(A::result == B::result)> { 82 | crust_static_assert(IsSame::result); 83 | }; 84 | 85 | template 86 | struct NEVal : BoolVal<(A::result != B::result)> { 87 | crust_static_assert(IsSame::result); 88 | }; 89 | 90 | template 91 | struct LTVal : BoolVal<(A::result < B::result)> { 92 | crust_static_assert(IsSame::result); 93 | }; 94 | 95 | template 96 | struct LEVal : BoolVal<(A::result <= B::result)> { 97 | crust_static_assert(IsSame::result); 98 | }; 99 | 100 | template 101 | struct GTVal : BoolVal<(A::result > B::result)> { 102 | crust_static_assert(IsSame::result); 103 | }; 104 | 105 | template 106 | struct GEVal : BoolVal<(A::result >= B::result)> { 107 | crust_static_assert(IsSame::result); 108 | }; 109 | } // namespace crust 110 | 111 | 112 | #endif // CRUST_NUM_MOD_HPP 113 | -------------------------------------------------------------------------------- /.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | Language: Cpp 3 | 4 | MaxEmptyLinesToKeep: 2 5 | 6 | ColumnLimit: 80 7 | IndentWidth: 2 8 | AccessModifierOffset: -2 9 | ConstructorInitializerIndentWidth: 4 10 | ContinuationIndentWidth: 4 11 | 12 | NamespaceIndentation: None 13 | CompactNamespaces: false 14 | 15 | AlignAfterOpenBracket: AlwaysBreak 16 | AlignConsecutiveMacros: false 17 | AlignConsecutiveAssignments: false 18 | AlignConsecutiveDeclarations: false 19 | AlignEscapedNewlines: Right 20 | AlignOperands: false 21 | AlignTrailingComments: true 22 | 23 | AllowAllArgumentsOnNextLine: true 24 | AllowAllConstructorInitializersOnNextLine: true 25 | AllowAllParametersOfDeclarationOnNextLine: true 26 | AllowShortBlocksOnASingleLine: true 27 | AllowShortCaseLabelsOnASingleLine: false 28 | AllowShortFunctionsOnASingleLine: All 29 | AllowShortLambdasOnASingleLine: All 30 | AllowShortIfStatementsOnASingleLine: Never 31 | AllowShortLoopsOnASingleLine: false 32 | AlwaysBreakAfterDefinitionReturnType: None 33 | AlwaysBreakAfterReturnType: None 34 | AlwaysBreakBeforeMultilineStrings: false 35 | AlwaysBreakTemplateDeclarations: Yes 36 | BinPackArguments: false 37 | BinPackParameters: false 38 | BraceWrapping: 39 | AfterCaseLabel: false 40 | AfterClass: false 41 | AfterControlStatement: false 42 | AfterEnum: false 43 | AfterFunction: false 44 | AfterNamespace: false 45 | AfterObjCDeclaration: false 46 | AfterStruct: false 47 | AfterUnion: false 48 | AfterExternBlock: false 49 | BeforeCatch: false 50 | BeforeElse: false 51 | IndentBraces: false 52 | SplitEmptyFunction: false 53 | SplitEmptyRecord: false 54 | SplitEmptyNamespace: false 55 | BreakBeforeBinaryOperators: None 56 | BreakBeforeBraces: Attach 57 | BreakBeforeInheritanceComma: false 58 | BreakInheritanceList: AfterColon 59 | BreakBeforeTernaryOperators: false 60 | BreakConstructorInitializersBeforeComma: false 61 | BreakConstructorInitializers: AfterColon 62 | BreakAfterJavaFieldAnnotations: false 63 | BreakStringLiterals: true 64 | 65 | CommentPragmas: '^ IWYU pragma:' 66 | 67 | ConstructorInitializerAllOnOneLineOrOnePerLine: false 68 | 69 | Cpp11BracedListStyle: true 70 | DeriveLineEnding: true 71 | DerivePointerAlignment: false 72 | DisableFormat: false 73 | ExperimentalAutoDetectBinPacking: false 74 | FixNamespaceComments: true 75 | ForEachMacros: 76 | - foreach 77 | - Q_FOREACH 78 | - BOOST_FOREACH 79 | IncludeBlocks: Preserve 80 | IncludeCategories: 81 | - Regex: '^"(llvm|llvm-c|clang|clang-c)/' 82 | Priority: 2 83 | SortPriority: 0 84 | - Regex: '^(<|"(gtest|gmock|isl|json)/)' 85 | Priority: 3 86 | SortPriority: 0 87 | - Regex: '.*' 88 | Priority: 1 89 | SortPriority: 0 90 | IncludeIsMainRegex: '(Test)?$' 91 | IncludeIsMainSourceRegex: '' 92 | IndentCaseLabels: false 93 | IndentGotoLabels: false 94 | IndentPPDirectives: None 95 | 96 | IndentWrappedFunctionNames: false 97 | KeepEmptyLinesAtTheStartOfBlocks: true 98 | MacroBlockBegin: '' 99 | MacroBlockEnd: '' 100 | 101 | PenaltyBreakAssignment: 2 102 | PenaltyBreakBeforeFirstCallParameter: 19 103 | PenaltyBreakComment: 300 104 | PenaltyBreakFirstLessLess: 120 105 | PenaltyBreakString: 1000 106 | PenaltyBreakTemplateDeclaration: 10 107 | PenaltyExcessCharacter: 1000000 108 | PenaltyReturnTypeOnItsOwnLine: 3 109 | PointerAlignment: Right 110 | ReflowComments: true 111 | SortIncludes: true 112 | SortUsingDeclarations: true 113 | SpaceAfterCStyleCast: false 114 | SpaceAfterLogicalNot: false 115 | SpaceAfterTemplateKeyword: true 116 | SpaceBeforeAssignmentOperators: true 117 | SpaceBeforeCpp11BracedList: false 118 | SpaceBeforeCtorInitializerColon: true 119 | SpaceBeforeInheritanceColon: true 120 | SpaceBeforeParens: ControlStatements 121 | SpaceBeforeRangeBasedForLoopColon: true 122 | SpaceInEmptyBlock: false 123 | SpaceInEmptyParentheses: false 124 | SpacesBeforeTrailingComments: 1 125 | SpacesInAngles: false 126 | SpacesInConditionalStatement: false 127 | SpacesInContainerLiterals: true 128 | SpacesInCStyleCastParentheses: false 129 | SpacesInParentheses: false 130 | SpacesInSquareBrackets: false 131 | SpaceBeforeSquareBrackets: false 132 | Standard: Latest 133 | StatementMacros: 134 | - Q_UNUSED 135 | - QT_REQUIRE_VERSION 136 | TabWidth: 8 137 | UseCRLF: false 138 | UseTab: Never 139 | ... 140 | -------------------------------------------------------------------------------- /test/cmp.cpp: -------------------------------------------------------------------------------- 1 | #include "gtest/gtest.h" 2 | 3 | #include "crust/cmp.hpp" 4 | #include "crust/tuple.hpp" 5 | #include "crust/utility.hpp" 6 | 7 | 8 | using namespace crust; 9 | using namespace cmp; 10 | using ops::bind; 11 | 12 | 13 | GTEST_TEST(cmp, derive) { 14 | crust_static_assert(Require::result); 15 | crust_static_assert(Require::result); 16 | crust_static_assert(Require::result); 17 | crust_static_assert(Require::result); 18 | crust_static_assert(Require::result); 19 | crust_static_assert(Require::result); 20 | crust_static_assert(Require::result); 21 | crust_static_assert(Require::result); 22 | crust_static_assert(Require::result); 23 | crust_static_assert(Require::result); 24 | crust_static_assert(Require::result); 25 | crust_static_assert(Require::result); 26 | 27 | crust_static_assert(Require::result); 28 | crust_static_assert(Require::result); 29 | crust_static_assert(Require::result); 30 | crust_static_assert(Require::result); 31 | crust_static_assert(Require::result); 32 | crust_static_assert(Require::result); 33 | crust_static_assert(Require::result); 34 | crust_static_assert(Require::result); 35 | crust_static_assert(Require::result); 36 | crust_static_assert(Require::result); 37 | crust_static_assert(Require::result); 38 | crust_static_assert(Require::result); 39 | 40 | crust_static_assert(Require::result); 41 | crust_static_assert(Require::result); 42 | crust_static_assert(Require::result); 43 | crust_static_assert(Require::result); 44 | crust_static_assert(Require::result); 45 | crust_static_assert(Require::result); 46 | crust_static_assert(Require::result); 47 | crust_static_assert(Require::result); 48 | crust_static_assert(Require::result); 49 | crust_static_assert(Require::result); 50 | crust_static_assert(Require::result); 51 | crust_static_assert(Require::result); 52 | 53 | crust_static_assert(Require::result); 54 | crust_static_assert(Require::result); 55 | crust_static_assert(Require::result); 56 | crust_static_assert(Require::result); 57 | crust_static_assert(Require::result); 58 | crust_static_assert(Require::result); 59 | crust_static_assert(Require::result); 60 | crust_static_assert(Require::result); 61 | crust_static_assert(Require::result); 62 | crust_static_assert(Require::result); 63 | crust_static_assert(Require::result); 64 | crust_static_assert(Require::result); 65 | } 66 | 67 | 68 | GTEST_TEST(cmp, ordering) { 69 | crust_static_assert(make_less() == make_less()); 70 | crust_static_assert(make_less() == make_less()); 71 | crust_static_assert(make_equal() == make_equal()); 72 | crust_static_assert(make_greater() == make_greater()); 73 | EXPECT_TRUE(make_less() < make_equal()); 74 | EXPECT_TRUE(make_less() < make_greater()); 75 | EXPECT_TRUE(make_equal() < make_greater()); 76 | 77 | EXPECT_EQ(make_equal().then(make_less()), make_less()); 78 | EXPECT_EQ(make_less().then(make_equal()), make_less()); 79 | EXPECT_EQ(make_greater().then(make_equal()), make_greater()); 80 | EXPECT_EQ( 81 | make_equal().then_with(bind([]() { return make_less(); })), make_less()); 82 | EXPECT_EQ( 83 | make_less().then_with(bind([]() { return make_equal(); })), make_less()); 84 | EXPECT_EQ( 85 | make_greater().then_with(bind([]() { return make_equal(); })), 86 | make_greater()); 87 | } 88 | 89 | 90 | GTEST_TEST(cmp, min_max) { 91 | EXPECT_EQ(tuple(0, 'b'), min(tuple(0, 'b'), tuple(1, 'a'))); 92 | EXPECT_EQ(tuple(1, 'a'), max(tuple(0, 'b'), tuple(1, 'a'))); 93 | 94 | EXPECT_EQ( 95 | tuple(0, 'b'), 96 | min_by_key( 97 | tuple(0, 'b'), tuple(1, 'a'), bind([](const Tuple &value) { 98 | return value.get<0>(); 99 | }))); 100 | 101 | EXPECT_EQ( 102 | tuple(1, 'a'), 103 | min_by_key( 104 | tuple(0, 'b'), tuple(1, 'a'), bind([](const Tuple &value) { 105 | return value.get<1>(); 106 | }))); 107 | 108 | EXPECT_EQ( 109 | tuple(1, 'a'), 110 | max_by_key( 111 | tuple(0, 'b'), tuple(1, 'a'), bind([](const Tuple &value) { 112 | return value.get<0>(); 113 | }))); 114 | 115 | EXPECT_EQ( 116 | tuple(0, 'b'), 117 | max_by_key( 118 | tuple(0, 'b'), tuple(1, 'a'), bind([](const Tuple &value) { 119 | return value.get<1>(); 120 | }))); 121 | } 122 | 123 | GTEST_TEST(cmp, reverse) { 124 | crust_static_assert(sizeof(cmp::Reverse) == sizeof(i32)); 125 | crust_static_assert(Require, PartialEq>::result); 126 | crust_static_assert(Require, Eq>::result); 127 | crust_static_assert(Require, PartialOrd>::result); 128 | crust_static_assert(Require, Ord>::result); 129 | 130 | cmp::Reverse a{0}; 131 | cmp::Reverse b{1}; 132 | 133 | EXPECT_TRUE(a > b); 134 | } 135 | -------------------------------------------------------------------------------- /test/enum.cpp: -------------------------------------------------------------------------------- 1 | #include "gtest/gtest.h" 2 | 3 | #include "crust/cmp.hpp" 4 | #include "crust/enum.hpp" 5 | #include "crust/utility.hpp" 6 | 7 | #include "raii_checker.hpp" 8 | 9 | 10 | using namespace crust; 11 | 12 | template 13 | struct VisitType { 14 | constexpr bool operator()(const T &) const { return true; } 15 | 16 | template 17 | constexpr bool operator()(const U &) const { 18 | return false; 19 | } 20 | }; 21 | 22 | struct ClassA; 23 | 24 | struct ClassB; 25 | 26 | struct EnumA; 27 | 28 | namespace crust { 29 | template <> 30 | CRUST_IMPL_FOR(clone::Clone){}; 31 | 32 | template <> 33 | CRUST_IMPL_FOR(clone::Clone){}; 34 | 35 | template <> 36 | struct BluePrint : TmplType> {}; 37 | } // namespace crust 38 | 39 | struct ClassA : test::RAIIChecker, Impl> { 40 | CRUST_USE_BASE_CONSTRUCTORS(ClassA, test::RAIIChecker); 41 | }; 42 | 43 | struct ClassB : test::RAIIChecker, Impl> { 44 | CRUST_USE_BASE_CONSTRUCTORS(ClassB, test::RAIIChecker); 45 | }; 46 | 47 | struct EnumA : Enum, Derive> { 48 | CRUST_ENUM_USE_BASE(EnumA, Enum); 49 | }; 50 | 51 | GTEST_TEST(enum_, enum_) { 52 | crust_static_assert(!Require::result); 53 | crust_static_assert(!Require::result); 54 | 55 | auto recorder = std::make_shared(test::RAIIRecorder{}); 56 | 57 | EnumA a; 58 | a = EnumA{ClassA{recorder}}; 59 | EXPECT_TRUE(a.visit(VisitType{})); 60 | a = EnumA{ClassB{recorder}}; 61 | EXPECT_TRUE(a.visit(VisitType{})); 62 | a = ClassA{recorder}; 63 | EXPECT_TRUE(a.visit(VisitType{})); 64 | a = ClassB{recorder}; 65 | EXPECT_TRUE(a.visit(VisitType{})); 66 | 67 | EnumA b; 68 | b = a; 69 | EXPECT_TRUE(b.visit(VisitType{})); 70 | 71 | a = ClassA{recorder}; 72 | b = a.clone(); 73 | EXPECT_TRUE(b.visit(VisitType{})); 74 | 75 | a = ClassB{recorder}; 76 | b.clone_from(a); 77 | EXPECT_TRUE(b.visit(VisitType{})); 78 | 79 | b = move(a); 80 | EXPECT_TRUE(b.visit(VisitType{})); 81 | 82 | EnumA c{b}; 83 | EXPECT_TRUE(b.visit(VisitType{})); 84 | 85 | EnumA d{move(c)}; 86 | EXPECT_TRUE(d.visit(VisitType{})); 87 | } 88 | 89 | class A; 90 | class B; 91 | class C; 92 | class D; 93 | class E; 94 | class F; 95 | struct EnumB; 96 | struct EnumC; 97 | struct EnumD; 98 | struct EnumE; 99 | 100 | namespace crust { 101 | template <> 102 | struct BluePrint : TmplType> {}; 103 | 104 | template <> 105 | struct BluePrint : TmplType> {}; 106 | 107 | template <> 108 | struct BluePrint : TmplType> {}; 109 | 110 | template <> 111 | struct BluePrint : TmplType> {}; 112 | 113 | template <> 114 | struct BluePrint : TmplType> {}; 115 | 116 | template <> 117 | struct BluePrint : TmplType> {}; 118 | 119 | template <> 120 | struct BluePrint : TmplType> {}; 121 | 122 | template <> 123 | struct BluePrint : TmplType> {}; 124 | 125 | template <> 126 | struct BluePrint : TmplType> {}; 127 | 128 | template <> 129 | struct BluePrint : TmplType> {}; 130 | } // namespace crust 131 | 132 | CRUST_ENUM_VARIANT(A); 133 | CRUST_ENUM_VARIANT(B); 134 | CRUST_ENUM_TUPLE_VARIANT(C, C, i32); 135 | CRUST_ENUM_TUPLE_VARIANT(D, D, i32); 136 | CRUST_ENUM_TUPLE_VARIANT(E, E, i32); 137 | CRUST_ENUM_TUPLE_VARIANT(F, F, i32); 138 | 139 | struct crust_ebco EnumB : 140 | Enum, 141 | Derive, Trait> { 142 | CRUST_ENUM_USE_BASE(EnumB, Enum); 143 | }; 144 | 145 | struct crust_ebco EnumC : 146 | Enum, 147 | Derive, Trait> { 148 | CRUST_ENUM_USE_BASE(EnumC, Enum); 149 | }; 150 | 151 | struct crust_ebco EnumD : 152 | Enum, A, B>, 153 | Derive, Trait> { 154 | CRUST_ENUM_USE_BASE(EnumD, Enum, A, B>); 155 | }; 156 | 157 | struct crust_ebco EnumE : 158 | Enum, A, B, C, D, E, F>, 159 | Derive, Trait> { 160 | CRUST_ENUM_USE_BASE(EnumE, Enum, A, B, C, D, E, F>); 161 | }; 162 | 163 | 164 | GTEST_TEST(enum_, tag_only) { 165 | crust_static_assert(sizeof(EnumB) == sizeof(i32)); 166 | crust_static_assert(sizeof(EnumC) == 2 * sizeof(i32)); 167 | crust_static_assert(sizeof(EnumD) == sizeof(i16)); 168 | crust_static_assert(sizeof(EnumE) == 2 * sizeof(isize)); 169 | } 170 | 171 | GTEST_TEST(enum_, raii) { 172 | crust_static_assert(std::is_trivially_copyable::value); 173 | crust_static_assert(std::is_standard_layout::value); 174 | 175 | EnumC a; 176 | a = A{}; 177 | EXPECT_TRUE(a.visit(VisitType{})); 178 | a = B{}; 179 | EXPECT_TRUE(a.visit(VisitType{})); 180 | a = C{}; 181 | EXPECT_TRUE(a.visit(VisitType{})); 182 | a = D{}; 183 | EXPECT_TRUE(a.visit(VisitType{})); 184 | a = E{}; 185 | EXPECT_TRUE(a.visit(VisitType{})); 186 | a = F{}; 187 | EXPECT_TRUE(a.visit(VisitType{})); 188 | } 189 | 190 | struct EnumF; 191 | 192 | namespace crust { 193 | template <> 194 | struct BluePrint : TmplType> {}; 195 | } // namespace crust 196 | 197 | 198 | struct crust_ebco EnumF : 199 | Enum, 200 | Derive, Trait> { 201 | CRUST_ENUM_USE_BASE(EnumF, Enum); 202 | }; 203 | 204 | 205 | GTEST_TEST(enum_, cmp) { 206 | EXPECT_TRUE(EnumF{0} == EnumF{0}); 207 | EXPECT_TRUE(EnumF{0} != EnumF{'a'}); 208 | EXPECT_TRUE(EnumF{0} != EnumF{1}); 209 | } 210 | -------------------------------------------------------------------------------- /include/crust/helper/types.hpp: -------------------------------------------------------------------------------- 1 | #ifndef CRUST_HELPER_TYPES_HPP 2 | #define CRUST_HELPER_TYPES_HPP 3 | 4 | 5 | #include "crust/num/mod.hpp" 6 | #include "crust/utility.hpp" 7 | 8 | 9 | namespace crust { 10 | namespace _impl_types { 11 | template 12 | struct Types : TmplVal {}; 13 | 14 | template 15 | struct TypesPushFront; 16 | 17 | template 18 | struct TypesPushFront> : TmplType> {}; 19 | 20 | template 21 | struct TypesIndex; 22 | 23 | template 24 | struct TypesIndex> : 25 | TypesIndex> {}; 26 | 27 | template 28 | struct TypesIndex<0, Types> : TmplType {}; 29 | 30 | template 31 | struct TypesAfter; 32 | 33 | template 34 | struct TypesAfter> : 35 | TypesAfter> {}; 36 | 37 | template 38 | struct TypesAfter<0, Types> : TmplType> {}; 39 | 40 | template 41 | struct TypesBefore; 42 | 43 | template 44 | struct TypesBefore> : 45 | TypesPushFront< 46 | Field, 47 | typename TypesBefore>::Result> {}; 48 | 49 | template 50 | struct TypesBefore<0, Types> : TmplType> {}; 51 | 52 | template 53 | struct TypesCountType; 54 | 55 | template 56 | struct TypesCountType> : 57 | SumVal>...> {}; 58 | 59 | template 60 | struct TypesIncludeVal : GTVal, TmplVal> {}; 61 | 62 | template 63 | struct TypesDuplicateVal; 64 | 65 | template 66 | struct TypesDuplicateVal> : 67 | Any>, 68 | TypesDuplicateVal>> {}; 69 | 70 | template <> 71 | struct TypesDuplicateVal> : BoolVal {}; 72 | 73 | template 74 | struct TypesFirstIndex; 75 | 76 | template 77 | struct TypesFirstIndex> : 78 | IncVal>> {}; 79 | 80 | template 81 | struct TypesFirstIndex> : TmplVal {}; 82 | 83 | template 84 | struct TypesPrevType : 85 | TypesIndex< 86 | DecVal>>::result, 87 | Types> {}; 88 | 89 | template 90 | struct TypesContainVal; 91 | 92 | template 93 | struct TypesContainVal> : 94 | Any...> {}; 95 | 96 | template 97 | using ZeroSizedTypeHolderFieldEnable = 98 | All::Result, ZeroSizedType>, 99 | Not::Result, 101 | typename TypesBefore::Result>>, 102 | Not::Result, Ts>>>; 103 | 104 | template 105 | struct TypesFirstContain : 106 | IfElse< 107 | All, 108 | ZeroSizedTypeConvertible< 109 | typename TypesIndex::Result, 110 | T>>, 111 | TmplVal, 112 | TypesFirstContain> {}; 113 | 114 | template 115 | using ZeroSizedTypeHolderField = InheritIf< 116 | typename TypesIndex::Result, 117 | ZeroSizedTypeHolderFieldEnable, 118 | TypesIndex>; 119 | 120 | template 121 | struct crust_ebco ZeroSizedTypeHolderImpl : 122 | ZeroSizedTypeHolderField, 123 | ZeroSizedTypeHolderImpl { 124 | constexpr ZeroSizedTypeHolderImpl() {} 125 | }; 126 | 127 | template 128 | struct ZeroSizedTypeHolderImpl<0, Ts> {}; 129 | 130 | template 131 | using ZeroSizedTypeHolder = 132 | ZeroSizedTypeHolderImpl>; 133 | 134 | template 135 | struct ZeroSizedTypeGetterImpl; 136 | 137 | template 138 | using ZeroSizedTypeGetter = ZeroSizedTypeGetterImpl< 139 | TypesContainVal< 140 | typename TypesIndex>::Result, 141 | Types>::result, 142 | index, 143 | Fields...>; 144 | 145 | template 146 | struct ZeroSizedTypeGetterImpl { 147 | using Self = ZeroSizedTypeHolder; 148 | using Result = typename TypesIndex>::Result; 149 | using Field = ZeroSizedTypeHolderField< 150 | TypesFirstIndex>::result, 151 | Types>; 152 | 153 | static constexpr const Result &inner(const Self &self) { 154 | return static_cast(self); 155 | } 156 | 157 | static constexpr Result &inner(Self &self) { 158 | return static_cast(self); 159 | } 160 | }; 161 | 162 | template 163 | struct ZeroSizedTypeGetterImpl { 164 | using Self = ZeroSizedTypeHolder; 165 | using Result = typename TypesIndex>::Result; 166 | static constexpr usize field_index = 167 | TypesFirstContain>::result; 168 | using Field = ZeroSizedTypeHolderField>; 169 | using Convert = ZeroSizedTypeConvert< 170 | typename TypesIndex>::Result, 171 | Result>; 172 | 173 | static constexpr const Result &inner(const Self &self) { 174 | return Convert::inner(static_cast(self)); 175 | } 176 | 177 | static constexpr Result &inner(Self &self) { 178 | return Convert::inner(static_cast(self)); 179 | } 180 | }; 181 | } // namespace _impl_types 182 | } // namespace crust 183 | 184 | 185 | #endif // CRUST_HELPER_TYPES_HPP 186 | -------------------------------------------------------------------------------- /include/crust/cmp_decl.hpp: -------------------------------------------------------------------------------- 1 | #ifndef CRUST_CMP_DECL_HPP 2 | #define CRUST_CMP_DECL_HPP 3 | 4 | 5 | #include "crust/utility.hpp" 6 | 7 | 8 | namespace crust { 9 | namespace option { 10 | template 11 | struct Option; 12 | } // namespace option 13 | 14 | using option::Option; 15 | 16 | namespace cmp { 17 | struct Ordering; 18 | 19 | CRUST_TRAIT(PartialEq, class Rhs = Self) { 20 | CRUST_TRAIT_USE_SELF(PartialEq); 21 | 22 | bool eq(const Rhs &other) const; 23 | 24 | constexpr bool ne(const Rhs &other) const { return !self().eq(other); } 25 | 26 | constexpr friend bool operator==(const Self &self_, const Rhs &other) { 27 | return static_cast> &>(self_).eq(other); 28 | } 29 | 30 | constexpr friend bool operator!=(const Self &self_, const Rhs &other) { 31 | return static_cast> &>(self_).ne(other); 32 | } 33 | }; 34 | 35 | CRUST_TRAIT(Eq) { CRUST_TRAIT_USE_SELF(Eq, Require); }; 36 | 37 | CRUST_TRAIT(PartialOrd, class Rhs = Self) { 38 | CRUST_TRAIT_USE_SELF(PartialOrd, Require); 39 | 40 | Option partial_cmp(const Rhs &other) const; 41 | 42 | constexpr bool lt(const Rhs &other) const; 43 | 44 | constexpr bool le(const Rhs &other) const; 45 | 46 | constexpr bool gt(const Rhs &other) const; 47 | 48 | constexpr bool ge(const Rhs &other) const; 49 | 50 | constexpr friend bool operator<(const Self &self_, const Rhs &other) { 51 | return static_cast> &>(self_).lt(other); 52 | } 53 | 54 | constexpr friend bool operator<=(const Self &self_, const Rhs &other) { 55 | return static_cast> &>(self_).le(other); 56 | } 57 | 58 | constexpr friend bool operator>(const Self &self_, const Rhs &other) { 59 | return static_cast> &>(self_).gt(other); 60 | } 61 | 62 | constexpr friend bool operator>=(const Self &self_, const Rhs &other) { 63 | return static_cast> &>(self_).ge(other); 64 | } 65 | }; 66 | 67 | CRUST_TRAIT(Ord) { 68 | CRUST_TRAIT_USE_SELF( 69 | Ord, 70 | Require, 71 | Require, 72 | Require); 73 | 74 | Ordering cmp(const Self &other) const; 75 | 76 | crust_cxx14_constexpr Self max(Self && other) && { 77 | return self>().gt(other) ? 78 | move(*static_cast(this)) : 79 | move(other); 80 | } 81 | 82 | crust_cxx14_constexpr Self min(Self && other) && { 83 | return self>().gt(other) ? 84 | move(other) : 85 | move(*static_cast(this)); 86 | } 87 | 88 | crust_cxx14_constexpr Self clamp(Self && min, Self && max) && { 89 | return crust_debug_assert(min <= max), 90 | self>().lt(min) ? 91 | move(min) : 92 | (self>().gt(max) ? 93 | move(max) : 94 | move(*static_cast(this))); 95 | } 96 | }; 97 | } // namespace cmp 98 | 99 | namespace _impl_derive { 100 | template ::result> 101 | struct TupleLikePartialEqHelper { 102 | static constexpr usize index = TupleLikeSize::result - rev_index; 103 | using Getter = TupleLikeGetter; 104 | using Remains = TupleLikePartialEqHelper; 105 | 106 | static constexpr bool eq(const Self &a, const Self &b) { 107 | return Getter::get(a) == Getter::get(b) && Remains::eq(a, b); 108 | } 109 | 110 | static constexpr bool ne(const Self &a, const Self &b) { 111 | return Getter::get(a) != Getter::get(b) || Remains::ne(a, b); 112 | } 113 | }; 114 | 115 | template 116 | struct TupleLikePartialEqHelper { 117 | static constexpr bool eq(const Self &, const Self &) { return true; } 118 | 119 | static constexpr bool ne(const Self &, const Self &) { return false; } 120 | }; 121 | 122 | template ::result> 123 | struct TupleLikePartialOrdHelper { 124 | static constexpr usize index = TupleLikeSize::result - rev_index; 125 | using Getter = TupleLikeGetter; 126 | using Remains = TupleLikePartialOrdHelper; 127 | 128 | static constexpr Option 129 | partial_cmp(const Self &a, const Self &b); 130 | 131 | static constexpr bool lt(const Self &a, const Self &b) { 132 | return Getter::get(a) != Getter::get(b) ? Getter::get(a) < Getter::get(b) : 133 | Remains::lt(a, b); 134 | } 135 | 136 | static constexpr bool le(const Self &a, const Self &b) { 137 | return Getter::get(a) != Getter::get(b) ? Getter::get(a) <= Getter::get(b) : 138 | Remains::le(a, b); 139 | } 140 | 141 | static constexpr bool gt(const Self &a, const Self &b) { 142 | return Getter::get(a) != Getter::get(b) ? Getter::get(a) > Getter::get(b) : 143 | Remains::gt(a, b); 144 | } 145 | 146 | static constexpr bool ge(const Self &a, const Self &b) { 147 | return Getter::get(a) != Getter::get(b) ? Getter::get(a) >= Getter::get(b) : 148 | Remains::ge(a, b); 149 | } 150 | }; 151 | 152 | template 153 | struct TupleLikePartialOrdHelper { 154 | static constexpr Option 155 | partial_cmp(const Self &, const Self &); 156 | 157 | static constexpr bool lt(const Self &, const Self &) { return false; } 158 | 159 | static constexpr bool le(const Self &, const Self &) { return true; } 160 | 161 | static constexpr bool gt(const Self &, const Self &) { return false; } 162 | 163 | static constexpr bool ge(const Self &, const Self &) { return true; } 164 | }; 165 | 166 | template ::result> 167 | struct TupleLikeOrdHelper { 168 | static constexpr usize index = TupleLikeSize::result - rev_index; 169 | using Getter = TupleLikeGetter; 170 | using Remains = TupleLikeOrdHelper; 171 | 172 | static constexpr cmp::Ordering cmp(const Self &a, const Self &b); 173 | }; 174 | 175 | template 176 | struct TupleLikeOrdHelper { 177 | static constexpr cmp::Ordering cmp(const Self &, const Self &); 178 | }; 179 | } // namespace _impl_derive 180 | } // namespace crust 181 | 182 | 183 | #endif // CRUST_CMP_DECL_HPP 184 | -------------------------------------------------------------------------------- /test/tuple.cpp: -------------------------------------------------------------------------------- 1 | #include "gtest/gtest.h" 2 | 3 | #include "crust/clone.hpp" 4 | #include "crust/cmp.hpp" 5 | #include "crust/tuple.hpp" 6 | #include "crust/utility.hpp" 7 | 8 | #include "raii_checker.hpp" 9 | 10 | 11 | #include 12 | 13 | using namespace crust; 14 | using namespace cmp; 15 | 16 | 17 | namespace { 18 | struct C; 19 | 20 | struct D; 21 | } // namespace 22 | 23 | namespace crust { 24 | template <> 25 | CRUST_IMPL_FOR(clone::Clone){}; 26 | 27 | template <> 28 | CRUST_IMPL_FOR(clone::Clone){}; 29 | } // namespace crust 30 | 31 | namespace { 32 | struct A {}; 33 | 34 | struct B : A {}; 35 | 36 | struct C : test::RAIIChecker, Impl> { 37 | CRUST_USE_BASE_CONSTRUCTORS(C, test::RAIIChecker); 38 | }; 39 | 40 | struct D : test::RAIIChecker, Impl> { 41 | CRUST_USE_BASE_CONSTRUCTORS(D, test::RAIIChecker); 42 | }; 43 | } // namespace 44 | 45 | 46 | GTEST_TEST(tuple, size_zero) { 47 | crust_static_assert(Require, ZeroSizedType>::result); 48 | crust_static_assert(Require>, ZeroSizedType>::result); 49 | crust_static_assert(Require, Tuple<>>, ZeroSizedType>::result); 50 | crust_static_assert( 51 | Require, Tuple<>, Tuple<>>, ZeroSizedType>::result); 52 | 53 | crust_static_assert(sizeof(Tuple<>) == 1); 54 | crust_static_assert(sizeof(Tuple>) == 1); 55 | crust_static_assert(sizeof(Tuple, Tuple<>>) == 1); 56 | crust_static_assert(sizeof(Tuple, Tuple<>, Tuple<>>) == 1); 57 | crust_static_assert(sizeof(Tuple, Tuple<>, Tuple>>) == 1); 58 | crust_static_assert(sizeof(Tuple, Tuple>, Tuple<>>) == 1); 59 | crust_static_assert(sizeof(Tuple>, Tuple<>, Tuple<>>) == 1); 60 | crust_static_assert( 61 | sizeof(Tuple, Tuple>, Tuple>>>) == 1); 62 | crust_static_assert( 63 | sizeof(Tuple, Tuple>>, Tuple>>) == 1); 64 | crust_static_assert( 65 | sizeof(Tuple>, Tuple<>, Tuple>>>) == 1); 66 | crust_static_assert( 67 | sizeof(Tuple>, Tuple>>, Tuple<>>) == 1); 68 | crust_static_assert( 69 | sizeof(Tuple>>, Tuple<>, Tuple>>) == 1); 70 | crust_static_assert( 71 | sizeof(Tuple>>, Tuple>, Tuple<>>) == 1); 72 | crust_static_assert(sizeof(Tuple, Tuple<>>) == sizeof(i32)); 73 | crust_static_assert(sizeof(Tuple, i32, Tuple<>>) == sizeof(i32)); 74 | crust_static_assert(sizeof(Tuple, Tuple<>, i32>) == sizeof(i32)); 75 | crust_static_assert( 76 | sizeof(Tuple, Tuple<>>) == 2 * sizeof(i32)); 77 | crust_static_assert( 78 | sizeof(Tuple, i32, Tuple<>>) == 2 * sizeof(i32)); 79 | crust_static_assert( 80 | sizeof(Tuple, Tuple<>, i32>) == 2 * sizeof(i32)); 81 | crust_static_assert( 82 | sizeof(Tuple, Tuple>) == 2 * sizeof(i32)); 83 | crust_static_assert( 84 | sizeof(Tuple, i32, Tuple>) == 2 * sizeof(i32)); 85 | crust_static_assert( 86 | sizeof(Tuple, Tuple, i32>) == 2 * sizeof(i32)); 87 | 88 | constexpr auto empty_1 = tuple(tuple(), tuple(), tuple()); 89 | crust_static_assert(empty_1.get<0>() == tuple()); 90 | crust_static_assert(empty_1.get<1>() == tuple()); 91 | crust_static_assert(empty_1.get<2>() == tuple()); 92 | 93 | constexpr auto empty_2 = tuple(tuple(), 1); 94 | crust_static_assert(empty_2.get<0>() == tuple()); 95 | crust_static_assert(empty_2.get<1>() == 1); 96 | 97 | constexpr auto empty_3 = tuple(tuple(), tuple(tuple())); 98 | crust_static_assert(empty_3.get<0>() == tuple()); 99 | crust_static_assert(empty_3.get<1>() == tuple(tuple())); 100 | crust_static_assert(empty_3.get<1>().get<0>() == tuple()); 101 | 102 | constexpr auto empty_4 = tuple(tuple(tuple()), tuple()); 103 | crust_static_assert(empty_4.get<0>() == tuple(tuple())); 104 | crust_static_assert(empty_4.get<1>() == tuple()); 105 | crust_static_assert(empty_4.get<0>().get<0>() == tuple()); 106 | 107 | constexpr auto empty_5 = 108 | tuple(tuple(tuple()), tuple(tuple(tuple())), tuple()); 109 | crust_static_assert(empty_5.get<0>() == tuple(tuple())); 110 | crust_static_assert(empty_5.get<0>().get<0>() == tuple()); 111 | crust_static_assert(empty_5.get<1>() == tuple(tuple(tuple()))); 112 | crust_static_assert(empty_5.get<1>().get<0>() == tuple(tuple())); 113 | crust_static_assert(empty_5.get<1>().get<0>().get<0>() == tuple()); 114 | crust_static_assert(empty_5.get<2>() == tuple()); 115 | 116 | crust_static_assert(Require, PartialEq>::result); 117 | crust_static_assert(Require, Eq>::result); 118 | 119 | crust_static_assert(std::is_trivially_copyable>::value); 120 | 121 | crust_static_assert(tuple() == tuple()); 122 | crust_static_assert(!(tuple() != tuple())); 123 | crust_static_assert(!(tuple() < tuple())); 124 | crust_static_assert(tuple() <= tuple()); 125 | crust_static_assert(!(tuple() > tuple())); 126 | crust_static_assert(tuple() >= tuple()); 127 | EXPECT_EQ(operator_partial_cmp(tuple(), tuple()), make_some(make_equal())); 128 | EXPECT_EQ(operator_cmp(tuple(), tuple()), make_equal()); 129 | } 130 | 131 | GTEST_TEST(tuple, size_one) { 132 | crust_static_assert(!Require, ZeroSizedType>::result); 133 | 134 | crust_static_assert(!Require, PartialEq>::result); 135 | crust_static_assert(!Require, Eq>::result); 136 | 137 | crust_static_assert(std::is_trivially_copyable>::value); 138 | 139 | crust_static_assert(Require, PartialEq>::result); 140 | crust_static_assert(Require, Eq>::result); 141 | 142 | crust_static_assert(tuple(0) == tuple(0)); 143 | crust_static_assert(!(tuple(0) != tuple(0))); 144 | crust_static_assert(!(tuple(0) < tuple(0))); 145 | crust_static_assert(tuple(0) <= tuple(0)); 146 | crust_static_assert(!(tuple(0) > tuple(0))); 147 | crust_static_assert(tuple(0) >= tuple(0)); 148 | EXPECT_EQ(operator_partial_cmp(tuple(0), tuple(0)), make_some(make_equal())); 149 | EXPECT_EQ(operator_cmp(tuple(0), tuple(0)), make_equal()); 150 | 151 | auto value = tuple(0); 152 | 153 | EXPECT_EQ(value.get<0>(), 0); 154 | 155 | ++value.get<0>(); 156 | 157 | EXPECT_EQ(value.get<0>(), 1); 158 | } 159 | 160 | GTEST_TEST(tuple, size_two) { 161 | crust_static_assert(!Require, ZeroSizedType>::result); 162 | 163 | crust_static_assert(!Require, PartialEq>::result); 164 | crust_static_assert(!Require, Eq>::result); 165 | 166 | crust_static_assert(std::is_trivially_copyable>::value); 167 | 168 | crust_static_assert(Require, PartialEq>::result); 169 | crust_static_assert(Require, Eq>::result); 170 | 171 | auto value = tuple(0, 'a'); 172 | 173 | EXPECT_TRUE(value.get<0>() == 0); 174 | EXPECT_TRUE(value.get<1>() == 'a'); 175 | 176 | ++value.get<0>(); 177 | ++value.get<1>(); 178 | 179 | EXPECT_TRUE(std::get<0>(value) == 1); 180 | EXPECT_TRUE(std::get<1>(value) == 'b'); 181 | 182 | crust_static_assert(tuple(true) != tuple(false)); 183 | crust_static_assert(tuple(1) > tuple(0)); 184 | crust_static_assert(tuple(0, 'b') > tuple(0, 'a')); 185 | 186 | EXPECT_TRUE(tuple(0, 1).cmp(tuple(0, 0)) == make_greater()); 187 | } 188 | 189 | GTEST_TEST(tuple, raii) { 190 | auto recorder = std::make_shared(test::RAIIRecorder{}); 191 | 192 | auto a = tuple(C{recorder}, D{recorder}, C{recorder}, D{recorder}); 193 | auto b = a; 194 | auto c = clone::clone(a); 195 | c.clone_from(b); 196 | auto d = move(a); 197 | } 198 | 199 | GTEST_TEST(tuple, tie) { 200 | const auto tuple2 = tuple(1, 2); 201 | const auto tuple7 = tuple(1, 2, 3, 4, 5, 6, 7); 202 | 203 | int a, b; 204 | tie(a, b) = tuple(1, 2); 205 | 206 | EXPECT_EQ(a, 1); 207 | EXPECT_EQ(b, 2); 208 | 209 | Ref c, d; 210 | tie(c, d) = tuple2; 211 | 212 | EXPECT_EQ(*c, 1); 213 | EXPECT_EQ(*d, 2); 214 | 215 | int e, f; 216 | tie(e, _) = tuple(1, 2); 217 | tie(_, f) = tuple(1, 2); 218 | 219 | EXPECT_EQ(e, 1); 220 | EXPECT_EQ(f, 2); 221 | 222 | int g, h; 223 | Ref i, j; 224 | tie(g, i, _, __, h, j) = tuple7; 225 | 226 | EXPECT_EQ(g, 1); 227 | EXPECT_EQ(*i, 2); 228 | EXPECT_EQ(h, 6); 229 | EXPECT_EQ(*j, 7); 230 | 231 | int k, l; 232 | tie(k, __) = tuple(1, 2, 3, 4, 5, 6, 7); 233 | tie(__, l) = tuple(1, 2, 3, 4, 5, 6, 7); 234 | 235 | EXPECT_EQ(k, 1); 236 | EXPECT_EQ(l, 7); 237 | 238 | int m, n, o, p; 239 | tie(tie(m, n), tie(o, p)) = tuple(tuple(1, 2), tuple(3, 4)); 240 | 241 | EXPECT_EQ(m, 1); 242 | EXPECT_EQ(n, 2); 243 | EXPECT_EQ(o, 3); 244 | EXPECT_EQ(p, 4); 245 | 246 | #if __cplusplus > 201402L 247 | auto [a1, b1] = tuple(1, 2); 248 | 249 | EXPECT_EQ(a1, 1); 250 | EXPECT_EQ(b1, 2); 251 | #endif 252 | } 253 | -------------------------------------------------------------------------------- /include/crust/ops/function.hpp: -------------------------------------------------------------------------------- 1 | #ifndef CRUST_OPS_FUNCTION_HPP 2 | #define CRUST_OPS_FUNCTION_HPP 3 | 4 | 5 | #include "crust/tuple_decl.hpp" 6 | #include "crust/utility.hpp" 7 | 8 | 9 | namespace crust { 10 | namespace ops { 11 | namespace _impl_fn { 12 | template < 13 | class Self, 14 | class F = decltype(&Self::operator()), 15 | F f = &Self::operator()> 16 | struct MemFnClosure; 17 | 18 | template 19 | struct MemFnClosure : 20 | TmplType {}; 21 | 22 | template 23 | struct MemFnClosure : 24 | TmplType {}; 25 | 26 | template 27 | struct RawMemFn; 28 | 29 | template 30 | struct RawMemFn { 31 | constexpr Ret operator()(const Self &self, Args... args) const { 32 | return (self.*f)(forward(args)...); 33 | } 34 | }; 35 | 36 | template 37 | struct RawMemFn { 38 | constexpr Ret operator()(Self &self, Args... args) const { 39 | return (self.*f)(forward(args)...); 40 | } 41 | }; 42 | 43 | template 44 | struct RawFn; 45 | 46 | template 47 | struct RawFn { 48 | constexpr Ret operator()(Args... args) const { 49 | return f(forward(args)...); 50 | } 51 | }; 52 | } // namespace _impl_fn 53 | 54 | template ::Result> 55 | struct Fn; 56 | 57 | template 58 | struct Fn { 59 | private: 60 | crust_static_assert(!IsConstOrRefVal::result); 61 | 62 | Self self; 63 | 64 | public: 65 | constexpr Fn(Self &&self) : self{move(self)} {} 66 | 67 | constexpr Ret operator()(Args... args) const { 68 | return self(forward(args)...); 69 | } 70 | }; 71 | 72 | template 73 | crust_always_inline Fn<_impl_fn::RawFn> bind(TmplVal) { 74 | return Fn<_impl_fn::RawFn>{_impl_fn::RawFn{}}; 75 | } 76 | 77 | template 78 | crust_always_inline Fn<_impl_fn::RawFn, T> bind(TmplVal) { 79 | return Fn<_impl_fn::RawFn, T>{_impl_fn::RawFn{}}; 80 | } 81 | 82 | template 83 | crust_always_inline Fn<_impl_fn::RawMemFn> bind(TmplVal) { 84 | return Fn<_impl_fn::RawMemFn>{_impl_fn::RawMemFn{}}; 85 | } 86 | 87 | template 88 | crust_always_inline Fn<_impl_fn::RawMemFn, T> bind(TmplVal) { 89 | return Fn<_impl_fn::RawMemFn, T>{_impl_fn::RawMemFn{}}; 90 | } 91 | 92 | template 93 | crust_always_inline constexpr Fn bind(T &&f) { 94 | return Fn{forward(f)}; 95 | } 96 | 97 | template 98 | crust_always_inline constexpr Fn bind(T &&f) { 99 | return Fn{forward(f)}; 100 | } 101 | 102 | template ::Result> 103 | struct FnMut; 104 | 105 | template 106 | struct FnMut { 107 | private: 108 | crust_static_assert(!IsConstOrRefVal::result); 109 | 110 | Self self; 111 | 112 | public: 113 | constexpr FnMut(Self &&self) : self{move(self)} {} 114 | 115 | crust_cxx14_constexpr Ret operator()(Args... args) { 116 | return self(forward(args)...); 117 | } 118 | }; 119 | 120 | template 121 | crust_always_inline FnMut<_impl_fn::RawFn> bind_mut(TmplVal) { 122 | return FnMut<_impl_fn::RawFn>{_impl_fn::RawFn{}}; 123 | } 124 | 125 | template 126 | crust_always_inline FnMut<_impl_fn::RawFn, T> bind_mut(TmplVal) { 127 | return FnMut<_impl_fn::RawFn, T>{_impl_fn::RawFn{}}; 128 | } 129 | 130 | template 131 | crust_always_inline FnMut<_impl_fn::RawMemFn> bind_mut(TmplVal) { 132 | return FnMut<_impl_fn::RawMemFn>{_impl_fn::RawMemFn{}}; 133 | } 134 | 135 | template 136 | crust_always_inline FnMut<_impl_fn::RawMemFn, T> bind_mut(TmplVal) { 137 | return FnMut<_impl_fn::RawMemFn, T>{_impl_fn::RawMemFn{}}; 138 | } 139 | 140 | template 141 | crust_always_inline constexpr FnMut bind_mut(T &&f) { 142 | return FnMut{forward(f)}; 143 | } 144 | 145 | template 146 | crust_always_inline constexpr FnMut bind_mut(T &&f) { 147 | return FnMut{forward(f)}; 148 | } 149 | 150 | namespace _impl_fn { 151 | template 152 | struct DestructorStaticWrapper { 153 | static crust_cxx14_constexpr void inner(Base *self) { 154 | reinterpret_cast(self)->~Self(); 155 | } 156 | }; 157 | 158 | template 159 | struct FnVTable { 160 | void (*drop)(void *); 161 | usize size; 162 | usize align; 163 | Ret (*call)(const void *, Args...); 164 | }; 165 | 166 | template 167 | struct StaticFnVTable { 168 | static const FnVTable vtable; 169 | 170 | static constexpr Ret call(const void *self, Args &&...args) { 171 | return (*reinterpret_cast(self))(forward(args)...); 172 | } 173 | }; 174 | 175 | template 176 | const FnVTable StaticFnVTable::vtable{ 177 | DestructorStaticWrapper::inner, 178 | sizeof(Self), 179 | alignof(Self), 180 | call, 181 | }; 182 | 183 | template 184 | struct FnMutVTable { 185 | void (*drop)(void *); 186 | usize size; 187 | usize align; 188 | Ret (*call)(void *, Args...); 189 | }; 190 | 191 | template 192 | struct StaticFnMutVTable { 193 | static const FnMutVTable vtable; 194 | 195 | static crust_cxx14_constexpr Ret call(void *self, Args... args) { 196 | return (*reinterpret_cast(self))(forward(args)...); 197 | } 198 | }; 199 | 200 | template 201 | const FnMutVTable StaticFnMutVTable::vtable{ 202 | DestructorStaticWrapper::inner, 203 | sizeof(Self), 204 | alignof(Self), 205 | call, 206 | }; 207 | } // namespace _impl_fn 208 | 209 | template 210 | struct DynFn; 211 | 212 | template 213 | struct DynFnMut; 214 | 215 | template 216 | struct DynFn { // TODO: optimize for zero sized type 217 | private: 218 | void *self; 219 | const _impl_fn::FnVTable *vtable; 220 | 221 | void drop() { 222 | if (self != nullptr) { 223 | vtable->drop(self); 224 | delete reinterpret_cast(self); 225 | self = nullptr; 226 | } 227 | } 228 | 229 | void move_from(DynFn &&other) { 230 | self = other.self; 231 | vtable = other.vtable; 232 | other.self = nullptr; 233 | } 234 | 235 | public: 236 | template 237 | constexpr DynFn(Self &&self) : 238 | self{new Self{forward(self)}}, 239 | vtable{&_impl_fn::StaticFnVTable::vtable} {} 240 | 241 | template 242 | constexpr DynFn(TmplVal) : DynFn{_impl_fn::RawFn{}} {} 243 | 244 | DynFn(DynFn &&other) noexcept { move_from(move(other)); } 245 | 246 | DynFn &operator=(DynFn &&other) noexcept { 247 | if (this != &other) { 248 | drop(); 249 | move_from(move(other)); 250 | } 251 | 252 | return *this; 253 | } 254 | 255 | constexpr Ret operator()(Args &&...args) const { 256 | return vtable->call(self, forward(args)...); 257 | } 258 | 259 | ~DynFn() { drop(); } 260 | }; 261 | 262 | template 263 | struct DynFnMut { 264 | private: 265 | void *self; 266 | const _impl_fn::FnMutVTable *vtable; 267 | 268 | void drop() { 269 | if (self != nullptr) { 270 | vtable->drop(self); 271 | delete reinterpret_cast(self); 272 | self = nullptr; 273 | } 274 | } 275 | 276 | void move_from(DynFnMut &&other) { 277 | self = other.self; 278 | vtable = other.vtable; 279 | other.self = nullptr; 280 | } 281 | 282 | public: 283 | template 284 | constexpr DynFnMut(Self &&self) : 285 | self{new Self{forward(self)}}, 286 | vtable{&_impl_fn::StaticFnMutVTable::vtable} {} 287 | 288 | template 289 | constexpr DynFnMut(TmplVal) : DynFnMut{_impl_fn::RawFn{}} {} 290 | 291 | DynFnMut(DynFnMut &&other) noexcept { move_from(move(other)); } 292 | 293 | DynFnMut &operator=(DynFnMut &&other) noexcept { 294 | if (this != &other) { 295 | drop(); 296 | move_from(move(other)); 297 | } 298 | 299 | return *this; 300 | } 301 | 302 | crust_cxx14_constexpr Ret operator()(Args... args) { 303 | return vtable->call(self, forward(args)...); 304 | } 305 | 306 | ~DynFnMut() { drop(); } 307 | }; 308 | } // namespace ops 309 | } // namespace crust 310 | 311 | 312 | #endif // CRUST_OPS_FUNCTION_HPP 313 | -------------------------------------------------------------------------------- /include/crust/tuple.hpp: -------------------------------------------------------------------------------- 1 | #ifndef CRUST_TUPLE_HPP 2 | #define CRUST_TUPLE_HPP 3 | 4 | 5 | #include "tuple_decl.hpp" 6 | 7 | #include 8 | 9 | #include "crust/cmp.hpp" 10 | #include "crust/option.hpp" 11 | 12 | 13 | namespace crust { 14 | template 15 | constexpr Option ImplFor< 16 | cmp::PartialOrd, 17 | EnableIf<_impl_derive::ImplForTupleStruct>>:: 18 | partial_cmp(const Self &other) const { 19 | return PartialOrdHelper::partial_cmp(self(), other); 20 | } 21 | 22 | template 23 | constexpr cmp::Ordering 24 | ImplFor, EnableIf<_impl_derive::ImplForTupleStruct>>:: 25 | cmp(const Self &other) const { 26 | return OrdHelper::cmp(self(), other); 27 | } 28 | 29 | template 30 | constexpr Tuple::Result...> 31 | tuple(Fields &&...fields) { 32 | return Tuple::Result...>{ 33 | forward(fields)...}; 34 | } 35 | 36 | namespace _impl_tuple { 37 | template 38 | struct TieTuple; 39 | 40 | template 41 | struct LetField { 42 | T *ptr; 43 | 44 | constexpr LetField(T &ref) : ptr{&ref} {} 45 | 46 | crust_cxx14_constexpr void assign_ref(const T &other) { *ptr = other; } 47 | 48 | crust_cxx14_constexpr void assign_ref_mut(T &other) { *ptr = other; } 49 | 50 | crust_cxx14_constexpr void assign_move(T &&other) { *ptr = move(other); } 51 | }; 52 | 53 | template 54 | struct LetField> { 55 | TieTuple fields; 56 | 57 | constexpr LetField(TieTuple &&fields) : fields{move(fields)} {} 58 | 59 | template 60 | crust_cxx14_constexpr void assign_ref(const T &other) { 61 | fields = other; 62 | } 63 | 64 | template 65 | crust_cxx14_constexpr void assign_ref_mut(T &other) { 66 | fields = other; 67 | } 68 | 69 | template 70 | crust_cxx14_constexpr void assign_move(T &&other) { 71 | fields = move(other); 72 | } 73 | }; 74 | 75 | template <> 76 | struct LetField<_impl_utility::Ignore> { 77 | constexpr LetField(const _impl_utility::Ignore &) {} 78 | 79 | template 80 | crust_cxx14_constexpr void assign_ref(const T &) const {} 81 | 82 | template 83 | crust_cxx14_constexpr void assign_ref_mut(T &) const {} 84 | 85 | template 86 | crust_cxx14_constexpr void assign_move(T &&) const {} 87 | }; 88 | 89 | template <> 90 | struct LetField<_impl_utility::IgnoreRange> { 91 | constexpr LetField(const _impl_utility::IgnoreRange &) {} 92 | 93 | template 94 | crust_cxx14_constexpr void assign_ref(const T &) const {} 95 | 96 | template 97 | crust_cxx14_constexpr void assign_ref_mut(T &) const {} 98 | 99 | template 100 | crust_cxx14_constexpr void assign_move(T &&) const {} 101 | }; 102 | 103 | template 104 | struct LetField> { 105 | Ref *ptr; 106 | 107 | constexpr LetField(Ref &ref) : ptr{&ref} {} 108 | 109 | crust_cxx14_constexpr void assign_ref(const Ref &other) { *ptr = other; } 110 | 111 | crust_cxx14_constexpr void assign_ref_mut(Ref &other) { *ptr = other; } 112 | 113 | crust_cxx14_constexpr void assign_move(Ref &&other) { *ptr = move(other); } 114 | 115 | crust_cxx14_constexpr void assign_ref(const T &other) { *ptr = ref(other); } 116 | 117 | crust_cxx14_constexpr void assign_ref_mut(T &other) { *ptr = ref(other); } 118 | }; 119 | 120 | template 121 | struct LetField> { 122 | RefMut *ptr; 123 | 124 | constexpr LetField(RefMut &ref) : ptr{&ref} {} 125 | 126 | crust_cxx14_constexpr void assign_ref(const RefMut &other) { 127 | *ptr = other; 128 | } 129 | 130 | crust_cxx14_constexpr void assign_ref_mut(RefMut &other) { *ptr = other; } 131 | 132 | crust_cxx14_constexpr void assign_move(RefMut &&other) { 133 | *ptr = move(other); 134 | } 135 | 136 | crust_cxx14_constexpr void assign(RefMut &other) { *ptr = move(other); } 137 | 138 | crust_cxx14_constexpr void assign_ref_mut(T &other) { *ptr = ref_mut(other); } 139 | }; 140 | 141 | template 142 | struct TieTupleHelperRemain; 143 | 144 | template 145 | struct TieTupleHelper { 146 | static constexpr usize index1 = 147 | _impl_derive::TupleLikeSize::result - r1_idx; 148 | static constexpr usize index2 = 149 | _impl_derive::TupleLikeSize::result - r2_idx; 150 | using Getter = _impl_derive::TupleLikeGetter; 151 | using This = typename _impl_derive::TupleLikeGetter::Result; 152 | static constexpr bool is_range = 153 | IsSame>::result; 154 | using Remains = 155 | typename TieTupleHelperRemain:: 156 | Result; 157 | 158 | static crust_cxx14_constexpr void assign_ref(Self &ref, const Base &tuple) { 159 | ref.template get().assign_ref(Getter::get(tuple)); 160 | Remains::assign_ref(ref, tuple); 161 | } 162 | 163 | static crust_cxx14_constexpr void assign_ref_mut(Self &ref, Base &tuple) { 164 | ref.template get().assign_ref_mut(Getter::get(tuple)); 165 | Remains::assign_ref_mut(ref, tuple); 166 | } 167 | 168 | static crust_cxx14_constexpr void assign_move(Self &ref, Base &&tuple) { 169 | ref.template get().assign_move(move(Getter::get(tuple))); 170 | Remains::assign_move(ref, move(tuple)); 171 | } 172 | }; 173 | 174 | template 175 | struct TieTupleHelper<0, 0, Self, Base> { 176 | static crust_cxx14_constexpr void assign_ref(Self &, const Base &) {} 177 | 178 | static crust_cxx14_constexpr void assign_ref_mut(Self &, Base &) {} 179 | 180 | static crust_cxx14_constexpr void assign_move(Self &, Base &&) {} 181 | }; 182 | 183 | template 184 | struct TieTupleHelperRemain : 185 | TmplType> {}; 186 | 187 | template 188 | struct TieTupleHelperRemain : 189 | TmplType> {}; 190 | 191 | template 192 | struct TieTuple { 193 | private: 194 | crust_static_assert(!Any...>::result); 195 | crust_static_assert( 196 | _impl_types::TypesCountType< 197 | _impl_utility::IgnoreRange, 198 | _impl_types::Types>::result <= 1); 199 | 200 | Tuple...> ref; 201 | 202 | public: 203 | template 204 | explicit constexpr TieTuple(Fs &&...fields) : 205 | ref{LetField{forward(fields)}...} {} 206 | 207 | template 208 | crust_cxx14_constexpr void operator=(const TupleStruct &tuple) { 209 | TieTupleHelper< 210 | sizeof...(Fields), 211 | sizeof...(Fs), 212 | TupleStruct...>, 213 | TupleStruct>::assign_ref(ref, tuple); 214 | } 215 | 216 | template 217 | crust_cxx14_constexpr void operator=(TupleStruct &tuple) { 218 | TieTupleHelper< 219 | sizeof...(Fields), 220 | sizeof...(Fs), 221 | TupleStruct...>, 222 | TupleStruct>::assign_ref_mut(ref, tuple); 223 | } 224 | 225 | template 226 | crust_cxx14_constexpr void operator=(TupleStruct &&tuple) { 227 | TieTupleHelper< 228 | sizeof...(Fields), 229 | sizeof...(Fs), 230 | TupleStruct...>, 231 | TupleStruct>::assign_move(ref, move(tuple)); 232 | } 233 | }; 234 | } // namespace _impl_tuple 235 | 236 | template 237 | crust_cxx14_constexpr 238 | _impl_tuple::TieTuple::Result...> 239 | tie(Fields &&...fields) { 240 | return _impl_tuple::TieTuple< 241 | typename RemoveConstOrRefType::Result...>{ 242 | forward(fields)...}; 243 | } 244 | } // namespace crust 245 | 246 | namespace std { 247 | 248 | /// c++ std bindings 249 | 250 | template 251 | struct tuple_size> : 252 | integral_constant< 253 | crust::usize, 254 | crust::_impl_derive::TupleLikeSize< 255 | crust::TupleStruct>::result> {}; 256 | 257 | template 258 | struct tuple_element> { 259 | using type = typename crust::_impl_derive:: 260 | TupleLikeGetter, index>::Result; 261 | }; 262 | 263 | template 264 | constexpr const typename tuple_element>::type & 265 | get(const crust::TupleStruct &object) { 266 | return crust::_impl_derive:: 267 | TupleLikeGetter, index>::get(object); 268 | } 269 | 270 | template 271 | constexpr typename tuple_element>::type & 272 | get(crust::TupleStruct &object) { 273 | return crust::_impl_derive:: 274 | TupleLikeGetter, index>::get(object); 275 | } 276 | } // namespace std 277 | 278 | 279 | #endif // CRUST_TUPLE_HPP 280 | -------------------------------------------------------------------------------- /include/crust/cmp.hpp: -------------------------------------------------------------------------------- 1 | #ifndef CRUST_CMP_HPP 2 | #define CRUST_CMP_HPP 3 | 4 | 5 | #include "cmp_decl.hpp" 6 | 7 | #include "crust/enum_decl.hpp" 8 | #include "crust/option.hpp" 9 | #include "crust/utility.hpp" 10 | 11 | 12 | namespace crust { 13 | namespace cmp { 14 | struct Less; 15 | struct Equal; 16 | struct Greater; 17 | struct Ordering; 18 | } // namespace cmp 19 | 20 | template <> 21 | struct BluePrint : TmplType> {}; 22 | 23 | template <> 24 | struct BluePrint : TmplType> {}; 25 | 26 | template <> 27 | struct BluePrint : TmplType> {}; 28 | 29 | template <> 30 | struct BluePrint : 31 | TmplType> {}; 32 | 33 | namespace cmp { 34 | CRUST_DISCRIMINANT_VARIANT(Less, -1); 35 | CRUST_DISCRIMINANT_VARIANT(Equal, 0); 36 | CRUST_DISCRIMINANT_VARIANT(Greater, 1); 37 | 38 | struct crust_ebco Ordering : 39 | Enum, Less, Equal, Greater>, 40 | Derive< 41 | Ordering, 42 | Trait, 43 | Trait, 44 | Trait, 45 | Trait> { 46 | CRUST_ENUM_USE_BASE(Ordering, Enum, Less, Equal, Greater>); 47 | 48 | crust_cxx17_constexpr Ordering reverse() const { 49 | return this->template visit( 50 | [](const Less &) { return Greater{}; }, 51 | [](const Equal &) { return Equal{}; }, 52 | [](const Greater &) { return Less{}; }); 53 | } 54 | 55 | crust_cxx17_constexpr Ordering then(const Ordering &other) const { 56 | return this->template visit( 57 | [](const Less &) { return Less{}; }, 58 | [&](const Equal &) { return other; }, 59 | [](const Greater &) { return Greater{}; }); 60 | } 61 | 62 | template 63 | constexpr Ordering then_with(ops::Fn f) const { 64 | return this->template visit( 65 | [](const Less &) { return Less{}; }, 66 | [&](const Equal &) { return f(); }, 67 | [](const Greater &) { return Greater{}; }); 68 | } 69 | }; 70 | 71 | crust_always_inline constexpr Ordering make_less() { return Less{}; } 72 | 73 | crust_always_inline constexpr Ordering make_equal() { return Equal{}; } 74 | 75 | crust_always_inline constexpr Ordering make_greater() { return Greater{}; } 76 | } // namespace cmp 77 | 78 | /// because c++ do not support add member function for primitive types, 79 | /// following two functions are used for invoking `partial_cmp' or `cmp' for 80 | /// both struct implemented `PartialOrd' or `Ord' trait and primitive types. 81 | template 82 | crust_always_inline constexpr Option 83 | operator_partial_cmp(const T &v1, const U &v2) { 84 | return v1.partial_cmp(v2); 85 | } 86 | 87 | template 88 | crust_always_inline constexpr cmp::Ordering 89 | operator_cmp(const T &v1, const T &v2) { 90 | return v1.cmp(v2); 91 | } 92 | 93 | #define _IMPL_PRIMITIVE(FN, ...) \ 94 | template <> \ 95 | FN(bool, ##__VA_ARGS__); \ 96 | template <> \ 97 | FN(char, ##__VA_ARGS__); \ 98 | template <> \ 99 | FN(u8, ##__VA_ARGS__); \ 100 | template <> \ 101 | FN(i8, ##__VA_ARGS__); \ 102 | template <> \ 103 | FN(u16, ##__VA_ARGS__); \ 104 | template <> \ 105 | FN(i16, ##__VA_ARGS__); \ 106 | template <> \ 107 | FN(u32, ##__VA_ARGS__); \ 108 | template <> \ 109 | FN(i32, ##__VA_ARGS__); \ 110 | template <> \ 111 | FN(u64, ##__VA_ARGS__); \ 112 | template <> \ 113 | FN(i64, ##__VA_ARGS__); \ 114 | template \ 115 | FN(T *, ##__VA_ARGS__) 116 | 117 | #define _IMPL_OPERATOR_CMP(type, ...) \ 118 | crust_always_inline constexpr cmp::Ordering operator_cmp( \ 119 | const type &v1, const type &v2) { \ 120 | return v1 < v2 ? cmp::make_less() : \ 121 | (v1 > v2 ? cmp::make_greater() : cmp::make_equal()); \ 122 | } 123 | 124 | _IMPL_PRIMITIVE(_IMPL_OPERATOR_CMP); 125 | 126 | #undef _IMPL_OPERATOR_CMP 127 | 128 | #define _IMPL_OPERATOR_PARTIAL_CMP(type, ...) \ 129 | crust_always_inline constexpr Option operator_partial_cmp( \ 130 | const type &v1, const type &v2) { \ 131 | return make_some(operator_cmp(v1, v2)); \ 132 | } 133 | 134 | _IMPL_PRIMITIVE(_IMPL_OPERATOR_PARTIAL_CMP); 135 | 136 | #undef _IMPL_OPERATOR_PARTIAL_CMP 137 | 138 | namespace cmp { 139 | // TODO: refactor 140 | template 141 | constexpr bool PartialOrd::lt(const Rhs &other) const { 142 | return operator_partial_cmp(self(), other) == make_some(make_less()); 143 | } 144 | 145 | template 146 | constexpr bool PartialOrd::le(const Rhs &other) const { 147 | return operator_partial_cmp(self(), other) == make_some(make_greater()) || 148 | operator_partial_cmp(self(), other) == make_some(make_equal()); 149 | } 150 | 151 | template 152 | constexpr bool PartialOrd::gt(const Rhs &other) const { 153 | return operator_partial_cmp(self(), other) == make_some(make_greater()); 154 | } 155 | 156 | template 157 | constexpr bool PartialOrd::ge(const Rhs &other) const { 158 | return operator_partial_cmp(self(), other) == make_some(make_greater()) || 159 | operator_partial_cmp(self(), other) == make_some(make_equal()); 160 | } 161 | 162 | template 163 | constexpr T min(T &&v1, T &&v2) { 164 | crust_static_assert(Require::result); 165 | return move(v1).min(forward(v2)); 166 | } 167 | 168 | template 169 | constexpr T 170 | min_by(T &&v1, T &&v2, ops::Fn compare) { 171 | crust_static_assert(Require::result); 172 | return compare(v1, v2) == make_greater() ? forward(v2) : forward(v1); 173 | } 174 | 175 | template 176 | constexpr T min_by_key(T &&v1, T &&v2, ops::Fn f) { 177 | crust_static_assert(Require::result); 178 | return min_by( 179 | forward(v1), forward(v2), ops::bind([&](const T &v1, const T &v2) { 180 | return operator_cmp(f(v1), f(v2)); 181 | })); 182 | } 183 | 184 | template 185 | constexpr T max(T &&v1, T &&v2) { 186 | crust_static_assert(Require::result); 187 | return move(v1).max(forward(v2)); 188 | } 189 | 190 | template 191 | constexpr T 192 | max_by(T &&v1, T &&v2, ops::Fn compare) { 193 | crust_static_assert(Require::result); 194 | return compare(v1, v2) == make_greater() ? forward(v1) : forward(v2); 195 | } 196 | 197 | template 198 | constexpr T max_by_key(T &&v1, T &&v2, ops::Fn f) { 199 | crust_static_assert(Require::result); 200 | return max_by( 201 | forward(v1), forward(v2), ops::bind([&](const T &v1, const T &v2) { 202 | return operator_cmp(f(v1), f(v2)); 203 | })); 204 | } 205 | 206 | template 207 | struct crust_ebco Reverse : 208 | TupleStruct, 209 | Derive, Trait>, 210 | Impl< 211 | Reverse, 212 | Trait, 213 | Trait, 214 | Trait, 215 | Trait> { 216 | CRUST_USE_BASE_CONSTRUCTORS(Reverse, TupleStruct); 217 | }; 218 | } // namespace cmp 219 | 220 | template 221 | CRUST_IMPL_FOR(cmp::PartialEq>, Require) { 222 | CRUST_IMPL_USE_SELF(cmp::Reverse); 223 | 224 | constexpr bool eq(const Self &other) const { 225 | return other.template get<0>() == self().template get<0>(); 226 | } 227 | }; 228 | 229 | template 230 | CRUST_IMPL_FOR(cmp::Eq>, Require){}; 231 | 232 | template 233 | CRUST_IMPL_FOR(cmp::PartialOrd>, Require) { 234 | CRUST_IMPL_USE_SELF(cmp::Reverse); 235 | 236 | constexpr Option partial_cmp(const Self &other) const { 237 | return operator_partial_cmp( 238 | other.template get<0>(), self().template get<0>()); 239 | } 240 | 241 | constexpr bool lt(const Self &other) const { 242 | return other.template get<0>() < self().template get<0>(); 243 | } 244 | 245 | constexpr bool le(const Self &other) const { 246 | return other.template get<0>() <= self().template get<0>(); 247 | } 248 | 249 | constexpr bool gt(const Self &other) const { 250 | return other.template get<0>() > self().template get<0>(); 251 | } 252 | 253 | constexpr bool ge(const Self &other) const { 254 | return other.template get<0>() >= self().template get<0>(); 255 | } 256 | }; 257 | 258 | template 259 | CRUST_IMPL_FOR(cmp::Ord>, Require) { 260 | CRUST_IMPL_USE_SELF(cmp::Reverse); 261 | 262 | constexpr cmp::Ordering cmp(const Self &other) const { 263 | return operator_cmp(other.template get<0>(), self().template get<0>()); 264 | } 265 | }; 266 | 267 | #define _DERIVE_PRIMITIVE(PRIMITIVE, TRAIT, ...) \ 268 | struct Require : BoolVal {} 269 | 270 | _IMPL_PRIMITIVE(_DERIVE_PRIMITIVE, cmp::PartialEq); 271 | _IMPL_PRIMITIVE(_DERIVE_PRIMITIVE, cmp::Eq); 272 | _IMPL_PRIMITIVE(_DERIVE_PRIMITIVE, cmp::PartialOrd); 273 | _IMPL_PRIMITIVE(_DERIVE_PRIMITIVE, cmp::Ord); 274 | 275 | #undef _DERIVE_PRIMITIVE 276 | #undef _IMPL_PRIMITIVE 277 | 278 | // TODO: implement for float point numbers 279 | 280 | namespace _impl_derive { 281 | template 282 | constexpr Option 283 | TupleLikePartialOrdHelper::partial_cmp( 284 | const Self &a, const Self &b) { 285 | return make_some(operator_cmp(a, b)); // FIXME: temporary implement 286 | } 287 | 288 | template 289 | constexpr Option 290 | TupleLikePartialOrdHelper::partial_cmp( 291 | const Self &, const Self &) { 292 | return make_some(cmp::make_equal()); 293 | } 294 | 295 | template 296 | constexpr cmp::Ordering 297 | TupleLikeOrdHelper::cmp(const Self &a, const Self &b) { 298 | return operator_cmp(Getter::get(a), Getter::get(b)).then(Remains::cmp(a, b)); 299 | } 300 | 301 | template 302 | constexpr cmp::Ordering 303 | TupleLikeOrdHelper::cmp(const Self &, const Self &) { 304 | return cmp::make_equal(); 305 | } 306 | } // namespace _impl_derive 307 | } // namespace crust 308 | 309 | 310 | #endif // CRUST_CMP_HPP 311 | -------------------------------------------------------------------------------- /include/crust/tuple_decl.hpp: -------------------------------------------------------------------------------- 1 | #ifndef CRUST_TUPLE_DECL_HPP 2 | #define CRUST_TUPLE_DECL_HPP 3 | 4 | 5 | #include "crust/clone.hpp" 6 | #include "crust/cmp_decl.hpp" 7 | #include "crust/helper/types.hpp" 8 | #include "crust/utility.hpp" 9 | 10 | 11 | namespace crust { 12 | namespace _impl_tuple { 13 | template 14 | struct AllZeroSizedType; 15 | 16 | template 17 | struct AllZeroSizedType<_impl_types::Types> : 18 | All...> {}; 19 | 20 | template 21 | struct TupleSizedHolderImpl; 22 | 23 | template 24 | using TupleSizedHolderIsZST = 25 | Require::Result, ZeroSizedType>; 26 | 27 | template 28 | using TupleSizedHolderRemainZST = 29 | AllZeroSizedType::Result>; 30 | 31 | template 32 | using TupleSizedHolder = TupleSizedHolderImpl< 33 | TupleSizedHolderIsZST<_impl_types::Types>::result, 34 | TupleSizedHolderRemainZST<_impl_types::Types>::result, 35 | _impl_types::Types>; 36 | 37 | template 38 | struct TupleSizedHolderImpl< 39 | false, 40 | false, 41 | _impl_types::Types> { 42 | Field field; 43 | TupleSizedHolder remains; 44 | 45 | constexpr TupleSizedHolderImpl() : field{}, remains{} {} 46 | 47 | template 48 | explicit constexpr TupleSizedHolderImpl(T &&field, Ts &&...fields) : 49 | field{forward(field)}, remains{forward(fields)...} {} 50 | }; 51 | 52 | template 53 | struct TupleSizedHolderImpl> { 54 | Field field; 55 | 56 | constexpr TupleSizedHolderImpl() : field{} {} 57 | 58 | explicit constexpr TupleSizedHolderImpl(Field &&field, Fields &&...) : 59 | field{forward(field)} {} 60 | }; 61 | 62 | template 63 | struct TupleSizedHolderImpl> { 64 | TupleSizedHolder remains; 65 | 66 | constexpr TupleSizedHolderImpl() : remains{} {} 67 | 68 | explicit constexpr TupleSizedHolderImpl(Field &&, Fields &&...fields) : 69 | remains{forward(fields)...} {} 70 | }; 71 | 72 | template 73 | struct TupleSizedHolderImpl> { 74 | constexpr TupleSizedHolderImpl() {} 75 | 76 | explicit constexpr TupleSizedHolderImpl(Fields &&...) {} 77 | }; 78 | 79 | template 80 | struct TupleGetterImpl; 81 | 82 | template 83 | struct TupleGetterImpl : 84 | _impl_types::ZeroSizedTypeGetter {}; 85 | 86 | template 87 | struct TupleGetterImpl { 88 | using Self = TupleSizedHolder; 89 | using Result = typename _impl_types:: 90 | TypesIndex>::Result; 91 | 92 | static constexpr const Result &inner(const Self &self) { 93 | return TupleGetterImpl::inner(self.remains); 94 | } 95 | 96 | static constexpr Result &inner(Self &self) { 97 | return TupleGetterImpl::inner(self.remains); 98 | } 99 | }; 100 | 101 | template 102 | struct TupleGetterImpl<0, false, Field, Fields...> { 103 | using Self = TupleSizedHolder; 104 | using Result = Field; 105 | 106 | static constexpr const Result &inner(const Self &self) { return self.field; } 107 | 108 | static constexpr Result &inner(Self &self) { return self.field; } 109 | }; 110 | 111 | template 112 | using TupleGetter = TupleGetterImpl< 113 | index, 114 | Require< 115 | typename _impl_types::TypesIndex>:: 116 | Result, 117 | ZeroSizedType>::result, 118 | Fields...>; 119 | } // namespace _impl_tuple 120 | 121 | template 122 | struct TupleStruct : 123 | private _impl_types::ZeroSizedTypeHolder, 124 | private _impl_tuple::TupleSizedHolder { 125 | private: 126 | crust_static_assert(All>...>::result); 127 | 128 | template 129 | using Getter = _impl_tuple::TupleGetter; 130 | 131 | protected: 132 | CRUST_USE_BASE_CONSTRUCTORS( 133 | TupleStruct, _impl_tuple::TupleSizedHolder); 134 | 135 | public: 136 | template 137 | constexpr const typename Getter::Result &get() const { 138 | return Getter::inner(*this); 139 | } 140 | 141 | template 142 | crust_cxx14_constexpr typename Getter::Result &get() { 143 | return Getter::inner(*this); 144 | } 145 | }; 146 | 147 | template <> 148 | struct TupleStruct<> { 149 | protected: 150 | constexpr TupleStruct() {} 151 | }; 152 | 153 | namespace _impl_derive { 154 | template 155 | struct TupleLikeSize> : 156 | TmplVal {}; 157 | 158 | template 159 | struct TupleLikeGetter, index> { 160 | using Result = typename _impl_types:: 161 | TypesIndex>::Result; 162 | 163 | static constexpr const Result &get(const TupleStruct &self) { 164 | return self.template get(); 165 | } 166 | 167 | static constexpr Result &get(TupleStruct &self) { 168 | return self.template get(); 169 | } 170 | }; 171 | 172 | template 173 | struct IndexSequence {}; 174 | 175 | template 176 | struct IndexSequenceImpl : IndexSequenceImpl {}; 177 | 178 | template 179 | struct IndexSequenceImpl<0, indexs...> : TmplType> {}; 180 | 181 | template 182 | using MakeIndexSequence = typename IndexSequenceImpl::Result; 183 | 184 | template 185 | struct TupleLikeCloneHelper; 186 | 187 | template 188 | struct TupleLikeCloneHelper> { 189 | static constexpr Self clone(const Self &self) { 190 | return Self{clone::clone(self.template get())...}; 191 | } 192 | }; 193 | 194 | template class Trait, class... Args> 195 | struct ImplForTupleStructHelper : TmplVal {}; 196 | 197 | template < 198 | class... Fields, 199 | template 200 | class Trait, 201 | class... Args> 202 | struct ImplForTupleStructHelper, Trait, Args...> : 203 | All...> {}; 204 | 205 | template class Trait, class... Args> 206 | using ImplForTupleStruct = 207 | ImplForTupleStructHelper::Result, Trait, Args...>; 208 | } // namespace _impl_derive 209 | 210 | template 211 | CRUST_IMPL_FOR( 212 | ZeroSizedType, _impl_derive::ImplForTupleStruct){}; 213 | 214 | template 215 | CRUST_IMPL_FOR( 216 | clone::Clone, _impl_derive::ImplForTupleStruct) { 217 | CRUST_IMPL_USE_SELF(S); 218 | 219 | private: 220 | using CloneHelper = _impl_derive::TupleLikeCloneHelper< 221 | Self, 222 | _impl_derive::MakeIndexSequence< 223 | _impl_derive::TupleLikeSize::Result>::result>>; 224 | 225 | public: 226 | Self clone() const { return CloneHelper::clone(self()); } 227 | }; 228 | 229 | template 230 | CRUST_IMPL_FOR( 231 | cmp::PartialEq, _impl_derive::ImplForTupleStruct) { 232 | CRUST_IMPL_USE_SELF(S); 233 | 234 | private: 235 | using PartialEqHelper = _impl_derive:: 236 | TupleLikePartialEqHelper::Result>; 237 | 238 | public: 239 | constexpr bool eq(const Self &other) const { 240 | return PartialEqHelper::eq(self(), other); 241 | } 242 | 243 | constexpr bool ne(const Self &other) const { 244 | return PartialEqHelper::ne(self(), other); 245 | } 246 | }; 247 | 248 | template 249 | CRUST_IMPL_FOR(cmp::Eq, _impl_derive::ImplForTupleStruct){}; 250 | 251 | template 252 | CRUST_IMPL_FOR( 253 | cmp::PartialOrd, _impl_derive::ImplForTupleStruct) { 254 | CRUST_IMPL_USE_SELF(S); 255 | 256 | private: 257 | using PartialOrdHelper = _impl_derive:: 258 | TupleLikePartialOrdHelper::Result>; 259 | 260 | public: 261 | constexpr Option partial_cmp(const Self &other) const; 262 | 263 | constexpr bool lt(const Self &other) const { 264 | return PartialOrdHelper::lt(self(), other); 265 | } 266 | 267 | constexpr bool le(const Self &other) const { 268 | return PartialOrdHelper::le(self(), other); 269 | } 270 | 271 | constexpr bool gt(const Self &other) const { 272 | return PartialOrdHelper::gt(self(), other); 273 | } 274 | 275 | constexpr bool ge(const Self &other) const { 276 | return PartialOrdHelper::ge(self(), other); 277 | } 278 | }; 279 | 280 | template 281 | CRUST_IMPL_FOR(cmp::Ord, _impl_derive::ImplForTupleStruct) { 282 | CRUST_IMPL_USE_SELF(S); 283 | 284 | private: 285 | using OrdHelper = 286 | _impl_derive::TupleLikeOrdHelper::Result>; 287 | 288 | public: 289 | constexpr cmp::Ordering cmp(const Self &other) const; 290 | }; 291 | 292 | template 293 | struct crust_ebco Tuple : 294 | TupleStruct, 295 | Derive< 296 | Tuple, 297 | Trait, 298 | Trait, 299 | Trait, 300 | Trait, 301 | Trait, 302 | Trait> { 303 | CRUST_USE_BASE_CONSTRUCTORS(Tuple, TupleStruct); 304 | }; 305 | 306 | template 307 | struct BluePrint> : TmplType> {}; 308 | 309 | template 310 | struct ZeroSizedTypeConvertible, U> : 311 | Any<_impl_types::TypesIncludeVal>, 312 | _impl_types::TypesContainVal>> {}; 313 | 314 | namespace _impl_tuple { 315 | template 316 | struct ZeroSizedTypeConvertHelper; 317 | 318 | template 319 | struct ZeroSizedTypeConvertHelper, U, true> { 320 | static constexpr usize index = 321 | _impl_types::TypesFirstContain>::result; 322 | using Convert = ZeroSizedTypeConvert< 323 | typename _impl_types::TypesIndex>:: 324 | Result, 325 | U>; 326 | 327 | static constexpr const U &inner(const Tuple &self) { 328 | return Convert::inner(self.template get()); 329 | } 330 | 331 | static constexpr U &inner(Tuple &self) { 332 | return Convert::inner(self.template get()); 333 | } 334 | }; 335 | 336 | template 337 | struct ZeroSizedTypeConvertHelper, U, false> { 338 | static constexpr usize index = 339 | _impl_types::TypesFirstIndex>::result; 340 | 341 | static constexpr const U &inner(const Tuple &self) { 342 | return self.template get(); 343 | } 344 | 345 | static constexpr U &inner(Tuple &self) { 346 | return self.template get(); 347 | } 348 | }; 349 | } // namespace _impl_tuple 350 | 351 | template 352 | struct ZeroSizedTypeConvert, U> : 353 | _impl_tuple::ZeroSizedTypeConvertHelper< 354 | Tuple, 355 | U, 356 | _impl_types::TypesContainVal>:: 357 | result> {}; 358 | } // namespace crust 359 | 360 | 361 | #endif // CRUST_TUPLE_DECL_HPP 362 | -------------------------------------------------------------------------------- /include/crust/utility.hpp: -------------------------------------------------------------------------------- 1 | #ifndef CRUST_UTILITY_HPP 2 | #define CRUST_UTILITY_HPP 3 | 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | 11 | namespace crust { 12 | #if __cplusplus > 201103L 13 | #define crust_cxx14_constexpr constexpr 14 | #else 15 | #define crust_cxx14_constexpr 16 | #endif 17 | 18 | #if __cplusplus > 201402L 19 | #define crust_cxx17_constexpr constexpr 20 | #else 21 | #define crust_cxx17_constexpr 22 | #endif 23 | 24 | #if __cplusplus > 201402L 25 | #define crust_static_assert(...) static_assert(__VA_ARGS__) 26 | #else 27 | #define crust_static_assert(...) static_assert(__VA_ARGS__, #__VA_ARGS__) 28 | #endif 29 | 30 | #if defined(_MSC_VER) 31 | #define crust_ebco __declspec(empty_bases) 32 | #else 33 | #define crust_ebco 34 | #endif 35 | 36 | #if defined(__GNUC__) || defined(__clang__) 37 | #define crust_no_return __attribute__((noreturn)) 38 | #elif defined(_MSC_VER) 39 | #define crust_no_return __declspec(noreturn) 40 | #else 41 | #define crust_no_return 42 | #endif 43 | 44 | #if defined(__GNUC__) || defined(__clang__) 45 | #define crust_likely(x) __builtin_expect(!!(x), 1) 46 | #define crust_unlikely(x) __builtin_expect(!!(x), 0) 47 | #else 48 | #define crust_likely(x) x 49 | #define crust_unlikely(x) x 50 | #endif 51 | 52 | #if defined(__GNUC__) || defined(__clang__) 53 | #define crust_always_inline inline __attribute__((always_inline)) 54 | #elif defined(_MSC_VER) 55 | #define crust_always_inline __forceinline 56 | #else 57 | #define crust_always_inline inline 58 | #endif 59 | 60 | namespace _impl_utility { 61 | crust_no_return inline void 62 | panic(const char *file, int line, const char *msg) noexcept { 63 | fprintf(stderr, "Panic at file %s, line %d: %s\n", file, line, msg); 64 | exit(1); 65 | } 66 | 67 | #define crust_panic(msg) _impl_utility::panic(__FILE__, __LINE__, msg) 68 | 69 | #define crust_assert(expr) \ 70 | (crust_likely(expr) ? void(0) : [](const char *f, int l, const char *m) { \ 71 | fprintf(stderr, "Assert failed at file %s, line %d: %s\n", f, l, m); \ 72 | exit(-1); \ 73 | }(__FILE__, __LINE__, #expr)) 74 | 75 | #if defined(NODEBUG) 76 | #define crust_debug_assert(expr) 77 | #else 78 | #define crust_debug_assert(expr) crust_assert(expr) 79 | #endif 80 | 81 | #if defined(NODEBUG) 82 | #if defined(__GNUC__) || defined(__clang__) 83 | #define crust_unreachable() __builtin_unreachable() 84 | #elif defined(_MSC_VER) 85 | #define crust_unreachable() __assume(false) 86 | #else 87 | #define crust_unreachable() 88 | #endif 89 | #else 90 | #define crust_unreachable() crust_panic("should not reach here!") 91 | #endif 92 | } // namespace _impl_utility 93 | 94 | #define CRUST_MACRO(...) __VA_ARGS__ 95 | 96 | using i8 = int8_t; 97 | using u8 = uint8_t; 98 | using i16 = int16_t; 99 | using u16 = uint16_t; 100 | using i32 = int32_t; 101 | using u32 = uint32_t; 102 | using i64 = int64_t; 103 | using u64 = uint64_t; 104 | #if INTPTR_MAX == INT32_MAX 105 | using isize = i32; 106 | using usize = u32; 107 | #elif INTPTR_MAX == INT64_MAX 108 | using isize = i64; 109 | using usize = u64; 110 | #else 111 | #error "Unsupported bit width." 112 | #endif 113 | 114 | template 115 | struct TmplType { 116 | using Result = Value; 117 | }; 118 | 119 | template 120 | struct TmplVal : TmplType { 121 | static constexpr Type result = value; 122 | }; 123 | 124 | #define crust_tmpl_val(obj) \ 125 | ::crust::TmplVal {} 126 | 127 | template 128 | struct BoolVal : TmplVal {}; 129 | 130 | template 131 | struct Not : BoolVal {}; 132 | 133 | template 134 | struct All; 135 | 136 | template 137 | struct All : BoolVal::result> {}; 138 | 139 | template <> 140 | struct All<> : BoolVal {}; 141 | 142 | template 143 | struct Any; 144 | 145 | template 146 | struct Any : BoolVal::result> {}; 147 | 148 | template <> 149 | struct Any<> : BoolVal {}; 150 | 151 | namespace _impl_utility { 152 | template 153 | struct IfElse; 154 | 155 | template 156 | struct IfElse : A {}; 157 | 158 | template 159 | struct IfElse : B {}; 160 | } // namespace _impl_utility 161 | 162 | template 163 | using IfElse = _impl_utility::IfElse; 164 | 165 | template 166 | struct AsVal : TmplVal {}; 167 | 168 | template class... Tmpls> 169 | struct CompositionType; 170 | 171 | template class Tmpl, template class... Tmpls> 172 | struct CompositionType : 173 | CompositionType::Result, Tmpls...> {}; 174 | 175 | template 176 | struct CompositionType : TmplType {}; 177 | 178 | template 179 | struct IsSame : BoolVal {}; 180 | 181 | template 182 | struct IsSame : BoolVal {}; 183 | 184 | template 185 | struct RemoveRefType : TmplType {}; 186 | 187 | template 188 | struct RemoveRefType : TmplType {}; 189 | 190 | template 191 | struct RemoveRefType : TmplType {}; 192 | 193 | template 194 | struct RemoveConstType : TmplType {}; 195 | 196 | template 197 | struct RemoveConstType : TmplType {}; 198 | 199 | template 200 | struct RemoveConstOrRefType : 201 | CompositionType {}; 202 | 203 | template 204 | struct IsEmptyType : BoolVal::value> {}; 205 | 206 | template 207 | struct IsLValueRefVal : BoolVal {}; 208 | 209 | template 210 | struct IsLValueRefVal : BoolVal {}; 211 | 212 | template 213 | struct IsRValueRefVal : BoolVal {}; 214 | 215 | template 216 | struct IsRValueRefVal : BoolVal {}; 217 | 218 | template 219 | struct IsConstVal : BoolVal {}; 220 | 221 | template 222 | struct IsConstVal : BoolVal {}; 223 | 224 | template 225 | struct IsRefVal : Any, IsRValueRefVal> {}; 226 | 227 | template 228 | struct IsConstOrRefVal : Any, IsConstVal> {}; 229 | 230 | template 231 | struct IsBaseOfVal : 232 | All::value>, Not>> {}; 233 | 234 | template 235 | struct IsTriviallyCopyable : BoolVal::value> {}; 236 | 237 | template 238 | constexpr typename RemoveRefType::Result &&move(T &&t) { 239 | return static_cast::Result &&>(t); 240 | } 241 | 242 | template 243 | constexpr T &&forward(typename RemoveRefType::Result &t) { 244 | return static_cast(t); 245 | } 246 | 247 | template 248 | constexpr T &&forward(typename RemoveRefType::Result &&t) { 249 | crust_static_assert(!IsLValueRefVal::result); 250 | return static_cast(t); 251 | } 252 | 253 | #define CRUST_USE_BASE_CONSTRUCTORS(NAME, ...) \ 254 | template \ 255 | explicit constexpr NAME(Args &&...args) : \ 256 | __VA_ARGS__{::crust::forward(args)...} {} 257 | 258 | #define CRUST_TRAIT(TRAIT, ...) \ 259 | template \ 260 | struct TRAIT 261 | 262 | #define CRUST_TRAIT_USE_SELF(TRAIT, ...) \ 263 | private: \ 264 | template \ 265 | constexpr const ::crust::ImplFor &self() const { \ 266 | return *static_cast *>( \ 267 | static_cast(this)); \ 268 | } \ 269 | template \ 270 | crust_cxx14_constexpr ::crust::ImplFor &self() { \ 271 | return *static_cast<::crust::ImplFor *>(static_cast(this)); \ 272 | } \ 273 | \ 274 | protected: \ 275 | constexpr TRAIT() { \ 276 | crust_static_assert(!::crust::IsConstOrRefVal::result); \ 277 | crust_static_assert(::crust::IsBaseOfVal::result); \ 278 | crust_static_assert(::crust::All<__VA_ARGS__>::result); \ 279 | } \ 280 | \ 281 | public: 282 | 283 | template