Last active
February 8, 2019 11:16
-
-
Save nohajc/cca987592392dd18f6c1393f040bec76 to your computer and use it in GitHub Desktop.
C++17 constexpr map backed by array of pairs sorted at compilation time
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <array> | |
#include <cstdio> | |
#include <map> | |
#include <type_traits> | |
namespace ce { | |
template<class InputIt, class UnaryPredicate> | |
constexpr InputIt find_if_not(InputIt first, InputIt last, UnaryPredicate q) | |
{ | |
for (; first != last; ++first) { | |
if (!q(*first)) { | |
return first; | |
} | |
} | |
return last; | |
} | |
template<class ForwardIt1, class ForwardIt2> | |
constexpr void iter_swap(ForwardIt1 a, ForwardIt2 b) | |
{ | |
auto tmp = *a; | |
*a = *b; | |
*b = tmp; | |
} | |
template <typename RAIt> | |
constexpr auto distance(RAIt first, RAIt last) | |
{ | |
return last - first; | |
} | |
template <typename RAIt> | |
constexpr RAIt next(RAIt it, | |
typename std::iterator_traits<RAIt>::difference_type n = 1) | |
{ | |
return it + n; | |
} | |
template<class ForwardIt, class T, class Compare> | |
constexpr ForwardIt lower_bound(ForwardIt first, ForwardIt last, const T& value, Compare comp) | |
{ | |
ForwardIt it = first; | |
typename std::iterator_traits<ForwardIt>::difference_type count = 0, step = 0; | |
count = ce::distance(first,last); | |
while (count > 0) { | |
it = first; | |
step = count / 2; | |
std::advance(it, step); | |
if (comp(*it, value)) { | |
first = ++it; | |
count -= step + 1; | |
} | |
else | |
count = step; | |
} | |
return first; | |
} | |
template<class ForwardIt, class UnaryPredicate> | |
constexpr ForwardIt partition(ForwardIt first, ForwardIt last, UnaryPredicate p) | |
{ | |
first = ce::find_if_not(first, last, p); | |
if (first == last) return first; | |
for (ForwardIt i = ce::next(first); i != last; ++i) { | |
if (p(*i)) { | |
ce::iter_swap(i, first); | |
++first; | |
} | |
} | |
return first; | |
} | |
constexpr int strcmp(const char* a, const char* b) { | |
unsigned i = 0; | |
while (a[i] && b[i]) { | |
if (a[i] < b[i]) return -1; | |
if (a[i] > b[i]) return 1; | |
i++; | |
} | |
return 0; | |
} | |
template <class ForwardIt, class Compare = std::less<>> | |
constexpr void quicksort(ForwardIt first, ForwardIt last, Compare cmp = Compare{}) | |
{ | |
if(first == last) return; | |
const auto pivot = *ce::next(first, ce::distance(first,last)/2); | |
ForwardIt middle1 = ce::partition(first, last, | |
[=](const auto& em) { return cmp(em, pivot); }); | |
ForwardIt middle2 = ce::partition(middle1, last, | |
[=](const auto& em) { return !cmp(pivot, em); }); | |
quicksort(first, middle1, cmp); | |
quicksort(middle2, last, cmp); | |
} | |
template <typename Range> | |
constexpr auto str_sort(Range&& range) | |
{ | |
quicksort(std::begin(range), std::end(range), | |
[](const char* a, const char* b) { | |
return ce::strcmp(a, b) < 0; | |
} | |
); | |
return range; | |
} | |
template <typename Range, class Compare = std::less<>> | |
constexpr auto sort(Range&& range, Compare cmp = Compare{}) | |
{ | |
quicksort(std::begin(range), std::end(range), cmp); | |
return range; | |
} | |
template <typename Key, typename Value> | |
struct pair { | |
Key first; | |
Value second; | |
constexpr pair(Key k, Value v) : first(k), second(v) {} | |
}; | |
struct strkeyless { | |
template <typename T> | |
constexpr bool operator()( | |
const ce::pair<const char*, T>& a, | |
const ce::pair<const char*, T>& b) const | |
{ | |
return ce::strcmp(a.first, b.first) < 0; | |
} | |
}; | |
template <size_t N, | |
typename Key = const char*, | |
typename Value = const char*, | |
typename Cmp = ce::strkeyless | |
> | |
class map { | |
std::array<ce::pair<Key, Value>, N> data; | |
template <size_t... Is> | |
constexpr map(const ce::pair<Key, Value>(&arr)[N], std::index_sequence<Is...>) | |
: data(ce::sort(std::array{arr[Is]...}, Cmp{})) | |
{} | |
template <size_t... Is> | |
constexpr map(const Key *(&arr)[N], std::index_sequence<Is...>) | |
: data(ce::sort(std::array{ce::pair{arr[Is][0], arr[Is][1]}...}, Cmp{})) | |
{} | |
public: | |
constexpr map(const ce::pair<Key, Value> (&arr)[N]) | |
: map(arr, std::make_index_sequence<N>{}) | |
{} | |
constexpr map(const Key *(&arr)[N]) | |
: map(arr, std::make_index_sequence<N>{}) | |
{} | |
constexpr Value operator[](const Key& k) const { | |
auto begin = std::begin(data); | |
auto end = std::end(data); | |
auto it = ce::lower_bound(begin, end, k, [=](const auto& a, const Key& b) { | |
return ce::strcmp(a.first, b) < 0; | |
}); | |
return it == end ? Value{} : it->second; | |
} | |
}; | |
template <typename KeyVal, size_t N> | |
map(const KeyVal *(&)[N]) -> map<N, KeyVal, KeyVal>; | |
} | |
// ce::map elements can be declared in any order | |
// they will be sorted at compilation time for logarithmic lookup at runtime | |
// keys and values can be of any type but the default comparator for keys | |
// assumes C strings with lexicographic ordering | |
constexpr ce::map numtab({ | |
ce::pair{"spam", 13}, | |
ce::pair{"eggs", 14}, | |
ce::pair{"sausage", 15}, | |
ce::pair{"bacon", 16} | |
}); | |
// shorter syntax can be used when keys and values are of the same type | |
constexpr ce::map strtab({ | |
{"spam", "0_SPAM"}, | |
{"eggs", "1_EGGS"}, | |
{"sausage", "2_SAUSAGE"}, | |
{"bacon", "3_BACON"} | |
}); | |
int main() { | |
constexpr auto sorted = ce::str_sort(std::array{"spam", "eggs", "sausage", "bacon", "spam"}); | |
puts(sorted[0]); | |
constexpr const char key[] = "eggs"; | |
constexpr auto val = strtab[key]; | |
puts(val); | |
constexpr auto n = numtab[key]; | |
printf("%d\n", n); | |
{ | |
const char key[16] = {0}; | |
scanf("%s", key); | |
puts(strtab[key]); | |
} | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment