Skip to content

Instantly share code, notes, and snippets.

@JavierJF
Forked from ericniebler/constexpr_string.cpp
Last active December 12, 2016 11:41
Show Gist options
  • Save JavierJF/fd8c1cc693c946bd4fdd7db7b06c06cb to your computer and use it in GitHub Desktop.
Save JavierJF/fd8c1cc693c946bd4fdd7db7b06c06cb to your computer and use it in GitHub Desktop.
An almost perfect design for a hybrid constexpr/runtime string
// 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