Last active
August 31, 2017 21:21
-
-
Save gatchamix/ba7831ff43cb744d7257bc90821da285 to your computer and use it in GitHub Desktop.
meta-programming types and algorithms using C++17/2a
This file contains 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 <type_traits> | |
#include <utility> | |
#include <cstddef> | |
#include <string_view> | |
#include <iterator> | |
#include <limits> | |
#include <tuple> | |
// | |
// Declare basic_fixed_string. | |
template <class charT, size_t N> | |
class basic_fixed_string; | |
// Aliases fixed_string, u16fixed_string, u32fixed_string, wfixed_string. | |
template <size_t N> | |
using fixed_string = basic_fixed_string<char, N>; | |
template <size_t N> | |
using u16fixed_string = basic_fixed_string<char16_t, N>; | |
template <size_t N> | |
using u32fixed_string = basic_fixed_string<char32_t, N>; | |
template <size_t N> | |
using wfixed_string = basic_fixed_string<wchar_t, N>; | |
// Creates a fixed_string from a User-Defined Literal. | |
template <char... chars> | |
auto constexpr operator ""_fs() | |
{ | |
return fixed_string<sizeof...(chars)>{ {chars..., '\0'} }; | |
} | |
// Creates a fixed_string from a string literal. | |
template <class charT, size_t N> | |
auto constexpr make_fixed_string(charT const (&str)[N]) | |
{ | |
return basic_fixed_string<charT, N - 1>{ str }; | |
} | |
// Concatenations between fixed_strings and string literals. | |
template <class charT, size_t N, size_t M> | |
auto constexpr operator+( | |
basic_fixed_string<charT, N> const &lhs, | |
basic_fixed_string<charT, M> const &rhs) noexcept | |
{ | |
basic_fixed_string<charT, N + M> temp; | |
for (size_t i = 0; i < N; ++i) temp[i] = lhs[i]; | |
for (size_t i = 0; i < M; ++i) temp[N + i] = rhs[i]; | |
return temp; | |
} | |
template <class charT, size_t N1, size_t M> | |
auto constexpr operator+( | |
charT const (&lhs)[N1], | |
basic_fixed_string<charT, M> const &rhs) noexcept | |
{ | |
return make_fixed_string(lhs) + rhs; | |
} | |
template <class charT, size_t N, size_t M1> | |
auto constexpr operator+( | |
basic_fixed_string<charT, N> const &lhs, | |
charT const (&rhs)[M1]) noexcept | |
{ | |
return lhs + make_fixed_string(rhs); | |
} | |
// Comparisons between fixed_strings and string literals. | |
template <class charT, size_t N, size_t M> | |
auto constexpr operator==( | |
basic_fixed_string<charT, N> const &lhs, | |
basic_fixed_string<charT, M> const &rhs) noexcept | |
{ | |
if (N != M) return false; | |
for (size_t i = 0; i < N; ++i) | |
if (lhs[i] != rhs[i]) return false; | |
return true; | |
} | |
template <class charT, size_t N1, size_t M> | |
auto constexpr operator==( | |
charT const (&lhs)[N1], | |
basic_fixed_string<charT, M> const &rhs) noexcept | |
{ | |
return make_fixed_string(lhs) == rhs; | |
} | |
template <class charT, size_t N, size_t M1> | |
auto constexpr operator==( | |
basic_fixed_string<charT, N> const &lhs, | |
charT const (&rhs)[M1]) noexcept | |
{ | |
return lhs == make_fixed_string(rhs); | |
} | |
template <class charT, size_t N, size_t M> | |
auto constexpr operator!=( | |
basic_fixed_string<charT, N> const &lhs, | |
basic_fixed_string<charT, M> const &rhs) noexcept | |
{ | |
return !(lhs == rhs); | |
} | |
template <class charT, size_t N1, size_t M> | |
auto constexpr operator!=( | |
charT const (&lhs)[N1], | |
basic_fixed_string<charT, M> const &rhs) noexcept | |
{ | |
return make_fixed_string(lhs) != rhs; | |
} | |
template <class charT, size_t N, size_t M1> | |
auto constexpr operator!=( | |
basic_fixed_string<charT, N> const &lhs, | |
charT const (&rhs)[M1]) noexcept | |
{ | |
return lhs + make_fixed_string(rhs); | |
} | |
template <class charT, size_t N, size_t M> | |
auto constexpr operator<( | |
basic_fixed_string<charT, N> const &lhs, | |
basic_fixed_string<charT, M> const &rhs) noexcept | |
{ | |
constexpr auto K = (N < M ? N : M); | |
for (size_t i = 0; i < K; ++i) | |
if (lhs[i] < rhs[i]) | |
return true; | |
else if (lhs[i] > rhs[i]) | |
return false; | |
if (N == M) | |
return false; | |
else if (N < M) | |
return true; | |
else /* N > M */ | |
return false; | |
} | |
template <class charT, size_t N1, size_t M> | |
auto constexpr operator<( | |
charT const (&lhs)[N1], | |
basic_fixed_string<charT, M> const &rhs) noexcept | |
{ | |
return make_fixed_string(lhs) < rhs; | |
} | |
template <class charT, size_t N, size_t M1> | |
auto constexpr operator<( | |
basic_fixed_string<charT, N> const &lhs, | |
charT const (&rhs)[M1]) noexcept | |
{ | |
return lhs < make_fixed_string(rhs); | |
} | |
template <class charT, size_t N, size_t M> | |
auto constexpr operator>( | |
basic_fixed_string<charT, N> const &lhs, | |
basic_fixed_string<charT, M> const &rhs) noexcept | |
{ | |
return rhs < lhs; | |
} | |
template <class charT, size_t N1, size_t M> | |
auto constexpr operator>( | |
charT const (&lhs)[N1], | |
basic_fixed_string<charT, M> const &rhs) noexcept | |
{ | |
return make_fixed_string(lhs) > rhs; | |
} | |
template <class charT, size_t N, size_t M1> | |
auto constexpr operator>( | |
basic_fixed_string<charT, N> const &lhs, | |
charT const (&rhs)[M1]) noexcept | |
{ | |
return lhs > make_fixed_string(rhs); | |
} | |
template <class charT, size_t N, size_t M> | |
auto constexpr operator<=( | |
basic_fixed_string<charT, N> const &lhs, | |
basic_fixed_string<charT, M> const &rhs) noexcept | |
{ | |
return !(rhs < lhs); | |
} | |
template <class charT, size_t N1, size_t M> | |
auto constexpr operator<=( | |
charT const (&lhs)[N1], | |
basic_fixed_string<charT, M> const &rhs) noexcept | |
{ | |
return make_fixed_string(lhs) <= rhs; | |
} | |
template <class charT, size_t N, size_t M1> | |
auto constexpr operator<=( | |
basic_fixed_string<charT, N> const &lhs, | |
charT const (&rhs)[M1]) noexcept | |
{ | |
return lhs <= make_fixed_string(rhs); | |
} | |
template <class charT, size_t N, size_t M> | |
auto constexpr operator>=( | |
basic_fixed_string<charT, N> const &lhs, | |
basic_fixed_string<charT, M> const &rhs) noexcept | |
{ | |
return !(lhs < rhs); | |
} | |
template <class charT, size_t N1, size_t M> | |
auto constexpr operator>=( | |
charT const (&lhs)[N1], | |
basic_fixed_string<charT, M> const &rhs) noexcept | |
{ | |
return make_fixed_string(lhs) >= rhs; | |
} | |
template <class charT, size_t N, size_t M1> | |
auto constexpr operator>=( | |
basic_fixed_string<charT, N> const &lhs, | |
charT const (&rhs)[M1]) noexcept | |
{ | |
return lhs >= make_fixed_string(rhs); | |
} | |
// Swap equal size fixed_strings. | |
template <class charT, size_t N> | |
auto constexpr swap( | |
basic_fixed_string<charT, N> &lhs, | |
basic_fixed_string<charT, N> &rhs) noexcept | |
{ | |
for (size_t i = 0; i < N; ++i) swap(lhs[i], rhs[i]); | |
} | |
// Convert fixed_string to integeral values. | |
// TODO: Implement index + base parameters | |
template <size_t N> | |
auto constexpr __sto_integral_impl(fixed_string<N> const &str, size_t pos) | |
{ | |
auto val = 0ull; | |
for (; pos < N; ++pos) | |
{ | |
val *= 10; | |
auto const c = str[pos]; | |
if (c < '0' || c > '9') throw std::invalid_argument(""); | |
auto const temp = val + static_cast<unsigned>(c - '0'); | |
if (temp < val) throw std::invalid_argument(""); | |
val = temp; | |
} | |
return val; | |
} | |
template <class T, size_t N> | |
auto constexpr __sto_unsigned_impl(fixed_string<N> const &str) | |
{ | |
size_t i = 0; | |
if (str[0] == '+') ++i; | |
auto const val = __sto_integral_impl(str, i); | |
auto constexpr max = std::numeric_limits<T>::max(); | |
if (val > max) throw std::out_of_range(""); | |
return static_cast<T>(val); | |
} | |
template <class T, size_t N> | |
auto constexpr __sto_signed_impl(fixed_string<N> const &str) | |
{ | |
if (str[0] == '-') | |
{ | |
auto const val = __sto_integral_impl(str, 1); | |
constexpr auto min = std::numeric_limits<T>::min(); | |
constexpr auto max = 0 - static_cast<std::make_unsigned_t<T>>(min); | |
if (val > max) throw std::out_of_range(""); | |
return static_cast<T>(0 - val); | |
} | |
else | |
return __sto_unsigned_impl<T>(str); | |
} | |
template <size_t N> | |
auto constexpr stoi(fixed_string<N> const &str) | |
{ return __sto_signed_impl<int>(str); } | |
template <size_t N> | |
auto constexpr stou(fixed_string<N> const &str) | |
{ return __sto_unsigned_impl<unsigned>(str); } | |
template <size_t N> | |
auto constexpr stol(fixed_string<N> const &str) | |
{ return __sto_signed_impl<long>(str); } | |
template <size_t N> | |
auto constexpr stoul(fixed_string<N> const &str) | |
{ return __sto_unsigned_impl<unsigned long>(str); } | |
template <size_t N> | |
auto constexpr stoll(fixed_string<N> const &str) | |
{ return __sto_signed_impl<long long>(str); } | |
template <size_t N> | |
auto constexpr stoull(fixed_string<N> const &str) | |
{ return __sto_unsigned_impl<unsigned long long>(str); } | |
// Convert integer to decimal fixed_string. | |
auto constexpr __count_num_digits(unsigned long long val) noexcept | |
{ | |
size_t num_digits = 0; | |
do { ++num_digits; val /= 10; } while (val > 0); | |
return num_digits; | |
} | |
template <auto val> | |
auto constexpr to_fixed_string() noexcept | |
{ | |
if constexpr (val >= 0) | |
{ | |
auto constexpr N = __count_num_digits(val); | |
fixed_string<N> str; | |
str[0] = '0'; | |
auto remaining = val; | |
for (size_t pos = N - 1; remaining > 0; --pos, remaining /= 10) | |
str[pos] = '0' + (remaining % 10); | |
return str; | |
} | |
else | |
{ return "-" + to_fixed_string<0ull - val>(); } | |
} | |
template <class charT, size_t N> | |
class basic_fixed_string { | |
public: | |
using view = std::basic_string_view<charT>; | |
static auto constexpr npos = view::npos; | |
// Default constructors (copy, move, copy-asign, move-assign) | |
basic_fixed_string(basic_fixed_string const&) noexcept = default; | |
basic_fixed_string(basic_fixed_string&&) noexcept = default; | |
basic_fixed_string& operator=(basic_fixed_string const&) noexcept = default; | |
basic_fixed_string& operator=(basic_fixed_string&&) noexcept = default; | |
// Implicit conversion to string_view | |
constexpr operator view() const noexcept | |
{ return { data_, N }; } | |
// Default construct to all zeros. | |
constexpr basic_fixed_string() noexcept | |
: data_{} | |
{} | |
// Converting constructor from string literal. | |
constexpr basic_fixed_string(charT const (&arr)[N + 1]) noexcept | |
: data_{} | |
{ | |
for (size_t i = 0; i < N; ++i) data_[i] = arr[i]; | |
} | |
// Assign from string literal. | |
auto constexpr operator=(const charT(&arr)[N + 1]) | |
{ | |
for (size_t i = 0; i < N + 1; ++i) data_[i] = arr[i]; | |
return *this; | |
} | |
// c/r/begin, c/r/end. | |
auto constexpr begin() noexcept | |
{ return data_; } | |
auto constexpr begin() const noexcept | |
{ return data_; } | |
auto constexpr end() noexcept | |
{ return data_ + N; } | |
auto constexpr end() const noexcept | |
{ return data_ + N; } | |
auto constexpr rbegin() noexcept | |
{ return make_reverse_iterator(end()); } | |
auto constexpr rbegin() const noexcept | |
{ return make_reverse_iterator(end()); } | |
auto constexpr rend() noexcept | |
{ return make_reverse_iterator(begin()); } | |
auto constexpr rend() const noexcept | |
{ return make_reverse_iterator(begin()); } | |
auto constexpr cbegin() const noexcept | |
{ return data_; } | |
auto constexpr cend() const noexcept | |
{ return data_ + N; } | |
auto constexpr crbegin() const noexcept | |
{ return make_reverse_iterator(end()); } | |
auto constexpr crend() const noexcept | |
{ return make_reverse_iterator(begin()); } | |
// size, empty, length. | |
auto constexpr size() const noexcept | |
{ return N; } | |
auto constexpr empty() const noexcept | |
{ return N == 0; } | |
auto constexpr length() const noexcept | |
{ return N; } | |
// str[pos] | |
auto constexpr& operator[](size_t pos) noexcept | |
{ return data_[pos]; } | |
auto constexpr& operator[](size_t pos) const noexcept | |
{ return data_[pos]; } | |
// str.at(pos) | |
auto constexpr& at(size_t pos) | |
{ | |
if (pos >= N) throw std::out_of_range(""); | |
return data_[pos]; | |
} | |
auto constexpr& at(size_t pos) const | |
{ | |
if (pos >= N) throw std::out_of_range(""); | |
return data_[pos]; | |
} | |
// front, back. | |
auto constexpr& front() const noexcept | |
{ return data_[0]; } | |
auto constexpr& front() noexcept | |
{ return data_[0]; } | |
auto constexpr& back() const noexcept | |
{ return data_[N - 1]; } | |
auto constexpr& back() noexcept | |
{ return data_[N - 1]; } | |
private: | |
static auto constexpr __substr_length(size_t pos, size_t count) | |
{ | |
if (pos >= N) | |
return static_cast<size_t>(0); | |
else if (count == npos || pos + count > N) | |
return N - pos; | |
else | |
return count; | |
} | |
public: | |
// str.substr<pos,count>() | |
auto constexpr substr(size_t pos = 0, size_t count = npos) const noexcept | |
{ | |
basic_fixed_string result; | |
auto const n = __substr_length(pos, count); | |
for (size_t i = 0; i < n; ++i) result[i] = data_[pos + i]; | |
return result; | |
} | |
// str.substr<pos,count>() | |
template <size_t pos = 0, size_t count = npos> | |
auto constexpr substr() const noexcept | |
{ | |
auto constexpr n = __substr_length(pos, count); | |
basic_fixed_string<charT, n> result; | |
for (size_t i = 0; i < n; ++i) result[i] = data_[pos + i]; | |
return result; | |
} | |
// str1.assign(str2). Must be equal size. | |
auto constexpr assign(view str) | |
{ | |
if (str.size() != N) throw std::out_of_range(""); | |
for (size_t i = 0; i < N; ++i) data_[i] = str[i]; | |
return *this; | |
} | |
// Replace substring. | |
auto constexpr replace(size_t pos, view str) | |
{ | |
if (pos + str.size() > N) throw std::out_of_range(""); | |
for (size_t i = 0; i < str.size(); ++i) data_[i] = str[i]; | |
return *this; | |
} | |
// Swap with fixed_string of equal size. | |
auto constexpr swap(basic_fixed_string &str) | |
{ | |
for (size_t i = 0; i < N; ++i) swap(data_[i], str[i]); | |
} | |
// Null-terminated C string. | |
auto constexpr c_str() const noexcept | |
{ return data_; } | |
auto constexpr data() const noexcept | |
{ return data_; } | |
auto constexpr compare(view str) const noexcept | |
{ return view(*this).compare(str); } | |
auto constexpr compare(size_t pos1, size_t n1, view str) const | |
{ return view(*this).compare(pos1, n1, str); } | |
auto constexpr compare(size_t pos1, size_t n1, view str, size_t pos2, size_t n2 = npos) const | |
{ return view(*this).compare(pos1, n1, str, pos2, n2); } | |
auto constexpr compare(charT const *s) const | |
{ return view(*this).compare(s); } | |
auto constexpr compare(size_t pos1, size_t n1, charT const *s) const | |
{ return view(*this).compare(pos1, n1, s); } | |
auto constexpr compare(size_t pos1, size_t n1, charT const *s, size_t n2) const | |
{ return view(*this).compare(pos1, n1, s, n2); } | |
auto constexpr find(view str, size_t pos = 0) const noexcept | |
{ return view(*this).find(str, pos); } | |
auto constexpr find(charT c, size_t pos = 0) const noexcept | |
{ return view(*this).find(c, pos); } | |
auto constexpr find(charT const *s, size_t pos, size_t count) const | |
{ return view(*this).find(s, pos, count); } | |
auto constexpr find(charT const *s, size_t pos = 0) const | |
{ return view(*this).find(s, pos); } | |
auto constexpr rfind(view str, size_t pos = npos) const noexcept | |
{ return view(*this).rfind(str, pos); } | |
auto constexpr rfind(charT c, size_t pos = npos) const noexcept | |
{ return view(*this).rfind(c, pos); } | |
auto constexpr rfind(charT const *s, size_t pos, size_t n) const | |
{ return view(*this).rfind(s, pos, n); } | |
auto constexpr rfind(charT const *s, size_t pos = npos) const | |
{ return view(*this).rfind(s, pos); } | |
auto constexpr find_first_of(view str, size_t pos = 0) const noexcept | |
{ return view(*this).find_first_of(str, pos); } | |
auto constexpr find_first_of(charT c, size_t pos = 0) const noexcept | |
{ return view(*this).find_first_of(c, pos); } | |
auto constexpr find_first_of(charT const *s, size_t pos, size_t n) const | |
{ return view(*this).find_first_of(s, pos, n); } | |
auto constexpr find_first_of(charT const *s, size_t pos = 0) const | |
{ return view(*this).find_first_of(s, pos); } | |
auto constexpr find_last_of(view str, size_t pos = npos) const noexcept | |
{ return view(*this).find_last_of(str, pos); } | |
auto constexpr find_last_of(charT c, size_t pos = npos) const noexcept | |
{ return view(*this).find_last_of(c, pos); } | |
auto constexpr find_last_of(charT const *s, size_t pos, size_t n) const | |
{ return view(*this).find_last_of(s, pos, n); } | |
auto constexpr find_last_of(charT const *s, size_t pos = npos) const | |
{ return view(*this).find_last_of(s, pos); } | |
auto constexpr find_first_not_of(view str, size_t pos = 0) const noexcept | |
{ return view(*this).find_first_not_of(str, pos); } | |
auto constexpr find_first_not_of(charT c, size_t pos = 0) const noexcept | |
{ return view(*this).find_first_not_of(c, pos); } | |
auto constexpr find_first_not_of(charT const *s, size_t pos, size_t n) const | |
{ return view(*this).find_first_not_of(s, pos, n); } | |
auto constexpr find_first_not_of(charT const *s, size_t pos = 0) const | |
{ return view(*this).find_first_not_of(s, pos); } | |
auto constexpr find_last_not_of(view str, size_t pos = npos) const noexcept | |
{ return view(*this).find_last_not_of(str, pos); } | |
auto constexpr find_last_not_of(charT c, size_t pos = npos) const noexcept | |
{ return view(*this).find_last_not_of(c, pos); } | |
auto constexpr find_last_not_of(charT const *s, size_t pos, size_t n) const | |
{ return view(*this).find_last_not_of(s, pos, n); } | |
auto constexpr find_last_not_of(charT const *s, size_t pos = npos) const | |
{ return view(*this).find_last_not_of(s, pos); } | |
private: | |
charT data_[N + 1]; // (+1 is for terminating null) | |
}; | |
// | |
// type_t/_v | |
template <class... Ts> | |
struct type_t | |
{ | |
static auto constexpr size = sizeof...(Ts); | |
template <std::size_t N> | |
using type = std::tuple_element_t<N, std::tuple<Ts...>>; | |
}; | |
template <class... Ts> | |
auto constexpr type_v = type_t<Ts...>{}; | |
template <class... L, class... R> | |
auto constexpr operator+(type_t<L...>, type_t<R...>) | |
{ return type_t<L..., R...>{}; } | |
template <class... L, class... R> | |
auto constexpr operator==(type_t<L...>, type_t<R...>) | |
{ return std::is_same_v<type_t<L...>, type_t<R...>>; } | |
// remove | |
template <class T, class... Ts> | |
auto constexpr remove(type_t<Ts...>) | |
{ return (std::conditional_t<std::is_same_v<T, Ts>, type_t<>, type_t<Ts>>{} + ...); } | |
template <class T, class... Ts> | |
auto constexpr remove(type_t<T> = {}, type_t<Ts...> ts = {}) | |
{ return remove<T>(ts); } | |
// contains | |
template <class T, class... Ts> | |
auto constexpr contains(type_t<Ts...>) | |
{ return (std::is_same_v<T, Ts> || ...); } | |
template <class T, class... Ts> | |
auto constexpr contains(type_t<T> = {}, type_t<Ts...> ts = {}) | |
{ return contains<T>(ts); } | |
// find | |
namespace detail | |
{ | |
template <class T, class... Ts, auto... Is> | |
auto constexpr find_impl__(type_t<T>, type_t<Ts...>, std::index_sequence<Is...>) | |
{ | |
auto idx = sizeof...(Ts) + 2; | |
((std::is_same_v<T, Ts> ? idx = Is + 1 : 0) || ...); | |
return idx - 1; | |
} | |
} | |
template <class T, class... Ts> | |
auto constexpr find(type_t<Ts...> ts) | |
{ return detail::find_impl__(type_v<T>, ts, std::make_index_sequence<ts.size>{}); } | |
template <class T, class... Ts> | |
auto constexpr find(type_t<T> = {}, type_t<Ts...> ts = {}) | |
{ return find<T>(ts); } | |
// C++2a | |
//template <class T, class... Ts> | |
//auto constexpr find(type_t<T> = {}, type_t<Ts...> = {}) | |
//{ | |
// return []<auto... Is> | |
// (std::index_sequence<Is...>) | |
// { | |
// auto idx = sizeof...(Ts) + 1; | |
// ((std::is_same_v<T, Ts> ? idx = Is : 0) || ...); | |
// return idx; | |
// } | |
// (std::make_index_sequence<sizeof...(Ts)>{}); | |
//} | |
// unique | |
template <class T, class... Ts> | |
auto constexpr is_unique(type_t<T, Ts...> = {}) | |
{ | |
if constexpr (sizeof...(Ts) < 2) | |
{ return true; } | |
else | |
{ return ((!std::is_same_v<T, Ts>) && ...) && is_unique<Ts...>(); } | |
} | |
// remove_at | |
namespace detail | |
{ | |
template <auto I, class... Ts, auto... Is> | |
auto constexpr remove_at_impl(std::index_sequence<Is...>) | |
{ return (std::conditional_t<I == Is, type_t<>, type_t<Ts>>{} + ...); } | |
} | |
template <std::size_t I, class... Ts> | |
auto constexpr remove_at(type_t<Ts...> ts = {}) | |
{ return detail::remove_at_impl<I, Ts...>(std::make_index_sequence<ts.size>{}); } | |
// C++2a | |
//template <std::size_t I, auto... Vs> | |
//auto constexpr remove_at(auto_t<Vs...> vs) | |
//{ | |
// return []<auto... Is> | |
// (std::index_sequence<Is...>) | |
// { return (std::conditional_t<I == Is, auto_t<>, auto_t<Vs>>{} + ...); } | |
// (std::make_index_sequence<vs.size>{}); | |
//} | |
// pop_front | |
namespace detail | |
{ | |
template <class T, class... Ts> | |
auto constexpr pop_front_impl(type_t<T, Ts...>) | |
{ return type_v<Ts...>; } | |
} | |
template <class... Ts> | |
auto constexpr pop_front(type_t<Ts...> ts = {}) | |
{ return detail::pop_front_impl(ts); } | |
// pop_back | |
template <class... Ts> | |
auto constexpr pop_back(type_t<Ts...> ts = {}) | |
{ return remove_at<ts.size - 1>(ts); } | |
// push_front | |
template <class T, class... Ts> | |
auto constexpr push_front(type_t<Ts...>) | |
{ return type_v<T, Ts...>; } | |
template <class T, class... Ts> | |
auto constexpr push_front(type_t<T> = {}, type_t<Ts...> ts = {}) | |
{ return push_front<T>(ts); } | |
// push_back | |
template <class T, class... Ts> | |
auto constexpr push_back(type_t<Ts...>) | |
{ return type_v<Ts..., T>; } | |
template <class T, class... Ts> | |
auto constexpr push_back(type_t<T> = {}, type_t<Ts...> ts = {}) | |
{ return push_back<T>(ts); } | |
// | |
// auto_t/_v | |
template <auto... Vs> | |
struct auto_t | |
{ | |
static auto constexpr size = sizeof...(Vs); | |
using types = type_t<decltype(Vs)...>; | |
}; | |
template <auto... Vs> | |
auto constexpr auto_v = auto_t<Vs...>{}; | |
template <auto... L, auto... R> | |
auto constexpr operator+(auto_t<L...>, auto_t<R...>) | |
{ return auto_t<L..., R...>{}; } | |
template <auto... L, auto... R> | |
auto constexpr operator==(auto_t<L...>, auto_t<R...>) | |
{ return std::is_same_v<auto_t<L...>, auto_t<R...>>; } | |
// min / max | |
template <auto... Vs> | |
auto constexpr min(auto_t<Vs...> = {}) | |
{ | |
using type = std::common_type_t<decltype(Vs)...>; // replace | |
auto val = std::numeric_limits<type>::max(); | |
(((val > Vs) ? val = Vs : 0), ...); | |
return val; | |
} | |
template <auto... Vs> | |
auto constexpr max(auto_t<Vs...> = {}) | |
{ | |
using type = std::common_type_t<decltype(Vs)...>; // replace | |
auto val = std::numeric_limits<type>::min(); | |
(((val < Vs) ? val = Vs : 0), ...); | |
return val; | |
} | |
// remove | |
template <auto V, auto... Vs> | |
auto constexpr remove(auto_t<Vs...>) | |
{ return (std::conditional_t<V == Vs, auto_t<>, auto_t<Vs>>{} + ...); } | |
template <auto V, auto... Vs> | |
auto constexpr remove(auto_t<V> = {}, auto_t<Vs...> = {}) | |
{ return (std::conditional_t<V == Vs, auto_t<>, auto_t<Vs>>{} + ...); } | |
// contains | |
template <auto V, auto... Vs> | |
auto constexpr contains(auto_t<V> = {}, auto_t<Vs...> = {}) | |
{ return ((V == Vs) || ...); } | |
// find | |
namespace detail | |
{ | |
template <auto V, auto... Vs, auto... Is> | |
auto constexpr find_impl__(auto_t<V>, auto_t<Vs...>, std::index_sequence<Is...>) | |
{ | |
auto idx = sizeof...(Vs) + 2; | |
((V == Vs ? idx = Is + 1 : 0) || ...); | |
return idx - 1; | |
} | |
} | |
template <auto V, auto... Vs> | |
auto constexpr find(auto_t<Vs...> vs) | |
{ return detail::find_impl__(auto_v<V>, vs, std::make_index_sequence<vs.size>{}); } | |
template <auto V, auto... Vs> | |
auto constexpr find(auto_t<V> = {}, auto_t<Vs...> vs = {}) | |
{ return find<V>(vs); } | |
// C++2a | |
//template <auto V, auto... Vs> | |
//auto constexpr find(auto_t<V> = {}, auto_t<Vs...> = {}) | |
//{ | |
// return []<auto... Is> | |
// (std::index_sequence<Is...>) | |
// { | |
// auto idx = sizeof...(Vs) + 1; | |
// ((V == Vs ? idx = Is : 0) || ...); | |
// return idx; | |
// } | |
// (std::make_index_sequence<sizeof...(Vs)>{}); | |
//} | |
// is_unique | |
namespace detail | |
{ | |
template <auto V, auto... Vs> | |
auto constexpr is_unique_impl(auto_t<V, Vs...> = {}) | |
{ | |
if constexpr (sizeof...(Vs) == 0) | |
{ return true; } | |
else | |
{ return ((V != Vs) && ...) && is_unique_impl<Vs...>(); } | |
} | |
} | |
template <auto... Vs> | |
auto constexpr is_unique(auto_t<Vs...> vs = {}) | |
{ | |
if constexpr (sizeof...(Vs) < 2) | |
{ return true; } | |
else | |
{ return detail::is_unique_impl(vs); } | |
} | |
// get | |
namespace detail | |
{ | |
template <auto Idx, auto V, auto... Vs, auto I, auto... Is> | |
auto constexpr get_impl(auto_t<V, Vs...>, std::index_sequence<I, Is...>) | |
{ | |
if constexpr (Idx == I) | |
{ return V; } | |
else | |
{ return get_impl<Idx>(auto_v<Vs...>, std::index_sequence<Is...>{}); } | |
} | |
} | |
template <std::size_t I, auto... Vs> | |
auto constexpr get(auto_t<Vs...> vs) | |
{ return detail::get_impl<I>(vs, std::make_index_sequence<vs.size>{}); } | |
template <std::size_t I, auto... Vs> | |
auto constexpr get(auto_t<I> = {}, auto_t<Vs...> vs = {}) | |
{ return get<I>(vs); } | |
// remove_at | |
namespace detail | |
{ | |
template <auto I, auto... Vs, auto... Is> | |
auto constexpr remove_at_impl(std::index_sequence<Is...>) | |
{ return (std::conditional_t<I == Is, auto_t<>, auto_t<Vs>>{} + ...); } | |
} | |
template <std::size_t I, auto... Vs> | |
auto constexpr remove_at(auto_t<Vs...> vs) | |
{ return detail::remove_at_impl<I, Vs...>(std::make_index_sequence<vs.size>{}); } | |
template <std::size_t I, auto... Vs> | |
auto constexpr remove_at(auto_t<I> = {}, auto_t<Vs...> vs = {}) | |
{ return remove_at<I>(vs); } | |
// C++2a | |
//template <std::size_t I, auto... Vs> | |
//auto constexpr remove_at(auto_t<Vs...> vs) | |
//{ | |
// return []<auto... Is> | |
// (std::index_sequence<Is...>) | |
// { return (std::conditional_t<I == Is, auto_t<>, auto_t<Vs>>{} + ...); } | |
// (std::make_index_sequence<vs.size>{}); | |
//} | |
// pop_front | |
namespace detail | |
{ | |
template <auto V, auto... Vs> | |
auto constexpr pop_front_impl(auto_t<V, Vs...>) | |
{ return auto_v<Vs...>; } | |
} | |
template <auto... Vs> | |
auto constexpr pop_front(auto_t<Vs...> vs = {}) | |
{ return detail::pop_front_impl(vs); } | |
// pop_back | |
template <auto... Vs> | |
auto constexpr pop_back(auto_t<Vs...> vs = {}) | |
{ return remove_at<vs.size - 1>(vs); } | |
// push_front | |
template <auto V, auto... Vs> | |
auto constexpr push_front(auto_t<Vs...>) | |
{ return auto_v<V, Vs...>; } | |
template <auto V, auto... Vs> | |
auto constexpr push_front(auto_t<V> = {}, auto_t<Vs...> vs = {}) | |
{ return push_front<V>(vs); } | |
// push_back | |
template <auto V, auto... Vs> | |
auto constexpr push_back(auto_t<Vs...>) | |
{ return auto_v<Vs..., V>; } | |
template <auto V, auto... Vs> | |
auto constexpr push_back(auto_t<V> = {}, auto_t<Vs...> vs = {}) | |
{ return push_back<V>(vs); } | |
// find_all | |
namespace detail | |
{ | |
template <auto V, auto... Vs, auto... Is> | |
auto constexpr find_all_impl(std::index_sequence<Is...>) | |
{ return (std::conditional_t<V == Vs, auto_t<Is>, auto_t<>>{} + ...); } | |
} | |
template <auto V, auto... Vs> | |
auto constexpr find_all(auto_t<Vs...> vs) | |
{ return detail::find_all_impl<V, Vs...>(std::make_index_sequence<vs.size>{}); } | |
template <auto V, auto... Vs> | |
auto constexpr find_all(auto_t<V> = {}, auto_t<Vs...> vs = {}) | |
{ return find_all<V>(vs); } | |
// C++2a | |
//template <auto V, auto... Vs> | |
//auto constexpr find_all(auto_t<Vs...> vs) | |
//{ | |
// return []<auto... Is> | |
// (std::index_sequence<Is...>) | |
// { return (std::conditional_t<V == Vs, auto_t<Is>, auto_t<>>{} + ...); } | |
// (std::make_index_sequence<vs.size>{}); | |
//} | |
// | |
// common type | |
template <class...> | |
struct common_type; | |
template <class... Ts> | |
struct common_type<type_t<Ts...>> | |
{ using type = std::common_type_t<Ts...>; }; | |
template <auto... Vs> | |
struct common_type<auto_t<Vs...>> | |
{ using type = std::common_type_t<decltype(Vs)...>; }; | |
// flatten | |
template <class T> | |
auto constexpr flatten(T) | |
{ return T{}; } | |
template <class... Ts> | |
auto constexpr flatten(type_t<Ts...> = {}) | |
{ return auto_v<> + (flatten(Ts{}) + ...); } | |
// subset type_t | |
namespace detail | |
{ | |
template <std::size_t B, std::size_t E, class... Ts, auto... Is> | |
auto constexpr subset_impl(std::index_sequence<Is...>) | |
{ return (std::conditional_t<(Is < B) || (Is > E), type_t<>, type_t<Ts>>{} + ...); } | |
} | |
template <std::size_t B, std::size_t E, class... Ts> | |
auto constexpr subset(type_t<Ts...> ts) | |
{ return detail::subset_impl<B, E, Ts...>(std::make_index_sequence<ts.size>{}); } | |
template <std::size_t B, std::size_t E, class... Ts> | |
auto constexpr subset(auto_t<B> = {}, auto_t<E> = {}, type_t<Ts...> ts = {}) | |
{ return subset<B, E>(ts); } | |
// C++2a | |
//template <std::size_t B, std::size_t E, class... Ts> | |
//auto constexpr subset(type_t<Ts...> ts) | |
//{ | |
// return []<auto... Is> | |
// (std::index_sequence<Is...>) | |
// { return (std::conditional_t<(Is < B) || (Is > E), type_t<>, type_t<Ts>>{} + ...); } | |
// (std::make_index_sequence<ts.size>{}); | |
//} | |
// subset auto_t | |
namespace detail | |
{ | |
template <std::size_t B, std::size_t E, auto... Vs, auto... Is> | |
auto constexpr subset_impl(std::index_sequence<Is...>) | |
{ return (std::conditional_t<(Is < B) || (Is > E), auto_t<>, auto_t<Vs>>{} + ...); } | |
} | |
template <std::size_t B, std::size_t E, auto... Vs> | |
auto constexpr subset(auto_t<Vs...> vs) | |
{ return detail::subset_impl<B, E, Vs...>(std::make_index_sequence<vs.size>{}); } | |
template <std::size_t B, std::size_t E, auto... Vs> | |
auto constexpr subset(auto_t<B> = {}, auto_t<E> = {}, auto_t<Vs...> vs = {}) | |
{ return subset<B, E>(vs); } | |
// C++2a | |
//template <std::size_t B, std::size_t E, auto... Vs> | |
//auto constexpr subset(auto_t<Vs...> vs) | |
//{ | |
// return []<auto... Is> | |
// (std::index_sequence<Is...>) | |
// { return (std::conditional_t<(Is < B) || (Is > E), auto_t<>, auto_t<Vs>>{} + ...); } | |
// (std::make_index_sequence<vs.size>{}); | |
//} | |
// | |
// raise | |
template <class Seq> | |
auto constexpr raise(Seq) | |
{ return type_v<Seq>; } | |
// find_all type_t | |
namespace detail | |
{ | |
template <class T, class... Ts, auto... Is> | |
auto constexpr find_all_impl(std::index_sequence<Is...>) | |
{ return (std::conditional_t<std::is_same_v<T, Ts>, auto_t<Is>, auto_t<>>{} + ...); } | |
} | |
template <class T, class... Ts> | |
auto constexpr find_all(type_t<Ts...> ts) | |
{ return detail::find_all_impl<T, Ts...>(std::make_index_sequence<ts.size>{}); } | |
template <class T, class... Ts> | |
auto constexpr find_all(type_t<T> = {}, type_t<Ts...> ts = {}) | |
{ return find_all<T>(ts); } | |
// tokenise shared | |
namespace detail | |
{ | |
template <class Seq, auto B, auto E, auto... Tail> | |
auto constexpr tokenise_impl(Seq seq, auto_t<B, E, Tail...>) | |
{ | |
auto constexpr first = raise(subset<B, E - 1>(seq)); | |
if constexpr (sizeof...(Tail) == 0) | |
{ return first + raise(subset<E + 1, seq.size - 1>(seq)); } | |
else | |
{ return first + tokenise_impl(seq, auto_v<E + 1, Tail...>); } | |
} | |
} | |
// tokenise type_t | |
template <class T, class... Ts> | |
auto constexpr tokenise(type_t<Ts...> ts) | |
{ | |
auto constexpr offs = auto_v<0> + find_all<T>(ts); | |
if constexpr (offs.size < 2) | |
{ return raise(ts); } | |
else | |
{ return detail::tokenise_impl(ts, offs); } | |
} | |
template <class T, class... Ts> | |
auto constexpr tokenise(type_t<T> = {}, type_t<Ts...> ts = {}) | |
{ return tokenise<T>(ts); } | |
// tokenise auto_t | |
template <auto V, auto... Vs> | |
auto constexpr tokenise(auto_t<Vs...> vs) | |
{ | |
auto constexpr offs = auto_v<0> + find_all<V>(vs); | |
if constexpr (offs.size < 2) | |
{ return raise(vs); } | |
else | |
{ return detail::tokenise_impl(vs, offs); } | |
} | |
template <auto V, auto... Vs> | |
auto constexpr tokenise(auto_t<V> = {}, auto_t<Vs...> vs = {}) | |
{ return tokenise<V>(vs); } | |
// | |
// UDLs (signed) | |
template <char... Cs> | |
char constexpr to_c_str[] = { Cs..., '\0' }; | |
template <char... Vs> | |
auto constexpr to_fixed_string(auto_t<Vs...> vs) | |
{ return make_fixed_string(to_c_str<Vs...>); } | |
namespace detail | |
{ | |
template <class... Ts> | |
auto constexpr udl_i_impl(type_t<Ts...>) | |
{ return (auto_v<stoi(to_fixed_string(Ts{}))> + ...); } | |
template <class... Ts> | |
auto constexpr udl_l_impl(type_t<Ts...>) | |
{ return (auto_v<stol(to_fixed_string(Ts{}))> + ...); } | |
template <class... Ts> | |
auto constexpr udl_ll_impl(type_t<Ts...>) | |
{ return (auto_v<stoll(to_fixed_string(Ts{}))> + ...); } | |
} | |
template <char... Vs> | |
auto constexpr operator ""_i() | |
{ return detail::udl_i_impl(tokenise<'\'', Vs...>()); } | |
template <char... Vs> | |
auto constexpr operator ""_l() | |
{ return detail::udl_l_impl(tokenise<'\'', Vs...>()); } | |
template <char... Vs> | |
auto constexpr operator ""_ll() | |
{ return detail::udl_ll_impl(tokenise<'\'', Vs...>()); } | |
// C++2a | |
//template <char... Vs> | |
//auto constexpr operator ""_i() | |
//{ | |
// return []<class... Ts> | |
// (type_t<Ts...>) | |
// { return (auto_v<stoi(to_fixed_string(Ts{}))> + ...); } | |
// (tokenise<'\'', Vs...>()); | |
//} | |
//template <char... Vs> | |
//auto constexpr operator ""_l() | |
//{ | |
// return []<class... Ts> | |
// (type_t<Ts...>) | |
// { return (auto_v<stol(to_fixed_string(Ts{}))> + ...); } | |
// (tokenise<'\'', Vs...>()); | |
//} | |
//template <char... Vs> | |
//auto constexpr operator ""_ll() | |
//{ | |
// return []<class... Ts> | |
// (type_t<Ts...>) | |
// { return (auto_v<stoll(to_fixed_string(Ts{}))> + ...); } | |
// (tokenise<'\'', Vs...>()); | |
//} | |
// UDLs (unsigned) | |
namespace detail | |
{ | |
template <class... Ts> | |
auto constexpr udl_u_impl(type_t<Ts...>) | |
{ return (auto_v<stou(to_fixed_string(Ts{}))> + ...); } | |
template <class... Ts> | |
auto constexpr udl_ul_impl(type_t<Ts...>) | |
{ return (auto_v<stol(to_fixed_string(Ts{}))> + ...); } | |
template <class... Ts> | |
auto constexpr udl_ull_impl(type_t<Ts...>) | |
{ return (auto_v<stoll(to_fixed_string(Ts{}))> + ...); } | |
} | |
template <char... Vs> | |
auto constexpr operator ""_u() | |
{ return detail::udl_u_impl(tokenise<'\'', Vs...>()); } | |
template <char... Vs> | |
auto constexpr operator ""_ul() | |
{ return detail::udl_ul_impl(tokenise<'\'', Vs...>()); } | |
template <char... Vs> | |
auto constexpr operator ""_ull() | |
{ return detail::udl_ull_impl(tokenise<'\'', Vs...>()); } | |
// C++2a | |
//template <char... Vs> | |
//auto constexpr operator ""_u() | |
//{ | |
// return []<class... Ts> | |
// (type_t<Ts...>) | |
// { return (auto_v<stou(to_fixed_string(Ts{}))> + ...); } | |
// (tokenise<'\'', Vs...>()); | |
//} | |
//template <char... Vs> | |
//auto constexpr operator ""_ul() | |
//{ | |
// return []<class... Ts> | |
// (type_t<Ts...>) | |
// { return (auto_v<stoul(to_fixed_string(Ts{}))> + ...); } | |
// (tokenise<'\'', Vs...>()); | |
//} | |
//template <char... Vs> | |
//auto constexpr operator ""_ull() | |
//{ | |
// return []<class... Ts> | |
// (type_t<Ts...>) | |
// { return (auto_v<stoull(to_fixed_string(Ts{}))> + ...); } | |
// (tokenise<'\'', Vs...>()); | |
//} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment