C++11
Due to the null-termination of constexpr const char*
string literals, you can simply use std::strlen, and if it ought to work compile-time, a custom function is easily definable:
constexpr std::size_t _strlen(char const* s, std::size_t count = 0) {
return (*s == '\0') ? count : _strlen(s + 1, count + 1);
}
(C++11 constexpr functions can only contain exactly one return statement, so use recursion.)
If sizeof
has to additionally work like on arrays (i.e. included the null-termination), you can define string literals using variadic templates like so:
constexpr std::size_t _strlen(char const* s, std::size_t count = 0) {
return (*s == '\0') ? count : _strlen(s + 1, count + 1);
}
// Parameter pack helpers (similar to C++14)
template<std::size_t ...> struct _index_sequence { using type = _index_sequence; };
template<std::size_t N, std::size_t ... S> struct gen_pack_sequence: gen_pack_sequence<N - 1, N - 1, S...> {};
template<std::size_t ... S> struct gen_pack_sequence<0, S...> : _index_sequence<S...> {};
template<std::size_t N> using _make_index_sequence = typename gen_pack_sequence<N>::type;
template<char ... Chars> struct char_sequence {
static constexpr char value[] = { Chars... };
template<template<char...> class Template> using param_pack = Template<Chars...>;
};
template<char ... Chars> constexpr char char_sequence<Chars...>::value[];
template<char ... Chars> struct char_string: char_sequence<Chars..., '\0'> {};
// Compile-time string literals ; Concept: https://ideone.com/QvXuYf
template<typename S, typename T> struct _struct_to_string;
template<typename S, std::size_t ... Indices> struct _struct_to_string<S, _index_sequence<Indices...>> { typedef char_string<S::get()[Indices] ...> type; };
template<typename S> struct struct_to_string { typedef _make_index_sequence<_strlen(S::get())> indices; typedef typename _struct_to_string<S, indices>::type type; };
#define CONSTEXPR_STRING(name, s) \
struct name ## __struct { constexpr static const char* get() { return s; } }; \
typedef struct_to_string<name ## __struct>::type name
Usage with output:
CONSTEXPR_STRING(var, "Hello!");
constexpr const char* _var = var::value;
constexpr std::size_t _varlen = _strlen(_var);
int main()
{
std::cout << _var << std::endl; // "Hello!" has length 6
std::cout << _varlen << std::endl; // 6
std::cout << _strlen(_var) << std::endl; // 6
std::cout << std::strlen(_var) << std::endl; // 6
std::cout << sizeof(var::value) << std::endl; // 7: size of { 'H', 'e', 'l', 'l', 'o', '!', '\0' }
std::cout << sizeof(_var) << std::endl; // 8: a pointer is 64 bits = 8 bytes
}
This might seem overkill for one could simply use an array like
constexpr char _var[] = "hello";
but using ::value
circumvents the problems that C-style arrays cannot be return values of functions and there are no constexpr parameters in C++. The char_string
type can be extended to support all possible constexpr string features and is in particular flexible via using ::param_pack
, which comes in handy for example w.r.t. compile-time string concatenation. (Here is an example of how it can be used.)
constexpr auto
? – Hauger