Mudah Menyatakan String Kompilasi-Waktu di C ++

137

Mampu membuat dan memanipulasi string selama waktu kompilasi di C ++ memiliki beberapa aplikasi yang berguna. Meskipun dimungkinkan untuk membuat string waktu kompilasi dalam C ++, prosesnya sangat rumit, karena string harus dideklarasikan sebagai urutan karakter yang bervariasi, misalnya

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

Operasi seperti penggabungan string, ekstraksi substring, dan banyak lainnya, dapat dengan mudah diimplementasikan sebagai operasi pada urutan karakter. Apakah mungkin untuk mendeklarasikan string kompilasi waktu dengan lebih mudah? Jika tidak, apakah ada proposal dalam karya yang memungkinkan deklarasi kompilasi waktu-waktu yang sesuai?

Mengapa Pendekatan yang Ada Gagal

Idealnya, kami ingin dapat mendeklarasikan string waktu kompilasi sebagai berikut:

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

atau, menggunakan literal yang ditentukan pengguna,

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

di mana decltype(str2)akan memiliki constexprkonstruktor. Versi messier dari pendekatan 1 dimungkinkan untuk diterapkan, dengan memanfaatkan fakta bahwa Anda dapat melakukan hal berikut:

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

Namun, array perlu memiliki hubungan eksternal, jadi untuk mendapatkan pendekatan 1 agar berfungsi, kita harus menulis sesuatu seperti ini:

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

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

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

Tak perlu dikatakan, ini sangat merepotkan. Pendekatan 2 sebenarnya tidak mungkin diterapkan. Jika kita mendeklarasikan constexproperator ( ) literal, lalu bagaimana kita menentukan tipe pengembalian? Karena kita membutuhkan operator untuk mengembalikan urutan karakter variadik, maka kita perlu menggunakan const char*parameter untuk menentukan tipe pengembalian:

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

Ini menghasilkan kesalahan kompilasi, karena sbukan a constexpr. Berusaha mengatasi ini dengan melakukan hal berikut tidak banyak membantu.

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

Standar menentukan bahwa bentuk operator literal khusus ini disediakan untuk tipe integer dan floating-point. Sementara 123_sakan bekerja, abc_stidak akan. Bagaimana jika kita membuang semua literal yang didefinisikan pengguna, dan hanya menggunakan constexprfungsi biasa ?

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

Seperti sebelumnya, kita mengalami masalah bahwa array, sekarang menjadi parameter constexprfungsi, itu sendiri bukan lagi sebuah constexprtipe.

Saya percaya itu harus mungkin untuk mendefinisikan makro preprocessor C yang mengambil string dan ukuran string sebagai argumen, dan mengembalikan urutan yang terdiri dari karakter dalam string (menggunakan BOOST_PP_FOR, stringifikasi, array subscript, dan sejenisnya). Namun, saya tidak punya waktu (atau cukup minat) untuk mengimplementasikan makro =)

batal-pointer
sumber
2
Boost memiliki makro yang mendefinisikan string yang dapat digunakan sebagai ekspresi konstan. Yah, itu mendefinisikan kelas yang memiliki anggota string. Apakah Anda memeriksanya?
Pubby
6
Apakah Anda memeriksa cpp-next.com/archive/2012/10/… ?
Evgeny Panasyuk
1
Stack Overflow bukan tempat yang tepat untuk bertanya tentang apakah ada proposal untuk sesuatu. Tempat terbaik untuk ini adalah situs C ++ .
Nicol Bolas
1
Pada dasarnya, Anda memperluas karakter yang disimpan dalam array / ptr ke dalam paket parameter (seperti yang dilakukan Xeo). Meskipun mereka tidak terpecah menjadi argumen templat non-tipe, Anda dapat menggunakannya dalam constexprfungsi dan menginisialisasi array (oleh karena itu, concat, substr dll).
dyp
1
@MareInfinitus Singkatnya, constexprstring dapat diuraikan selama waktu kompilasi, sehingga Anda dapat mengambil jalur kode yang berbeda tergantung pada hasilnya. Pada dasarnya, Anda dapat membuat EDL dalam C ++; aplikasi ini tidak terbatas.
void-pointer

Jawaban:

127

Saya belum melihat apa pun yang cocok dengan keanggunan Scott Schurr yangstr_const disajikan di C ++ Now 2012 . Itu memang membutuhkan constexpr.

