Khai báo chuỗi thời gian biên dịch thuận tiện trong C ++


137

Có thể tạo và thao tác các chuỗi trong thời gian biên dịch trong C ++ có một số ứng dụng hữu ích. Mặc dù có thể tạo các chuỗi thời gian biên dịch trong C ++, nhưng quá trình này rất phức tạp, vì chuỗi cần phải được khai báo là một chuỗi ký tự biến đổi, vd

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

Các hoạt động như nối chuỗi, trích xuất chuỗi con và nhiều thao tác khác, có thể dễ dàng được thực hiện như các thao tác trên chuỗi ký tự. Có thể khai báo chuỗi thời gian biên dịch thuận tiện hơn không? Nếu không, có một đề xuất trong các tác phẩm sẽ cho phép khai báo chuỗi thời gian biên dịch thuận tiện không?

Tại sao phương pháp tiếp cận hiện tại thất bại

Lý tưởng nhất, chúng tôi muốn có thể khai báo các chuỗi thời gian biên dịch như sau:

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

hoặc, sử dụng nghĩa đen do người dùng định nghĩa,

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

nơi decltype(str2)sẽ có một nhà constexprxây dựng. Có thể thực hiện phiên bản lộn xộn hơn của cách tiếp cận 1, lợi dụng thực tế là bạn có thể làm như sau:

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

Tuy nhiên, mảng sẽ cần phải có liên kết bên ngoài, vì vậy để có cách tiếp cận 1 hoạt động, chúng ta sẽ phải viết một cái gì đó như thế này:

/* Implementation of array to sequence goes here. */

constexpr const char str[] = "Hello, world!";

int main()
{
    using s = string<13, str>;
    return 0;
}

Không cần phải nói, điều này rất bất tiện. Cách tiếp cận 2 thực sự không thể thực hiện được. Nếu chúng ta khai báo một constexprtoán tử ( ), thì chúng ta sẽ chỉ định kiểu trả về như thế nào? Vì chúng ta cần toán tử trả về một chuỗi các ký tự, nên chúng ta sẽ cần sử dụng const char*tham số để chỉ định kiểu trả về:

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

Điều này dẫn đến một lỗi biên dịch, bởi vì skhông phải là a constexpr. Cố gắng khắc phục điều này bằng cách làm như sau không giúp được gì nhiều.

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

Tiêu chuẩn cho rằng dạng toán tử nghĩa đen cụ thể này được dành riêng cho các kiểu số nguyên và dấu phẩy động. Trong khi 123_ssẽ làm việc, abc_ssẽ không. Điều gì sẽ xảy ra nếu chúng ta bỏ hoàn toàn nghĩa đen do người dùng định nghĩa và chỉ sử dụng một constexprchức năng thông thường ?

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

Như trước đây, chúng ta gặp vấn đề là mảng, bây giờ là một tham số cho constexprhàm, bản thân nó không còn là một constexprkiểu.

Tôi tin rằng có thể định nghĩa một macro tiền xử lý C lấy một chuỗi và kích thước của chuỗi làm đối số và trả về một chuỗi bao gồm các ký tự trong chuỗi (sử dụng BOOST_PP_FOR, xâu chuỗi , đăng ký mảng và tương tự). Tuy nhiên, tôi không có thời gian (hoặc đủ quan tâm) để triển khai macro như vậy =)


2
Boost có một macro xác định một chuỗi có thể được sử dụng như một biểu thức không đổi. Vâng, nó định nghĩa một lớp có thành viên chuỗi. Bạn đã kiểm tra xem?
Pubby


1
Stack Overflow không phải là nơi thích hợp để hỏi về việc liệu một đề xuất cho một cái gì đó có tồn tại hay không. Nơi tốt nhất cho việc này sẽ là trang web C ++ .
Nicol Bolas

1
Về cơ bản, bạn mở rộng các ký tự được lưu trữ trong mảng / ptr thành gói tham số (giống như Xeo đã làm). Mặc dù chúng không được phân chia thành các đối số khuôn mẫu không phải là kiểu, nhưng bạn có thể sử dụng chúng trong các constexprhàm và khởi tạo các mảng (do đó, concat, lớp nền, v.v.).
dyp

