Cleanup constexpr namespace and some metaprogramming junk

This commit is contained in:
rexy712 2020-05-02 14:04:08 -07:00
parent c99abad226
commit c622eb7323
6 changed files with 164 additions and 105 deletions

View File

@ -29,7 +29,7 @@ if(ENABLE_PROFILING)
target_link_options(rexy PRIVATE -fsanitize=address -fno-omit-frame-pointer -fno-optimize-sibling-calls)
endif()
set(LIBREXY_PUBLIC_HEADERS "include/rexy/steal.hpp" "include/rexy/binary.hpp" "include/rexy/string_base.hpp" "include/rexy/string.hpp" "include/rexy/filerd.hpp" "include/rexy/string_base.tpp")
set(LIBREXY_PUBLIC_HEADERS "include/rexy/traits.hpp" "include/rexy/steal.hpp" "include/rexy/binary.hpp" "include/rexy/string_base.hpp" "include/rexy/string.hpp" "include/rexy/filerd.hpp" "include/rexy/string_base.tpp")
target_compile_options(rexy PRIVATE -Wall -Wextra -pedantic -std=c++17)
install(TARGETS rexy

View File

@ -30,15 +30,15 @@ namespace rexy::cx{
auto value = *pivot;
//move pivot value all the way to the right side to preserve it
swap(*pivot, *right);
cx::swap(*pivot, *right);
for(auto it = left;it != right;++it){
if(cmp(*it, value)){
swap(*left, *it);
cx::swap(*left, *it);
++left;
}
}
//move pivot value back to proper position
swap(*left, *right);
cx::swap(*left, *right);
return left;
}
template<class Iter, class Compare>

View File

@ -25,13 +25,18 @@
#include "hash.hpp"
#include <climits> //CHAR_BIT
#include <cstddef> //size_t, ptrdiff_t
#include <utility> //pair
#include <type_traits> //decay
#include <initializer_list>
namespace rexy::cx{
template<class Key, class Value>
struct element
{
struct element{
using key_type = Key;
using value_type = Value;
Key key;
Value value;
};
@ -40,103 +45,126 @@ namespace rexy::cx{
class hashmap
{
public:
static constexpr size_t single_bucket_bit = size_t{1} << ((sizeof(size_t)*CHAR_BIT) - 1);
static constexpr size_t max_size = N;
using key_type = Key;
using mapped_type = Value;
using value_type = element<Key,Value>;
using size_type = size_t;
using difference_type = ptrdiff_t;
using hasher = Hash;
using reference = mapped_type&;
using const_reference = const mapped_type&;
using pointer = mapped_type*;
using const_pointer = const mapped_type*;
static constexpr size_type single_bucket_bit = size_type{1} << ((sizeof(size_type)*CHAR_BIT) - 1);
static constexpr size_type max_size = N;
static_assert((max_size & single_bucket_bit) == 0);
using key_type = Key;
using value_type = Value;
using hash_type = Hash;
private:
array<Value,N> m_values; //perfect hash table
array<size_t,N> m_g; //'salt' values for indexing into the perfect hash table
array<mapped_type,N> m_values; //perfect hash table
array<size_type,N> m_g; //'salt' values for indexing into the perfect hash table
public:
constexpr hashmap(const element<Key,Value>(&elements)[N]){
array<vector<element<Key,Value>,N>,N> buckets;
array<bool,N> slots_used;
size_t current_bucket = 0;
//place all keys into buckets
for(auto& element : elements){
buckets[Hash{}(element.key) % max_size].push_back(element);
}
//sort the buckets based on size, largest first
quicksort(buckets.begin(), buckets.end(), [](auto&& left, auto&& right) -> bool{
return left.size() > right.size();
});
//for each bucket, try different values of 'd' to try to find a hash that doesn't collide
for(current_bucket = 0;current_bucket < buckets.size();++current_bucket){
auto& bucket = buckets[current_bucket];
//only handle buckets containing collisions
if(bucket.size() <= 1)
break;
array<bool,N> pass_slots_used;
vector<size_t,N> pass_slots;
size_t d = 1;
for(size_t i = 0;i < bucket.size();){
size_t slot = Hash{}(bucket[i].key, d) % max_size;
if(pass_slots_used[slot] || slots_used[slot]){
//slot already in use, try another value for 'd'
++d;
i = 0;
pass_slots_used.fill(false);
pass_slots.clear();
}else{
//slot is good to go
pass_slots_used[slot] = true;
pass_slots.push_back(slot);
++i;
}
}
//store the successful value of 'd' at index of the first hash for this bucket
m_g[Hash{}(bucket[0].key) % max_size] = d;
//take the value from the temporary bucket into the permanent slot
for(size_t i = 0;i < bucket.size();++i){
m_values[pass_slots[i]] = std::move(bucket[i].value);
slots_used[pass_slots[i]] = true;
}
}
//Handle remaining single value buckets
size_t next_free_slot = 0;
for(;current_bucket < buckets.size();++current_bucket){
auto& bucket = buckets[current_bucket];
if(bucket.size() == 0)
break;
for(;slots_used[next_free_slot];++next_free_slot);
m_g[Hash{}(bucket[0].key) % max_size] = (next_free_slot | single_bucket_bit);
m_values[next_free_slot] = std::move(bucket[0].value);
slots_used[next_free_slot] = true;
}
}
constexpr hashmap(const value_type(&elements)[N]);
//no key checks. give a correct key or get a random answer :)
constexpr value_type& operator[](const Key& key){
size_t d = m_g[Hash{}(key) % max_size];
if(d & single_bucket_bit)
return m_values[d & ~single_bucket_bit];
return m_values[Hash{}(key, d) % max_size];
}
constexpr const value_type& operator[](const Key& key)const{
size_t d = m_g[Hash{}(key) % max_size];
if(d & single_bucket_bit)
return m_values[d & ~single_bucket_bit];
return m_values[Hash{}(key, d) % max_size];
}
template<class U, class UHash = hash<std::decay_t<U>>>
constexpr reference operator[](U&& u);
template<class U, class UHash = hash<std::decay_t<U>>>
constexpr const_reference operator[](U&& u)const;
};
template<class Key, class Value, size_t N, class Hash>
constexpr hashmap<Key,Value,N,Hash>::hashmap(const value_type(&elements)[N]){
array<vector<value_type,N>,N> buckets;
array<bool,N> slots_used;
size_type current_bucket = 0;
//place all keys into buckets
for(auto& element : elements){
buckets[Hash{}(element.key) % max_size].push_back(element);
}
//sort the buckets based on size, largest first
quicksort(buckets.begin(), buckets.end(), [](auto&& left, auto&& right) -> bool{
return left.size() > right.size();
});
//for each bucket, try different values of 'd' to try to find a hash that doesn't collide
for(current_bucket = 0;current_bucket < buckets.size();++current_bucket){
auto& bucket = buckets[current_bucket];
//only handle buckets containing collisions
if(bucket.size() <= 1)
break;
array<bool,N> pass_slots_used;
vector<size_type,N> pass_slots;
size_type d = 1;
for(size_type i = 0;i < bucket.size();){
size_type slot = Hash{}(bucket[i].key, d) % max_size;
if(pass_slots_used[slot] || slots_used[slot]){
//slot already in use, try another value for 'd'
++d;
i = 0;
pass_slots_used.fill(false);
pass_slots.clear();
}else{
//slot is good to go
pass_slots_used[slot] = true;
pass_slots.push_back(slot);
++i;
}
}
//store the successful value of 'd' at index of the first hash for this bucket
m_g[Hash{}(bucket[0].key) % max_size] = d;
//take the value from the temporary bucket into the permanent slot
for(size_type i = 0;i < bucket.size();++i){
m_values[pass_slots[i]] = std::move(bucket[i].value);
slots_used[pass_slots[i]] = true;
}
}
//Handle remaining single value buckets
size_type next_free_slot = 0;
for(;current_bucket < buckets.size();++current_bucket){
auto& bucket = buckets[current_bucket];
if(bucket.size() == 0)
break;
for(;slots_used[next_free_slot];++next_free_slot);
m_g[Hash{}(bucket[0].key) % max_size] = (next_free_slot | single_bucket_bit);
m_values[next_free_slot] = std::move(bucket[0].value);
slots_used[next_free_slot] = true;
}
}
//no key checks. give a correct key or get a random answer :)
template<class Key, class Value, size_t N, class Hash>
template<class U, class UHash>
constexpr auto hashmap<Key,Value,N,Hash>::operator[](U&& key) -> reference{
auto d = m_g[UHash{}(std::forward<U>(key)) % max_size];
if(d & single_bucket_bit)
return m_values[d & ~single_bucket_bit];
return m_values[UHash{}(std::forward<U>(key), d) % max_size];
}
template<class Key, class Value, size_t N, class Hash>
template<class U, class UHash>
constexpr auto hashmap<Key,Value,N,Hash>::operator[](U&& key)const -> const_reference{
auto d = m_g[UHash{}(std::forward<U>(key)) % max_size];
if(d & single_bucket_bit)
return m_values[d & ~single_bucket_bit];
return m_values[UHash{}(std::forward<U>(key), d) % max_size];
}
template<class Key, class Value, size_t N, class Hash = hash<Key>>
constexpr auto make_hashmap(const element<Key,Value>(&list)[N]){
constexpr auto make_hashmap(const typename hashmap<Key,Value,N,Hash>::value_type(&list)[N]){
return hashmap<Key,Value,N,Hash>(list);
}
}

View File

@ -25,6 +25,7 @@
#include "steal.hpp"
#include "cx/utility.hpp"
#include "traits.hpp"
namespace rexy{
@ -166,36 +167,36 @@ namespace rexy{
namespace detail{
std::true_type is_string_helper(string_expr*);
std::false_type is_string_helper(...);
template<class T>
struct is_string{
static constexpr bool value = std::is_same<std::true_type,decltype(is_string_helper(std::declval<typename std::decay<T>::type*>()))>::value;
static constexpr bool value = rexy::is_type<T,string_expr>::value;
};
std::true_type is_string_base(string_base*);
std::false_type is_string_base(...);
template<class T>
struct is_concrete_string{
static constexpr bool value = std::is_same<std::true_type,decltype(is_string_base(std::declval<typename std::decay<T>::type*>()))>::value;
static constexpr bool value = rexy::is_type<T,string_base>::value;
};
template<class... Ts>
using enable_if_string = std::enable_if_t<(is_string<Ts>::value && ...),int>;
template<class... Ts>
using enable_if_concrete_string = std::enable_if_t<(is_concrete_string<Ts>::value && ...),int>;
template<class... Ts>
using enable_if_expr_string = std::enable_if_t<(rexy::is_template_type<Ts,string_cat_expr>::value && ...),int>;
template<class Targ>
struct appender
struct string_appender
{
private:
Targ& m_targ;
public:
appender(Targ& t);
constexpr string_appender(Targ& t);
template<class L, class R>
void operator()(const string_cat_expr<L,R>& str);
void operator()(const string_base& str);
constexpr void operator()(const string_cat_expr<L,R>& str);
template<class Str, std::enable_if_t<!rexy::is_template_type<Str,string_cat_expr>::value,int> = 0>
constexpr void operator()(Str&& str);
};
} //namespace detail
template<class Str1, class Str2, detail::enable_if_concrete_string<Str1,Str2> = 0>
constexpr bool operator==(Str1&& left, Str2&& right){
return left.valid() && right.valid() && left.length() == right.length() && !cx::strcmp(left.get(), right.get());

View File

@ -219,7 +219,7 @@ namespace rexy{
string_cat_expr<Left,Right>::operator string_intermediary<Alloc>(void){
size_t len = length();
string_intermediary<Alloc> ret(len);
detail::appender<string_intermediary<Alloc>> append(ret);
detail::string_appender<string_intermediary<Alloc>> append(ret);
append(*this);
return ret;
}
@ -259,15 +259,16 @@ namespace rexy{
namespace detail{
template<class Targ>
appender<Targ>::appender(Targ& t): m_targ(t){}
constexpr string_appender<Targ>::string_appender(Targ& t): m_targ(t){}
template<class Targ>
template<class L, class R>
void appender<Targ>::operator()(const string_cat_expr<L,R>& str){
constexpr void string_appender<Targ>::operator()(const string_cat_expr<L,R>& str){
(*this)(str.left());
(*this)(str.right());
}
template<class Targ>
void appender<Targ>::operator()(const string_base& str){
template<class Str, std::enable_if_t<!rexy::is_template_type<Str,string_cat_expr>::value,int>>
constexpr void string_appender<Targ>::operator()(Str&& str){
m_targ.append(str.get(), str.length());
}
}

29
include/rexy/traits.hpp Normal file
View File

@ -0,0 +1,29 @@
#ifndef REXY_TRAITS_HPP
#define REXY_TRAITS_HPP
#include <type_traits> //is_same, decay, integral_constant, declval
namespace rexy{
template<class T, class U>
struct is_type{
static std::true_type check(U*);
static std::false_type check(...);
static constexpr bool value = std::is_same<std::true_type,decltype(check(std::declval<std::decay_t<T>*>()))>::value;
};
template<class T, template<class...> class U>
struct is_template_type_helper{
static constexpr bool value = false;
};
template<template<class...> class U, class... Args>
struct is_template_type_helper<U<Args...>,U>{
static constexpr bool value = true;
};
template<class T, template<class...> class U>
struct is_template_type : public is_template_type_helper<std::decay_t<T>,U>{};
}
#endif