diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..bfd118e --- /dev/null +++ b/.clang-format @@ -0,0 +1,2 @@ +BasedOnStyle: Google +ColumnLimit: 120 diff --git a/CMakeLists.txt b/CMakeLists.txt index 43626ab..6a8ecf6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,16 +1,12 @@ cmake_minimum_required(VERSION 3.20) -project(const_mapper CXX) +project(const_mapper_tests CXX) +set(CMAKE_C_COMPILER "/usr/bin/clang") +set(CMAKE_CXX_COMPILER "/usr/bin/clang++") set(CMAKE_CXX_STANDARD 17) enable_testing() -include(FetchContent) -FetchContent_Declare( - googletest - URL https://github.com/google/googletest/archive/refs/tags/v1.14.0.zip -) -FetchContent_MakeAvailable(googletest) find_package(GTest REQUIRED) @@ -19,7 +15,13 @@ include_directories(${PROJECT_NAME} ${GTEST_INCLUDE_DIRS} ) -add_executable(${PROJECT_NAME} test/test_const_mapper.cpp) -target_compile_options(${PROJECT_NAME} PRIVATE -Wall -Wextra -Wpedantic -Werror) +add_executable(${PROJECT_NAME} + test/main.cpp + test/test_const_mapper.cpp + test/test_performance.cpp + test/test_utils.cpp + test/test_example.cpp + ) +target_compile_options(${PROJECT_NAME} PRIVATE -O0 -Wall -Wextra -Wpedantic -Werror) target_link_libraries(${PROJECT_NAME} ${GTEST_LIBRARIES}) diff --git a/README.md b/README.md index 4a8f70d..1fc4f8f 100644 --- a/README.md +++ b/README.md @@ -4,9 +4,11 @@ ## Features -- **Only depends STL**: not need to add extra library. -- **Static Definition**: Enables compile-time definition of constant mappings, ensuring high efficiency and reduced runtime overhead. -- **Bi-Directional Conversion**: Supports conversions in both directions between any defined pairs, making it a highly flexible solution. +- **Only depends on STL**: no need to add any extra libraries. +- **Single Header File**: All you need is include `const_mapper.hpp`. +- **Compile-Time Mapping**: Supports compile-time mapping. As well as at runtime mapping. +- **All-Directional Conversion**: Supports conversions in all directions between any pairs. +- **Pattern Matching**: Supports complex conversion by pattern matching. - **Support C++17 or later** ## Installation @@ -15,15 +17,86 @@ This is single header file library.
Just include the header file in your source code. ## Example + +[test_const_mapper.cpp](test/test_const_mapper.cpp) will help for you understanding how to use this library. + +### Simple Conversion +```cpp + using namespace const_mapper; + constexpr auto map = ConstMapper<3, std::string_view, int, std::uint8_t>{{{ + {"value_0", 0, 0}, + {"value_1", 1, 10}, + {"value_2", 2, 20}, + }}}; + + // str0 == "value_1"; + constexpr auto str0 = map.to(1); + + // str1 == "value_2"; + constexpr auto str1 = map.to(20); + + // int0 == 2; + constexpr auto int0 = map.to(20); +``` + +### Pattern Conversion ```cpp -constexpr auto map = const_mapper::ConstMapper<3, std::string_view, int, std::uint8_t>{{{ - {"value_0", 0, 0}, - {"value_1", 1, 10}, - {"value_2", 2, 20}}}}; - -constexpr auto str1 = map.to(1); // str == "value_1"; -constexpr auto str2 = map.to(20); // str == "value_2"; -constexpr auto uint0 = map.to(0); // str == "value_2"; + using namespace const_mapper; + constexpr auto map = ConstMapper<6, std::string_view, Range, Anyable>{{{ + {"less2 & 1", {CompareType::LessThan, 2}, 1}, + {"less2 & 2", {CompareType::LessThan, 2}, 2}, + {"larger5", {CompareType::LargerThan, 5}, {}}, + {"Any", {}, {}}, + }}}; + + // str0 == "less2 & 1" + constexpr auto str0 = map.pattern_match(std::make_tuple(Result{}, 1, 1)); + + // str1 == "less2 & 2" + constexpr auto str1 = map.pattern_match(std::make_tuple(Result{}, 1, 2)); + + // str2 == "larger5" + constexpr auto str2 = map.pattern_match(std::make_tuple(Result{}, 6, -1)); + + // str3 == "Any" + constexpr auto str3 = map.pattern_match(std::make_tuple(Result{}, 5, -1)); +``` + +## Performance Test + +check test detail -> [test_performance.cpp](test/test_performance.cpp) + + +### optimization: -O0 +``` +[----------] 5 tests from Performance +[ RUN ] Performance.ref_std_unordered_map +[ OK ] Performance.ref_std_unordered_map (619 ms) +[ RUN ] Performance.ref_std_map +[ OK ] Performance.ref_std_map (983 ms) +[ RUN ] Performance.const_mapper_to +[ OK ] Performance.const_mapper_to (731 ms) +[ RUN ] Performance.const_mapper_pattern_match +[ OK ] Performance.const_mapper_pattern_match (1299 ms) +[ RUN ] Performance.const_mapper_pattern_match_pick_up_2_values +[ OK ] Performance.const_mapper_pattern_match_pick_up_2_values (1880 ms) +[----------] 5 tests from Performance (5513 ms total) +``` + +### optimization: -O3 +``` +[----------] 5 tests from Performance +[ RUN ] Performance.ref_std_unordered_map +[ OK ] Performance.ref_std_unordered_map (42 ms) +[ RUN ] Performance.ref_std_map +[ OK ] Performance.ref_std_map (139 ms) +[ RUN ] Performance.const_mapper_to +[ OK ] Performance.const_mapper_to (35 ms) +[ RUN ] Performance.const_mapper_pattern_match +[ OK ] Performance.const_mapper_pattern_match (36 ms) +[ RUN ] Performance.const_mapper_pattern_match_pick_up_2_values +[ OK ] Performance.const_mapper_pattern_match_pick_up_2_values (38 ms) +[----------] 5 tests from Performance (292 ms total) ``` ## License diff --git a/include/const_mapper.hpp b/include/const_mapper.hpp index d36a04f..e739721 100644 --- a/include/const_mapper.hpp +++ b/include/const_mapper.hpp @@ -24,41 +24,372 @@ #pragma once #include +#include #include #include #include namespace const_mapper { + +/** + * Use with `pattern_match`. + * Select as return value of `pattern_match`. + * + * @see ConstMapper::pattern_match + */ +class Result {}; + +/** + * Use with `pattern_match`. + * Ignore this value on `pattern_match`. + * + * @see ConstMapper::pattern_match + */ +class Ignore {}; + template class ConstMapper { - template - using Type = typename std::tuple_element >::type; + public: + /** + * Type of array element. + */ + using Tuple = std::tuple; + + explicit constexpr ConstMapper(std::array list); + + /** + * Simple convert. + * @param i_to index of return value. + * @param i_from index of key value. + * @return first i_to value that i_from value match with array element. + */ + template + constexpr auto to(const Key &key) const; + + /** + * Type matching conversion. + * @param To type of return value. if there are 2 or more `To` value in `Tuple`, pick-up first value. + * @param From type of key value. if there are 2 or more `From` value in `Tuple`, pick-up first value. + * @return first `To` value that `From` value match with array element. + */ + template + constexpr To to(const Key &key) const; + + /** + * Pattern matching conversion. + * + * @param pattern Pattern of matching. At least one value must be `Result`. tuple_size must be same as size of Tuple. + * @return values that are setted Result in pattern. If one `Result,` return value. If two or more `Result`s, return + * tuple of `Result` + */ + template + constexpr auto pattern_match(const std::tuple &pattern) const; + + private: + std::array map_data_; + + /** + * Implementation of pattern_match + * @see pattern_match + */ + template + constexpr auto pattern_match_impl(const std::tuple &pattern) const; + + /** + * compare pattern and array elements. + * @see pattern_match + */ + template + constexpr bool check_pattern_match(const Tuple &tuple, const PatternTuple &pattern_tuple) const; + + /** + * compare t0 and t1. + * @return true if (t0 == t1) or (t1 is `Result`) or (t1 is `Ignore`), otherwise false. + */ + template + static constexpr bool compare(const T0 &t0, const T1 &t1); + + /** + * compare t0 and t1. + * Specialized for `Result`. + * @return true + */ + template + static constexpr bool compare([[maybe_unused]] const T &t0, [[maybe_unused]] const Result &t1); + + /** + * compare t0 and t1. + * Specialized for `Ignore`. + * @return true + */ + template + static constexpr bool compare([[maybe_unused]] const T &t0, [[maybe_unused]] const Ignore &t1); + + /** + * Get result of `pattern_match` + * @see pattern_match + * @return tuple of results. + */ + template + constexpr auto get_result(const Tuple &value_tuple) const; + + /** + * Get result of `pattern_match` + * @see pattern_match + * @return tuple of results. + */ + template + constexpr auto get_result_impl(const Tuple &value_tuple) const; + + static constexpr auto tuple_size(); +}; + +template +class Anyable { + public: + using type = T; + + constexpr Anyable(); + constexpr Anyable(T value); + + constexpr bool operator==(const Anyable &rhs) const; + constexpr bool operator==(const T &rhs) const; + + constexpr std::optional value() const; + constexpr operator std::optional() const; + private: + std::optional value_; +}; + +enum class CompareType { + Any, + LargerThan, + LargerEqual, + Equal, + LessEqual, + LessThan, +}; + +template +class Range { public: - explicit constexpr ConstMapper(std::array, N> list) - : map_data_(list) {} + using type = T; + + constexpr Range(); + constexpr Range(CompareType compare_type, T value); + + constexpr bool operator==(const T &rhs) const; + + private: + CompareType compare_type_ = CompareType::Any; + T value_; +}; +} // namespace const_mapper + +namespace { +/** + * タプルの要素から指定した型のインデックスを取得する。 + * 複数の要素と一致する場合は一番最初のもの。 + */ +template +inline constexpr std::size_t tuple_index() { + if constexpr (index >= std::tuple_size_v) { + return std::tuple_size_v; // `not found` or `index is out of range`. + } else if constexpr (std::is_same_v>) { + return index; + } else { + return tuple_index(); + } +} + +template +inline constexpr std::size_t tuple_index() { + constexpr auto index = tuple_index(); + + static_assert(index < std::tuple_size_v, "Tuple does not contain T."); + + return index; +} - template - constexpr To to(const From &key) const { +template +inline constexpr auto tuple_contains() { + return tuple_index() < std::tuple_size_v; +} + +template +constexpr auto un_tuple_if_one_element(const std::tuple &tuple) { + if constexpr (sizeof...(Args) == 1) { + return std::get<0>(tuple); + } else { + return tuple; + } +} +} // namespace + +namespace const_mapper { + +template +constexpr Anyable::Anyable() {} + +template +constexpr Anyable::Anyable(T value) : value_(value) {} + +template +constexpr bool Anyable::operator==(const Anyable &rhs) const { + return !value_ || !rhs.value_ || (*value_ == *rhs.value_); +} + +template +constexpr bool Anyable::operator==(const T &rhs) const { + return !value_ || (*value_ == rhs); +} + +template +constexpr std::optional Anyable::value() const { + return value_; +} + +template +constexpr Anyable::operator std::optional() const { + return value_; +} + +template +constexpr Range::Range() : value_(){}; + +template +constexpr Range::Range(CompareType compare_type, T value) : compare_type_(compare_type), value_(value) {} + +template +constexpr bool Range::operator==(const T &rhs) const { + switch (compare_type_) { + case CompareType::LargerThan: + return rhs > value_; + case CompareType::LargerEqual: + return rhs >= value_; + case CompareType::Equal: + return rhs == value_; + case CompareType::LessEqual: + return rhs <= value_; + case CompareType::LessThan: + return rhs < value_; + case CompareType::Any: + return true; + + default: + throw std::logic_error("no implement error"); + } +} + +template +constexpr auto ConstMapper::tuple_size() { + return std::tuple_size_v; +} + +template +constexpr ConstMapper::ConstMapper(std::array list) : map_data_(list) { + static_assert(N > 1, "N must grater than 1."); +} + +template +template +constexpr auto ConstMapper::to(const Key &key) const { + if constexpr (!(i_to < tuple_size())) { + static_assert(false, "i_to out of tuple range"); + } else if constexpr (!(i_from < tuple_size())) { + static_assert(false, "i_from out of tuple range"); + } else { for (const auto &tuple : map_data_) { - if (std::get(tuple) == key) { - return std::get(tuple); + if (std::get(tuple) == key) { + return std::get(tuple); } } throw std::out_of_range("key not found."); } +} - template - constexpr Type to(const Type &key) const { - for (const auto &tuple : map_data_) { - if (std::get(tuple) == key) { - return std::get(tuple); - } +template +template +constexpr To ConstMapper::to(const Key &key) const { + constexpr auto i_to = tuple_index(); + constexpr auto i_from = tuple_index(); + + if constexpr (!(i_to < tuple_size())) { + static_assert(false, "Tuple does not contain `To` element."); + } else if constexpr (!(i_from < tuple_size())) { + static_assert(false, "Tuple does not contain `From` element."); + } else { + return to(key); + } +} + +template +template +constexpr auto ConstMapper::pattern_match(const std::tuple &pattern) const { + static_assert(tuple_size() == sizeof...(Types), "tuple size dose not match."); + static_assert(tuple_contains, Result>(), "No Result value."); + return pattern_match_impl(pattern); +} + +template +template +constexpr auto ConstMapper::pattern_match_impl(const std::tuple &pattern) const { + for (const auto &tuple : map_data_) { + if (check_pattern_match<0>(tuple, pattern)) { + return un_tuple_if_one_element(get_result>(tuple)); } - throw std::out_of_range("key not found."); } + throw std::out_of_range("key not found."); +} - private: - std::array, N> map_data_; -}; +template +template +constexpr bool ConstMapper::check_pattern_match(const Tuple &tuple, + const PatternTuple &pattern_tuple) const { + if constexpr (index + 1 == tuple_size()) { + return compare(std::get(tuple), std::get(pattern_tuple)); + } else { + return compare(std::get(tuple), std::get(pattern_tuple)) && + check_pattern_match(tuple, pattern_tuple); + } +} + +template +template +constexpr bool ConstMapper::compare(const T0 &t0, const T1 &t1) { + return (t0 == t1); +} + +template +template +constexpr bool ConstMapper::compare([[maybe_unused]] const T &t0, [[maybe_unused]] const Result &t1) { + return true; +} + +template +template +constexpr bool ConstMapper::compare([[maybe_unused]] const T &t0, [[maybe_unused]] const Ignore &t1) { + return true; +} + +template +template +constexpr auto ConstMapper::get_result(const Tuple &value_tuple) const { + constexpr auto i = tuple_index(); + + return get_result_impl(value_tuple); +} + +template +template +constexpr auto ConstMapper::get_result_impl(const Tuple &value_tuple) const { + constexpr auto i = tuple_index(); + + if constexpr (i == tuple_size()) { + return std::tuple(std::get(value_tuple)); + } else { + return std::tuple_cat(std::tuple(std::get(value_tuple)), get_result_impl(value_tuple)); + } +} } // namespace const_mapper diff --git a/test/main.cpp b/test/main.cpp new file mode 100644 index 0000000..4d820af --- /dev/null +++ b/test/main.cpp @@ -0,0 +1,6 @@ +#include + +int main(int argc, char **argv) { + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/test/test_const_mapper.cpp b/test/test_const_mapper.cpp index 2e6d716..8ca0a96 100644 --- a/test/test_const_mapper.cpp +++ b/test/test_const_mapper.cpp @@ -2,20 +2,22 @@ #include "const_mapper.hpp" -static constexpr auto map = - const_mapper::ConstMapper<4, std::string_view, int, std::uint8_t>{ - {{{"value_0", 0, 0}, - {"value_1", -1, 1}, - {"value_2", -2, 2}, - {"value_3", -3, 3}}}}; +using namespace const_mapper; + +TEST(TestConstMapper, to_index) { + constexpr auto map = ConstMapper<4, std::string_view, int, std::uint8_t>{{{ + {"value_0", 0, 0}, + {"value_1", -1, 1}, + {"value_2", -2, 2}, + {"value_3", -3, 3}, + }}}; -TEST(TestConstMapper, to_type) { for (auto i = 0; i < 4; ++i) { auto string = "value_" + std::to_string(i); - auto value_int = map.to(string); - auto value_uint = map.to(string); - auto value_str = map.to(i); + auto value_int = map.to<1, 0>(string); + auto value_uint = map.to<2, 0>(string); + auto value_str = map.to<0, 2>(i); EXPECT_EQ(value_int, -i); EXPECT_EQ(value_uint, i); @@ -23,22 +25,29 @@ TEST(TestConstMapper, to_type) { } try { - map.to(""); + map.to<0, 1>(100); EXPECT_TRUE(false); - } catch (const std::out_of_range& e) { + } catch (const std::out_of_range &e) { // expected here - } catch (const std::exception& e) { + } catch (const std::exception &e) { EXPECT_TRUE(false); } } -TEST(TestConstMapper, to_index) { +TEST(TestConstMapper, to_type) { + constexpr auto map = ConstMapper<4, std::string_view, int, std::uint8_t>{{{ + {"value_0", 0, 0}, + {"value_1", -1, 1}, + {"value_2", -2, 2}, + {"value_3", -3, 3}, + }}}; + for (auto i = 0; i < 4; ++i) { auto string = "value_" + std::to_string(i); - auto value_int = map.to<1, 0>(string); - auto value_uint = map.to<2, 0>(string); - auto value_str = map.to<0, 2>(i); + auto value_int = map.to(string); + auto value_uint = map.to(string); + auto value_str = map.to(i); EXPECT_EQ(value_int, -i); EXPECT_EQ(value_uint, i); @@ -46,16 +55,118 @@ TEST(TestConstMapper, to_index) { } try { - map.to<0, 1>(100); + map.to(""); EXPECT_TRUE(false); - } catch (const std::out_of_range& e) { + } catch (const std::out_of_range &e) { // expected here - } catch (const std::exception& e) { + } catch (const std::exception &e) { EXPECT_TRUE(false); } } -int main(int argc, char** argv) { - ::testing::InitGoogleTest(&argc, argv); - return RUN_ALL_TESTS(); -} \ No newline at end of file +TEST(TestConstMapper, pattern) { + constexpr auto map = ConstMapper<6, std::string_view, Anyable, Anyable>{{{ + {"value_0", 0, 0}, + {"value_1", -1, 1}, + {"value_2", -2, 2}, + {"value_3", -3, 3}, + {"value_4", -4, {}}, + {"value_any", {}, {}}, + }}}; + + { + constexpr auto value_str = map.pattern_match(std::tuple({}, 0, 0)); + constexpr auto expected = "value_0"; + EXPECT_EQ(value_str, expected); + } + { + constexpr auto value_str = map.pattern_match(std::tuple({}, -2, 2)); + constexpr auto expected = "value_2"; + EXPECT_EQ(value_str, expected); + } + { + constexpr auto value_str = map.pattern_match(std::tuple({}, -4, 2)); + constexpr auto expected = "value_4"; + EXPECT_EQ(value_str, expected); + } + { + constexpr auto value_str = map.pattern_match(std::tuple({}, {}, 2)); + constexpr auto expected = "value_2"; + EXPECT_EQ(value_str, expected); + } + { + constexpr auto value_str = map.pattern_match(std::tuple({}, -1, 2)); + constexpr auto expected = "value_any"; + EXPECT_EQ(value_str, expected); + } + { + constexpr auto value = map.pattern_match(std::tuple({}, {}, 2)); + constexpr auto expected = std::tuple>("value_2", -2); + EXPECT_EQ(value, expected); + } +} + +TEST(TestConstMapper, pattern_any) { + using namespace const_mapper; + constexpr auto map = ConstMapper<4, std::string_view, Anyable>{{{ + {"value_2", 2}, + {"value_3", 3}, + {"value_any", {}}, + }}}; + + for (auto i = 0; i < 4; ++i) { + auto string = "value_" + std::to_string(i); + + auto value_str = map.to>(i); + + if (i < 2) { + constexpr auto any_str = "value_any"; + EXPECT_EQ(value_str, any_str); + } else { + EXPECT_EQ(value_str, string); + } + } +} + +TEST(TestConstMapper, range) { + using namespace const_mapper; + constexpr auto map = ConstMapper<6, std::string_view, Range>{{{ + {"less 2", {CompareType::LessThan, 2}}, + {"less equal 2", {CompareType::LessEqual, 2}}, + {"equal 3", {CompareType::Equal, 3}}, + {"larger 5", {CompareType::LargerThan, 5}}, + {"larger equal 5", {CompareType::LargerEqual, 5}}, + {"any", {}}, + }}}; + + { + constexpr auto value_str = map.to>(1); + constexpr auto expected = "less 2"; + EXPECT_EQ(value_str, expected); + } + { + constexpr auto value_str = map.to>(2); + constexpr auto expected = "less equal 2"; + EXPECT_EQ(value_str, expected); + } + { + constexpr auto value_str = map.to>(3); + constexpr auto expected = "equal 3"; + EXPECT_EQ(value_str, expected); + } + { + constexpr auto value_str = map.to>(4); + constexpr auto expected = "any"; + EXPECT_EQ(value_str, expected); + } + { + constexpr auto value_str = map.to>(5); + constexpr auto expected = "larger equal 5"; + EXPECT_EQ(value_str, expected); + } + { + constexpr auto value_str = map.to>(6); + constexpr auto expected = "larger 5"; + EXPECT_EQ(value_str, expected); + } +} diff --git a/test/test_example.cpp b/test/test_example.cpp new file mode 100644 index 0000000..f00a7b1 --- /dev/null +++ b/test/test_example.cpp @@ -0,0 +1,73 @@ +#include + +#include "const_mapper.hpp" + +TEST(TestExample, simple) { + using namespace const_mapper; + constexpr auto map = ConstMapper<3, std::string_view, int, std::uint8_t>{{{ + {"value_0", 0, 0}, + {"value_1", 1, 10}, + {"value_2", 2, 20}, + }}}; + + // str0 == "value_1"; + constexpr auto str0 = map.to(1); + + // str1 == "value_2"; + constexpr auto str1 = map.to(20); + + // int0 == 2; + constexpr auto int0 = map.to(20); + + { + constexpr auto expected = "value_1"; + EXPECT_EQ(str0, expected); + } + { + constexpr auto expected = "value_2"; + EXPECT_EQ(str1, expected); + } + { + constexpr int expected = 2; + EXPECT_EQ(int0, expected); + } +} + +TEST(TestExample, pattern_match) { + using namespace const_mapper; + constexpr auto map = ConstMapper<6, std::string_view, Range, Anyable>{{{ + {"less2 & 1", {CompareType::LessThan, 2}, 1}, + {"less2 & 2", {CompareType::LessThan, 2}, 2}, + {"larger5", {CompareType::LargerThan, 5}, {}}, + {"Any", {}, {}}, + }}}; + + // str0 == "less2 & 1" + constexpr auto str0 = map.pattern_match(std::make_tuple(Result{}, 1, 1)); + + // str1 == "less2 & 2" + constexpr auto str1 = map.pattern_match(std::make_tuple(Result{}, 1, 2)); + + // str2 == "larger5" + constexpr auto str2 = map.pattern_match(std::make_tuple(Result{}, 6, -1)); + + // str3 == "Any" + constexpr auto str3 = map.pattern_match(std::make_tuple(Result{}, 5, -1)); + + { + constexpr auto expected = "less2 & 1"; + EXPECT_EQ(str0, expected); + } + { + constexpr auto expected = "less2 & 2"; + EXPECT_EQ(str1, expected); + } + { + constexpr auto expected = "larger5"; + EXPECT_EQ(str2, expected); + } + { + constexpr auto expected = "Any"; + EXPECT_EQ(str3, expected); + } +} diff --git a/test/test_performance.cpp b/test/test_performance.cpp new file mode 100644 index 0000000..f3d4539 --- /dev/null +++ b/test/test_performance.cpp @@ -0,0 +1,105 @@ +#include + +#include + +#include "const_mapper.hpp" + +using namespace const_mapper; + +namespace { +static constexpr auto loop = 10000000; +} + +TEST(Performance, ref_std_unordered_map) { + std::random_device rd; + std::mt19937 gen(rd()); + std::uniform_int_distribution distrib(0, 9); + + const auto map = std::unordered_map{ + {0, 0}, {1, -1}, {2, -2}, {3, -3}, {4, -4}, {5, -5}, {6, -6}, {7, -7}, {8, -8}, {9, -9}, + }; + + for (auto j = 0; j < loop; ++j) { + auto i = distrib(gen); + auto expected = -i; + auto value = map.at(static_cast(i)); + EXPECT_EQ(value, expected); + } +} + +TEST(Performance, ref_std_map) { + std::random_device rd; + std::mt19937 gen(rd()); + std::uniform_int_distribution distrib(0, 9); + + const auto map = std::map{ + {0, 0}, {1, -1}, {2, -2}, {3, -3}, {4, -4}, {5, -5}, {6, -6}, {7, -7}, {8, -8}, {9, -9}, + }; + + for (auto j = 0; j < loop; ++j) { + auto i = distrib(gen); + auto expected = -i; + auto value = map.at(static_cast(i)); + EXPECT_EQ(value, expected); + } +} + +TEST(Performance, const_mapper_to) { + std::random_device rd; + std::mt19937 gen(rd()); + std::uniform_int_distribution distrib(0, 9); + + constexpr auto map = ConstMapper<10, std::uint8_t, int>{ + {{{0, 0}, {1, -1}, {2, -2}, {3, -3}, {4, -4}, {5, -5}, {6, -6}, {7, -7}, {8, -8}, {9, -9}}}}; + + for (auto j = 0; j < loop; ++j) { + auto i = distrib(gen); + auto expected = -i; + auto value = map.to(static_cast(i)); + EXPECT_EQ(value, expected); + } +} + +TEST(Performance, const_mapper_pattern_match) { + std::random_device rd; + std::mt19937 gen(rd()); + std::uniform_int_distribution distrib(0, 9); + + constexpr auto map = ConstMapper<10, std::uint8_t, int>{ + {{{0, 0}, {1, -1}, {2, -2}, {3, -3}, {4, -4}, {5, -5}, {6, -6}, {7, -7}, {8, -8}, {9, -9}}}}; + + for (auto j = 0; j < loop; ++j) { + auto i = distrib(gen); + auto expected = -i; + auto key = static_cast(i); + auto value = map.pattern_match(std::make_tuple(key, Result{})); + EXPECT_EQ(value, expected); + } +} + +TEST(Performance, const_mapper_pattern_match_pick_up_2_values) { + std::random_device rd; + std::mt19937 gen(rd()); + std::uniform_int_distribution distrib(0, 9); + + constexpr auto map = ConstMapper<10, std::uint8_t, std::uint16_t, int>{{{ + {0, 0, 0}, + {1, 2, -1}, + {2, 4, -2}, + {3, 6, -3}, + {4, 8, -4}, + {5, 10, -5}, + {6, 12, -6}, + {7, 14, -7}, + {8, 16, -8}, + {9, 18, -9}, + }}}; + + for (auto j = 0; j < loop; ++j) { + auto i = distrib(gen); + auto expected = std::make_tuple(static_cast(2 * i), -i); + auto key = static_cast(i); + auto value = map.pattern_match(std::make_tuple(key, Result{}, Result{})); + EXPECT_EQ(value, expected); + } +} diff --git a/test/test_utils.cpp b/test/test_utils.cpp new file mode 100644 index 0000000..8bf814f --- /dev/null +++ b/test/test_utils.cpp @@ -0,0 +1,26 @@ +#include + +#include "const_mapper.hpp" + +using namespace const_mapper; + +TEST(TestUtils, tuple_index) { + using tuple = std::tuple; + + constexpr auto str_index = tuple_index(); + constexpr auto int_index = tuple_index(); + constexpr auto uint_index = tuple_index(); + // constexpr auto uint_index = tuple_index_v; // must fail. + + static_assert(str_index == 0); + static_assert(int_index == 1); + static_assert(uint_index == 2); + std::cout << "Test output" << std::endl; +} + +TEST(TestUtils, un_tuple) { + constexpr auto tmp0 = un_tuple_if_one_element(std::tuple(10)); + EXPECT_EQ(tmp0, 10); + constexpr auto tmp1 = un_tuple_if_one_element(std::tuple(10, 20)); + EXPECT_EQ(tmp1, std::tuple(10, 20)); +}