1
@MareInfinitus Tóm lại, các constexprchuỗi có thể được phân tích cú pháp trong thời gian biên dịch, do đó bạn có thể thực hiện các đường dẫn mã khác nhau tùy thuộc vào kết quả. Về cơ bản, bạn có thể tạo EDL trong C ++; các ứng dụng là khá vô hạn.
void-con trỏ

Câu trả lời:


127

Tôi chưa thấy bất cứ điều gì phù hợp với sự thanh lịch của Scott Schurrstr_const được trình bày tại C ++ Now 2012 . Nó không yêu cầu constexprmặc dù.

Đây là cách bạn có thể sử dụng nó và những gì nó có thể làm:

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!
}

Nó không nhận được mát hơn nhiều so với kiểm tra phạm vi thời gian biên dịch!

Cả việc sử dụng và triển khai đều không có macro. Và không có giới hạn nhân tạo về kích thước chuỗi. Tôi sẽ đăng việc thực hiện ở đây, nhưng tôi tôn trọng bản quyền ngầm của Scott. Việc thực hiện là trên một slide trình bày của mình được liên kết ở trên.


3
Các hoạt động có thể tạo các chuỗi constexpr mới (như nối chuỗi và trích xuất chuỗi con) có thể hoạt động với phương pháp này không? Có lẽ sử dụng hai lớp chuỗi constexpr (một dựa trên str_constvà lớp kia dựa trên sequence), điều này có thể có thể. Người dùng sẽ sử dụng str_constđể khởi tạo chuỗi, nhưng các hoạt động tiếp theo tạo chuỗi mới sẽ trả về sequencecác đối tượng.
void-con trỏ

5
Đây là một đoạn mã tốt. Tuy nhiên, cách tiếp cận này vẫn có một lỗ hổng so với một chuỗi được khai báo với một chuỗi ký tự là các tham số mẫu: str_const là một giá trị không đổi và không phải là một loại, do đó ngăn chặn việc sử dụng nhiều thành ngữ siêu lập trình.
Jean-Bernard Jansen

1
@JBJansen, có thể, không có hàm băm, để biên dịch một chuỗi thành một loại mà sau đó có thể được sử dụng làm tham số mẫu. Mỗi chuỗi khác nhau cho một loại khác nhau. Ý tưởng cơ bản là biến chuỗi thành một gói ký tự template<char... cs>. Về lý thuyết, bạn có thể xây dựng một cái gì đó có một chuỗi ký tự và biên dịch nội dung thành một hàm. Xem câu trả lời bằng dyp. Một thư viện trông rất đầy đủ là ẩn dụ . Về cơ bản, bạn có thể định nghĩa bất kỳ ánh xạ nào từ các chuỗi ký tự thành các loại và triển khai nó với loại công nghệ này.
Aaron McDaid

1
Tôi không chia sẻ sự nhiệt tình mà không hoạt động với các siêu giao diện mẫu - rất khó chịu vì sự thỏa hiệp ngớ ngẩn mà các hàm constexpr sẽ có thể gọi được trong thời gian chạy - không có sự kết hợp thực sự, đòi hỏi định nghĩa về một mảng char (xấu xí trong tiêu đề) - mặc dù điều này đúng với hầu hết các giải pháp macroless nhờ sự thỏa hiệp của constexpr đã nói ở trên - và việc kiểm tra phạm vi không gây ấn tượng cho tôi nhiều vì ngay cả constexpr const char * thấp cũng có điều đó. Tôi đã cuộn chuỗi gói tham số của riêng mình, cũng có thể được tạo từ một chữ (sử dụng một siêu dữ liệu) với chi phí của một định nghĩa mảng.
Arne Vogel

2
@ user975326: Tôi vừa xem lại việc thực hiện này và có vẻ như tôi đã thêm một constexpr operator==. Lấy làm tiếc. Bài thuyết trình của Scott sẽ giúp bạn bắt đầu về cách làm điều này. Nó dễ dàng hơn nhiều trong C ++ 14 so với C ++ 11. Tôi thậm chí sẽ không cố gắng thử trong C ++ 11. Xem constexprcác cuộc nói chuyện mới nhất của Scott tại đây: youtube.com/user/CppCon
Howard Hinnant

41

