166

Being able to create and manipulate strings during compile-time in C++ has several useful applications. Although it is possible to create compile-time strings in C++, the process is very cumbersome, as the string needs to be declared as a variadic sequence of characters, e.g.

using str = sequence<'H', 'e', 'l', 'l', 'o', ',', ' ', 'w', 'o', 'r', 'l', 'd', '!'>; 

Operations such as string concatenation, substring extraction, and many others, can easily be implemented as operations on sequences of characters. Is it possible to declare compile-time strings more conveniently? If not, is there a proposal in the works that would allow for convenient declaration of compile-time strings?

Why Existing Approaches Fail

Ideally, we would like to be able to declare compile-time strings as follows:

// Approach 1 using str1 = sequence<"Hello, world!">; 

or, using user-defined literals,

// Approach 2 constexpr auto str2 = "Hello, world!"_s; 

where decltype(str2) would have a constexpr constructor. A messier version of approach 1 is possible to implement, taking advantage of the fact that you can do the following:

template <unsigned Size, const char Array[Size]> struct foo; 

However, the array would need to have external linkage, so to get approach 1 to work, we would have to write something like this:

/* Implementation of array to sequence goes here. */ constexpr const char str[] = "Hello, world!"; int main() { using s = string<13, str>; return 0; } 

Needless to say, this is very inconvenient. Approach 2 is actually not possible to implement. If we were to declare a (constexpr) literal operator, then how would we specify the return type? Since we need the operator to return a variadic sequence of characters, so we would need to use the const char* parameter to specify the return type:

constexpr auto operator"" _s(const char* s, size_t n) -> /* Some metafunction using `s` */ 

This results in a compile error, because s is not a constexpr. Trying to work around this by doing the following does not help much.

template <char... Ts> constexpr sequence<Ts...> operator"" _s() { return {}; } 

The standard dictates that this specific literal operator form is reserved for integer and floating-point types. While 123_s would work, abc_s would not. What if we ditch user-defined literals altogether, and just use a regular constexpr function?

template <unsigned Size> constexpr auto string(const char (&array)[Size]) -> /* Some metafunction using `array` */ 

As before, we run into the problem that the array, now a parameter to the constexpr function, is itself no longer a constexpr type.

I believe it should be possible to define a C preprocessor macro that takes a string and the size of the string as arguments, and returns a sequence consisting of the characters in the string (using BOOST_PP_FOR, stringification, array subscripts, and the like). However, I do not have the time (or enough interest) to implement such a macro =)

13
  • 2
    Boost has a macro which defines a string which can be used as a constant expression. Well, it defines a class which has a string member. Did you check that out? Commented Apr 7, 2013 at 2:17
  • 6
    Did you check cpp-next.com/archive/2012/10/… ? Commented Apr 7, 2013 at 2:35
  • 1
    Stack Overflow is not the appropriate place to ask about whether a proposal for something exists. The best place for this would be the C++ site. Commented Apr 7, 2013 at 3:42
  • 1
    Basically, you expand the characters stored in the array/ptr into a parameter pack (like Xeo did). Though they're not split into non-type template arguments, you can use them within constexpr functions and initialize arrays (therefore, concat, substr etc). Commented Apr 8, 2013 at 11:27
  • 1
    @MareInfinitus In short, constexpr strings can be parsed during compile-time, so that you can take different code paths depending on the results. Essentially, you can create EDLs within C++; the applications are pretty limitless. Commented Apr 14, 2013 at 11:36

21 Answers 21

143

I haven't seen anything to match the elegance of Scott Schurr's str_const presented at C++ Now 2012. It does require constexpr though.

Here's how you can use it, and what it can do:

int main() { constexpr str_const my_string = "Hello, world!"; static_assert(my_string.size() == 13, ""); static_assert(my_string[4] == 'o', ""); constexpr str_const my_other_string = my_string; static_assert(my_string == my_other_string, ""); constexpr str_const world(my_string, 7, 5); static_assert(world == "world", ""); // constexpr char x = world[5]; // Does not compile because index is out of range! } 

It doesn't get much cooler than compile-time range checking!

Both the use, and the implementation, is free of macros. And there is no artificial limit on string size. I'd post the implementation here, but I'm respecting Scott's implicit copyright. The implementation is on a single slide of his presentation linked to above.

Update C++17

In the years since I posted this answer, std::string_view has become part of our tool chest. Here is how I would rewrite the above using string_view:

#include <string_view> int main() { constexpr std::string_view my_string = "Hello, world!"; static_assert(my_string.size() == 13); static_assert(my_string[4] == 'o'); constexpr std::string_view my_other_string = my_string; static_assert(my_string == my_other_string); constexpr std::string_view world(my_string.substr(7, 5)); static_assert(world == "world"); // constexpr char x = world.at(5); // Does not compile because index is out of range! } 
Sign up to request clarification or add additional context in comments.

19 Comments

Can operations that create new constexpr strings (like string concatenation and substring extraction) work with this approach? Perhaps using two constexpr-string classes (one based on str_const and the other based on sequence), this may be possible. The user would use str_const to initialize the string, but subsequent operations that create new strings would return sequence objects.
This is a good piece of code. However, this approach still have a flaw compared to a string declared with a character sequence as template parameters : a str_const is a constant value, and not a type, thus preventing the use of a lot of metaprogramming idioms.
I don't share the enthusiasm… doesn't work with template metafunctions – very annoying because of the silly compromise that constexpr functions shall be callable at runtime – no true concatenation, requires definition of a char array (ugly in header) – though this is true of most macroless solutions thanks to the aforementioned constexpr compromise – and the range checking doesn't impress me much because even the lowly constexpr const char * has that. I rolled my own parameter pack string, which can also be made from a literal (using a metafunction) at the cost of an array definition.
I don't think the example provided in the answer is correct. The code from page 29 of Scotts presentation doesn't have a equality operator - so you can't do static_assert(my_string == my_other_string, "");
@user975326: I just reviewed my implementation of this and it looks like I added a constexpr operator==. Sorry. Scott's presentation should get you started on how to do this. It is much easier in C++14 than in C++11. I wouldn't even bother trying in C++11. See Scott's latest constexpr talks here: youtube.com/user/CppCon
|
46

I believe it should be possible to define a C preprocessor macro that takes a string and the size of the string as arguments, and returns a sequence consisting of the characters in the string (using BOOST_PP_FOR, stringification, array subscripts, and the like). However, I do not have the time (or enough interest) to implement such a macro

it is possible to implement this without relying on boost, using very simple macro and some of C++11 features:

  1. lambdas variadic
  2. templates
  3. generalized constant expressions
  4. non-static data member initializers
  5. uniform initialization

(the latter two are not strictly required here)

  1. we need to be able to instantiate a variadic template with user supplied indicies from 0 to N - a tool also useful for example to expand tuple into variadic template function's argument (see questions: How do I expand a tuple into variadic template function's arguments?
    "unpacking" a tuple to call a matching function pointer)

    namespace variadic_toolbox { template<unsigned count, template<unsigned...> class meta_functor, unsigned... indices> struct apply_range { typedef typename apply_range<count-1, meta_functor, count-1, indices...>::result result; }; template<template<unsigned...> class meta_functor, unsigned... indices> struct apply_range<0, meta_functor, indices...> { typedef typename meta_functor<indices...>::result result; }; } 
  2. then define a variadic template called string with non-type parameter char:

    namespace compile_time { template<char... str> struct string { static constexpr const char chars[sizeof...(str)+1] = {str..., '\0'}; }; template<char... str> constexpr const char string<str...>::chars[sizeof...(str)+1]; } 
  3. now the most interesting part - to pass character literals into string template:

    namespace compile_time { template<typename lambda_str_type> struct string_builder { template<unsigned... indices> struct produce { typedef string<lambda_str_type{}.chars[indices]...> result; }; }; } #define CSTRING(string_literal) \ []{ \ struct constexpr_string_type { const char * chars = string_literal; }; \ return variadic_toolbox::apply_range<sizeof(string_literal)-1, \ compile_time::string_builder<constexpr_string_type>::produce>::result{}; \ }() 

