Skip to content

Commit 18612ca

Browse files
committed
Added constant_map for compile-time maps
1 parent 44a731b commit 18612ca

File tree

5 files changed

+495
-0
lines changed

5 files changed

+495
-0
lines changed

include/vista/constant_map.hpp

+152
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
#ifndef VISTA_CONSTANT_MAP_HPP
2+
#define VISTA_CONSTANT_MAP_HPP
3+
4+
///////////////////////////////////////////////////////////////////////////////
5+
//
6+
// Copyright (C) 2020 Bjorn Reese <[email protected]>
7+
//
8+
// Distributed under the Boost Software License, Version 1.0.
9+
// (See accompanying file LICENSE_1_0.txt or copy at
10+
// http://www.boost.org/LICENSE_1_0.txt)
11+
//
12+
///////////////////////////////////////////////////////////////////////////////
13+
14+
#include <vista/functional.hpp> // less
15+
#include <vista/utility.hpp> // pair
16+
#include <vista/array.hpp>
17+
18+
namespace vista
19+
{
20+
21+
//! @brief Constant associative array.
22+
//!
23+
//! Suitable to build compile-time lookup tables.
24+
//!
25+
//! constexpr auto table = make_constant_map<int, int>(
26+
//! {
27+
//! { 2, 42 },
28+
//! { 4, 44 }
29+
//! });
30+
//!
31+
//! static_assert(table[2] == 42, "");
32+
//!
33+
//! Notice that the N template parameter is automatically chosen from the
34+
//! input data when using the make_constant_map() factory functions.
35+
36+
template <typename Key,
37+
typename T,
38+
std::size_t N,
39+
typename Compare = vista::less<Key>>
40+
class constant_map
41+
: protected vista::array<pair<Key, T>, N>
42+
{
43+
using storage = vista::template array<pair<Key, T>, N>;
44+
45+
public:
46+
using key_type = Key;
47+
using mapped_type = T;
48+
using value_type = pair<Key, T>;
49+
using size_type = typename storage::size_type;
50+
using const_iterator = typename storage::const_iterator;
51+
using key_compare = Compare;
52+
struct value_compare
53+
{
54+
constexpr bool operator()(const value_type& lhs, const value_type& rhs) const noexcept
55+
{
56+
return key_compare{}(lhs.first, rhs.first);
57+
}
58+
};
59+
60+
//! @brief Creates empty associative array.
61+
62+
constexpr constant_map() noexcept = default;
63+
64+
//! @brief Creates associative array from unsorted array.
65+
66+
explicit constexpr constant_map(value_type (&&input)[N]) noexcept(vista::detail::is_nothrow_swappable<value_type>::value);
67+
68+
//! @brief Returns the number of elements in span.
69+
70+
using storage::size;
71+
72+
//! @brief Checks if span contains key.
73+
//!
74+
//! Logarithmic time complexity.
75+
76+
VISTA_CXX14_CONSTEXPR
77+
bool contains(const key_type& key) const noexcept;
78+
79+
//! @brief Returns iterator to element with given key.
80+
//!
81+
//! If key not found, then returns iterator to the entry at the position where
82+
//! the searched-for entry should have been, or the end iterator.
83+
//!
84+
//! Logarithmic time complexity.
85+
86+
VISTA_CXX14_CONSTEXPR
87+
const_iterator lower_bound(const key_type& key) const noexcept;
88+
89+
//! @brief Returns iterator to element with given key.
90+
//!
91+
//! If key not found, return end()
92+
//!
93+
//! Logarithmic time complexity.
94+
95+
VISTA_CXX14_CONSTEXPR
96+
const_iterator find(const key_type& key) const noexcept;
97+
98+
//! @brief Returns reference to element with given key.
99+
//!
100+
//! If key not found, generate compile-time error in constexpr context.
101+
//! Undefined behaviour in non-constexpr context.
102+
//!
103+
//! @pre contains(key)
104+
105+
VISTA_CXX14_CONSTEXPR
106+
const mapped_type& operator[](const key_type& key) const noexcept;
107+
108+
//! @brief Returns iterator to the beginning of the span.
109+
110+
using storage::begin;
111+
using storage::cbegin;
112+
113+
//! @brief Returns iterator to the ending of the span.
114+
115+
using storage::end;
116+
using storage::cend;
117+
118+
constexpr key_compare key_comp() const noexcept;
119+
constexpr value_compare value_comp() const noexcept;
120+
121+
private:
122+
static constexpr storage to_sorted(value_type (&&input)[N]) noexcept(vista::detail::is_nothrow_swappable<value_type>::value);
123+
124+
private:
125+
// Error functions deliberately left non-constexpr to trigger compile-time error.
126+
const mapped_type& error_key_not_found(const mapped_type& v) const noexcept { return v; }
127+
128+
struct value_key_compare
129+
{
130+
constexpr bool operator()(const value_type& value, const key_type& key) const noexcept
131+
{
132+
return key_compare{}(value.first, key);
133+
}
134+
};
135+
};
136+
137+
//! @brief Creates constant associative array with given input.
138+
139+
template <typename Key,
140+
typename T,
141+
std::size_t N,
142+
typename Compare = vista::less<Key>>
143+
constexpr auto make_constant_map(pair<Key, T> (&&input)[N]) noexcept(vista::detail::is_nothrow_swappable<decltype(*input)>::value) -> constant_map<Key, T, N, Compare>
144+
{
145+
return constant_map<Key, T, N, Compare>{ std::forward<decltype(input)>(input) };
146+
}
147+
148+
} // namespace vista
149+
150+
#include <vista/detail/constant_map.ipp>
151+
152+
#endif // VISTA_CONSTANT_MAP_HPP