Inilah cara Anda dapat menggunakannya, dan apa yang dapat dilakukan:

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

Itu tidak mendapatkan jauh lebih dingin daripada memeriksa rentang waktu kompilasi!

Baik penggunaan, dan implementasinya, bebas dari makro. Dan tidak ada batasan buatan pada ukuran string. Saya akan memposting implementasinya di sini, tapi saya menghormati hak cipta implisit Scott. Implementasinya ada pada satu slide presentasi yang terhubung ke atas.

Howard Hinnant
sumber
3
Dapatkah operasi yang membuat string constexpr baru (seperti penggabungan string dan ekstraksi substring) bekerja dengan pendekatan ini? Mungkin menggunakan dua kelas constexpr-string (satu berdasarkan str_constdan yang lainnya berdasarkan sequence), ini dimungkinkan. Pengguna akan menggunakan str_constuntuk menginisialisasi string, tetapi operasi selanjutnya yang membuat string baru akan mengembalikan sequenceobjek.
void-pointer
5
Ini adalah kode yang bagus. Namun, pendekatan ini masih memiliki kelemahan dibandingkan dengan string yang dinyatakan dengan urutan karakter sebagai parameter templat: str_const adalah nilai konstan, dan bukan tipe, sehingga mencegah penggunaan banyak idiom metaprogramming.
Jean-Bernard Jansen
1
@JBJansen, adalah mungkin, tanpa fungsi hash, untuk mengkompilasi string ke tipe yang kemudian dapat digunakan sebagai parameter template. Setiap string berbeda memberikan tipe yang berbeda. Ide dasarnya adalah mengubah string menjadi paket karakter template<char... cs>. Secara teori, Anda bisa membangun sesuatu yang mengambil string literal dan mengkompilasi konten ke suatu fungsi. Lihat jawabannya dengan dyp. Perpustakaan yang tampak sangat lengkap adalah metaparse . Pada dasarnya, Anda dapat menentukan pemetaan apa pun dari string literal ke tipe, dan menerapkannya dengan teknologi semacam ini.
Aaron McDaid
1
Saya tidak berbagi antusiasme ... tidak bekerja dengan metafunctions template - sangat menjengkelkan karena kompromi konyol bahwa fungsi constexpr dapat dipanggil pada saat runtime - tidak ada rangkaian yang benar, memerlukan definisi array char (jelek di header) - meskipun ini benar untuk sebagian besar solusi macroless berkat kompromi constexpr yang disebutkan di atas - dan pemeriksaan jangkauan tidak membuat saya terkesan banyak karena bahkan constexpr const char * yang rendah pun memilikinya. Saya menggulung string paket parameter saya sendiri, yang juga dapat dibuat dari literal (menggunakan metafungsi) dengan mengorbankan definisi array.
Arne Vogel
2
@ user975326: Saya baru mengulas implementasi ini dan sepertinya saya menambahkan a constexpr operator==. Maaf. Presentasi Scott harus membantu Anda memulai cara melakukan ini. Jauh lebih mudah di C ++ 14 daripada di C ++ 11. Saya bahkan tidak akan repot mencoba di C ++ 11. Lihat constexprpembicaraan terakhir Scott di sini: youtube.com/user/CppCon
Howard Hinnant
41

Saya percaya seharusnya bisa mendefinisikan makro preprocessor C yang mengambil string dan ukuran string sebagai argumen, dan mengembalikan urutan yang terdiri dari karakter dalam string (menggunakan BOOST_PP_FOR, stringifikasi, subscript array, dan sejenisnya). Namun, saya tidak punya waktu (atau cukup minat) untuk mengimplementasikan makro semacam itu

adalah mungkin untuk mengimplementasikan ini tanpa mengandalkan boost, menggunakan makro yang sangat sederhana dan beberapa fitur C ++ 11:

  1. lambdas variadic
  2. template
  3. ekspresi konstan umum
  4. inisialisasi anggota data non-statis
  5. inisialisasi seragam