a simple concatenation demonstration shows the usage:

 namespace compile_time { template<char... str0, char... str1> string<str0..., str1...> operator*(string<str0...>, string<str1...>) { return {}; } } int main() { auto str0 = CSTRING("hello"); auto str1 = CSTRING(" world"); std::cout << "runtime concat: " << str_hello.chars << str_world.chars << "\n <=> \n"; std::cout << "compile concat: " << (str_hello * str_world).chars << std::endl; } 

https://ideone.com/8Ft2xu

6 Comments

This is so simple that I still can't believe it works. +1! One thing: shouldn't you use size_t instead of unsigned?
And what about using operator+ instead of operator*? (str_hello + str_world)
I prefer this solution over the popular Scott Schurr's str_const method, since this method ensures that the underlying data is constexpr. Schurr's method lets me create a str_const at runtime with a char[] stack variable; I can't safely return a str_const from a function or pass it to another thread.
The link is dead... can anyone repost it? @Glenn ?
You should add an extra pair of braces around the lambda in your CSTRING macro. Otherwise you can't create a CSTRING inside a call to an [] operator, as double [[ are reserved for attributes.
|
29

Edit: as Howard Hinnant (and me somewhat in my comment to the OP) pointed out, you might not need a type with every single character of the string as a single template argument. If you do need this, there's a macro-free solution below.

There's a trick I found while trying to work with strings at compile time. It requires to introduce another type besides the "template string", but within functions, you can limit the scope of this type.

It doesn't use macros but rather some C++11 features.

#include <iostream> // helper function constexpr unsigned c_strlen( char const* str, unsigned count = 0 ) { return ('\0' == str[0]) ? count : c_strlen(str+1, count+1); } // destination "template string" type template < char... chars > struct exploded_string { static void print() { char const str[] = { chars... }; std::cout.write(str, sizeof(str)); } }; // struct to explode a `char const*` to an `exploded_string` type template < typename StrProvider, unsigned len, char... chars > struct explode_impl { using result = typename explode_impl < StrProvider, len-1, StrProvider::str()[len-1], chars... > :: result; }; // recursion end template < typename StrProvider, char... chars > struct explode_impl < StrProvider, 0, chars... > { using result = exploded_string < chars... >; }; // syntactical sugar template < typename StrProvider > using explode = typename explode_impl < StrProvider, c_strlen(StrProvider::str()) > :: result; int main() { // the trick is to introduce a type which provides the string, rather than // storing the string itself struct my_str_provider { constexpr static char const* str() { return "hello world"; } }; auto my_str = explode < my_str_provider >{}; // as a variable using My_Str = explode < my_str_provider >; // as a type my_str.print(); } 

7 Comments

I have just spent the weekend independently developing a similar piece of code, and making a very basic system to parse type strings, e.g. pair<int,pair<char,double>>. I was proud of myself and then discovered this answer, and the metaparse library today! I really should search SO more thoroughly before starting silly projects like this :-) I guess that, in theory, a fully C++ compiler could be built from this kind of technology. What's the craziest thing that has been built with this?
I don't know. I have never really used these techniques in a real-world project, so I didn't pursue the approach. Though I think I remember a slight variation of the local-type trick which was slightly more convenient.. maybe a local static char[].
Do you mean my_str.print(); instead of str.print();?
Is there a C++ 14 slightly shorter version?
Instead of the recursive printer, I think an easier option is to do char str[] = {ttc...}; std::cout << str << std::endl;
|
11

If you don't want to use the Boost solution you can create simple macros that will do something similar:

#define MACRO_GET_1(str, i) \ (sizeof(str) > (i) ? str[(i)] : 0) #define MACRO_GET_4(str, i) \ MACRO_GET_1(str, i+0), \ MACRO_GET_1(str, i+1), \ MACRO_GET_1(str, i+2), \ MACRO_GET_1(str, i+3) #define MACRO_GET_16(str, i) \ MACRO_GET_4(str, i+0), \ MACRO_GET_4(str, i+4), \ MACRO_GET_4(str, i+8), \ MACRO_GET_4(str, i+12) #define MACRO_GET_64(str, i) \ MACRO_GET_16(str, i+0), \ MACRO_GET_16(str, i+16), \ MACRO_GET_16(str, i+32), \ MACRO_GET_16(str, i+48) #define MACRO_GET_STR(str) MACRO_GET_64(str, 0), 0 //guard for longer strings using seq = sequence<MACRO_GET_STR("Hello world!")>; 