include/vista/detail/constant_map.ipp

+80
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
///////////////////////////////////////////////////////////////////////////////
2+
//
3+
// Copyright (C) 2020 Bjorn Reese <[email protected]>
4+
//
5+
// Distributed under the Boost Software License, Version 1.0.
6+
// (See accompanying file LICENSE_1_0.txt or copy at
7+
// http://www.boost.org/LICENSE_1_0.txt)
8+
//
9+
///////////////////////////////////////////////////////////////////////////////
10+
11+
#include <vista/algorithm.hpp>
12+
13+
namespace vista
14+
{
15+
16+
template <typename K, typename T, std::size_t N, typename C>
17+
constexpr constant_map<K, T, N, C>::constant_map(value_type (&&input)[N]) noexcept(vista::detail::is_nothrow_swappable<value_type>::value)
18+
: storage{ to_sorted(std::forward<decltype(input)>(input)) }
19+
{
20+
}
21+
22+
template <typename K, typename T, std::size_t N, typename C>
23+
VISTA_CXX14_CONSTEXPR
24+
bool constant_map<K, T, N, C>::contains(const key_type& key) const noexcept
25+
{
26+
return find(key) != end();
27+
}
28+
29+
template <typename K, typename T, std::size_t N, typename C>
30+
VISTA_CXX14_CONSTEXPR
31+
auto constant_map<K, T, N, C>::lower_bound(const key_type& key) const noexcept -> const_iterator
32+
{
33+
return lower_bound_sorted(begin(),
34+
end(),
35+
key,
36+
value_key_compare{});
37+
}
38+
39+
template <typename K, typename T, std::size_t N, typename C>
40+
VISTA_CXX14_CONSTEXPR
41+
auto constant_map<K, T, N, C>::find(const key_type& key) const noexcept -> const_iterator
42+
{
43+
auto where = lower_bound(key);
44+
if (where == end() || key_compare{}(key, where->first))
45+
return end();
46+
return where;
47+
}
48+
49+
template <typename K, typename T, std::size_t N, typename C>
50+
VISTA_CXX14_CONSTEXPR
51+
auto constant_map<K, T, N, C>::operator[](const key_type& key) const noexcept -> const mapped_type&
52+
{
53+
auto where = lower_bound(key);
54+
if (where == end() || key_compare{}(key, where->first))
55+
return error_key_not_found(where->second);
56+
return where->second;
57+
}
58+
59+
template <typename K, typename T, std::size_t N, typename C>
60+
constexpr auto constant_map<K, T, N, C>::key_comp() const noexcept -> key_compare
61+
{
62+
return key_compare{};
63+
}
64+
65+
template <typename K, typename T, std::size_t N, typename C>
66+
constexpr auto constant_map<K, T, N, C>::value_comp() const noexcept -> value_compare
67+
{
68+
return value_compare{};
69+
}
70+
71+
template <typename K, typename T, std::size_t N, typename C>
72+
constexpr auto constant_map<K, T, N, C>::to_sorted(value_type (&&input)[N]) noexcept(vista::detail::is_nothrow_swappable<value_type>::value) -> storage
73+
{
74+
storage result {};
75+
vista::copy(input, input + N, result.begin());
76+
vista::insertion_sort<true>(result.begin(), result.end(), value_compare{});
77+
return result;
78+
}
79+
80+
} // namespace vista

test/CMakeLists.txt

+4
Original file line numberDiff line numberDiff line change
@@ -46,12 +46,16 @@ vista_add_test(priority_view_suite priority_view_suite.cpp)
4646

4747
vista_add_test(map_view_suite map_view_suite.cpp)
4848
vista_add_test(map_array_suite map_array_suite.cpp)
49+
vista_add_test(constant_map_suite constant_map_suite.cpp)
4950

5051
if ("cxx_std_14" IN_LIST CMAKE_CXX_COMPILE_FEATURES)
5152

5253
vista_add_compile_test(algorithm_constexpr_suite algorithm_constexpr_suite.cpp)
5354
target_compile_features(algorithm_constexpr_suite PRIVATE cxx_std_14)
5455

