在 C++ 中方便地声明编译时字符串

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/15858141/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-27 19:50:08  来源:igfitidea点击:

Conveniently Declaring Compile-Time Strings in C++

c++stringc++11metaprogramminguser-defined-literals

提问by void-pointer

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.

能够在 C++ 编译时创建和操作字符串有几个有用的应用。尽管可以在 C++ 中创建编译时字符串,但该过程非常繁琐,因为字符串需要声明为可变字符序列,例如

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 constexprconstructor. A messier version of approach 1 is possible to implement, taking advantage of the fact that you can do the following:

哪里decltype(str2)会有constexpr构造函数。可以实现方法 1 的更混乱版本,利用您可以执行以下操作的事实:

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:

但是,数组需要有外部链接,因此要使方法 1 起作用,我们必须编写如下内容:

/* 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:

不用说,这非常不方便。方法2实际上是不可能实现的。如果我们要声明一个 ( constexpr) 文字运算符,那么我们将如何指定返回类型?由于我们需要运算符返回一个可变字符序列,所以我们需要使用const char*参数来指定返回类型:

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

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

这会导致编译错误,因为s不是constexpr. 尝试通过执行以下操作来解决此问题并没有多大帮助。

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_swould work, abc_swould not. What if we ditch user-defined literals altogether, and just use a regular constexprfunction?

标准规定这种特定的文字运算符形式保留用于整数和浮点类型。虽然123_s会起作用,abc_s但不会。如果我们完全放弃用户定义的文字,而只使用常规constexpr函数会怎样?

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 constexprfunction, is itself no longer a constexprtype.

和以前一样,我们遇到了一个问题,数组现在是constexpr函数的参数,它本身不再是一种constexpr类型。

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

我相信应该可以定义一个 C 预处理器宏,它将字符串和字符串的大小作为参数,并返回由字符串中的字符组成的序列(使用BOOST_PP_FOR、字符串化、数组下标等)。但是,我没有时间(或足够的兴趣)来实现这样的宏 =)

采纳答案by Howard Hinnant

I haven't seen anything to match the elegance of Scott Schurr's str_constpresented at C++ Now 2012. It does require constexprthough.

我没有看到任何与Scott Schurrstr_constC++ Now 2012展示的优雅相匹配的东西。constexpr虽然它确实需要。

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.

使用和实现都没有宏。并且对字符串大小没有人为限制。我会在这里发布实现,但我尊重 Scott 的隐含版权。实现位于与上面链接的他的演示文稿的单张幻灯片上。

回答by user1115339

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

我相信应该可以定义一个 C 预处理器宏,它将字符串和字符串的大小作为参数,并返回由字符串中的字符组成的序列(使用 BOOST_PP_FOR、字符串化、数组下标等)。但是,我没有时间(或足够的兴趣)来实现这样的宏

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

可以在不依赖 boost 的情况下使用非常简单的宏和一些 C++11 特性来实现:

  1. lambdas variadic
  2. templates
  3. generalized constant expressions
  4. non-static data member initializers
  5. uniform initialization
  1. lambdas 可变参数
  2. 模板
  3. 广义常量表达式
  4. 非静态数据成员初始值设定项
  5. 统一初始化

(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..., '
    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{};    \
        }()
    
    '}; }; 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  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;
        };
    }
    
  1. 我们需要能够使用用户提供的从 0 到 N 的索引实例化一个可变参数模板——一个工具也很有用,例如将元组扩展为可变参数模板函数的参数(请参阅问题: 如何将元组扩展为可变参数模板函数的参数?
    “解包”一个元组以调用匹配的函数指针

    namespace  compile_time
    {
        template<char...  str>
        struct  string
        {
            static  constexpr  const char  chars[sizeof...(str)+1] = {str..., '
    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{};    \
        }()
    
    '}; }; template<char... str> constexpr const char string<str...>::chars[sizeof...(str)+1]; }
  2. 然后用非类型参数 char 定义一个名为 string 的可变参数模板:

        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;
        }
    
  3. 现在最有趣的部分 - 将字符文字传递到字符串模板中:

    #include <iostream>
    
    // helper function
    constexpr unsigned c_strlen( char const* str, unsigned count = 0 )
    {
        return ('
    #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!")>;
    
    ' == 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(); }

a simple concatenation demonstration shows the usage:

一个简单的串联演示显示了用法:

template <int N>
constexpr char at(char const(&s)[N], int i)
{
    return i >= N ? '
#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
' : s[i]; }

https://ideone.com/8Ft2xu

https://ideone.com/8Ft2xu

回答by dyp

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.

编辑:正如 Howard Hinnant(以及我在对 OP 的评论中所指出的)所指出的,您可能不需要将字符串的每个字符都作为单个模板参数的类型。如果您确实需要这个,下面有一个无宏的解决方案。

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.

它不使用宏,而是使用一些 C++11 特性。

#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...,'
#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");
'}; 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; }

回答by Yankes

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

如果您不想使用Boost 解决方案,您可以创建简单的宏来做类似的事情:

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

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

唯一的问题是 64 个字符的固定大小(加上额外的零)。但它可以根据您的需要轻松更改。

回答by Evgeny Panasyuk

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)

我相信应该可以定义一个 C 预处理器宏,它将字符串和字符串的大小作为参数,并返回由字符串中的字符组成的序列(使用 BOOST_PP_FOR、字符串化、数组下标等)

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

有文章:Abel Sinkovics 和 Dave Abrahams在 C++ 模板元程序中使用字符串

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

它对您使用宏 + BOOST_PP_REPEAT 的想法有所改进- 它不需要将显式大小传递给宏。简而言之,它基于字符串大小的固定上限和“字符串溢出保护”:

//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', '
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(); \
  }()
') -> 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', '
template <typename String>
void test(String) {
  // ... String = string_t<'H','e','l','l','o','
// 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 ))>
'> } test(CSTR("Hello"));
'); 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 '
#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;
}
' 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] == '
012A1370  mov         eax,2  
012A1375  ret  
' ? 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"; }

plus conditional boost::mpl::push_back.

加上有条件的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.

我改变了对 Yankes 解决方案的接受答案,因为它解决了这个特定的问题,并且在不使用 constexpr 或复杂的预处理器代码的情况下优雅地完成了。

If you accept trailing zeros, hand-written macro looping, 2xrepetion 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:

如果您接受尾随零、手写宏循环、扩展宏中字符串的2倍重复,并且没有 Boost - 那么我同意 - 它会更好。不过,使用 Boost 将只有三行:

LIVE DEMO

现场演示

##代码##

回答by Niceman

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

似乎没有人喜欢我的另一个答案:-<。所以在这里我展示了如何将 str_const 转换为真实类型:

##代码##

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

使用 clang++ -stdlib=libc++ -std=c++14 (clang 3.7) 编译

回答by kacey

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

这是为每个传递的编译时字符串创建 std::tuple<char...> 的简洁 C++14 解决方案。

##代码##

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

这是创建一个独特的编译时类型的一个,从另一个宏帖子中删减。

##代码##

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

用户定义的文字还不能用于这一点真的太糟糕了。

回答by átila Neves

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:

一位同事要求我在编译时连接内存中的字符串。它还包括在编译时实例化单个字符串。完整的代码清单在这里:

##代码##

回答by smilingthax

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

kacey 用于创建唯一编译时类型的解决方案,只需稍作修改,也可以与 C++11 一起使用:

##代码##

Use:

用:

##代码##

回答by florestan

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.

在玩 boost hana 地图时,我遇到了这个线程。由于没有一个答案解决了我的问题,我找到了一个不同的解决方案,我想在这里添加它,因为它可能对其他人有帮助。

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_STRINGmacro generates a lambda, which can't be used in constexprcontext. On the other hand, the map needs strings with different content to be different types.

我的问题是,当使用带有 hana 字符串的 boost hana 映射时,编译器仍然生成一些运行时代码(见下文)。原因显然是要在编译时查询地图,它必须是constexpr. 这是不可能的,因为BOOST_HANA_STRING宏会生成一个无法在constexpr上下文中使用的 lambda 。另一方面,地图需要不同内容的字符串为不同类型。

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.

由于该线程中的解决方案要么使用 lambda,要么不为不同的内容提供不同的类型,我发现以下方法很有帮助。它还避免了hackystr<'a', 'b', 'c'>语法。

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

基本思想是str_const在字符的散列上有一个 Scott Schurr 的模板版本。它是c++14,但c++11应该可以通过crc32函数的递归实现来实现(请参阅此处)。

##代码##

Usage:

用法:

##代码##

Resulting assembler code with clang-cl5.0 is:

使用clang-cl5.0生成的汇编代码是:

##代码##