The only problem is the fixed size of 64 chars (plus additional zero). But it can easily be changed depending on your needs.

2 Comments

I like this solution a lot; it's very simple and does the job elegantly. Is it possible to modify the macro so that nothing is appended sizeof(str) > i (instead of appending the extra 0, tokens)? It's easy to define a trim metafunction that will do this after the macro has already been called, but it would be nice if the macro itself could be modified.
Is impossible because parser dont understand sizeof(str). Its possible to manually add string size like MACRO_GET_STR(6, "Hello") but this require Boost macros to work because writing it manually require 100 times more code (you need implements simple thing like 1+1).
8

Here's a succinct C++14 solution to creating a std::tuple<char...> for each compile-time string passed.

#include <tuple> #include <utility> namespace detail { template <std::size_t ... indices> decltype(auto) build_string(const char * str, std::index_sequence<indices...>) { return std::make_tuple(str[indices]...); } } template <std::size_t N> constexpr decltype(auto) make_string(const char(&str)[N]) { return detail::build_string(str, std::make_index_sequence<N>()); } auto HelloStrObject = make_string("hello"); 

And here's one for creating a unique compile-time type, trimmed down from the other macro post.

#include <utility> template <char ... Chars> struct String {}; template <typename Str, std::size_t ... indices> decltype(auto) build_string(std::index_sequence<indices...>) { return String<Str().chars[indices]...>(); } #define make_string(str) []{\ struct Str { const char * chars = str; };\ return build_string<Str>(std::make_index_sequence<sizeof(str)>());\ }() auto HelloStrObject = make_string("hello"); 

It's really too bad that user-defined literals can't be used for this yet.

1 Comment

Actually, they can using an extension supported by GCC/Clang, but I'm going to wait before this is added to the standard before posting it as an answer.
7

Nobody seems to like my other answer :-<. So here I show how to convert a str_const to a real type:

#include <iostream> #include <utility> // constexpr string with const member functions class str_const { private: const char* const p_; const std::size_t sz_; public: template<std::size_t N> constexpr str_const(const char(&a)[N]) : // ctor p_(a), sz_(N-1) {} constexpr char operator[](std::size_t n) const { return n < sz_ ? p_[n] : throw std::out_of_range(""); } constexpr std::size_t size() const { return sz_; } // size() }; template <char... letters> struct string_t{ static char const * c_str() { static constexpr char string[]={letters...,'\0'}; return string; } }; template<str_const const& str,std::size_t... I> auto constexpr expand(std::index_sequence<I...>){ return string_t<str[I]...>{}; } template<str_const const& str> using string_const_to_type = decltype(expand<str>(std::make_index_sequence<str.size()>{})); constexpr str_const hello{"Hello World"}; using hello_t = string_const_to_type<hello>; int main() { // char c = hello_t{}; // Compile error to print type std::cout << hello_t::c_str(); return 0; } 

Compiles with clang++ -stdlib=libc++ -std=c++14 (clang 3.7)

1 Comment

Works well, but not for msvc 2019, as it complains about str.size() not being constexpr. Can be fixed by adding a 2nd using separatly deducing str.size(). Maybe that held back some upvotes ;-)
6

I believe it should be possible to define a C preprocessor macro that takes a string and the size of the string as arguments, and returns a sequence consisting of the characters in the string (using BOOST_PP_FOR, stringification, array subscripts, and the like)

There is article: Using strings in C++ template metaprograms by Abel Sinkovics and Dave Abrahams.

It has some improvement over your idea of using macro + BOOST_PP_REPEAT - it doesn't require passing explicit size to macro. In short, it is based on fixed upper limit for string size and "string overrun protection":

template <int N> constexpr char at(char const(&s)[N], int i) { return i >= N ? '\0' : s[i]; } 

plus conditional boost::mpl::push_back.


I changed my accepted answer to Yankes' solution, since it solves this specific problem, and does so elegantly without the use of constexpr or complex preprocessor code.

If you accept trailing zeros, hand-written macro looping, 2x repetion of string in expanded macro, and don't have Boost - then I agree - it is better. Though, with Boost it would be just three lines:

LIVE DEMO

#include <boost/preprocessor/repetition/repeat.hpp> #define GET_STR_AUX(_, i, str) (sizeof(str) > (i) ? str[(i)] : 0), #define GET_STR(str) BOOST_PP_REPEAT(64,GET_STR_AUX,str) 0 

2 Comments

I initially changed the solution to Yankes', since he provided the first working example here. At this point, there's a lot of good competing ideas. It was my mistake in picking an answer so early. I'll currently remark this question as unanswered, and hold off until I get the time to try out the ideas that everyone has posted here. There's a lot of useful information in the answers people have given here...
I agree - for instance, I like Howard Hinnant example.
5

A colleague challenged me to concatenate strings in memory at compile-time. It includes instantiating individual strings at compile-time as well. The full code listing is here:

