163

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 =)

Alexis Wilke
  • 19,179
  • 10
  • 84
  • 156
void-pointer
  • 14,247
  • 11
  • 43
  • 61
  • 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? – Pubby Apr 07 '13 at 02:17
  • External linkage is not a requirement anymore for non-type template arguments. In fact, `str` has internal linkage in your example. (Minor point though, you still can't use string literals.) – Luc Danton Apr 07 '13 at 02:20
  • 6
    Did you check http://cpp-next.com/archive/2012/10/using-strings-in-c-template-metaprograms/ ? – Evgeny Panasyuk Apr 07 '13 at 02: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](http://www.isocpp.org). – Nicol Bolas Apr 07 '13 at 03:42
  • @EvgenyPanasyuk Ah, so implementing this using the preprocessor is easier than I thought. Do you want to post that as the answer? Abrahams' solution seems elegant to me. – void-pointer Apr 07 '13 at 04:57
  • @NicolBolas What I actually meant to ask is a little different from what the title implied. I have now edited the title and the body to reflect my question more accurately. If no method exists in C++11 to conveniently initialize compile-time strings, then yes, I was wondering if there is a proposal in the works. However, Evgeny Panasyuk pointed out an article of David Abrahams that gives an elegant solution. Therefore, I think that this question can still be useful to other people who are looking for information about compile-time strings. – void-pointer Apr 07 '13 at 05:03
  • Do you really need single characters as non-type template arguments? You might want to consider using constexpr arrays, see e.g. [this SO question, Xeo's (and my) answer](http://stackoverflow.com/questions/13292237/c-concat-two-const-char-string-literals/13293223) – dyp Apr 07 '13 at 14:29
  • @DyP I think I need the characters to be non-type template arguments. Correct me if I'm wrong, but once two `std::array`s have been returned from the `constexpr` function in Xeo's answer, they can no longer be concatenated during compile time. I would like the ability to manipulate strings during compile-time (e.g. concatenation, substring extraction, etc.), after they have been initialized. – void-pointer Apr 08 '13 at 07:33
  • That is what the SO question I mentioned is all about: Concatenating strings at compile-time. Also see [this library](http://constexprstr.svn.sourceforge.net/viewvc/constexprstr/main.cpp?revision=9&view=markup) for compile-time string manipulation; I think I've even seen a more enhanced library (can't find it atm). – dyp Apr 08 '13 at 11:15
  • 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). – dyp Apr 08 '13 at 11:27
  • @DyP Interesting, so the array member in the `constexpr` string class can be used in subsequent `constexpr` statements. I'll play around with this and see how the two implementations compare. Using arrays instead of variadic sequences of `char` may make things easier. – void-pointer Apr 08 '13 at 12:27
  • Where do you need this? What is the difference between this one and const char* / string for your use case? – Mare Infinitus Apr 13 '13 at 16:37
  • 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. – void-pointer Apr 14 '13 at 11:36

20 Answers20

137

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!
}
Howard Hinnant
  • 206,506
  • 52
  • 449
  • 577
  • 1
    Also see [this blog](http://akrzemi1.wordpress.com/2011/05/11/parsing-strings-at-compile-time-part-i/). And I know I've seen a library already (with append, substr etc.) – dyp Apr 07 '13 at 15:10
  • Your `StrWrap` and Scott's `str_const` are nearly identical. – Howard Hinnant Apr 07 '13 at 15:17
  • 4
    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. – void-pointer Apr 07 '13 at 23:59
  • 7
    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. – Jean-Bernard Jansen May 31 '14 at 18:42
  • The use of constexpr hash function can enable some of them, with possible collisions. Note that implicit type conversions works on constexpr, thus a str_const can be implictily converted to a int (usable as non-type template parameter) if you add a constexpr conversion operator. – Jean-Bernard Jansen May 31 '14 at 18:47
  • @void-pointer "It doesn't get much cooler than compile-time range checking!".. What about compile-time string concatenation and hashing? Check out [my demo of a literal string-list class](http://www.daniweb.com/software-development/cpp/code/482276/c11-compile-time-string-concatenation-with-constexpr) (an extension of `str_const`). Constexpr can be used to create almost anything you want, as long as you understand the rules. – Mikael Persson Jul 24 '14 at 18:47
  • @MikaelPersson Thanks for the link, I've been looking for a library like that. I know that [Sprout](https://github.com/bolero-MURAKAMI/Sprout) also implements similar `constexpr` operations for strings., but I haven't had much of a chance to use either of them yet. – void-pointer Jul 25 '14 at 10:00
  • 1
    @JBJansen, it is possible, without hash functions, to compile a string to a type that can then be used as a template parameter. Each different string gives a different type. The basic idea is to turn the string into a character pack `template`. In theory, you could build something that takes a literal string and compiles the contents to a function. See the answer by dyp. A very complete-looking library is [metaparse](http://abel.web.elte.hu/mpllibs/metaparse/). Essentially, you can define any mapping from literal strings to types, and implement it with this kind of technology. – Aaron McDaid Jul 13 '15 at 18:31
  • 1
    @AaronMcDaid Yes you can, but if you only use standard code (even C++14), the syntax is awful. Things will change once the standard implements N3599 and N4198. You can find some experiments with N3599 (partially implemented as a GNU extension in GCC and Clang) in https://github.com/duckie/named_tuple/tree/tagged_types_migration (soon to be merged to master, check the named_tuple_tests.cc file, the README is out of date) – Jean-Bernard Jansen Jul 15 '15 at 14:27
  • 2
    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. – Arne Vogel Aug 05 '15 at 09:36
  • @ArneVogel: Sounds interesting. Link? – Howard Hinnant Aug 05 '15 at 14:09
  • The github link doesn't work. It seems it'll have to be non-raw now... (removing `?raw=true` allows to at least read the PDF). – Ruslan Aug 08 '15 at 15:31
  • 2
    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 Nov 05 '15 at 11:02
  • 2
    @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: https://www.youtube.com/user/CppCon – Howard Hinnant Nov 05 '15 at 15:26
  • 1
    @HowardHinnant Note that «Scott»'s `conststr` comes from here: https://en.cppreference.com/w/cpp/language/constexpr He says so in his PDF (page 72). He made some changes, but it's still very close. There is no author name on the C++ Reference page, though. – Alexis Wilke May 09 '19 at 02:02
  • 1
    I'm not familiar with CTRE. But C++17's `string_view` is the first thing I would reach for today. – Howard Hinnant Nov 16 '21 at 20:59
  • Hi @HowardHinnant, could you share your implementation of `str_const`? It seems `constexpr str_const world(my_string, 7, 5); static_assert(world == "world", "");` doesn't compile. Since ctor and operator are not overloaded in Scott's's example. I can't figure out how to separate the const char* in compile time like `constexpr str_const world(my_string, 7, 5);`. – Louis Go Dec 13 '21 at 14:09
  • Updated to C++17 and `string_view`. – Howard Hinnant Dec 13 '21 at 17:48
  • Just because its been years, doesn't mean this wasn't good code. It just means its now the standard. – Paul Bruner Mar 02 '22 at 20:19
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

Community
  • 1
  • 1
user1115339
  • 509
  • 3
  • 4
  • 2
    This is so simple that I still can't believe it works. +1! One thing: shouldn't you use size_t instead of unsigned? – kirbyfan64sos Dec 10 '14 at 21:27
  • 2
    And what about using `operator+` instead of `operator*`? `(str_hello + str_world)` – Remy Lebeau Apr 16 '15 at 02:02
  • 1
    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. – Glenn Mar 26 '16 at 01:00
  • The link is dead... can anyone repost it? @Glenn ? – einpoklum Mar 26 '16 at 13:05
  • 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. – florestan Nov 10 '18 at 21:58
  • Also, if you intend to use the `CSTRING` macro within a class template, you need to add a `typename` between `return` and `variadic_toolbox...` and a `template` before `produce`. See my 2nd answer below for a copy-paste-ready version containing the fixes from both of my comments. – florestan Nov 14 '18 at 11:36
28

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();
}
 
dyp
  • 38,334
  • 13
  • 112
  • 177
  • 1
    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>`. 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? – Aaron McDaid Jul 13 '15 at 18:25
  • 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[]`. – dyp Jul 13 '15 at 18:52
  • Do you mean `my_str.print();` instead of `str.print();`? – mike Apr 26 '16 at 04:52
  • Is there a C++ 14 slightly shorter version? – mike Apr 26 '16 at 10:58
  • It's a shame you have to make the provider (at least in C++11) - I'd really like to be able to use a string in the same statement :/ – Alec Teal Apr 23 '18 at 15:34
  • 3
    Instead of the recursive printer, I think an easier option is to do `char str[] = {ttc...}; std::cout << str << std::endl;` – Ajay Brahmakshatriya Oct 17 '20 at 05:28
  • @AjayBrahmakshatriya True. I don't think I knew expansion tricks back in '13. I'm updating to `cout.write` though, to deal with null characters. – dyp Oct 19 '20 at 08:31
12

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.

Alexis Wilke
  • 19,179
  • 10
  • 84
  • 156
Yankes
  • 1,958
  • 19
  • 20
  • 1
    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. – void-pointer Apr 07 '13 at 09:23
  • 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`). – Yankes Apr 07 '13 at 10:28
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
Evgeny Panasyuk
  • 9,076
  • 1
  • 33
  • 54
  • 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... – void-pointer Apr 08 '13 at 09:53
  • I agree - for instance, I like Howard Hinnant example. – Evgeny Panasyuk Apr 08 '13 at 13:02
6

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.

kacey
  • 133
  • 1
  • 4
  • 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. – void-pointer Sep 20 '15 at 08:46
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";
}
Átila Neves
  • 1,351
  • 11
  • 14
  • You sure it is done at compile time? There's been [a discussion about this](http://stackoverflow.com/questions/13292237/c-concat-two-const-char-string-literals/13293223) some time ago, and to me, the result is not clear. – dyp Apr 09 '13 at 17:23
  • 1
    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. – Átila Neves Apr 10 '13 at 07:11
  • 1
    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. – dyp Apr 10 '13 at 12:45
  • 2
    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. – Átila Neves May 26 '14 at 12:50
5

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)

Niceman
  • 463
  • 4
  • 8
  • 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 ;-) – Zacharias Mar 06 '20 at 13:48
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!)>();
Niceman
  • 463
  • 4
  • 8
2

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"));
smilingthax
  • 5,254
  • 1
  • 23
  • 19
2

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  
florestan
  • 4,405
  • 2
  • 14
  • 28
2

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);
zurrutik
  • 21
  • 2
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" 
Community
  • 1
  • 1
Yankes
  • 1,958
  • 19
  • 20
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")];
}
florestan
  • 4,405
  • 2
  • 14
  • 28
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/blob/master/include/tacklelib/tackle/tmpl_string.hpp

Tests: https://github.com/andry81/tacklelib/blob/master/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.

Andry
  • 2,273
  • 29
  • 28
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>{})
QuarticCat
  • 1,314
  • 6
  • 20
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

Steven Sun
  • 141
  • 6
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"))>>);
}
欢乐的Xiaox
  • 479
  • 4
  • 6
0

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.

Boann
  • 48,794
  • 16
  • 117
  • 146
0

Some improvements were made:

#pragma once
// 姜安富 2023-6-17
// 编译期字符串拼接
// 使用方式:
// constexpr auto demo_1 = constr::Str("123");
// constexpr auto demo_2 = constr::Str({ '4', '5', '6', 0 }); // 这种方式定义字符串时,最后要多附加一个0
// constexpr auto demo_sum = demo_1 + demo_2;

namespace constr 
{
// 创建整数序列
// 将整数转换为整数序列,如将 10 转换为 0,1,2,3,4,5,6,7,8,9 
// 使用方式大致为:
// gen_seq<10>::type;
// 实现C++14的make_integer_sequence功能为了在C++11标准下使用

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 
{

// 编译期字符串
template<int Size>
struct String
{
    char str[Size + 1]; // 常字符串的数据
    int size = Size; // 字符串数据长度,不包括最后的0字符

    // 定义字符类型,让 Char<0>、Char<1>、Char<2>、Char<3>这样的类型都定义为char类型
    template<char> 
    using Char = char;

    // 构造函数1,为了使用 "abc"的形式来初始化str,需要使用整数序列
    template <int... Index>
    constexpr String(const char* str, seq<Index...>) :str{ str[Index]..., 0 }
    {}
    // 构造函数2,通过传入常字符串来构造对象,使用C++11提供的委托构造函数语法调用构造函数1
    constexpr String(const char* str) : String(str, gen_seq<Size>())
    {}

    // 构造函数3,通过传入多个字符来拼接字符串
    template <int... Index>
    constexpr String(seq<Index...>, Char<Index>... str) : str{ str..., 0 }
    {}

    // 构造函数4,拷贝构造函数
    template <int... Index>
    constexpr String(const String<Size>& other) : str{ other.str[Index]..., 0 }
    {}

    // 构造函数5,用一个单字符初始化
    constexpr String(char c) : str{ c , 0}
    {}

    // 构造函数6,初始化空编译期字符串
    constexpr String() : str{ 0 }
    {}

};

// 辅助字符串相加
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...>)
    {
        // 调用constr::String的构造函数3来实现相加
        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...>)
    {
        // 调用constr::String的构造函数3来实现加一个字符
        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...>)
    {
        // 调用constr::String的构造函数3来实现加一个字符
        return String<sizeof...(Index) + 1>(gen_seq<sizeof...(Index) + 1>(), c, str1.str[Index]...);
    }
};

// 字符串加字符串
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>());
}

// 后加一个字符
template<int Size>
constexpr String<Size + 1> operator+(const String<Size>& str, char c)
{
    return Assist::Add(str, c, gen_seq<Size>());
}

// 前加一个字符
template<int Size>
constexpr String<Size + 1> operator+(char c, const String<Size>& str)
{
    return Assist::Add(c, str, gen_seq<Size>());
}

// 后加一个字符串 例: 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);
}

// 前加一个字符串 例: "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 =
jaf
  • 1
  • 1