Tôi tin rằng có thể định nghĩa một macro tiền xử lý C lấy một chuỗi và kích thước của chuỗi làm đối số và trả về một chuỗi bao gồm các ký tự trong chuỗi (sử dụng BOOST_PP_FOR, xâu chuỗi, đăng ký mảng và tương tự). Tuy nhiên, tôi không có thời gian (hoặc đủ quan tâm) để thực hiện một macro như vậy

có thể thực hiện điều này mà không cần dựa vào boost, sử dụng macro rất đơn giản và một số tính năng của C ++ 11:

  1. lambdas matrixdic
  2. mẫu
  3. biểu thức hằng tổng quát
  4. khởi tạo thành viên dữ liệu không tĩnh
  5. khởi tạo thống nhất

(hai cái sau không được yêu cầu nghiêm ngặt ở đây)

  1. chúng ta cần có khả năng khởi tạo một mẫu matrixdic với các chỉ số do người dùng cung cấp từ 0 đến N - một công cụ cũng hữu ích để mở rộng tuple thành đối số của hàm mẫu matrixdic (xem câu hỏi: Làm cách nào để tôi mở rộng một tuple thành các đối số của hàm mẫu matrixdic?
    " giải nén "một tuple để gọi một con trỏ hàm phù hợp )

    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. sau đó xác định một mẫu matrixdic được gọi là chuỗi với tham số không phải kiểu 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. bây giờ là phần thú vị nhất - để chuyển các ký tự chữ thành mẫu chuỗi:

    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{};    \
        }()
    

một trình diễn ghép nối đơn giản cho thấy việc sử dụng:

    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


1
Điều này đơn giản đến mức tôi vẫn không thể tin rằng nó hoạt động. +1! Một điều: bạn không nên sử dụng size_t thay vì không dấu?
kirbyfan64sos

1
Và những gì về việc sử dụng operator+thay vì operator*? (str_hello + str_world)
Rémy Lebeau

Tôi thích giải pháp này hơn phương pháp str_const phổ biến của Scott Schurr, vì phương pháp này đảm bảo rằng dữ liệu cơ bản là constexpr. Phương thức của Schurr cho phép tôi tạo một str_const trong thời gian chạy với biến stack [char]; Tôi không thể trả lại str_const một cách an toàn từ một hàm hoặc chuyển nó sang một luồng khác.
Glenn

Liên kết đã chết ... bất cứ ai cũng có thể đăng lại? @Glenn?
einpoklum