//Arrange strings contiguously in memory at compile-time from string literals. //All free functions prefixed with "my" to faciliate grepping the symbol tree //(none of them should show up). #include <iostream> using std::size_t; //wrapper for const char* to "allocate" space for it at compile-time template<size_t N> struct String { //C arrays can only be initialised with a comma-delimited list //of values in curly braces. Good thing the compiler expands //parameter packs into comma-delimited lists. Now we just have //to get a parameter pack of char into the constructor. template<typename... Args> constexpr String(Args... args):_str{ args... } { } const char _str[N]; }; //takes variadic number of chars, creates String object from it. //i.e. myMakeStringFromChars('f', 'o', 'o', '\0') -> String<4>::_str = "foo" template<typename... Args> constexpr auto myMakeStringFromChars(Args... args) -> String<sizeof...(Args)> { return String<sizeof...(args)>(args...); } //This struct is here just because the iteration is going up instead of //down. The solution was to mix traditional template metaprogramming //with constexpr to be able to terminate the recursion since the template //parameter N is needed in order to return the right-sized String<N>. //This class exists only to dispatch on the recursion being finished or not. //The default below continues recursion. template<bool TERMINATE> struct RecurseOrStop { template<size_t N, size_t I, typename... Args> static constexpr String<N> recurseOrStop(const char* str, Args... args); }; //Specialisation to terminate recursion when all characters have been //stripped from the string and converted to a variadic template parameter pack. template<> struct RecurseOrStop<true> { template<size_t N, size_t I, typename... Args> static constexpr String<N> recurseOrStop(const char* str, Args... args); }; //Actual function to recurse over the string and turn it into a variadic //parameter list of characters. //Named differently to avoid infinite recursion. template<size_t N, size_t I = 0, typename... Args> constexpr String<N> myRecurseOrStop(const char* str, Args... args) { //template needed after :: since the compiler needs to distinguish //between recurseOrStop being a function template with 2 paramaters //or an enum being compared to N (recurseOrStop < N) return RecurseOrStop<I == N>::template recurseOrStop<N, I>(str, args...); } //implementation of the declaration above //add a character to the end of the parameter pack and recurse to next character. template<bool TERMINATE> template<size_t N, size_t I, typename... Args> constexpr String<N> RecurseOrStop<TERMINATE>::recurseOrStop(const char* str, Args... args) { return myRecurseOrStop<N, I + 1>(str, args..., str[I]); } //implementation of the declaration above //terminate recursion and construct string from full list of characters. template<size_t N, size_t I, typename... Args> constexpr String<N> RecurseOrStop<true>::recurseOrStop(const char* str, Args... args) { return myMakeStringFromChars(args...); } //takes a compile-time static string literal and returns String<N> from it //this happens by transforming the string literal into a variadic paramater //pack of char. //i.e. myMakeString("foo") -> calls myMakeStringFromChars('f', 'o', 'o', '\0'); template<size_t N> constexpr String<N> myMakeString(const char (&str)[N]) { return myRecurseOrStop<N>(str); } //Simple tuple implementation. The only reason std::tuple isn't being used //is because its only constexpr constructor is the default constructor. //We need a constexpr constructor to be able to do compile-time shenanigans, //and it's easier to roll our own tuple than to edit the standard library code. //use MyTupleLeaf to construct MyTuple and make sure the order in memory //is the same as the order of the variadic parameter pack passed to MyTuple. template<typename T> struct MyTupleLeaf { constexpr MyTupleLeaf(T value):_value(value) { } T _value; }; //Use MyTupleLeaf implementation to define MyTuple. //Won't work if used with 2 String<> objects of the same size but this //is just a toy implementation anyway. Multiple inheritance guarantees //data in the same order in memory as the variadic parameters. template<typename... Args> struct MyTuple: public MyTupleLeaf<Args>... { constexpr MyTuple(Args... args):MyTupleLeaf<Args>(args)... { } }; //Helper function akin to std::make_tuple. Needed since functions can deduce //types from parameter values, but classes can't. template<typename... Args> constexpr MyTuple<Args...> myMakeTuple(Args... args) { return MyTuple<Args...>(args...); } //Takes a variadic list of string literals and returns a tuple of String<> objects. //These will be contiguous in memory. Trailing '\0' adds 1 to the size of each string. //i.e. ("foo", "foobar") -> (const char (&arg1)[4], const char (&arg2)[7]) params -> // -> MyTuple<String<4>, String<7>> return value template<size_t... Sizes> constexpr auto myMakeStrings(const char (&...args)[Sizes]) -> MyTuple<String<Sizes>...> { //expands into myMakeTuple(myMakeString(arg1), myMakeString(arg2), ...) return myMakeTuple(myMakeString(args)...); } //Prints tuple of strings template<typename T> //just to avoid typing the tuple type of the strings param void printStrings(const T& strings) { //No std::get or any other helpers for MyTuple, so intead just cast it to //const char* to explore its layout in memory. We could add iterators to //myTuple and do "for(auto data: strings)" for ease of use, but the whole //point of this exercise is the memory layout and nothing makes that clearer //than the ugly cast below. const char* const chars = reinterpret_cast<const char*>(&strings); std::cout << "Printing strings of total size " << sizeof(strings); std::cout << " bytes:\n"; std::cout << "-------------------------------\n"; for(size_t i = 0; i < sizeof(strings); ++i) { chars[i] == '\0' ? std::cout << "\n" : std::cout << chars[i]; } std::cout << "-------------------------------\n"; std::cout << "\n\n"; } int main() { { constexpr auto strings = myMakeStrings("foo", "foobar", "strings at compile time"); printStrings(strings); } { constexpr auto strings = myMakeStrings("Some more strings", "just to show Jeff to not try", "to challenge C++11 again :P", "with more", "to show this is variadic"); printStrings(strings); } std::cout << "Running 'objdump -t |grep my' should show that none of the\n"; std::cout << "functions defined in this file (except printStrings()) are in\n"; std::cout << "the executable. All computations are done by the compiler at\n"; std::cout << "compile-time. printStrings() executes at run-time.\n"; } 

4 Comments

You sure it is done at compile time? There's been a discussion about this some time ago, and to me, the result is not clear.
Running objdump -t a.out |grep my finds nothing. When I started typing this code I kept experimenting with removing constexpr from the functions and objdump showed them when constexpr was omitted. I'm 99.9% confident it happens at compile time.
If you look at the disassembly (-S), you'll notice that gcc (4.7.2) does indeed resolve the constexpr functions at compile-time. Yet, the strings are not assembled at compile-time. Rather, (if I interpret it correctly) for each char of those "assembled" strings, there's an own movb operation, which is arguably the optimization you were looking for.
That's true. I tried again with gcc 4.9 and it still does the same thing. I always thought this was the compiler being stupid though.Only yesterday did I think to try a different compiler. With clang, the bytewise movs aren't there at all. With gcc, -Os gets rid of them too, but -O3 does the same thing.
3

Your approach #1 is the correct one.

However, the array would need to have external linkage, so to get approach 1 to work, we would have to write something like this: constexpr const char str[] = "Hello, world!";

No, not correct. This compiles with clang and gcc. I hope its standard c++11, but i am not a language laywer.

#include <iostream> template <char... letters> struct string_t{ static char const * c_str() { static constexpr char string[]={letters...,'\0'}; return string; } }; // just live with it, but only once using Hello_World_t = string_t<'H','e','l','l','o',' ','w','o','r','l','d','!'>; template <typename Name> void print() { //String as template parameter std::cout << Name::c_str(); } int main() { std::cout << Hello_World_t::c_str() << std::endl; print<Hello_World_t>(); return 0; } 