(dua yang terakhir tidak sepenuhnya diperlukan di sini)

  1. kita harus dapat instantiate templat variadic dengan indeks yang disediakan pengguna dari 0 hingga N - sebuah alat yang juga berguna misalnya untuk memperluas tuple ke argumen fungsi templat variadic (lihat pertanyaan: Bagaimana saya memperluas tuple ke argumen fungsi templat variadic?
    " membongkar "tuple untuk memanggil pointer fungsi yang cocok )

    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. lalu tentukan templat variadic yang disebut string dengan parameter parameter non-type:

    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. sekarang bagian yang paling menarik - untuk meneruskan literal karakter ke dalam templat string:

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

demonstrasi rangkaian sederhana menunjukkan penggunaan:

    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

pengguna1115339
sumber
1
Ini sangat sederhana sehingga saya masih tidak percaya itu berhasil. +1! Satu hal: bukankah seharusnya Anda menggunakan size_t bukannya unsigned?
kirbyfan64sos
1
Dan bagaimana dengan menggunakan, operator+bukan operator*? (str_hello + str_world)
Remy Lebeau
Saya lebih suka solusi ini daripada metode str_const Scott Schurr yang populer, karena metode ini memastikan bahwa data yang mendasarinya adalah constexpr. Metode Schurr memungkinkan saya membuat str_const saat runtime dengan variabel stack char []; Saya tidak dapat dengan aman mengembalikan str_const dari suatu fungsi atau meneruskannya ke utas lainnya.
Glenn
Tautannya sudah mati ... adakah yang bisa memposting ulang? @ Glenn?
einpoklum
Anda harus menambahkan sepasang kawat gigi tambahan di sekitar lambda di CSTRINGmakro Anda . Kalau tidak, Anda tidak dapat membuat CSTRINGpanggilan di dalam ke []operator, karena dobel [[dicadangkan untuk atribut.
florestan
21

Sunting: seperti yang ditunjukkan Howard Hinnant (dan saya dalam komentar saya kepada OP), Anda mungkin tidak perlu jenis dengan setiap karakter string sebagai argumen templat tunggal. Jika Anda memang membutuhkan ini, ada solusi bebas makro di bawah ini.

Ada trik yang saya temukan saat mencoba bekerja dengan string pada waktu kompilasi. Ini membutuhkan untuk memperkenalkan tipe lain selain "string template", tetapi dalam fungsi, Anda dapat membatasi ruang lingkup jenis ini.

Itu tidak menggunakan makro melainkan beberapa fitur 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();
}
dyp
sumber
1
Saya baru saja menghabiskan akhir pekan secara mandiri mengembangkan potongan kode yang serupa, dan membuat sistem yang sangat dasar untuk mengurai string tipe, misalnya pair<int,pair<char,double>>. Saya bangga pada diri saya dan kemudian menemukan jawaban ini, dan perpustakaan metaparse hari ini! Saya benar-benar harus mencari SO lebih teliti sebelum memulai proyek konyol seperti ini :-) Saya kira, secara teori, kompiler C ++ sepenuhnya dapat dibangun dari teknologi semacam ini. Apa hal paling gila yang telah dibangun dengan ini?
Aaron McDaid
Saya tidak tahu Saya tidak pernah benar-benar menggunakan teknik ini dalam proyek dunia nyata, jadi saya tidak mengejar pendekatan itu. Meskipun saya pikir saya ingat sedikit variasi dari tipuan tipe lokal yang sedikit lebih nyaman .. mungkin statis lokal char[].
dyp
Apakah maksud Anda my_str.print();alih-alih str.print();?
mike
Apakah ada versi C ++ 14 yang sedikit lebih pendek?
mike
Sayang sekali Anda harus membuat provider (setidaknya dalam C ++ 11) - Saya benar-benar ingin dapat menggunakan string dalam pernyataan yang sama: /
Alec Teal
10

Jika Anda tidak ingin menggunakan solusi Boost, Anda dapat membuat makro sederhana yang akan melakukan hal serupa:

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

Satu-satunya masalah adalah ukuran tetap 64 karakter (ditambah nol tambahan). Tetapi dapat dengan mudah diubah tergantung pada kebutuhan Anda.

