Skip to content

Instantly share code, notes, and snippets.

@newlawrence
Created April 15, 2022 10:02
Show Gist options
  • Save newlawrence/a88c86558500d8f482328698c3dbb068 to your computer and use it in GitHub Desktop.
Save newlawrence/a88c86558500d8f482328698c3dbb068 to your computer and use it in GitHub Desktop.
Portable C++17 compile time string implementation
#include <ostream>
#include <string>
#include <string_view>
#include <cstdint>
#define CTSTR_DEFINE_LITERAL_OPERATOR(NAMESPACE, CHARSET) \
namespace NAMESPACE { \
using ctstr_t = basic_ctstr_t<charset::CHARSET>; \
namespace literals { \
constexpr auto operator""_cs(char const* str, std::size_t sz) noexcept { \
return ctstr_t{{str, sz}}; \
} \
using namespace ::ctstr::NAMESPACE; \
} \
}
namespace ctstr {
enum class charset : std::size_t {
ExtendedASCII = 8u,
ASCII = 7u,
Alphanumeric = 6u,
Alphabetic = 5u
};
template<charset CS>
class basic_ctstr_t {
public:
using value_type = std::uintmax_t;
static constexpr auto chr_sz = static_cast<std::size_t>(CS);
static constexpr auto max_length = sizeof(value_type) * 8u / chr_sz;
friend std::ostream& operator<<(std::ostream& os, basic_ctstr_t str) {
str.apply_([&os](char chr) { os << chr; });
return os;
}
explicit constexpr basic_ctstr_t(std::string_view str) noexcept
: basic_ctstr_t{make_<0u>(str.data(), str.length(), 0u)}
{}
explicit constexpr basic_ctstr_t(value_type hash) noexcept
: hash_{hash}
{}
constexpr operator value_type() const noexcept {
return hash_;
}
[[nodiscard]] operator std::string() const {
constexpr char const zeros[max_length + 1] = {'\0'};
std::string str{zeros};
apply_([&str](char chr) { str += chr; });
return str;
}
private:
static constexpr value_type encode_(char chr, std::size_t c) noexcept {
if constexpr (CS == charset::Alphabetic) {
if (chr >= 'A' && chr <= 'Z')
chr -= ('A' - char{1});
else if (chr >= 'a' && chr <= 'z')
chr -= ('a' - char{1});
else
return 0u;
}
if constexpr (CS == charset::Alphanumeric) {
if (chr == '_')
chr -= ('_' - char{1});
else if (chr >= '0' && chr <= '9')
chr -= ('0' - char{2});
else if (chr >= 'A' && chr <= 'Z')
chr -= ('A' - char{12});
else if (chr >= 'a' && chr <= 'z')
chr -= ('a' - char{38});
else
return 0u;
}
if constexpr (CS == charset::ASCII)
if (static_cast<value_type>(chr) >= (1u << chr_sz))
return 0u;
return static_cast<value_type>(chr) << (c * chr_sz);
}
static constexpr char decode_(value_type hash, std::size_t c) noexcept {
auto chr = static_cast<char>((hash >> (c * chr_sz)) % (1u << chr_sz));
if (!chr)
return '\0';
if constexpr (CS == charset::Alphabetic)
return chr + ('a' - char{1});
if constexpr (CS == charset::Alphanumeric) {
if (static_cast<value_type>(chr) == 1)
return chr + ('_' - char{1});
else if (static_cast<value_type>(chr) <= 11)
return chr + ('0' - char{2});
else if (static_cast<value_type>(chr) <= 37)
return chr + ('A' - char{12});
else
return chr + ('a' - char{38});
}
return chr;
}
template<std::size_t C>
static constexpr basic_ctstr_t
make_(char const* str, std::size_t sz, value_type hash) noexcept {
if constexpr (!C)
if (sz > max_length)
return basic_ctstr_t{0u};
if (!*str)
return basic_ctstr_t{hash};
else if (auto next_hash = encode_(*str, C))
hash += next_hash;
else
return basic_ctstr_t{0u};
if constexpr (C < max_length - 1u)
if (C < sz - 1u)
return make_<C + 1u>(str + 1, sz, hash);
return basic_ctstr_t{hash};
}
template<typename F>
void apply_(F f) const noexcept {
for (auto c = 0u; c < max_length; ++c) {
auto chr = decode_(hash_, c);
if (!chr)
break;
f(chr);
}
}
value_type hash_;
};
CTSTR_DEFINE_LITERAL_OPERATOR(asciiext, ExtendedASCII)
CTSTR_DEFINE_LITERAL_OPERATOR(ascii, ASCII)
CTSTR_DEFINE_LITERAL_OPERATOR(alphanum, Alphanumeric)
CTSTR_DEFINE_LITERAL_OPERATOR(alpha, Alphabetic)
}
#undef CTSTR_DEFINE_LITERAL_OPERATOR
#include <iostream>
int main() {
using namespace ctstr::ascii::literals;
std::cout << "max_length: " << ctstr_t::max_length << std::endl;
std::cout << "FooBarBaz"_cs << std::endl;
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment