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 constexpr
konstruktor. 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 constexpr
operator ( ) 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 s
bukan 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_s
akan bekerja, abc_s
tidak akan. Bagaimana jika kita membuang semua literal yang didefinisikan pengguna, dan hanya menggunakan constexpr
fungsi 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 constexpr
fungsi, itu sendiri bukan lagi sebuah constexpr
tipe.
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 =)
sumber
constexpr
fungsi dan menginisialisasi array (oleh karena itu, concat, substr dll).constexpr
string 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.Jawaban:
Saya belum melihat apa pun yang cocok dengan keanggunan Scott Schurr yang
str_const
disajikan di C ++ Now 2012 . Itu memang membutuhkanconstexpr
.Inilah cara Anda dapat menggunakannya, dan apa yang dapat dilakukan:
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.
sumber
str_const
dan yang lainnya berdasarkansequence
), ini dimungkinkan. Pengguna akan menggunakanstr_const
untuk menginisialisasi string, tetapi operasi selanjutnya yang membuat string baru akan mengembalikansequence
objek.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.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. Lihatconstexpr
pembicaraan terakhir Scott di sini: youtube.com/user/CppConadalah mungkin untuk mengimplementasikan ini tanpa mengandalkan boost, menggunakan makro yang sangat sederhana dan beberapa fitur C ++ 11:
(dua yang terakhir tidak sepenuhnya diperlukan di sini)
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 )
lalu tentukan templat variadic yang disebut string dengan parameter parameter non-type:
sekarang bagian yang paling menarik - untuk meneruskan literal karakter ke dalam templat string:
demonstrasi rangkaian sederhana menunjukkan penggunaan:
https://ideone.com/8Ft2xu
sumber
operator+
bukanoperator*
?(str_hello + str_world)
CSTRING
makro Anda . Kalau tidak, Anda tidak dapat membuatCSTRING
panggilan di dalam ke[]
operator, karena dobel[[
dicadangkan untuk atribut.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.
sumber
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?char[]
.my_str.print();
alih-alihstr.print();
?Jika Anda tidak ingin menggunakan solusi Boost, Anda dapat membuat makro sederhana yang akan melakukan hal serupa:
Satu-satunya masalah adalah ukuran tetap 64 karakter (ditambah nol tambahan). Tetapi dapat dengan mudah diubah tergantung pada kebutuhan Anda.
sumber
sizeof(str) > i
(alih-alih menambahkan0,
token tambahan )? Sangat mudah untuk mendefinisikantrim
metafungsi yang akan melakukan ini setelah makro dipanggil, tetapi alangkah baiknya jika makro itu sendiri dapat dimodifikasi.sizeof(str)
. Mungkin untuk menambahkan ukuran string secara manual sepertiMACRO_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 seperti1+1
).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":
plus boost bersyarat :: mpl :: push_back .
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
sumber
Sepertinya tidak ada yang menyukai jawaban saya yang lain: - <. Jadi di sini saya menunjukkan bagaimana mengkonversi str_const ke tipe nyata:
Kompilasi dengan clang ++ -stdlib = libc ++ -std = c ++ 14 (clang 3.7)
sumber
Inilah solusi ringkas C ++ 14 untuk membuat std :: tuple <char ...> untuk setiap string waktu kompilasi yang dilewati.
Dan ini satu untuk membuat tipe waktu kompilasi yang unik, dipangkas dari posting makro lainnya.
Sangat buruk bahwa literal yang ditentukan pengguna belum dapat digunakan untuk ini.
sumber
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:
sumber
objdump -t a.out |grep my
tidak menemukan apa pun. Ketika saya mulai mengetik kode ini, saya terus bereksperimen dengan menghapusconstexpr
dari fungsi danobjdump
menunjukkannya ketikaconstexpr
dihilangkan. Saya 99,9% yakin itu terjadi pada waktu kompilasi.-S
), Anda akan melihat bahwa gcc (4.7.2) memang menyelesaikanconstexpr
fungsi pada waktu kompilasi. Namun, string tidak dirakit pada saat kompilasi. Alih-alih, (jika saya menafsirkannya dengan benar) untuk setiap karakter dari string "rakitan" itu, adamovb
operasi sendiri , yang bisa dibilang optimasi yang Anda cari.berdasarkan ide dari Howard Hinnant Anda dapat membuat kelas literal yang akan menambahkan dua literal secara bersamaan.
sumber
str_at
datangnyastr_at<int I>(const char* a) { return a[i]; }
Pendekatan Anda # 1 adalah yang benar.
Tidak, tidak benar. Ini mengkompilasi dengan dentang dan gcc. Saya berharap standarnya c ++ 11, tetapi saya bukan laywer bahasa.
Apa yang benar-benar saya sukai untuk c ++ 17 akan menjadi berikut ini untuk menjadi setara (untuk menyelesaikan pendekatan # 1)
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
Jika Anda tidak keberatan dengan makro, daripada ini berfungsi (sedikit dimodifikasi dari jawaban Yankes):
sumber
solusi kacey untuk membuat tipe waktu kompilasi yang unik, dengan sedikit modifikasi, juga dapat digunakan dengan C ++ 11:
Menggunakan:
sumber
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 karenaBOOST_HANA_STRING
makro 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 peretasan
str<'a', 'b', 'c'>
sintaks .Ide dasarnya adalah memiliki versi Scott Schurr's
str_const
templated pada hash karakter. Memangc++14
, tetapic++11
harus dimungkinkan dengan implementasicrc32
fungsi yang berulang (lihat di sini ).Pemakaian:
Kode assembler yang dihasilkan dengan
clang-cl
5.0 adalah:sumber
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 .
sumber
Implementasi saya sendiri didasarkan pada pendekatan dari
Boost.Hana
string (kelas template dengan karakter variadic), tetapi hanya menggunakanC++11
standar danconstexpr
fungsi 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:
Detail tentang
constexpr
batas waktu kompilasi fungsi: https://www.boost.org/doc/libs/1_65_0/libs/hana/doc/html/index.html#tutorial-appendix-constexprUntuk detail penggunaan lainnya, lihat tes.
Seluruh proyek saat ini adalah percobaan.
sumber
Di C ++ 17 dengan fungsi makro pembantu, mudah untuk membuat kompilasi string waktu:
Dan ini adalah contoh penggunaan:
sumber