What I would really love for c++17 would be the following to be equivalent (to complete approach #1)

// for template <char...> <"Text"> == <'T','e','x','t'> 

Something very similar already exists in the standard for templated user defined literals,as void-pointer also mentions, but only for digits. Until then another little trick is to use the override editing mode + copy and paste of

string_t<' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' '>; 

If you do not mind the macro, than this works(slighty modified from Yankes answer):

#define MACRO_GET_1(str, i) \ (sizeof(str) > (i) ? str[(i)] : 0) #define MACRO_GET_4(str, i) \ MACRO_GET_1(str, i+0), \ MACRO_GET_1(str, i+1), \ MACRO_GET_1(str, i+2), \ MACRO_GET_1(str, i+3) #define MACRO_GET_16(str, i) \ MACRO_GET_4(str, i+0), \ MACRO_GET_4(str, i+4), \ MACRO_GET_4(str, i+8), \ MACRO_GET_4(str, i+12) #define MACRO_GET_64(str, i) \ MACRO_GET_16(str, i+0), \ MACRO_GET_16(str, i+16), \ MACRO_GET_16(str, i+32), \ MACRO_GET_16(str, i+48) //CT_STR means Compile-Time_String #define CT_STR(str) string_t<MACRO_GET_64(#str, 0), 0 >//guard for longer strings print<CT_STR(Hello World!)>(); 

Comments

3

kacey's solution for creating a unique compile-time type can, with minor modifications, also be used with C++11:

template <char... Chars> struct string_t {}; namespace detail { template <typename Str,unsigned int N,char... Chars> struct make_string_t : make_string_t<Str,N-1,Str().chars[N-1],Chars...> {}; template <typename Str,char... Chars> struct make_string_t<Str,0,Chars...> { typedef string_t<Chars...> type; }; } // namespace detail #define CSTR(str) []{ \ struct Str { const char *chars = str; }; \ return detail::make_string_t<Str,sizeof(str)>::type(); \ }() 

Use:

template <typename String> void test(String) { // ... String = string_t<'H','e','l','l','o','\0'> } test(CSTR("Hello")); 

Comments

3

While playing with the boost hana map, I came across this thread. As non of the answers solved my problem, I found a different solution which I want to add here as it could be potentially helpful for others.

My problem was that when using the boost hana map with hana strings, the compiler still generated some runtime code (see below). The reason was obviously that to query the map at compile-time it must be constexpr. This isn't possible as the BOOST_HANA_STRING macro generates a lambda, which can't be used in constexpr context. On the other hand, the map needs strings with different content to be different types.

As the solutions in this thread are either using a lambda or not providing different types for different contents, I found the following approach helpful. Also it avoids the hacky str<'a', 'b', 'c'> syntax.

The basic idea is having a version of Scott Schurr's str_const templated on the hash of the characters. It is c++14, but c++11 should be possible with a recursive implementation of the crc32 function (see here).

// str_const from https://github.com/boostcon/cppnow_presentations_2012/blob/master/wed/schurr_cpp11_tools_for_class_authors.pdf?raw=true #include <string> template<unsigned Hash> ////// <- This is the difference... class str_const2 { // constexpr string private: const char* const p_; const std::size_t sz_; public: template<std::size_t N> constexpr str_const2(const char(&a)[N]) : // ctor p_(a), sz_(N - 1) {} constexpr char operator[](std::size_t n) const { // [] return n < sz_ ? p_[n] : throw std::out_of_range(""); } constexpr std::size_t size() const { return sz_; } // size() constexpr const char* const data() const { return p_; } }; // Crc32 hash function. Non-recursive version of https://stackoverflow.com/a/23683218/8494588 static constexpr unsigned int crc_table[256] = { 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d }; template<size_t N> constexpr auto crc32(const char(&str)[N]) { unsigned int prev_crc = 0xFFFFFFFF; for (auto idx = 0; idx < sizeof(str) - 1; ++idx) prev_crc = (prev_crc >> 8) ^ crc_table[(prev_crc ^ str[idx]) & 0xFF]; return prev_crc ^ 0xFFFFFFFF; } // Conveniently create a str_const2 #define CSTRING(text) str_const2 < crc32( text ) >( text ) // Conveniently create a hana type_c<str_const2> for use in map #define CSTRING_TYPE(text) hana::type_c<decltype(str_const2 < crc32( text ) >( text ))> 

Usage:

#include <boost/hana.hpp> #include <boost/hana/map.hpp> #include <boost/hana/pair.hpp> #include <boost/hana/type.hpp> namespace hana = boost::hana; int main() { constexpr auto s2 = CSTRING("blah"); constexpr auto X = hana::make_map( hana::make_pair(CSTRING_TYPE("aa"), 1) ); constexpr auto X2 = hana::insert(X, hana::make_pair(CSTRING_TYPE("aab"), 2)); constexpr auto ret = X2[(CSTRING_TYPE("aab"))]; return ret; } 

Resulting assembler code with clang-cl 5.0 is:

012A1370 mov eax,2 012A1375 ret 

Comments

3

In C++17 with a helper macro function it's easy to create compile time strings:

template <char... Cs> struct ConstexprString { static constexpr int size = sizeof...( Cs ); static constexpr char buffer[size] = { Cs... }; }; template <char... C1, char... C2> constexpr bool operator==( const ConstexprString<C1...>& lhs, const ConstexprString<C2...>& rhs ) { if( lhs.size != rhs.size ) return false; return std::is_same_v<std::integer_sequence<char, C1...>, std::integer_sequence<char, C2...>>; } template <typename F, std::size_t... Is> constexpr auto ConstexprStringBuilder( F f, std::index_sequence<Is...> ) { return ConstexprString<f( Is )...>{}; } #define CONSTEXPR_STRING( x ) \ ConstexprStringBuilder( []( std::size_t i ) constexpr { return x[i]; }, \ std::make_index_sequence<sizeof(x)>{} ) 

And this is a usage example:

auto n = CONSTEXPR_STRING( "ab" ); auto m = CONSTEXPR_STRING( "ab" ); static_assert(n == m); 

Comments

2

C++17 solution

I would recommend you to use a custom literal operator

template <char... chars> using text = std::integer_sequence<char, chars...>; template <typename T, T... chars> constexpr text<chars...> operator""_text() { return { }; } 

Which will allow you to get from "some_text"_text equivalent of the std::integer_sequence<'s','o','m','e','_','t','e','x','t'> And then create a template where you will check and use this type

template <typename> struct Field; template <char... chars> struct Field<text<chars...>> { static constexpr std::string_view Name() { constexpr const char data[] = {chars..., '\0'}; return data; } }; 

Finally you will be able to use it like:

Field<decltype("field_name"_text)> object; static_assert(object.Name() == std::string_view("field_name")); 

C++20 solution

You will need a custom class for you text, it will store you text in memory

template <size_t N> struct StringLiteral { constexpr StringLiteral(const char (&_str)[N]) : value_(std::to_array(_str)) {} constexpr std::string_view Name() const { return std::string_view(value_.data(), N - 1); } const std::array<char, N> value_; }; 

And in C++20 you can finally use custom classes as a template argument and you can write:

template <StringLiteral name__> struct Field { constexpr std::string_view Name() const { return name__.Name(); } }; 

The creation of such an object will be really simple:

Field<"field_name"> object; static_assert(object.Name() == std::string_view("field_name")); 

1 Comment

1

based on idea from Howard Hinnant you can create literal class that will add two literals together.

template<int> using charDummy = char; template<int... dummy> struct F { const char table[sizeof...(dummy) + 1]; constexpr F(const char* a) : table{ str_at<dummy>(a)..., 0} { } constexpr F(charDummy<dummy>... a) : table{ a..., 0} { } constexpr F(const F& a) : table{ a.table[dummy]..., 0} { } template<int... dummyB> constexpr F<dummy..., sizeof...(dummy)+dummyB...> operator+(F<dummyB...> b) { return { this->table[dummy]..., b.table[dummyB]... }; } }; template<int I> struct get_string { constexpr static auto g(const char* a) -> decltype( get_string<I-1>::g(a) + F<0>(a + I)) { return get_string<I-1>::g(a) + F<0>(a + I); } }; template<> struct get_string<0> { constexpr static F<0> g(const char* a) { return {a}; } }; template<int I> constexpr auto make_string(const char (&a)[I]) -> decltype( get_string<I-2>::g(a) ) { return get_string<I-2>::g(a); } constexpr auto a = make_string("abc"); constexpr auto b = a+ make_string("def"); // b.table == "abcdef" 

2 Comments

where's str_at coming from?
its somthing like that: str_at<int I>(const char* a) { return a[i]; }
1

What you are looking for is N3599 Literal operator templates for strings. It was proposed for C++ in 2013 but there was no consensus on the details and it was never added to the standard.

However, GCC and Clang support it as an extension. It lets you split string literals to a template parameter pack of characters:

// some template type to represent a string template <char... chars> struct TemplateString { static constexpr char value[] = { chars... }; template <char... chars2> constexpr auto operator+(TemplateString<chars2...>) const { // compile-time concatenation, oh yeah! return TemplateString<chars..., chars2...>{}; } }; // a custom user-defined literal called by the compiler when you use your _suffix template <typename CharType, CharType... chars> constexpr auto operator""_tstr () { // since all the chars are constants here, you can do compile-time // processing with constexpr functions and/or template metaprogramming, // and then return whatever converted type you like return TemplateString<chars...>{}; } // auto = TemplateString<'H', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd', '!'> constexpr auto str = "Hello"_tstr + " world!"_tstr; cout << str.value << endl; 

As a fallback, the tricks using a macro get you to the same place (as shown in the answer by smilingthax, for example).

Please note that those are the only two ways to accept string literals and split them to constexpr chars: either you use the extension, or you use macro hackery at the call site.

Comments

0

I'd like to add two very small improvements to the answer of @user1115339. I mentioned them in the comments to the answer, but for convenience I'll put a copy paste solution here.

The only difference is the FIXED_CSTRING macro, which allows to use the strings within class templates and as arguments to the index operator (useful if you have e.g. a compiletime map).

Live example.

namespace variadic_toolbox { template<unsigned count, template<unsigned...> class meta_functor, unsigned... indices> struct apply_range { typedef typename apply_range<count-1, meta_functor, count-1, indices...>::result result; }; template<template<unsigned...> class meta_functor, unsigned... indices> struct apply_range<0, meta_functor, indices...> { typedef typename meta_functor<indices...>::result result; }; } namespace compile_time { template<char... str> struct string { static constexpr const char chars[sizeof...(str)+1] = {str..., '\0'}; }; template<char... str> constexpr const char string<str...>::chars[sizeof...(str)+1]; template<typename lambda_str_type> struct string_builder { template<unsigned... indices> struct produce { typedef string<lambda_str_type{}.chars[indices]...> result; }; }; } #define CSTRING(string_literal) \ []{ \ struct constexpr_string_type { const char * chars = string_literal; }; \ return variadic_toolbox::apply_range<sizeof(string_literal)-1, \ compile_time::string_builder<constexpr_string_type>::produce>::result{}; \ }() #define FIXED_CSTRING(string_literal) \ ([]{ \ struct constexpr_string_type { const char * chars = string_literal; }; \ return typename variadic_toolbox::apply_range<sizeof(string_literal)-1, \ compile_time::string_builder<constexpr_string_type>::template produce>::result{}; \ }()) struct A { auto test() { return FIXED_CSTRING("blah"); // works // return CSTRING("blah"); // works too } template<typename X> auto operator[](X) { return 42; } }; template<typename T> struct B { auto test() { // return CSTRING("blah");// does not compile return FIXED_CSTRING("blah"); // works } }; int main() { A a; //return a[CSTRING("blah")]; // fails with error: two consecutive ' [ ' shall only introduce an attribute before ' [ ' token return a[FIXED_CSTRING("blah")]; } 

Comments

0

Adapted from #QuarticCat's answer

template <char...> struct Str { }; #define STRNAME(str) _constexpr_string_type_helper_##str #define STR(str) \ auto STRNAME(str) = []<size_t... Is>(std::index_sequence<Is...>) \ { \ constexpr char chars[] = #str; \ return Str<chars[Is]...>{}; \ } \ (std::make_index_sequence<sizeof(#str) - 1>{}); \ decltype(STRNAME(str)) 

Full code here

Comments

0

Non lambda version, using std::min and sizeof.
Buy the length of string is limited to 256.
This can be used in unevaluated context, such as decltype or sizeof.
I used stamp macros to reduce the code size.

#include <type_traits> #include <utility> template <char...> struct Str { }; namespace char_mpl { constexpr auto first(char val, char...) { return val; } constexpr auto second(char, char val, char...) { return val; } template <class S1, class S2> struct Concat; template <char... lefts, char... rights> struct Concat<Str<lefts...>, Str<rights...>> { using type = Str<lefts..., rights...>; }; template <size_t right_count, class Right> struct Take; template <template <char...> class Right, char... vals> struct Take<0, Right<vals...>> { using type = Str<>; }; template <template <char...> class Right, char... vals> struct Take<1, Right<vals...>> { using type = Str<first(vals...)>; }; template <template <char...> class Right, char... vals> struct Take<2, Right<vals...>> { using type = Str<first(vals...), second(vals...)>; }; template <size_t lhs, size_t rhs> concept greater = lhs > rhs; // this may be improved for speed. template <size_t n, char left, char... vals> requires greater<n, 2> struct Take<n, Str<left, vals...>> { using type = Concat<Str<left>, // typename Take<n - 1, Str<vals...>>::type// >::type; }; };// namespace char_mpl template <int length, char... vals> struct RawStr { constexpr auto ch(char c, int i) { return c; } constexpr static auto to_str() { return typename char_mpl::Take<length, Str<vals...>>::type{}; } }; #define STAMP4(n, STR, stamper) \ stamper(n, STR) stamper(n + 1, STR) \ stamper(n + 2, STR) stamper(n + 3, STR) #define STAMP16(n, STR, stamper) \ STAMP4(n, STR, stamper) \ STAMP4(n + 4, STR, stamper) \ STAMP4(n + 8, STR, stamper) \ STAMP4(n + 12, STR, stamper) #define STAMP64(n, STR, stamper) \ STAMP16(n, STR, stamper) \ STAMP16(n + 16, STR, stamper) \ STAMP16(n + 32, STR, stamper) \ STAMP16(n + 48, STR, stamper) #define STAMP256(n, STR, stamper) \ STAMP64(n, STR, stamper) \ STAMP64(n + 64, STR, stamper) \ STAMP64(n + 128, STR, stamper) \ STAMP64(n + 192, STR, stamper) #define STAMP(n, STR, stamper) stamper(STAMP##n, STR, n) #define CH(STR, i) STR[std::min<size_t>(sizeof(STR) - 1, i)] #define CSTR_STAMPER_CASE(n, STR) CH(STR, n), #define CSTR_STAMPER(stamper, STR, n) \ RawStr<sizeof(STR) - 1, \ stamper(0, STR, CSTR_STAMPER_CASE) \ CH(STR, 256)> #define CSTR(STR) (STAMP(256, STR, CSTR_STAMPER){}).to_str() int main() { constexpr auto s = CSTR("12345"); decltype(CSTR("123123")); sizeof(CSTR("123123")); static_assert( std::is_same_v< Str<'1'>, std::remove_cvref_t<decltype(CSTR("1"))>>); static_assert( std::is_same_v< Str<'1', '2'>, std::remove_cvref_t<decltype(CSTR("12"))>>); static_assert( std::is_same_v< Str<'1', '2', '3', '4', '5'>, std::remove_cvref_t<decltype(CSTR("12345"))>>); } 

Comments

0

@smilingthax's solution can be shorter by using std::index_sequence:

template<char...> struct Str {}; template<class T, size_t... Is> [[nodiscard]] constexpr auto helper(std::index_sequence<Is...>) { return Str<T{}.chars[Is]...>{}; } #define STR(str) \ [] { \ struct Temp { \ const char* chars = str; \ }; \ return helper<Temp>(std::make_index_sequence<sizeof(str) - 1>{}); \ }() 

or even shorter:

template<char...> struct Str {}; #define STR(str) \ []<size_t... Is>(std::index_sequence<Is...>) { \ return Str<str[Is]...>{}; \ } \ (std::make_index_sequence<sizeof(str) - 1>{}) 

Comments

0

My own implementation is based on approach from the Boost.Hana string (template class with variadic characters), but utilizes only the C++11 standard and constexpr functions with strict check on compiletimeness (would be a compile time error if not a compile time expression). Can be constructed from the usual raw C string instead of fancy {'a', 'b', 'c' } (through a macro).

Implementation: https://github.com/andry81/tacklelib/tree/HEAD/include/tacklelib/tackle/tmpl_string.hpp

Tests: https://github.com/andry81/tacklelib/tree/HEAD/src/tests/unit/test_tmpl_string.cpp

Usage examples:

const auto s0 = TACKLE_TMPL_STRING(0, "012"); // "012" const char c1_s0 = UTILITY_CONSTEXPR_GET(s0, 1); // '1' const auto s1 = TACKLE_TMPL_STRING(0, "__012", 2); // "012" const char c1_s1 = UTILITY_CONSTEXPR_GET(s1, 1); // '1' const auto s2 = TACKLE_TMPL_STRING(0, "__012__", 2, 3); // "012" const char c1_s2 = UTILITY_CONSTEXPR_GET(s2, 1); // '1' // TACKLE_TMPL_STRING(0, "012") and TACKLE_TMPL_STRING(1, "012") // - semantically having different addresses. // So id can be used to generate new static array class field to store // a string bytes at different address. // Can be overloaded in functions with another type to express the compiletimeness between functions: template <uint64_t id, typename CharT, CharT... tchars> const overload_resolution_1 & test_overload_resolution(const tackle::tmpl_basic_string<id, CharT, tchars...> &); template <typename CharT> const overload_resolution_2 & test_overload_resolution(const tackle::constexpr_basic_string<CharT> &); // , where `constexpr_basic_string` is another approach which loses // the compiletimeness between function signature and body border, // because even in a `constexpr` function the compile time argument // looses the compiletimeness nature and becomes a runtime one. 

The details about a constexpr function compile time border: https://www.boost.org/doc/libs/1_65_0/libs/hana/doc/html/index.html#tutorial-appendix-constexpr

For other usage details see the tests.

The entire project currently is experimental.

Comments

0

Some improvements were made:

#pragma once // jaf 2023-6-17 // Compile-time string concatenation // Usage: // constexpr auto demo_1 = constr::Str("123"); // constexpr auto demo_2 = constr::Str({ '4', '5', '6', 0 }); // When you define a string this way, you append an extra zero at the end // constexpr auto demo_sum = demo_1 + demo_2; namespace constr { // Creating sequences of integers // Converting an integer to a sequence of integers, such as 10 to 0,1,2,3,4,5,6,7,8,9 // The usage is roughly as follows: // gen_seq<10>::type; // Implement the C++14 make_integer_sequence function for use with the C++11 standard template<class T> using Invoke = typename T::type; template<unsigned...> struct seq { using type = seq; }; template<class S1, class S2> struct concat; template<unsigned... I1, unsigned... I2> struct concat<seq<I1...>, seq<I2...>> : seq<I1..., (sizeof...(I1) + I2)...> {}; template<class S1, class S2> using Concat = Invoke<concat<S1, S2>>; template<unsigned N> struct gen_seq; template<unsigned N> using GenSeq = Invoke<gen_seq<N>>; template<unsigned N> struct gen_seq : Concat<GenSeq<N / 2>, GenSeq<N - N / 2>> {}; template<> struct gen_seq<0> : seq<> {}; template<> struct gen_seq<1> : seq<0> {}; } namespace constr { // Compile-time string template<int Size> struct String { char str[Size + 1]; // int size = Size; // The length of the string data, excluding the final 0 character // Define the character type.Let the types Char<0>, Char<1>, Char<2>, and Char<3> be defined as char template<char> using Char = char; // Constructor 1, in order to initialize str with the form "abc", a sequence of integers is used template <int... Index> constexpr String(const char* str, seq<Index...>) :str{ str[Index]..., 0 } {} // Constructor 2, which constructs the object by passing in a constant string constexpr String(const char* str) : String(str, gen_seq<Size>()) {} // Constructor 3, which concatenates strings by passing multiple characters template <int... Index> constexpr String(seq<Index...>, Char<Index>... str) : str{ str..., 0 } {} // Constructor 4, Copy constructor template <int... Index> constexpr String(const String<Size>& other) : str{ other.str[Index]..., 0 } {} // Constructor 5, initialized with a single character constexpr String(char c) : str{ c , 0} {} // Constructor 6, which initializes the empty compile time string constexpr String() : str{ 0 } {} }; // Auxiliary string addition struct Assist { template <int... AIndex,int... BIndex> constexpr static String<sizeof...(AIndex) + sizeof...(BIndex)> Add(const String<sizeof...(AIndex)>& str1, const String<sizeof...(BIndex)>& str2, seq<AIndex...>, seq<BIndex...>) { // Call constr::String constructor 3 to do the addition return String<sizeof...(AIndex) + sizeof...(BIndex)>(gen_seq<sizeof...(AIndex) + sizeof...(BIndex)>(), str1.str[AIndex]..., str2.str[BIndex]...); } template <int... Index> constexpr static String<sizeof...(Index) + 1> Add(const String<sizeof...(Index)>& str1, char c, seq<Index...>) { // Call constr::String constructor 3 to add a character return String<sizeof...(Index) + 1>(gen_seq<sizeof...(Index) + 1>(), str1.str[Index]..., c); } template <int... Index> constexpr static String<sizeof...(Index) + 1> Add(char c, const String<sizeof...(Index)>& str1, seq<Index...>) { // Call constr::String constructor 3 to add a character return String<sizeof...(Index) + 1>(gen_seq<sizeof...(Index) + 1>(), c, str1.str[Index]...); } }; // String plus string template<int ASize, int BSize> constexpr String<ASize + BSize> operator+(const String<ASize>& str1, const String<BSize>& str2) { return Assist::Add(str1, str2, gen_seq<ASize>(), gen_seq<BSize>()); } // Followed by a character template<int Size> constexpr String<Size + 1> operator+(const String<Size>& str, char c) { return Assist::Add(str, c, gen_seq<Size>()); } // Prefix it with a string template<int Size> constexpr String<Size + 1> operator+(char c, const String<Size>& str) { return Assist::Add(c, str, gen_seq<Size>()); } // Followed by a string ,example: constr::str("123") + "abc"; template<int Size1, int I> constexpr String<Size1 + I - 1> operator+(const String<Size1>& str1, const char(&str2)[I]) { return str1 + String<I - 1>(str2); } // Prefix it with a string ,example: "abc" + constr::str("123"); template<int Size1, int I> constexpr String<Size1 + I - 1> operator+(const char(&str1)[I], const String<Size1>& str2) { return String<I - 1>(str1) + str2; } template<int I> constexpr String<I - 1> Str(const char(&a)[I]) { return String<I - 1>(a); } constexpr String<0> Str() { return String<0>(); } constexpr String<1> Str(char c) { return String<1>(c); } } 

Test:

int main(int argc, char** argv) { constexpr auto demo_1 = constr::Str("123"); constexpr auto demo_2 = constr::Str({ '4', '5', '6', '\0'}); constexpr auto demo_3 = demo_1 + demo_2; constexpr auto demo_4 = demo_1 + 'a'; constexpr auto demo_5 = 'a' + demo_1; constexpr auto demo_6 = demo_1 + "abc"; constexpr auto demo_7 = "abc" + demo_1 ; constexpr auto demo_8 = constr::Str('a'); constexpr auto demo_9 = constr::Str(); std::cout << "demo_3.size = " << demo_3.size << std::endl; std::cout << "demo_3.str = " << demo_3.str << std::endl; std::cout << "demo_4.str = " << demo_4.str << std::endl; std::cout << "demo_5.str = " << demo_5.str << std::endl; std::cout << "demo_6.str = " << demo_6.str << std::endl; std::cout << "demo_7.str = " << demo_7.str << std::endl; std::cout << "demo_8.str = " << demo_8.str << std::endl; std::cout << "demo_9.str = " << demo_9.str << std::endl; return 0; } 

Test result:

demo_3.size = 6 demo_3.str = 123456 demo_4.str = 123a demo_5.str = a123 demo_6.str = 123abc demo_7.str = abc123 demo_8.str = a demo_9.str = 

3 Comments

Please use only english
My English is not good, so it is difficult to express meaning in English annotation .Removing the annotation does not affect the result.
English is not my native language, but code should be written only in English even comments. Please remember that code is usually shared not only on SO, but with other developers in same company who do not use your specific language.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.