Yankes
sumber
Saya suka solusi ini; itu sangat sederhana dan melakukan pekerjaan dengan elegan. Apakah mungkin untuk memodifikasi makro sehingga tidak ada yang ditambahkan sizeof(str) > i(alih-alih menambahkan 0,token tambahan )? Sangat mudah untuk mendefinisikan trimmetafungsi yang akan melakukan ini setelah makro dipanggil, tetapi alangkah baiknya jika makro itu sendiri dapat dimodifikasi.
void-pointer
Tidak mungkin karena parser tidak mengerti sizeof(str). Mungkin untuk menambahkan ukuran string secara manual seperti MACRO_GET_STR(6, "Hello")tetapi ini membutuhkan makro Peningkatan untuk bekerja karena menulisnya secara manual memerlukan 100 kali lebih banyak kode (Anda perlu mengimplementasikan hal-hal sederhana seperti 1+1).
Yankes
6

Saya percaya seharusnya dapat mendefinisikan makro preprocessor C yang mengambil string dan ukuran string sebagai argumen, dan mengembalikan urutan yang terdiri dari karakter dalam string (menggunakan BOOST_PP_FOR, stringifikasi, array subscript, dan sejenisnya)

Ada artikel: Menggunakan string dalam C ++ template metaprograms oleh Abel Sinkovics dan Dave Abrahams.

Ini memiliki beberapa peningkatan atas ide Anda untuk menggunakan makro + BOOST_PP_REPEAT - tidak perlu meneruskan ukuran eksplisit ke makro. Singkatnya, ini didasarkan pada batas atas tetap untuk ukuran string dan "perlindungan overrun tali":

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

plus boost bersyarat :: mpl :: push_back .


Saya mengubah jawaban yang saya terima untuk solusi Yankes, karena solusi ini memecahkan masalah khusus ini, dan melakukannya dengan elegan tanpa menggunakan kode preprocessor constexpr atau kompleks.

Jika Anda menerima nol tambahan, pengulangan makro tulisan tangan, pengulangan string 2x di makro diperluas, dan tidak memiliki Peningkatan - maka saya setuju - itu lebih baik. Padahal, dengan Boost hanya ada tiga baris:

LIVE DEMO

#include <boost/preprocessor/repetition/repeat.hpp>
#define GET_STR_AUX(_, i, str) (sizeof(str) > (i) ? str[(i)] : 0),
#define GET_STR(str) BOOST_PP_REPEAT(64,GET_STR_AUX,str) 0
Evgeny Panasyuk
sumber
Saya awalnya mengubah solusi untuk Yankes, karena ia memberikan contoh kerja pertama di sini. Pada titik ini, ada banyak ide bersaing yang bagus. Itu adalah kesalahan saya dalam memilih jawaban sedini mungkin. Saat ini saya akan mengatakan pertanyaan ini sebagai tidak dijawab, dan tunggu sampai saya mendapatkan waktu untuk mencoba ide-ide yang telah diposting semua orang di sini. Ada banyak informasi berguna dalam jawaban yang diberikan orang-orang di sini ...
void-pointer
Saya setuju - misalnya, saya suka contoh Howard Hinnant.
Evgeny Panasyuk
5

Sepertinya tidak ada yang menyukai jawaban saya yang lain: - <. Jadi di sini saya menunjukkan bagaimana mengkonversi str_const ke tipe nyata:

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

Kompilasi dengan clang ++ -stdlib = libc ++ -std = c ++ 14 (clang 3.7)

Orang baik
sumber
Bekerja dengan baik, tetapi tidak untuk msvc 2019, karena mengeluh tentang str.size () tidak menjadi constexpr. Dapat diperbaiki dengan menambahkan 2 menggunakan str.size deducing terpisah (). Mungkin itu menahan beberapa upvotes ;-)
Zacharias
4

Inilah solusi ringkas C ++ 14 untuk membuat std :: tuple <char ...> untuk setiap string waktu kompilasi yang dilewati.

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

Dan ini satu untuk membuat tipe waktu kompilasi yang unik, dipangkas dari posting makro lainnya.

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

Sangat buruk bahwa literal yang ditentukan pengguna belum dapat digunakan untuk ini.

kacey
sumber
Sebenarnya, mereka dapat menggunakan ekstensi yang didukung oleh GCC / Dentang, tapi saya akan menunggu sebelum ini ditambahkan ke standar sebelum mempostingnya sebagai jawaban.
void-pointer
3

Seorang kolega menantang saya untuk menggabungkan string dalam memori pada waktu kompilasi. Ini termasuk instantiating string individu pada waktu kompilasi juga. Daftar kode lengkap ada di sini:

//Arrange strings contiguously in memory at compile-time from string literals.
//All free functions prefixed with "my" to faciliate grepping the symbol tree
//(none of them should show up).