Bạn nên thêm một cặp niềng răng xung quanh lambda trong CSTRINGmacro của bạn . Nếu không, bạn không thể tạo một CSTRINGcuộc gọi đến một []nhà điều hành, vì gấp đôi [[được dành riêng cho các thuộc tính.
florestan

21

Chỉnh sửa: như Howard Hinnant (và tôi phần nào trong nhận xét của tôi với OP) đã chỉ ra, bạn có thể không cần một loại với mỗi ký tự của chuỗi dưới dạng một đối số mẫu duy nhất. Nếu bạn cần điều này, có một giải pháp không có macro dưới đây.

Có một mẹo tôi tìm thấy khi cố gắng làm việc với các chuỗi tại thời gian biên dịch. Nó yêu cầu giới thiệu một loại khác ngoài "chuỗi mẫu", nhưng trong các hàm, bạn có thể giới hạn phạm vi của loại này.

Nó không sử dụng macro mà là một số tính năng của C ++ 11.

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

1
Tôi vừa dành cuối tuần độc lập để phát triển một đoạn mã tương tự và tạo ra một hệ thống rất cơ bản để phân tích các chuỗi kiểu, ví dụ pair<int,pair<char,double>>. Tôi rất tự hào về bản thân mình và sau đó khám phá ra câu trả lời này, và metaparse thư viện ngày hôm nay! Tôi thực sự nên tìm kiếm SO kỹ hơn trước khi bắt đầu các dự án ngớ ngẩn như thế này :-) Tôi đoán rằng, về mặt lý thuyết, một trình biên dịch C ++ đầy đủ có thể được xây dựng từ loại công nghệ này. Điều điên rồ nhất đã được xây dựng với điều này là gì?
Aaron McDaid

Tôi không biết. Tôi chưa bao giờ thực sự sử dụng các kỹ thuật này trong một dự án thực tế, vì vậy tôi đã không theo đuổi phương pháp này. Mặc dù tôi nghĩ rằng tôi nhớ một biến thể nhỏ của thủ thuật kiểu cục bộ thuận tiện hơn một chút .. có thể là tĩnh cục bộ char[].
dyp

Bạn có nghĩa là my_str.print();thay vì str.print();?
mike

Có phiên bản C ++ 14 ngắn hơn một chút không?
mike

Thật xấu hổ khi bạn phải làm cho nhà cung cấp (ít nhất là trong C ++ 11) - Tôi thực sự muốn có thể sử dụng một chuỗi trong cùng một tuyên bố: /
Alec Teal

10

Nếu bạn không muốn sử dụng giải pháp Boost, bạn có thể tạo các macro đơn giản sẽ làm điều tương tự:

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

Vấn đề duy nhất là kích thước cố định 64 ký tự (cộng thêm số không). Nhưng nó có thể dễ dàng thay đổi tùy thuộc vào nhu cầu của bạn.


Tôi thích giải pháp này rất nhiều; nó rất đơn giản và làm công việc một cách thanh lịch. Có thể sửa đổi macro để không có gì được nối thêm sizeof(str) > i(thay vì nối thêm các 0,mã thông báo bổ sung )? Thật dễ dàng để xác định một siêu trimdữ liệu sẽ thực hiện việc này sau khi macro đã được gọi, nhưng thật tuyệt nếu bản thân macro có thể được sửa đổi.
void-con trỏ

Là không thể bởi vì trình phân tích cú pháp không hiểu sizeof(str). Có thể tự thêm kích thước chuỗi như thế MACRO_GET_STR(6, "Hello")nhưng điều này yêu cầu các macro Boost hoạt động vì viết nó theo cách thủ công yêu cầu mã nhiều hơn 100 lần (bạn cần thực hiện những điều đơn giản như 1+1).
Yankes

6

Tôi tin rằng có thể xác định macro tiền xử lý C lấy một chuỗi và kích thước của chuỗi làm đối số và trả về một chuỗi bao gồm các ký tự trong chuỗi (sử dụng BOOST_PP_FOR, xâu chuỗi, đăng ký mảng và tương tự)

Có bài viết: Sử dụng các chuỗi trong siêu dữ liệu mẫu C ++ của Abel Sinkovics và Dave Abrahams.

Nó có một số cải tiến so với ý tưởng của bạn về việc sử dụng macro + BOOST_PP_REPEAT - nó không yêu cầu chuyển kích thước rõ ràng sang macro. Nói tóm lại, nó dựa trên giới hạn trên cố định cho kích thước chuỗi và "bảo vệ chống tràn chuỗi":

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

cộng với tăng điều kiện :: mpl :: push_back .


Tôi đã thay đổi câu trả lời được chấp nhận của mình thành giải pháp của Yankes, vì nó giải quyết được vấn đề cụ thể này và thực hiện một cách tao nhã mà không cần sử dụng mã constexpr hoặc mã tiền xử lý phức tạp.

Nếu bạn chấp nhận các số 0 ở cuối, lặp macro viết tay, lặp lại 2 lần chuỗi trong macro mở rộng và không có Boost - thì tôi đồng ý - sẽ tốt hơn. Mặc dù, với Boost, nó sẽ chỉ là ba dòng:

BẢN THỬ TRỰC TIẾP

#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

Ban đầu tôi đã thay đổi giải pháp cho Yankes, vì anh ấy đã cung cấp ví dụ hoạt động đầu tiên ở đây. Tại thời điểm này, có rất nhiều ý tưởng cạnh tranh tốt. Đó là sai lầm của tôi khi chọn một câu trả lời sớm như vậy. Hiện tại tôi sẽ nhận xét câu hỏi này là chưa được trả lời, và chờ cho đến khi tôi có thời gian để thử các ý tưởng mà mọi người đã đăng ở đây. Có rất nhiều thông tin hữu ích trong các câu trả lời mà mọi người đã đưa ra ở đây ...
void-con trỏ

Tôi đồng ý - ví dụ, tôi thích ví dụ của Howard Hinnant.
Evgeny Panasyuk

5

Không ai có vẻ thích câu trả lời khác của tôi: - <. Vì vậy, ở đây tôi chỉ ra cách chuyển đổi str_const thành loại thực:

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

Biên dịch với clang ++ -stdlib = libc ++ -std = c ++ 14 (clang 3.7)


Hoạt động tốt, nhưng không phải cho msvc 2019, vì nó phàn nàn về str.size () không phải là constexpr. Có thể được sửa bằng cách thêm lần thứ 2 bằng cách sử dụng str.size () riêng biệt. Có lẽ điều đó đã kìm hãm một số upvote ;-)
Zacharias

