-
-
Save JavierJF/fd8c1cc693c946bd4fdd7db7b06c06cb to your computer and use it in GitHub Desktop.
An almost perfect design for a hybrid constexpr/runtime string
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
// Copyright Eric Niebler 2016 | |
#include <cstddef> | |
#include <cstdio> | |
#include <cstdint> | |
#include <cstring> | |
#include <utility> | |
#include <type_traits> | |
#define REQUIRES(X) \ | |
typename std::enable_if<(X), int>::type = 0 | |
using std::size_t; | |
constexpr char const* find_null(char const *z) { | |
return *z ? find_null(z+1) : z; | |
} | |
constexpr size_t constexpr_strlen(char const *z) { | |
return find_null(z) - z; | |
} | |
template <class T> | |
constexpr T constexpr_min(T a, T b) { | |
return b < a ? b : a; | |
} | |
template <class T> | |
constexpr T constexpr_max(T a, T b) { | |
return b < a ? a : b; | |
} | |
template <size_t N> | |
constexpr std::make_index_sequence<N>* indices() { | |
return nullptr; | |
} | |
enum class which_rep : char { small, big }; | |
enum class ctype : int { cexpr=0, dyn=1 }; | |
class cstring_b { | |
protected: | |
struct bigdata { | |
char* c_str_; | |
std::uint32_t size_; | |
char dummy_; | |
}; | |
template <size_t X> | |
friend auto cs(char const (&that)[X]); | |
}; | |
template <size_t N, ctype T> | |
class cstring {}; | |
template <size_t N> | |
class cstring<N, ctype::cexpr> : cstring_b { | |
protected: | |
static constexpr size_t M = | |
constexpr_max(N + 1, sizeof(bigdata)); | |
private: | |
using smalldata = char[M]; | |
union { | |
smalldata smalldata_; | |
bigdata bigdata_; | |
}; | |
template <class T> | |
static constexpr char get_at_(T const &t, size_t i, size_t size) { | |
return i < size ? t[i] : i == M-1 ? (char)((unsigned char)(M-1 - size)) : '\0'; | |
} | |
constexpr which_rep which_() const noexcept { | |
return static_cast<unsigned char>(smalldata_[M-1]) == | |
static_cast<unsigned char>((char)-1) | |
? which_rep::big | |
: which_rep::small; | |
} | |
template <class T, size_t... Is> | |
constexpr cstring(T const &t, size_t size, std::index_sequence<Is...>*) noexcept | |
: smalldata_{get_at_(t, Is, size)...} {} | |
static constexpr char s_empty[1]{}; | |
public: | |
constexpr cstring() noexcept : cstring{s_empty, 0, indices<M>()} {} | |
template <size_t X, REQUIRES(X <= M && M < 255)> | |
constexpr cstring(char const (&that)[X]) noexcept | |
: cstring{that, X-1, indices<M>()} { | |
} | |
constexpr char const * c_str() const noexcept { | |
return which_() == which_rep::small ? smalldata_ : bigdata_.c_str_; | |
} | |
constexpr std::size_t size() const noexcept { | |
return which_() == which_rep::small | |
? static_cast<size_t>(M-1 - static_cast<unsigned char>(smalldata_[M-1])) | |
: bigdata_.size_; | |
} | |
constexpr char const& operator[](std::size_t i) const noexcept { | |
return which_() == which_rep::small | |
? smalldata_[i] | |
: bigdata_.c_str_[i]; | |
} | |
constexpr bool is_small() const { | |
return which_() == which_rep::small; | |
} | |
}; | |
template <size_t N> | |
class cstring<N, ctype::dyn> : cstring_b { | |
protected: | |
static constexpr size_t M = | |
constexpr_max(N + 1, sizeof(bigdata)); | |
private: | |
using smalldata = char[M]; | |
union { | |
smalldata smalldata_; | |
bigdata bigdata_; | |
}; | |
template <class T> | |
static constexpr char get_at_(T const &t, size_t i, size_t size) { | |
return i < size ? t[i] : i == M-1 ? (char)((unsigned char)(M-1 - size)) : '\0'; | |
} | |
constexpr which_rep which_() const noexcept { | |
return static_cast<unsigned char>(smalldata_[M-1]) == | |
static_cast<unsigned char>((char)-1) | |
? which_rep::big | |
: which_rep::small; | |
} | |
template <class T, size_t... Is> | |
constexpr cstring(T const &t, size_t size, std::index_sequence<Is...>*) noexcept | |
: smalldata_{get_at_(t, Is, size)...} {} | |
static constexpr char s_empty[1]{}; | |
public: | |
constexpr cstring() noexcept : cstring{s_empty, 0, indices<M>()} {} | |
template <size_t X> | |
cstring(char const (&that)[X]) noexcept(false) | |
{ | |
smalldata_[M-1] = (char)-1; | |
bigdata_.size_ = X-1; | |
bigdata_.c_str_ = strdup(that); | |
} | |
// Destructor | |
~cstring() noexcept {} | |
constexpr char const * c_str() const noexcept { | |
return which_() == which_rep::small ? smalldata_ : bigdata_.c_str_; | |
} | |
constexpr std::size_t size() const noexcept { | |
return which_() == which_rep::small | |
? static_cast<size_t>(M-1 - static_cast<unsigned char>(smalldata_[M-1])) | |
: bigdata_.size_; | |
} | |
constexpr char const& operator[](std::size_t i) const noexcept { | |
return which_() == which_rep::small | |
? smalldata_[i] | |
: bigdata_.c_str_[i]; | |
} | |
constexpr bool is_small() const { | |
return which_() == which_rep::small; | |
} | |
}; | |
constexpr auto select(bool b) | |
{ | |
if( b ) { return ctype::cexpr; } | |
else { return ctype::dyn; } | |
} | |
template <size_t N, size_t X> | |
constexpr auto cs(char const (&that)[X]) | |
{ | |
constexpr size_t M = | |
constexpr_max(N + 1, sizeof(cstring_b::bigdata)); | |
return cstring<N, select(X <= M && M < 255)>(that); | |
} | |
int main() | |
{ | |
static constexpr cstring<23, ctype::cexpr> s{"hello world hello world"}; | |
constexpr char const& c = s[3]; | |
static_assert(s.size() == 23u, ""); | |
static_assert(sizeof(s) == 24u, ""); | |
static_assert(s[s.size()] == '\0', ""); | |
static_assert(s.is_small(), ""); | |
std::printf("\"%s\"\n", s.c_str()); | |
// Duplicate using helper function. | |
static constexpr cstring<23, ctype::cexpr> sd = cs<23>("hello world hello world"); | |
constexpr char const& cd = sd[3]; | |
static_assert(sd.size() == 23u, ""); | |
static_assert(sizeof(sd) == 24u, ""); | |
static_assert(sd[sd.size()] == '\0', ""); | |
static_assert(sd.is_small(), ""); | |
std::printf("\"%s\"\n", sd.c_str()); | |
static constexpr cstring<0, ctype::cexpr> s2; | |
constexpr char const& c2 = s2[0]; | |
static_assert(s2.size() == 0u, ""); | |
static_assert(sizeof(s2) == 16u, ""); | |
static_assert(s2[s2.size()] == '\0', ""); | |
static_assert(s2.is_small(), ""); | |
std::printf("\"%s\"\n", s2.c_str()); | |
cstring<5, ctype::dyn> b{"hello world!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"}; | |
std::printf("\"%s\"\n", b.c_str()); | |
if(!b.is_small()) { | |
std::printf("Is big! Size = %d\n", (int)b.size()); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment