Conveniently Declaring Compile-Time Strings in C++

2019-01-01 12:20发布

问题:

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

回答1:

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.



回答2:

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



回答3:

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);
}

// helper \"function\" struct
template < char t_c, char... tt_c >
struct rec_print
{
    static void print()
    {
        std::cout << t_c;
        rec_print < tt_c... > :: print ();
    }
};
    template < char t_c >
    struct rec_print < t_c >
    {
        static void print() { std::cout << t_c; }
    };


// destination \"template string\" type
template < char... tt_c >
struct exploded_string
{
    static void print()
    {
        rec_print < tt_c... > :: print();
    }
};

// struct to explode a `char const*` to an `exploded_string` type
template < typename T_StrProvider, unsigned t_len, char... tt_c >
struct explode_impl
{
    using result =
        typename explode_impl < T_StrProvider, t_len-1,
                                T_StrProvider::str()[t_len-1],
                                tt_c... > :: result;
};

    template < typename T_StrProvider, char... tt_c >
    struct explode_impl < T_StrProvider, 0, tt_c... >
    {
         using result = exploded_string < tt_c... >;
    };

// syntactical sugar
template < typename T_StrProvider >
using explode =
    typename explode_impl < T_StrProvider,
                            c_strlen(T_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();
}


回答4:

If you dont want use Boost solution you can create simple macro that will do similar thing:

#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!\")>;

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



回答5:

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


回答6:

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\";
}


回答7:

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.



回答8:

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\" 


回答9:

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)



回答10:

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  


回答11:

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!)>();


回答12:

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\"));


回答13:

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\")];
}