#include <iostream>

using std::size_t;

//wrapper for const char* to "allocate" space for it at compile-time
template<size_t N>
struct String {
    //C arrays can only be initialised with a comma-delimited list
    //of values in curly braces. Good thing the compiler expands
    //parameter packs into comma-delimited lists. Now we just have
    //to get a parameter pack of char into the constructor.
    template<typename... Args>
    constexpr String(Args... args):_str{ args... } { }
    const char _str[N];
};

//takes variadic number of chars, creates String object from it.
//i.e. myMakeStringFromChars('f', 'o', 'o', '\0') -> String<4>::_str = "foo"
template<typename... Args>
constexpr auto myMakeStringFromChars(Args... args) -> String<sizeof...(Args)> {
    return String<sizeof...(args)>(args...);
}

//This struct is here just because the iteration is going up instead of
//down. The solution was to mix traditional template metaprogramming
//with constexpr to be able to terminate the recursion since the template
//parameter N is needed in order to return the right-sized String<N>.
//This class exists only to dispatch on the recursion being finished or not.
//The default below continues recursion.
template<bool TERMINATE>
struct RecurseOrStop {
    template<size_t N, size_t I, typename... Args>
    static constexpr String<N> recurseOrStop(const char* str, Args... args);
};

//Specialisation to terminate recursion when all characters have been
//stripped from the string and converted to a variadic template parameter pack.
template<>
struct RecurseOrStop<true> {
    template<size_t N, size_t I, typename... Args>
    static constexpr String<N> recurseOrStop(const char* str, Args... args);
};

//Actual function to recurse over the string and turn it into a variadic
//parameter list of characters.
//Named differently to avoid infinite recursion.
template<size_t N, size_t I = 0, typename... Args>
constexpr String<N> myRecurseOrStop(const char* str, Args... args) {
    //template needed after :: since the compiler needs to distinguish
    //between recurseOrStop being a function template with 2 paramaters
    //or an enum being compared to N (recurseOrStop < N)
    return RecurseOrStop<I == N>::template recurseOrStop<N, I>(str, args...);
}

//implementation of the declaration above
//add a character to the end of the parameter pack and recurse to next character.
template<bool TERMINATE>
template<size_t N, size_t I, typename... Args>
constexpr String<N> RecurseOrStop<TERMINATE>::recurseOrStop(const char* str,
                                                            Args... args) {
    return myRecurseOrStop<N, I + 1>(str, args..., str[I]);
}

//implementation of the declaration above
//terminate recursion and construct string from full list of characters.
template<size_t N, size_t I, typename... Args>
constexpr String<N> RecurseOrStop<true>::recurseOrStop(const char* str,
                                                       Args... args) {
    return myMakeStringFromChars(args...);
}

//takes a compile-time static string literal and returns String<N> from it
//this happens by transforming the string literal into a variadic paramater
//pack of char.
//i.e. myMakeString("foo") -> calls myMakeStringFromChars('f', 'o', 'o', '\0');
template<size_t N>
constexpr String<N> myMakeString(const char (&str)[N]) {
    return myRecurseOrStop<N>(str);
}

//Simple tuple implementation. The only reason std::tuple isn't being used
//is because its only constexpr constructor is the default constructor.
//We need a constexpr constructor to be able to do compile-time shenanigans,
//and it's easier to roll our own tuple than to edit the standard library code.

//use MyTupleLeaf to construct MyTuple and make sure the order in memory
//is the same as the order of the variadic parameter pack passed to MyTuple.
template<typename T>
struct MyTupleLeaf {
    constexpr MyTupleLeaf(T value):_value(value) { }
    T _value;
};

//Use MyTupleLeaf implementation to define MyTuple.
//Won't work if used with 2 String<> objects of the same size but this
//is just a toy implementation anyway. Multiple inheritance guarantees
//data in the same order in memory as the variadic parameters.
template<typename... Args>
struct MyTuple: public MyTupleLeaf<Args>... {
    constexpr MyTuple(Args... args):MyTupleLeaf<Args>(args)... { }
};

//Helper function akin to std::make_tuple. Needed since functions can deduce
//types from parameter values, but classes can't.
template<typename... Args>
constexpr MyTuple<Args...> myMakeTuple(Args... args) {
    return MyTuple<Args...>(args...);
}