4

Đây là một giải pháp C ++ 14 ngắn gọn để tạo std :: tuple <char ...> cho mỗi chuỗi thời gian biên dịch được truyền.

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

Và đây là một cách để tạo một kiểu thời gian biên dịch duy nhất, được cắt bớt từ bài macro khác.

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

Thật tệ khi những chữ do người dùng định nghĩa chưa thể được sử dụng cho việc này.


Trên thực tế, họ có thể sử dụng tiện ích mở rộng được hỗ trợ bởi GCC / Clang, nhưng tôi sẽ đợi trước khi điều này được thêm vào tiêu chuẩn trước khi đăng nó dưới dạng câu trả lời.
void-con trỏ

3

Một đồng nghiệp đã thách thức tôi nối các chuỗi trong bộ nhớ vào thời gian biên dịch. Nó bao gồm cả việc khởi tạo các chuỗi riêng lẻ tại thời gian biên dịch. Danh sách mã đầy đủ ở đây:

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

Bạn có chắc chắn nó được thực hiện tại thời gian biên dịch? Đã có một cuộc thảo luận về điều này một thời gian trước đây, và với tôi, kết quả không rõ ràng.
dyp

Chạy objdump -t a.out |grep mykhông tìm thấy gì. Khi tôi bắt đầu nhập mã này, tôi tiếp tục thử nghiệm loại bỏ constexprkhỏi các chức năng và objdumphiển thị chúng khi constexprbị bỏ qua. Tôi tự tin 99,9% nó xảy ra vào thời gian biên dịch.
Átila Neves

1
Nếu bạn nhìn vào quá trình tháo gỡ ( -S), bạn sẽ nhận thấy gcc (4.7.2) thực sự giải quyết các constexprchức năng tại thời gian biên dịch. Tuy nhiên, các chuỗi không được lắp ráp tại thời gian biên dịch. Thay vào đó, (nếu tôi diễn giải chính xác) cho mỗi char của các chuỗi "được lắp ráp" đó, có một movbhoạt động riêng , được cho là tối ưu hóa mà bạn đang tìm kiếm.
dyp

2
Đúng. Tôi đã thử lại với gcc 4.9 và nó vẫn làm điều tương tự. Tôi luôn nghĩ rằng đây là trình biên dịch ngu ngốc. Chỉ mới hôm qua tôi đã nghĩ sẽ thử một trình biên dịch khác. Với tiếng lách cách, các Mov bytewise không có ở đó. Với gcc, -O cũng loại bỏ chúng, nhưng -O3 cũng làm điều tương tự.
Átila Neves

2

dựa trên ý tưởng từ Howard Hinnant, bạn có thể tạo lớp nghĩa đen sẽ thêm hai chữ với nhau.

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" 

từ đâu str_atđến
mic_e

một cái gì đó của nó như thế:str_at<int I>(const char* a) { return a[i]; }
Yankes

2

Cách tiếp cận số 1 của bạn là đúng.

Tuy nhiên, mảng sẽ cần phải có liên kết bên ngoài, vì vậy để tiếp cận 1 hoạt động, chúng ta sẽ phải viết một cái gì đó như thế này: constexpr const char str [] = "Xin chào, thế giới!";

Không, không đúng. Điều này biên dịch với clang và gcc. Tôi hy vọng tiêu chuẩn c ++ 11 của nó, nhưng tôi không phải là một ngôn ngữ.

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

Những gì tôi thực sự yêu thích cho c ++ 17 sẽ là những điều sau đây tương đương (để hoàn thành phương pháp số 1)

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

