Cleanup constexpr namespace and some metaprogramming junk
This commit is contained in:
parent
c99abad226
commit
c622eb7323
@ -29,7 +29,7 @@ if(ENABLE_PROFILING)
|
|||||||
target_link_options(rexy PRIVATE -fsanitize=address -fno-omit-frame-pointer -fno-optimize-sibling-calls)
|
target_link_options(rexy PRIVATE -fsanitize=address -fno-omit-frame-pointer -fno-optimize-sibling-calls)
|
||||||
endif()
|
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)
|
target_compile_options(rexy PRIVATE -Wall -Wextra -pedantic -std=c++17)
|
||||||
|
|
||||||
install(TARGETS rexy
|
install(TARGETS rexy
|
||||||
|
|||||||
@ -30,15 +30,15 @@ namespace rexy::cx{
|
|||||||
auto value = *pivot;
|
auto value = *pivot;
|
||||||
|
|
||||||
//move pivot value all the way to the right side to preserve it
|
//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){
|
for(auto it = left;it != right;++it){
|
||||||
if(cmp(*it, value)){
|
if(cmp(*it, value)){
|
||||||
swap(*left, *it);
|
cx::swap(*left, *it);
|
||||||
++left;
|
++left;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//move pivot value back to proper position
|
//move pivot value back to proper position
|
||||||
swap(*left, *right);
|
cx::swap(*left, *right);
|
||||||
return left;
|
return left;
|
||||||
}
|
}
|
||||||
template<class Iter, class Compare>
|
template<class Iter, class Compare>
|
||||||
|
|||||||
@ -25,13 +25,18 @@
|
|||||||
#include "hash.hpp"
|
#include "hash.hpp"
|
||||||
|
|
||||||
#include <climits> //CHAR_BIT
|
#include <climits> //CHAR_BIT
|
||||||
|
#include <cstddef> //size_t, ptrdiff_t
|
||||||
|
#include <utility> //pair
|
||||||
|
#include <type_traits> //decay
|
||||||
#include <initializer_list>
|
#include <initializer_list>
|
||||||
|
|
||||||
namespace rexy::cx{
|
namespace rexy::cx{
|
||||||
|
|
||||||
template<class Key, class Value>
|
template<class Key, class Value>
|
||||||
struct element
|
struct element{
|
||||||
{
|
using key_type = Key;
|
||||||
|
using value_type = Value;
|
||||||
|
|
||||||
Key key;
|
Key key;
|
||||||
Value value;
|
Value value;
|
||||||
};
|
};
|
||||||
@ -40,24 +45,42 @@ namespace rexy::cx{
|
|||||||
class hashmap
|
class hashmap
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
static constexpr size_t single_bucket_bit = size_t{1} << ((sizeof(size_t)*CHAR_BIT) - 1);
|
using key_type = Key;
|
||||||
static constexpr size_t max_size = N;
|
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);
|
static_assert((max_size & single_bucket_bit) == 0);
|
||||||
|
|
||||||
using key_type = Key;
|
|
||||||
using value_type = Value;
|
|
||||||
using hash_type = Hash;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
array<Value,N> m_values; //perfect hash table
|
array<mapped_type,N> m_values; //perfect hash table
|
||||||
array<size_t,N> m_g; //'salt' values for indexing into the perfect hash table
|
array<size_type,N> m_g; //'salt' values for indexing into the perfect hash table
|
||||||
|
|
||||||
public:
|
public:
|
||||||
constexpr hashmap(const element<Key,Value>(&elements)[N]){
|
constexpr hashmap(const value_type(&elements)[N]);
|
||||||
array<vector<element<Key,Value>,N>,N> buckets;
|
|
||||||
|
//no key checks. give a correct key or get a random answer :)
|
||||||
|
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;
|
array<bool,N> slots_used;
|
||||||
size_t current_bucket = 0;
|
size_type current_bucket = 0;
|
||||||
|
|
||||||
//place all keys into buckets
|
//place all keys into buckets
|
||||||
for(auto& element : elements){
|
for(auto& element : elements){
|
||||||
@ -77,11 +100,11 @@ namespace rexy::cx{
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
array<bool,N> pass_slots_used;
|
array<bool,N> pass_slots_used;
|
||||||
vector<size_t,N> pass_slots;
|
vector<size_type,N> pass_slots;
|
||||||
size_t d = 1;
|
size_type d = 1;
|
||||||
|
|
||||||
for(size_t i = 0;i < bucket.size();){
|
for(size_type i = 0;i < bucket.size();){
|
||||||
size_t slot = Hash{}(bucket[i].key, d) % max_size;
|
size_type slot = Hash{}(bucket[i].key, d) % max_size;
|
||||||
if(pass_slots_used[slot] || slots_used[slot]){
|
if(pass_slots_used[slot] || slots_used[slot]){
|
||||||
//slot already in use, try another value for 'd'
|
//slot already in use, try another value for 'd'
|
||||||
++d;
|
++d;
|
||||||
@ -99,14 +122,14 @@ namespace rexy::cx{
|
|||||||
m_g[Hash{}(bucket[0].key) % max_size] = d;
|
m_g[Hash{}(bucket[0].key) % max_size] = d;
|
||||||
|
|
||||||
//take the value from the temporary bucket into the permanent slot
|
//take the value from the temporary bucket into the permanent slot
|
||||||
for(size_t i = 0;i < bucket.size();++i){
|
for(size_type i = 0;i < bucket.size();++i){
|
||||||
m_values[pass_slots[i]] = std::move(bucket[i].value);
|
m_values[pass_slots[i]] = std::move(bucket[i].value);
|
||||||
slots_used[pass_slots[i]] = true;
|
slots_used[pass_slots[i]] = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//Handle remaining single value buckets
|
//Handle remaining single value buckets
|
||||||
size_t next_free_slot = 0;
|
size_type next_free_slot = 0;
|
||||||
|
|
||||||
for(;current_bucket < buckets.size();++current_bucket){
|
for(;current_bucket < buckets.size();++current_bucket){
|
||||||
auto& bucket = buckets[current_bucket];
|
auto& bucket = buckets[current_bucket];
|
||||||
@ -121,22 +144,27 @@ namespace rexy::cx{
|
|||||||
}
|
}
|
||||||
|
|
||||||
//no key checks. give a correct key or get a random answer :)
|
//no key checks. give a correct key or get a random answer :)
|
||||||
constexpr value_type& operator[](const Key& key){
|
template<class Key, class Value, size_t N, class Hash>
|
||||||
size_t d = m_g[Hash{}(key) % max_size];
|
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)
|
if(d & single_bucket_bit)
|
||||||
return m_values[d & ~single_bucket_bit];
|
return m_values[d & ~single_bucket_bit];
|
||||||
return m_values[Hash{}(key, d) % max_size];
|
return m_values[UHash{}(std::forward<U>(key), d) % max_size];
|
||||||
}
|
}
|
||||||
constexpr const value_type& operator[](const Key& key)const{
|
template<class Key, class Value, size_t N, class Hash>
|
||||||
size_t d = m_g[Hash{}(key) % max_size];
|
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)
|
if(d & single_bucket_bit)
|
||||||
return m_values[d & ~single_bucket_bit];
|
return m_values[d & ~single_bucket_bit];
|
||||||
return m_values[Hash{}(key, d) % max_size];
|
return m_values[UHash{}(std::forward<U>(key), d) % max_size];
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
template<class Key, class Value, size_t N, class Hash = hash<Key>>
|
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);
|
return hashmap<Key,Value,N,Hash>(list);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -25,6 +25,7 @@
|
|||||||
|
|
||||||
#include "steal.hpp"
|
#include "steal.hpp"
|
||||||
#include "cx/utility.hpp"
|
#include "cx/utility.hpp"
|
||||||
|
#include "traits.hpp"
|
||||||
|
|
||||||
namespace rexy{
|
namespace rexy{
|
||||||
|
|
||||||
@ -166,36 +167,36 @@ namespace rexy{
|
|||||||
|
|
||||||
|
|
||||||
namespace detail{
|
namespace detail{
|
||||||
std::true_type is_string_helper(string_expr*);
|
|
||||||
std::false_type is_string_helper(...);
|
|
||||||
template<class T>
|
template<class T>
|
||||||
struct is_string{
|
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>
|
template<class T>
|
||||||
struct is_concrete_string{
|
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>
|
template<class... Ts>
|
||||||
using enable_if_string = std::enable_if_t<(is_string<Ts>::value && ...),int>;
|
using enable_if_string = std::enable_if_t<(is_string<Ts>::value && ...),int>;
|
||||||
template<class... Ts>
|
template<class... Ts>
|
||||||
using enable_if_concrete_string = std::enable_if_t<(is_concrete_string<Ts>::value && ...),int>;
|
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>
|
template<class Targ>
|
||||||
struct appender
|
struct string_appender
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
Targ& m_targ;
|
Targ& m_targ;
|
||||||
public:
|
public:
|
||||||
appender(Targ& t);
|
constexpr string_appender(Targ& t);
|
||||||
template<class L, class R>
|
template<class L, class R>
|
||||||
void operator()(const string_cat_expr<L,R>& str);
|
constexpr void operator()(const string_cat_expr<L,R>& str);
|
||||||
void operator()(const string_base& 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
|
} //namespace detail
|
||||||
|
|
||||||
template<class Str1, class Str2, detail::enable_if_concrete_string<Str1,Str2> = 0>
|
template<class Str1, class Str2, detail::enable_if_concrete_string<Str1,Str2> = 0>
|
||||||
constexpr bool operator==(Str1&& left, Str2&& right){
|
constexpr bool operator==(Str1&& left, Str2&& right){
|
||||||
return left.valid() && right.valid() && left.length() == right.length() && !cx::strcmp(left.get(), right.get());
|
return left.valid() && right.valid() && left.length() == right.length() && !cx::strcmp(left.get(), right.get());
|
||||||
|
|||||||
@ -219,7 +219,7 @@ namespace rexy{
|
|||||||
string_cat_expr<Left,Right>::operator string_intermediary<Alloc>(void){
|
string_cat_expr<Left,Right>::operator string_intermediary<Alloc>(void){
|
||||||
size_t len = length();
|
size_t len = length();
|
||||||
string_intermediary<Alloc> ret(len);
|
string_intermediary<Alloc> ret(len);
|
||||||
detail::appender<string_intermediary<Alloc>> append(ret);
|
detail::string_appender<string_intermediary<Alloc>> append(ret);
|
||||||
append(*this);
|
append(*this);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
@ -259,15 +259,16 @@ namespace rexy{
|
|||||||
|
|
||||||
namespace detail{
|
namespace detail{
|
||||||
template<class Targ>
|
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 Targ>
|
||||||
template<class L, class R>
|
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.left());
|
||||||
(*this)(str.right());
|
(*this)(str.right());
|
||||||
}
|
}
|
||||||
template<class Targ>
|
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());
|
m_targ.append(str.get(), str.length());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
29
include/rexy/traits.hpp
Normal file
29
include/rexy/traits.hpp
Normal 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
|
||||||
Loading…
x
Reference in New Issue
Block a user