56+
vista_add_compile_test(constant_map_constexpr_suite constant_map_constexpr_suite.cpp)
57+
target_compile_features(constant_map_constexpr_suite PRIVATE cxx_std_14)
58+
5559
else()
5660
message("Skipping C++14 test suites")
5761
endif()

test/constant_map_constexpr_suite.cpp

+88
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
///////////////////////////////////////////////////////////////////////////////
2+
//
3+
// Copyright (C) 2020 Bjorn Reese <[email protected]>
4+
//
5+
// Distributed under the Boost Software License, Version 1.0.
6+
// (See accompanying file LICENSE_1_0.txt or copy at
7+
// http://www.boost.org/LICENSE_1_0.txt)
8+
//
9+
///////////////////////////////////////////////////////////////////////////////
10+
11+
#include <vista/utility.hpp> // get<I>
12+
#include <vista/constant_map.hpp>
13+
14+
using namespace vista;
15+
16+
//-----------------------------------------------------------------------------
17+
18+
constexpr auto increasing = make_constant_map<int, int>(
19+
{
20+
{ 11, 1 },
21+
{ 22, 2 },
22+
{ 33, 3 },
23+
{ 44, 4 }
24+
}
25+
);
26+
27+
static_assert(!increasing.contains(10), "");
28+
static_assert(increasing.contains(11), "");
29+
static_assert(increasing.contains(22), "");
30+
static_assert(increasing.contains(33), "");
31+
static_assert(increasing.contains(44), "");
32+
33+
static_assert(increasing.lower_bound(10) == increasing.begin() + 0, "");
34+
static_assert(increasing.lower_bound(11) == increasing.begin() + 0, "");
35+
static_assert(increasing.lower_bound(12) == increasing.begin() + 1, "");
36+
static_assert(increasing.lower_bound(22) == increasing.begin() + 1, "");
37+
static_assert(increasing.lower_bound(23) == increasing.begin() + 2, "");
38+
static_assert(increasing.lower_bound(33) == increasing.begin() + 2, "");
39+
static_assert(increasing.lower_bound(34) == increasing.begin() + 3, "");
40+
static_assert(increasing.lower_bound(44) == increasing.begin() + 3, "");
41+
static_assert(increasing.lower_bound(45) == increasing.end(), "");
42+
43+
static_assert(increasing.find(10) == increasing.end(), "");
44+
static_assert(increasing.find(11) == increasing.begin() + 0, "");
45+
static_assert(increasing.find(12) == increasing.end(), "");
46+
static_assert(increasing.find(22) == increasing.begin() + 1, "");
47+
static_assert(increasing.find(23) == increasing.end(), "");
48+
static_assert(increasing.find(33) == increasing.begin() + 2, "");
49+
static_assert(increasing.find(34) == increasing.end(), "");
50+
static_assert(increasing.find(44) == increasing.begin() + 3, "");
51+
static_assert(increasing.find(45) == increasing.end(), "");
52+
53+
static_assert(increasing.size() == 4, "");
54+
static_assert(increasing[11] == 1, "");
55+
static_assert(increasing[22] == 2, "");
56+
static_assert(increasing[33] == 3, "");
57+
static_assert(increasing[44] == 4, "");
58+
59+
static_assert(vista::get<1>(*increasing.find(11)) == 1, "");
60+
static_assert(vista::get<1>(*increasing.find(22)) == 2, "");
61+
static_assert(vista::get<1>(*increasing.find(33)) == 3, "");
62+
static_assert(vista::get<1>(*increasing.find(44)) == 4, "");
63+
64+
//-----------------------------------------------------------------------------
65+
66+
constexpr auto decreasing = make_constant_map<int, int>(
67+
{
68+
{ 44, 4 },
69+
{ 33, 3 },
70+
{ 22, 2 },
71+
{ 11, 1 }
72+
}
73+
);
74+
75+
static_assert(decreasing.size() == 4, "");
76+
static_assert(decreasing[11] == 1, "");
77+
static_assert(decreasing[22] == 2, "");
78+
static_assert(decreasing[33] == 3, "");
79+
static_assert(decreasing[44] == 4, "");
80+
static_assert(decreasing.find(0) == decreasing.end(), "");
81+
static_assert(decreasing.find(11) == decreasing.begin() + 0, "");
82+
static_assert(decreasing.find(22) == decreasing.begin() + 1, "");
83+
static_assert(decreasing.find(33) == decreasing.begin() + 2, "");
84+
static_assert(decreasing.find(44) == decreasing.begin() + 3, "");
85+
static_assert(vista::get<1>(*decreasing.find(11)) == 1, "");
86+
static_assert(vista::get<1>(*decreasing.find(22)) == 2, "");
87+
static_assert(vista::get<1>(*decreasing.find(33)) == 3, "");
88+
static_assert(vista::get<1>(*decreasing.find(44)) == 4, "");

0 commit comments

Comments
 (0)