Một cái gì đó rất giống nhau đã tồn tại trong tiêu chuẩn cho các chữ được định nghĩa bởi người dùng templated, vì con trỏ void cũng đề cập, nhưng chỉ cho các chữ số. Cho đến lúc đó một mẹo nhỏ khác là sử dụng chế độ chỉnh sửa ghi đè + sao chép và dán

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

Nếu bạn không quan tâm đến macro, thì điều này sẽ hoạt động (được sửa đổi một chút từ câu trả lời của Yankes):

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

2

Giải pháp của kacey để tạo một loại thời gian biên dịch độc đáo có thể, với các sửa đổi nhỏ, cũng được sử dụng với 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(); \
  }()

Sử dụng:

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

test(CSTR("Hello"));

2

Trong khi chơi với bản đồ boost hana, tôi đã bắt gặp chủ đề này. Vì không có câu trả lời nào giải quyết được vấn đề của tôi, tôi đã tìm thấy một giải pháp khác mà tôi muốn thêm vào đây vì nó có thể hữu ích cho những người khác.

Vấn đề của tôi là khi sử dụng bản đồ boost hana với chuỗi hana, trình biên dịch vẫn tạo ra một số mã thời gian chạy (xem bên dưới). Lý do rõ ràng là để truy vấn bản đồ vào thời gian biên dịch constexpr. Điều này là không thể vì BOOST_HANA_STRINGmacro tạo ra lambda, không thể sử dụng trong constexprngữ cảnh. Mặt khác, bản đồ cần các chuỗi có nội dung khác nhau để trở thành các loại khác nhau.

Vì các giải pháp trong chủ đề này là sử dụng lambda hoặc không cung cấp các loại khác nhau cho các nội dung khác nhau, tôi thấy cách tiếp cận sau đây hữu ích. Ngoài ra, nó tránh được str<'a', 'b', 'c'>cú pháp hacky .

Ý tưởng cơ bản là có một phiên bản của Scott Schurr được đặt str_consttrên băm của các nhân vật. Đó là c++14, nhưng c++11nên có thể với việc thực hiện đệ quy của crc32hàm (xem tại đây ).

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

Sử dụng:

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

Kết quả mã trình biên dịch với clang-cl5.0 là:

012A1370  mov         eax,2  
012A1375  ret  

0

Tôi muốn thêm hai cải tiến rất nhỏ vào câu trả lời của @ user1115339. Tôi đã đề cập đến chúng trong các bình luận cho câu trả lời, nhưng để thuận tiện, tôi sẽ đặt một giải pháp dán sao chép ở đây.

Sự khác biệt duy nhất là FIXED_CSTRINGmacro, cho phép sử dụng các chuỗi trong các mẫu lớp và làm đối số cho toán tử chỉ mục (hữu ích nếu bạn có ví dụ như một bản đồ tổng hợp).

Ví dụ sống .

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

0

Việc triển khai của riêng tôi dựa trên cách tiếp cận từ Boost.Hanachuỗi (lớp mẫu có các ký tự dao động), nhưng chỉ sử dụng C++11tiêu chuẩn và constexprhàm với kiểm tra nghiêm ngặt về tính đồng bộ (sẽ là lỗi thời gian biên dịch nếu không phải là biểu thức thời gian biên dịch). Có thể được xây dựng từ chuỗi C thô thông thường thay vì ưa thích {'a', 'b', 'c' }(thông qua macro).

Triển khai: https://sourceforge.net/p/tacklelib/tacklelib/HEAD/tree/trunk/include/tacklelib/tackle/tmpl_opes.hpp

Các thử nghiệm: https://sourceforge.net/p/tacklelib/tacklelib/HEAD/tree/trunk/src/tests/unit/test_tmpl_opes.cpp

Ví dụ sử dụng:

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.

Các chi tiết về constexprbiên giới thời gian của hàm: https://www.boost.org/doc/libs/1_65_0/libs/hana/doc/html/index.html#tutorial-appcill-constexpr

Đối với các chi tiết sử dụng khác xem các bài kiểm tra.

Toàn bộ dự án hiện đang thử nghiệm.


0

Trong C ++ 17 với chức năng macro trợ giúp, thật dễ dàng để tạo các chuỗi thời gian biên dịch:

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

Và đây là một ví dụ sử dụng:

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


static_assert(n == m);
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.