//Takes a variadic list of string literals and returns a tuple of String<> objects.
//These will be contiguous in memory. Trailing '\0' adds 1 to the size of each string.
//i.e. ("foo", "foobar") -> (const char (&arg1)[4], const char (&arg2)[7]) params ->
//                       ->  MyTuple<String<4>, String<7>> return value
template<size_t... Sizes>
constexpr auto myMakeStrings(const char (&...args)[Sizes]) -> MyTuple<String<Sizes>...> {
    //expands into myMakeTuple(myMakeString(arg1), myMakeString(arg2), ...)
    return myMakeTuple(myMakeString(args)...);
}

//Prints tuple of strings
template<typename T> //just to avoid typing the tuple type of the strings param
void printStrings(const T& strings) {
    //No std::get or any other helpers for MyTuple, so intead just cast it to
    //const char* to explore its layout in memory. We could add iterators to
    //myTuple and do "for(auto data: strings)" for ease of use, but the whole
    //point of this exercise is the memory layout and nothing makes that clearer
    //than the ugly cast below.
    const char* const chars = reinterpret_cast<const char*>(&strings);
    std::cout << "Printing strings of total size " << sizeof(strings);
    std::cout << " bytes:\n";
    std::cout << "-------------------------------\n";

    for(size_t i = 0; i < sizeof(strings); ++i) {
        chars[i] == '\0' ? std::cout << "\n" : std::cout << chars[i];
    }

    std::cout << "-------------------------------\n";
    std::cout << "\n\n";
}

int main() {
    {
        constexpr auto strings = myMakeStrings("foo", "foobar",
                                               "strings at compile time");
        printStrings(strings);
    }

    {
        constexpr auto strings = myMakeStrings("Some more strings",
                                               "just to show Jeff to not try",
                                               "to challenge C++11 again :P",
                                               "with more",
                                               "to show this is variadic");
        printStrings(strings);
    }

    std::cout << "Running 'objdump -t |grep my' should show that none of the\n";
    std::cout << "functions defined in this file (except printStrings()) are in\n";
    std::cout << "the executable. All computations are done by the compiler at\n";
    std::cout << "compile-time. printStrings() executes at run-time.\n";
}
Átila Neves
sumber
Anda yakin itu dilakukan pada waktu kompilasi? Sudah ada diskusi tentang ini beberapa waktu lalu, dan bagi saya, hasilnya tidak jelas.
dyp
Berlari objdump -t a.out |grep mytidak menemukan apa pun. Ketika saya mulai mengetik kode ini, saya terus bereksperimen dengan menghapus constexprdari fungsi dan objdumpmenunjukkannya ketika constexprdihilangkan. Saya 99,9% yakin itu terjadi pada waktu kompilasi.
Átila Neves
1
Jika Anda melihat pembongkaran ( -S), Anda akan melihat bahwa gcc (4.7.2) memang menyelesaikan constexprfungsi pada waktu kompilasi. Namun, string tidak dirakit pada saat kompilasi. Alih-alih, (jika saya menafsirkannya dengan benar) untuk setiap karakter dari string "rakitan" itu, ada movboperasi sendiri , yang bisa dibilang optimasi yang Anda cari.
dyp
2
Itu benar. Saya mencoba lagi dengan gcc 4.9 dan masih melakukan hal yang sama. Saya selalu berpikir ini adalah kompiler yang bodoh. Hanya kemarin saya berpikir untuk mencoba kompiler yang berbeda. Dengan dentang, gerakan selanjutnya tidak ada sama sekali. Dengan gcc, -Os menyingkirkan mereka juga, tetapi -O3 melakukan hal yang sama.
ilatila Neves
2

berdasarkan ide dari Howard Hinnant Anda dapat membuat kelas literal yang akan menambahkan dua literal secara bersamaan.

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" 
Yankes
sumber
dari mana str_atdatangnya
mic_e
sesuatu seperti itu:str_at<int I>(const char* a) { return a[i]; }
Yankes
2

Pendekatan Anda # 1 adalah yang benar.

Namun, array perlu memiliki hubungan eksternal, jadi untuk mendapatkan pendekatan 1 agar berfungsi, kita harus menulis sesuatu seperti ini: constexpr const char str [] = "Halo, dunia!";

