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));
+}