Skip to content

Instantly share code, notes, and snippets.

@nohajc
Last active February 8, 2019 11:16
Show Gist options
  • Save nohajc/cca987592392dd18f6c1393f040bec76 to your computer and use it in GitHub Desktop.
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
#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