├── CMakeLists.txt ├── LICENSE ├── README.md ├── metamap ├── algorithms │ ├── cat.hh │ ├── intersection.hh │ ├── make_metamap_skip.hh │ ├── map_reduce.hh │ ├── substract.hh │ └── tuple_utils.hh ├── make.hh └── metamap.hh └── tests ├── cat.cc ├── intersection.cc ├── make.cc ├── substract.cc └── tuple.cc /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.8) 2 | 3 | project(metamap) 4 | 5 | add_definitions(-std=c++1z) 6 | 7 | enable_testing() 8 | 9 | add_executable(make tests/make.cc) 10 | add_test(make make) 11 | 12 | add_executable(cat tests/cat.cc) 13 | add_test(cat cat) 14 | 15 | add_executable(intersection tests/intersection.cc) 16 | add_test(intersection intersection) 17 | 18 | add_executable(substract tests/substract.cc) 19 | add_test(substract substract) 20 | 21 | add_executable(tuple tests/tuple.cc) 22 | add_test(tuple tuple) 23 | 24 | 25 | install(DIRECTORY metamap DESTINATION include/iod 26 | FILES_MATCHING PATTERN "*.hh") 27 | install(DIRECTORY metamap DESTINATION include/iod 28 | FILES_MATCHING PATTERN "*.hpp") 29 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Matthieu Garrigues 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 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | iod::metamap 2 | =============================== 3 | 4 | ```iod::metamap``` is an immutable zero-cost key value map. All 5 | operations on metamaps are run by the compiler and have a O(1) 6 | runtime cost. This greatly helps to build high performance 7 | applications while keeping the flexibility of maps. 8 | Compile time has also been reduced thanks to a zero-compile-time cost 9 | key retrieve and the heavy use of parameter pack expansion. 10 | 11 | Note: This is a work in progress. 12 | 13 | 14 | Dependencies 15 | ============== 16 | 17 | [iod::symbol](https://github.com/iodcpp/symbol) 18 | 19 | 20 | Tutorial 21 | ============== 22 | 23 | Let's first define some [symbols](https://github.com/iodcpp/symbol). They will be 24 | used as map keys. 25 | 26 | ```c++ 27 | IOD_SYMBOL(a) 28 | IOD_SYMBOL(b) 29 | ``` 30 | 31 | A map is a set of key value pairs: 32 | 33 | ```c++ 34 | // Create a map 35 | auto m = iod::metamap(s::a = 1, s::b = 2); 36 | 37 | // Retrieve map values via direct member access. 38 | // Zero cost neither at runtime nor compile time. 39 | assert(m.a == 1); 40 | // Or via operator[]. 41 | assert(m[s::a] == 1); 42 | ``` 43 | 44 | Concatenation of two maps. Values of m1 are given the priority in case of dupplicate keys. 45 | 46 | ```c++ 47 | auto m3 = iod::cat(m1, m2); 48 | ``` 49 | 50 | Build the map containing keys present in m1 and m2, taking values from m1. 51 | 52 | ```c++ 53 | auto m4 = iod::intersection(m1, m2); 54 | ``` 55 | 56 | Build the map containing keys present in m1 but not in m2, taking values from m1. 57 | 58 | ```c++ 59 | auto m5 = iod::substract(m1, m2); 60 | ``` 61 | 62 | Map a function on all key value pairs: 63 | 64 | ```c++ 65 | iod::map(m1, [] (auto k, auto v) { std::cout << iod::symbol_string(k) << "=" << v << std::endl; }); 66 | ``` 67 | -------------------------------------------------------------------------------- /metamap/algorithms/cat.hh: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace iod { 6 | 7 | 8 | template 9 | inline decltype(auto) cat(const metamap& a, 10 | const metamap& b) 11 | { 12 | return metamap(*static_cast(&a)..., 13 | *static_cast(&b)...); 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /metamap/algorithms/intersection.hh: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | namespace iod { 8 | 9 | template 10 | inline decltype(auto) intersection(const metamap& a, 11 | const metamap& b) 12 | { 13 | return map_reduce(a, [&] (auto k, auto&& v) -> decltype(auto) { 14 | if constexpr(has_key>(k)) { 15 | return k = std::forward(v); 16 | } 17 | else return skip{}; }, make_metamap_skip); 18 | } 19 | 20 | } 21 | -------------------------------------------------------------------------------- /metamap/algorithms/make_metamap_skip.hh: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | namespace iod { 7 | 8 | 9 | struct skip {}; 10 | static struct { 11 | 12 | template 13 | inline decltype(auto) run(metamap map, skip, T&&... args) const 14 | { 15 | return run(map, std::forward(args)...); 16 | } 17 | 18 | template 19 | inline decltype(auto) run(metamap map, T1&& a, T&&... args) const 20 | { 21 | return run(cat(map, 22 | internal::make_metamap_helper(internal::exp_to_variable(std::forward(a)))), 23 | std::forward(args)...); 24 | } 25 | 26 | template 27 | inline decltype(auto) run(metamap map) const { return map; } 28 | 29 | template 30 | inline decltype(auto) operator()(T&&... args) const 31 | { 32 | // Copy values. 33 | return run(metamap<>{}, std::forward(args)...); 34 | } 35 | 36 | } make_metamap_skip; 37 | 38 | } 39 | -------------------------------------------------------------------------------- /metamap/algorithms/map_reduce.hh: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | 6 | namespace iod { 7 | 8 | // Map a function(key, value) on all kv pair 9 | template 10 | void map(const metamap& m, F fun) 11 | { 12 | auto apply = [&] (auto key) -> decltype(auto) 13 | { 14 | return fun(key, m[key]); 15 | }; 16 | 17 | apply_each(apply, typename M::_iod_symbol_type{}...); 18 | } 19 | 20 | // Map a function(key, value) on all kv pair. Ensure that the calling order 21 | // is kept. 22 | // template 23 | // void map_sequential2(F fun, O& obj) 24 | // {} 25 | // template 26 | // void map_sequential2(F fun, O& obj, M1 m1, M... ms) 27 | // { 28 | // auto apply = [&] (auto key) -> decltype(auto) 29 | // { 30 | // return fun(key, obj[key]); 31 | // }; 32 | 33 | // apply(m1); 34 | // map_sequential2(fun, obj, ms...); 35 | // } 36 | // template 37 | // void map_sequential(const metamap& m, F fun) 38 | // { 39 | // auto apply = [&] (auto key) -> decltype(auto) 40 | // { 41 | // return fun(key, m[key]); 42 | // }; 43 | 44 | // map_sequential2(fun, m, typename M::_iod_symbol_type{}...); 45 | // } 46 | 47 | // Map a function(key, value) on all kv pair (non const). 48 | template 49 | void map(metamap& m, F fun) 50 | { 51 | auto apply = [&] (auto key) -> decltype(auto) 52 | { 53 | return fun(key, m[key]); 54 | }; 55 | 56 | apply_each(apply, typename M::_iod_symbol_type{}...); 57 | } 58 | 59 | template 60 | auto apply_each2(F&& f, R&& r, E&&... e) 61 | { 62 | return r(f(std::forward(e))...); 63 | //(void)std::initializer_list{ 64 | // ((void)f(std::forward(e)), 0)...}; 65 | } 66 | 67 | // Map a function(key, value) on all kv pair an reduce 68 | // all the results value with the reduce(r1, r2, ...) function. 69 | template 70 | decltype(auto) map_reduce(const metamap& m, F map, R reduce) 71 | { 72 | auto apply = [&] (auto key) -> decltype(auto) 73 | { 74 | //return map(key, std::forward(m[key])); 75 | return map(key, m[key]); 76 | }; 77 | 78 | return apply_each2(apply, reduce, typename M::_iod_symbol_type{}...); 79 | //return reduce(apply(typename M::_iod_symbol_type{})...); 80 | } 81 | 82 | // Map a function(key, value) on all kv pair an reduce 83 | // all the results value with the reduce(r1, r2, ...) function. 84 | template 85 | decltype(auto) reduce(const metamap& m, R reduce) 86 | { 87 | auto apply = [&] (auto key) -> decltype(auto) 88 | { 89 | return m[key]; 90 | }; 91 | 92 | return reduce(apply(typename M::_iod_symbol_type{})...); 93 | } 94 | 95 | } 96 | -------------------------------------------------------------------------------- /metamap/algorithms/substract.hh: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | namespace iod { 8 | 9 | template 10 | inline auto substract(const metamap& a, 11 | const metamap& b) 12 | { 13 | return map_reduce(a, [&] (auto k, auto&& v) { 14 | if constexpr(!has_key>(k)) { 15 | return k = std::forward(v); 16 | } 17 | else return skip{}; }, make_metamap_skip); 18 | } 19 | 20 | } 21 | -------------------------------------------------------------------------------- /metamap/algorithms/tuple_utils.hh: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | namespace iod { 4 | 5 | 6 | template 7 | void apply_each(F&& f, E&&... e) 8 | { 9 | (void)std::initializer_list{ 10 | ((void)f(std::forward(e)), 0)...}; 11 | } 12 | 13 | template 14 | auto tuple_map_reduce_impl(F&& f, R&& reduce, E&&... e) 15 | { 16 | return reduce(f(std::forward(e))...); 17 | } 18 | 19 | template 20 | void tuple_map(T&& t, F&& f) 21 | { 22 | return std::apply([&] (auto&&... e) { apply_each(f, std::forward(e)...); }, 23 | std::forward(t)); 24 | } 25 | 26 | template 27 | auto tuple_reduce(T&& t, F&& f) 28 | { 29 | return std::apply(std::forward(f), std::forward(t)); 30 | } 31 | 32 | template 33 | decltype(auto) tuple_map_reduce(T&& m, F map, R reduce) 34 | { 35 | auto fun = [&] (auto... e) { 36 | return tuple_map_reduce_impl(map, reduce, e...); 37 | }; 38 | return std::apply(fun, m); 39 | } 40 | 41 | template 42 | inline std::tuple<> tuple_filter_impl() { return std::make_tuple(); } 43 | 44 | template 45 | auto tuple_filter_impl(M1 m1, M... m) { 46 | if constexpr (std::is_same::value) 47 | return tuple_filter_impl(m...); 48 | else 49 | return std::tuple_cat(std::make_tuple(m1), tuple_filter_impl(m...)); 50 | } 51 | 52 | template 53 | auto tuple_filter(const std::tuple& m) { 54 | 55 | auto fun = [] (auto... e) { return tuple_filter_impl(e...); }; 56 | return std::apply(fun, m); 57 | 58 | } 59 | 60 | } -------------------------------------------------------------------------------- /metamap/make.hh: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | namespace iod { 7 | 8 | 9 | template 10 | struct metamap; 11 | 12 | namespace internal 13 | { 14 | 15 | template 16 | decltype(auto) exp_to_variable_ref(const assign_exp& e) 17 | { 18 | return make_variable_reference(S{}, e.right); 19 | } 20 | 21 | template 22 | decltype(auto) exp_to_variable(const assign_exp& e) 23 | { 24 | typedef std::remove_const_t> vtype; 25 | return make_variable(S{}, e.right); 26 | } 27 | 28 | template 29 | inline decltype(auto) make_metamap_helper(T&&... args) 30 | { 31 | return metamap(std::forward(args)...); 32 | } 33 | 34 | } 35 | 36 | // Store copies of values in the map 37 | static struct { 38 | template 39 | inline decltype(auto) operator()(T&&... args) const 40 | { 41 | // Copy values. 42 | return internal::make_metamap_helper(internal::exp_to_variable(std::forward(args))...); 43 | } 44 | } make_metamap; 45 | 46 | // Store references of values in the map 47 | template 48 | inline decltype(auto) make_metamap_reference(T&&... args) 49 | { 50 | // Keep references. 51 | return internal::make_metamap_helper(internal::exp_to_variable_ref(std::forward(args))...); 52 | } 53 | 54 | } 55 | -------------------------------------------------------------------------------- /metamap/metamap.hh: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace iod { 6 | 7 | namespace internal 8 | { 9 | struct { 10 | template 11 | constexpr auto operator()(A&& a, B&&... b) 12 | { 13 | auto result = a; 14 | using expand_variadic_pack = int[]; 15 | (void)expand_variadic_pack{0, ((result += b), 0)... }; 16 | return result; 17 | } 18 | } reduce_add; 19 | 20 | } 21 | 22 | 23 | template 24 | struct metamap; 25 | 26 | template 27 | decltype(auto) find_first(metamap&& map, F fun); 28 | 29 | template 30 | struct metamap : public Ms... 31 | { 32 | typedef metamap self; 33 | // Constructors. 34 | inline metamap() = default; 35 | inline metamap(self&&) = default; 36 | inline metamap(const self&) = default; 37 | 38 | metamap(self& other) 39 | : metamap(const_cast(other)) {} 40 | 41 | template 42 | inline metamap(M&&... members) : Ms(std::forward(members))... {} 43 | 44 | // Assignemnt ? 45 | 46 | // Retrive a value. 47 | template 48 | decltype(auto) operator[](K k) 49 | { 50 | return symbol_member_access(*this, k); 51 | } 52 | 53 | template 54 | decltype(auto) operator[](K k) const 55 | { 56 | return symbol_member_access(*this, k); 57 | } 58 | 59 | }; 60 | 61 | template 62 | constexpr auto size(metamap) 63 | { 64 | return sizeof...(Ms); 65 | } 66 | 67 | template 68 | constexpr auto has_key(M&& map, K k) 69 | { 70 | return decltype(has_member(map, k)){}; 71 | } 72 | 73 | template 74 | constexpr auto has_key(K k) 75 | { 76 | return decltype(has_member(std::declval(), k)){}; 77 | } 78 | 79 | template 80 | constexpr auto get_or(M&& map, K k, O default_) 81 | { 82 | if constexpr(has_key(map, k)) { 83 | return map[k]; 84 | } 85 | else 86 | return default_; 87 | } 88 | 89 | template 90 | struct is_metamap { enum { ret = false }; }; 91 | template 92 | struct is_metamap> { enum { ret = true }; }; 93 | 94 | } 95 | 96 | #include 97 | #include 98 | #include 99 | #include 100 | #include 101 | #include 102 | -------------------------------------------------------------------------------- /tests/cat.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | IOD_SYMBOL(test1) 6 | IOD_SYMBOL(test2) 7 | IOD_SYMBOL(test3) 8 | 9 | using namespace iod; 10 | 11 | int main() 12 | { 13 | 14 | auto a = make_metamap(s::test1 = 12, s::test2 = 13); 15 | auto b = make_metamap(s::test3 = 14); 16 | 17 | auto c = cat(a, b); 18 | 19 | assert(c.test1 == 12); 20 | assert(c.test2 == 13); 21 | assert(c.test3 == 14); 22 | } 23 | -------------------------------------------------------------------------------- /tests/intersection.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | IOD_SYMBOL(test1) 7 | IOD_SYMBOL(test2) 8 | IOD_SYMBOL(test3) 9 | IOD_SYMBOL(test4) 10 | IOD_SYMBOL(test5) 11 | 12 | using namespace iod; 13 | 14 | int main() 15 | { 16 | auto a = make_metamap(s::test1 = 12, s::test2 = 13, s::test4 = 14, s::test5 = std::string("test")); 17 | 18 | auto b = make_metamap(s::test2 = 12, s::test3 = 14, s::test5 = 16); 19 | auto c = intersection(a, b); 20 | 21 | assert(!has_key(c, s::test1)); 22 | assert(has_key(c, s::test2)); 23 | assert(!has_key(c, s::test3)); 24 | assert(!has_key(c, s::test4)); 25 | assert(has_key(c, s::test5)); 26 | 27 | assert(c.test2 == 13); 28 | assert(c.test5 == "test"); 29 | } 30 | -------------------------------------------------------------------------------- /tests/make.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | IOD_SYMBOL(test1) 6 | IOD_SYMBOL(test2) 7 | IOD_SYMBOL(test3) 8 | 9 | using namespace iod; 10 | 11 | 12 | int main() 13 | { 14 | 15 | // Simple map. 16 | auto m = make_metamap(s::test1 = 12, s::test2 = 13); 17 | 18 | assert(m.test1 == 12); 19 | assert(m[s::test1] == 12); 20 | assert(m.test2 == 13); 21 | assert(m[s::test2] == 13); 22 | 23 | if constexpr(has_key(m, s::test3)) { assert(0); } 24 | if constexpr(!has_key(m, s::test1)) { assert(0); } 25 | if constexpr(!has_key(m, s::test2)) { assert(0); } 26 | 27 | 28 | // References. 29 | int x = 41; 30 | auto m2 = make_metamap_reference(s::test1 = x); 31 | 32 | assert(m2[s::test1] == 41); 33 | x++; 34 | assert(m2[s::test1] == 42); 35 | 36 | auto m3 = make_metamap(s::test1 = std::string("test")); 37 | assert(m3.test1 == "test"); 38 | 39 | // Copy. 40 | decltype(m3) m4 = m3; 41 | assert(m4.test1 == "test"); 42 | assert(m4.test1.data() != m3.test1.data()); 43 | } 44 | -------------------------------------------------------------------------------- /tests/substract.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | IOD_SYMBOL(test1) 6 | IOD_SYMBOL(test2) 7 | IOD_SYMBOL(test3) 8 | IOD_SYMBOL(test4) 9 | 10 | using namespace iod; 11 | 12 | int main() 13 | { 14 | 15 | auto a = make_metamap(s::test1 = 12, s::test2 = 13, s::test3 = 13, s::test4 = 14); 16 | auto b = make_metamap(s::test2 = 12, s::test3 = 14); 17 | 18 | auto c = substract(a, b); 19 | 20 | assert(has_key(c, s::test1)); 21 | assert(!has_key(c, s::test2)); 22 | assert(!has_key(c, s::test3)); 23 | assert(has_key(c, s::test4)); 24 | assert(c.test1 == 12); 25 | assert(c.test4 == 14); 26 | } 27 | -------------------------------------------------------------------------------- /tests/tuple.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | int main() 6 | { 7 | std::tuple x{2, 3.2f, 1}; 8 | 9 | assert(std::get<0>(iod::tuple_filter(x)) == 2); 10 | assert(std::get<1>(iod::tuple_filter(x)) == 1); 11 | assert(std::get<0>(iod::tuple_filter(x)) == 3.2f); 12 | } 13 | --------------------------------------------------------------------------------