Tidak, tidak benar. Ini mengkompilasi dengan dentang dan gcc. Saya berharap standarnya c ++ 11, tetapi saya bukan laywer bahasa.

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

Apa yang benar-benar saya sukai untuk c ++ 17 akan menjadi berikut ini untuk menjadi setara (untuk menyelesaikan pendekatan # 1)

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

Sesuatu yang sangat mirip sudah ada dalam standar untuk templated literals yang ditentukan pengguna, seperti void-pointer juga menyebutkan, tetapi hanya untuk digit. Sampai saat itu trik kecil lainnya adalah menggunakan mode pengeditan override + copy dan paste

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

Jika Anda tidak keberatan dengan makro, daripada ini berfungsi (sedikit dimodifikasi dari jawaban 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!)>();
Orang baik
sumber
2

solusi kacey untuk membuat tipe waktu kompilasi yang unik, dengan sedikit modifikasi, juga dapat digunakan dengan 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(); \
  }()

Menggunakan:

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

test(CSTR("Hello"));
smilingthax
sumber
2

Saat bermain dengan dorongan peta hana, saya menemukan utas ini. Karena tidak ada jawaban yang menyelesaikan masalah saya, saya menemukan solusi berbeda yang ingin saya tambahkan di sini karena dapat berpotensi membantu orang lain.

Masalah saya adalah ketika menggunakan boost hana map dengan hana strings, kompiler masih menghasilkan beberapa kode runtime (lihat di bawah). Alasannya jelas bahwa untuk menanyakan peta pada waktu kompilasi, itu harus constexpr. Ini tidak mungkin karena BOOST_HANA_STRINGmakro menghasilkan lambda, yang tidak dapat digunakan diconstexpr konteks. Di sisi lain, peta membutuhkan string dengan konten yang berbeda untuk jenis yang berbeda.

Karena solusi di utas ini menggunakan lambda atau tidak menyediakan jenis yang berbeda untuk konten yang berbeda, saya menemukan pendekatan berikut bermanfaat. Juga menghindari peretasanstr<'a', 'b', 'c'> sintaks .

Ide dasarnya adalah memiliki versi Scott Schurr's str_consttemplated pada hash karakter. Memang c++14, tetapi c++11harus dimungkinkan dengan implementasi crc32fungsi yang berulang (lihat di sini ).

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

Pemakaian:

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

Kode assembler yang dihasilkan dengan clang-cl5.0 adalah:

012A1370  mov         eax,2  
012A1375  ret  
penghijauan
sumber
0

Saya ingin menambahkan dua peningkatan yang sangat kecil ke jawaban @ user1115339. Saya menyebutkannya di komentar untuk jawabannya, tetapi untuk kenyamanan saya akan meletakkan solusi copy paste di sini.

Satu-satunya perbedaan adalah FIXED_CSTRING makro, yang memungkinkan untuk menggunakan string dalam templat kelas dan sebagai argumen untuk operator indeks (berguna jika Anda memiliki misalnya peta kompilasi).

Contoh langsung .

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

Implementasi saya sendiri didasarkan pada pendekatan dari Boost.Hanastring (kelas template dengan karakter variadic), tetapi hanya menggunakan C++11standar dan constexprfungsi dengan pemeriksaan ketat pada compiletimeness (akan menjadi kesalahan waktu kompilasi jika bukan ekspresi waktu kompilasi). Dapat dibuat dari string C mentah biasa alih-alih mewah {'a', 'b', 'c' }(melalui makro).

Implementasi: https://sourceforge.net/p/tacklelib/tacklelib/HEAD/tree/trunk/include/tacklelib/tackle/tmpl_string.hpp

Pengujian: https://sourceforge.net/p/tacklelib/tacklelib/HEAD/tree/trunk/src/tests/unit/test_tmpl_string.cpp

Contoh penggunaan:

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.

Detail tentang constexprbatas waktu kompilasi fungsi: https://www.boost.org/doc/libs/1_65_0/libs/hana/doc/html/index.html#tutorial-appendix-constexpr

Untuk detail penggunaan lainnya, lihat tes.

Seluruh proyek saat ini adalah percobaan.

Andry
sumber
0

Di C ++ 17 dengan fungsi makro pembantu, mudah untuk membuat kompilasi string waktu:

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

Dan ini adalah contoh penggunaan:

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


static_assert(n == m);
zurrutik
sumber