
Jika Anda memprogram dalam C ++, Anda mungkin bertanya-tanya mengapa Anda tidak dapat membandingkan dua string literal atau melakukan penggabungannya:
auto str = "hello" + "world";
Namun, seperti yang mereka katakan, "itu tidak mungkin, tetapi jika Anda benar-benar ingin, maka Anda bisa." Kami akan mematahkan stereotip di bawah potongan, dan tepat di tahap kompilasi.
Kenapa semua ini dibutuhkan
Di salah satu proyek yang sedang saya kerjakan, sudah biasa menggunakan std :: string sebagai konstanta string. Proyek ini memiliki beberapa modul yang mendefinisikan konstanta string global:
Saya pikir Anda sudah menebak apa yang terjadi suatu hari. SAMPLE_PLUGIN_PATH
mengambil nilai "sample.so"
, terlepas dari kenyataan bahwa PLUGIN_PATH
memiliki nilai "/usr/local/lib/project/plugins/"
, seperti yang diharapkan. Bagaimana ini bisa terjadi? Semuanya sangat sederhana, urutan inisialisasi objek global tidak ditentukan, pada saat inisialisasi SAMPLE_PLUGIN_PATH
variabel PLUGIN_PATH
kosong.
Selain itu, pendekatan ini memiliki sejumlah kelemahan. Pertama, pengecualian yang dilemparkan saat membuat objek global tidak tertangkap. Kedua, inisialisasi terjadi selama eksekusi program, yang menghabiskan waktu prosesor yang berharga.
Saat itulah saya memiliki ide untuk bekerja dengan string pada tahap kompilasi, yang akhirnya mengarah pada penulisan artikel ini.
Pada artikel ini, kami akan mempertimbangkan jalur yang dapat dioperasikan pada tahap kompilasi. Kami akan menyebut saluran tersebut statis.
Semua operasi yang diterapkan dimasukkan dalam perpustakaan untuk bekerja dengan string statis. Kode sumber perpustakaan tersedia di github, tautannya ada di akhir artikel.
Setidaknya C ++ 14 diperlukan untuk menggunakan perpustakaan.
Definisi String Statis
Kami mendefinisikan string statis sebagai array karakter, untuk kenyamanan kami menganggap bahwa string selalu berakhir dengan karakter nol:
template<size_t Size> using static_string = std::array<const char, Size>; constexpr static_string<6> hello = {'H', 'e', 'l', 'l', 'o', '\0'};
Di sini Anda dapat pergi dengan cara yang berbeda, dan mendefinisikan string sebagai tupel karakter. Pilihan ini bagi saya lebih memakan waktu dan kurang nyaman. Karena itu, tidak akan dipertimbangkan di sini.
Membuat string statis
Lihatlah definisi garis halo di atas, itu hanya mengerikan. Pertama, kita perlu terlebih dahulu menghitung panjang array. Kedua, Anda harus ingat untuk menulis karakter nol di bagian akhir. Ketiga, semua koma, tanda kurung, dan tanda kutip ini. Pasti ada sesuatu yang harus dilakukan tentang ini. Saya ingin menulis sesuatu seperti ini:
constexpr auto hello = make_static_string("hello");
Di sini salah satu bentuk templat variabel akan membantu kami, yang memungkinkan kami memperluas argumen templat sebagai indeks untuk inisialisasi agregat string statis kami dari string literal:
template<size_t Size, size_t ... Indexes> constexpr static_string<sizeof ... (Indexes) + 1> make_static_string(const char (& str)[Size]) { return {str[Indexes] ..., '\0'}; } constexpr auto hello = make_static_string<0, 1, 2, 3, 4>("hello");
Sudah lebih baik, tetapi indeks masih harus ditulis dengan tangan. Di sini kami juga mencatat bahwa jika Anda tidak menentukan semua indeks, Anda bisa mendapatkan substring dari string literal, dan jika Anda menuliskannya dalam urutan terbalik, maka kebalikannya:
constexpr hello1 = make_static_string<1, 2, 3>("hello");
Pertimbangan ini akan sangat bermanfaat bagi kita di masa depan.
Sekarang kita perlu entah bagaimana menghasilkan urutan indeks baris. Untuk melakukan ini, terapkan trik pewarisan. Tentukan struktur kosong (Anda perlu mewarisi sesuatu) dengan satu set indeks yang diperlukan sebagai parameter templat:
template<size_t ... Indexes> struct index_sequence {};
Kami mendefinisikan struktur generator yang akan menghasilkan indeks satu per satu, menyimpan penghitung di parameter pertama:
template<size_t Size, size_t ... Indexes> struct make_index_sequence : make_index_sequence<Size - 1, Size - 1, Indexes ...> {};
Kami juga akan menangani titik akhir rekursi, ketika semua indeks dihasilkan (penghitung adalah nol), kami membuang penghitung dan generator berubah menjadi urutan yang kami butuhkan:
template<size_t ... Indexes> struct make_index_sequence<0, Indexes ...> : index_sequence<Indexes ...> {};
Akibatnya, fungsi membuat string statis akan terlihat seperti ini:
template<size_t Size, size_t ... Indexes> constexpr static_string<sizeof ... (Indexes) + 1> make_static_string(const char (& str)[Size], index_sequence<Indexes ...>) { return {str[Indexes] ..., '\0'}; }
Kami akan menulis fungsi serupa untuk string statis, ini akan berguna bagi kami lebih lanjut:
template<size_t Size, size_t ... Indexes> constexpr static_string<sizeof ... (Indexes) + 1> make_static_string(const static_string<Size>& str, index_sequence<Indexes ...>) { return {str[Indexes] ..., '\0'}; }
Selanjutnya, untuk setiap fungsi yang mengambil string literal foo(const char (& str)[Size])
kita akan menulis fungsi serupa yang mengambil string foo(const static_string<Size>& str)
statis foo(const static_string<Size>& str)
. Tetapi untuk singkatnya, saya tidak akan menyebutkan ini.
Karena kita mengetahui panjang string literal, kita dapat secara otomatis menghasilkan urutan indeks, kita akan menulis pembungkus untuk fungsi di atas:
template<size_t Size> constexpr static_string<Size> make_static_string(const char (& str)[Size]) { return make_static_string(str, make_index_sequence<Size - 1>{}); }
Fungsi ini memungkinkan kita untuk melakukan apa yang kita inginkan di awal bab ini.
Jika tidak ada argumen, kami akan mengembalikan string statis kosong, yang hanya terdiri dari karakter nol:
constexpr static_string<1> make_static_string() { return {'\0'}; }
Kita juga perlu membuat string dari sejumlah karakter:
template<char ... Chars> constexpr static_string<sizeof ... (Chars) + 1> make_static_string(char_sequence<Chars ...>) { return {Chars ..., '\0'}; }
Ngomong-ngomong, segala sesuatu yang akan dijelaskan nanti dalam artikel ini didasarkan pada teknik yang dijelaskan dalam bab ini. Karena itu, jika sesuatu tetap tidak dapat dipahami, lebih baik membaca kembali bab ini.
Keluarkan string statis ke aliran
Semuanya sederhana di sini. Karena string kami diakhiri dengan karakter nol, itu cukup untuk menampilkan data array ke aliran:
template<size_t Size> std::ostream& operator<<(std::ostream& os, const static_string<Size>& str) { os << str.data(); return os; }
Ubah string statis menjadi std :: string
Tidak ada yang rumit di sini. Kami menginisialisasi string dengan data array:
template<size_t Size> std::string to_string(const static_string<Size>& str) { return std::string(str.data()); }
Perbandingan String Statis
Kami akan membandingkan karakter garis demi karakter sampai kami menemukan perbedaannya, atau sampai kami mencapai paling tidak satu dari garis tersebut. Karena constexpr untuk belum ditemukan, kami menggunakan rekursi dan operator ternary:
template<size_t Size1, size_t Size2> constexpr int static_string_compare( const static_string<Size1>& str1, const static_string<Size2>& str2, int index = 0) { return index >= Size1 && index >= Size2 ? 0 : index >= Size1 ? -1 : index >= Size2 ? 1 : str1[index] > str2[index] ? 1 : str1[index] < str2[index] ? -1 : static_string_compare(str1, str2, index + 1); }
Di masa mendatang, kami akan memerlukan versi komparator yang diperluas, kami akan memperkenalkan indeks individual untuk setiap baris mereka, dan kami juga akan membatasi jumlah karakter yang dibandingkan:
template<size_t Size1, size_t Size2> constexpr int static_string_compare( const static_string<Size1>& str1, size_t index1, const static_string<Size2>& str2, size_t index2, size_t cur_length, size_t max_length) { return cur_length > max_length || (index1 >= Size1 && index2 >= Size2) ? 0 : index1 >= Size1 ? -1 : index2 >= Size2 ? 1 : str1[index1] > str2[index2] ? 1 : str1[index1] < str2[index2] ? -1 : static_string_compare(str1, index1 + 1, str2, index2 + 1, cur_length + 1, max_length); }
Versi komparator ini akan memungkinkan kita untuk membandingkan tidak hanya seluruh string, tetapi juga masing-masing substring.
Rangkaian string statis
Untuk penggabungan, kami menggunakan templat variabel yang sama seperti pada bab tentang membuat string statis. Inisialisasi array terlebih dahulu dengan karakter dari baris pertama (tidak termasuk karakter null), kemudian yang kedua, dan akhirnya tambahkan karakter null ke akhir:
template<size_t Size1, size_t ... Indexes1, size_t Size2, size_t ... Indexes2> constexpr static_string<Size1 + Size2 - 1> static_string_concat_2( const static_string<Size1>& str1, index_sequence<Indexes1 ...>, const static_string<Size2>& str2, index_sequence<Indexes2 ...>) { return {str1[Indexes1] ..., str2[Indexes2] ..., '\0'}; } template<size_t Size1, size_t Size2> constexpr static_string<Size1 + Size2 - 1> static_string_concat_2( const static_string<Size1>& str1, const static_string<Size2>& str2) { return static_string_concat_2(str1, make_index_sequence<Size1 - 1>{}, str2, make_index_sequence<Size2 - 1>{}); }
Kami juga menerapkan templat variabel untuk menggabungkan jumlah string atau string string yang sewenang-wenang:
constexpr auto static_string_concat() { return make_static_string(); } template<typename Arg, typename ... Args> constexpr auto static_string_concat(Arg&& arg, Args&& ... args) { return static_string_concat_2(make_static_string(std::forward<Arg>(arg)), static_string_concat(std::forward<Args>(args) ...)); }
Operasi Pencarian String Statis
Pertimbangkan operasi pencarian karakter dan substring dalam string statis.
Cari karakter dalam string statis
Pencarian karakter tidak terlalu sulit, periksa karakter secara rekursif untuk semua indeks dan kembalikan indeks pertama jika ada kecocokan. Kami juga akan memberikan kesempatan untuk mengatur posisi awal pencarian dan nomor urut pertandingan:
template<size_t Size> constexpr size_t static_string_find(const static_string<Size>& str, char ch, size_t from, size_t nth) { return Size < 2 || from >= Size - 1 ? static_string_npos : str[from] != ch ? static_string_find(str, ch, from + 1, nth) : nth > 0 ? static_string_find(str, ch, from + 1, nth - 1) : from; }
The static_string_npos
konstan menunjukkan bahwa pencarian tidak berhasil. Kami mendefinisikannya sebagai berikut:
constexpr size_t static_string_npos = std::numeric_limits<size_t>::max();
Demikian pula, kami menerapkan pencarian ke arah yang berlawanan:
template<size_t Size> constexpr size_t static_string_rfind(const static_string<Size>& str, char ch, size_t from, size_t nth) { return Size < 2 || from > Size - 2 ? static_string_npos : str[from] != ch ? static_string_rfind(str, ch, from - 1, nth) : nth > 0 ? static_string_rfind(str, ch, from - 1, nth - 1) : from; }
Menentukan kemunculan karakter dalam string statis
Untuk menentukan kemunculan karakter, coba cari saja:
template<size_t Size> constexpr bool static_string_contains(const static_string<Size>& str, char ch) { return static_string_find(str, ch) != static_string_npos; }
Hitung jumlah kemunculan karakter dalam string statis
Menghitung jumlah kejadian diimplementasikan sepele:
template<size_t Size> constexpr size_t static_string_count(const static_string<Size>& str, char ch, size_t index) { return index >= Size - 1 ? 0 : (str[index] == ch ? 1 : 0) + static_string_count(str, ch, index + 1); }
Cari substring dalam string statis
Karena diasumsikan bahwa baris statis akan relatif kecil, kami tidak akan mengimplementasikan algoritma Knut-Morris-Pratt di sini, kami akan menerapkan algoritma kuadratik yang paling sederhana:
template<size_t Size, size_t SubSize> constexpr size_t static_string_find(const static_string<Size>& str, const static_string<SubSize>& substr, size_t from, size_t nth) { return Size < SubSize || from > Size - SubSize ? static_string_npos : static_string_compare(str, from, substr, 0, 1, SubSize - 1) != 0 ? static_string_find(str, substr, from + 1, nth) : nth > 0 ? static_string_find(str, substr, from + 1, nth - 1) : from; }
Demikian pula, kami menerapkan pencarian ke arah yang berlawanan:
template<size_t Size, size_t SubSize> constexpr size_t static_string_rfind(const static_string<Size>& str, const static_string<SubSize>& substr, size_t from, size_t nth) { return Size < SubSize || from > Size - SubSize ? static_string_npos : static_string_compare(str, from, substr, 0, 1, SubSize - 1) != 0 ? static_string_rfind(str, substr, from - 1, nth) : nth > 0 ? static_string_rfind(str, substr, from - 1, nth - 1) : from; }
Menentukan terjadinya substring dalam string statis
Untuk menentukan terjadinya substring, coba saja mencarinya:
template<size_t Size, size_t SubSize> constexpr bool static_string_contains(const static_string<Size>& str, const static_string<SubSize>& substr) { return static_string_find(str, substr) != static_string_npos; }
Menentukan apakah string statis dimulai / diakhiri dengan / pada substring yang diberikan
Menerapkan pembanding yang dijelaskan sebelumnya, kita dapat menentukan apakah string statis dimulai dengan substring yang diberikan:
template<size_t SubSize, size_t Size> constexpr bool static_string_starts_with(const static_string<Size>& str, const static_string<SubSize>& prefix) { return SubSize > Size ? false : static_string_compare(str, 0, prefix, 0, 1, SubSize - 1) == 0; }
Demikian pula untuk mengakhiri garis statis:
template<size_t SubSize, size_t Size> constexpr bool static_string_ends_with(const static_string<Size>& str, const static_string<SubSize>& suffix) { return SubSize > Size ? false : static_string_compare(str, Size - SubSize, suffix, 0, 1, SubSize - 1) == 0; }
Bekerja dengan substring string statis
Di sini kita melihat operasi yang terkait dengan substring dari string statis.
Mendapatkan substring, awalan, dan akhiran dari string statis
Seperti yang kami catat sebelumnya, untuk mendapatkan substring, Anda perlu membuat urutan indeks, dengan indeks awal dan akhir yang diberikan:
template<size_t Begin, size_t End, size_t ... Indexes> struct make_index_subsequence : make_index_subsequence<Begin, End - 1, End - 1, Indexes ...> {}; template<size_t Pos, size_t ... Indexes> struct make_index_subsequence<Pos, Pos, Indexes ...> : index_sequence<Indexes ...> {};
Kami menerapkan memperoleh substring dengan memeriksa awal dan akhir substring menggunakan static_assert
:
template<size_t Begin, size_t End, size_t Size> constexpr auto static_string_substring(const static_string<Size>& str) { static_assert(Begin <= End, "Begin is greater than End (Begin > End)"); static_assert(End <= Size - 1, "End is greater than string length (End > Size - 1)"); return make_static_string(str, make_index_subsequence<Begin, End>{}); }
Awalan adalah substring yang permulaannya bertepatan dengan awal garis statis asli:
template<size_t End, size_t Size> constexpr auto static_string_prefix(const static_string<Size>& str) { return static_string_substring<0, End>(str); }
Demikian pula untuk sufiks, hanya yang cocok:
template<size_t Begin, size_t Size> constexpr auto static_string_suffix(const static_string<Size>& str) { return static_string_substring<Begin, Size - 1>(str); }
Memisahkan string statis menjadi dua bagian pada indeks yang diberikan
Untuk memecah string statis pada indeks yang diberikan, cukup untuk mengembalikan awalan dan akhiran:
template<size_t Index, size_t Size> constexpr auto static_string_split(const static_string<Size>& str) { return std::make_pair(static_string_prefix<Index>(str), static_string_suffix<Index + 1>(str)); }
Pembalikan String Statis
Untuk membalikkan string statis, kami menulis generator indeks yang menghasilkan indeks dalam urutan terbalik:
template<size_t Size, size_t ... Indexes> struct make_reverse_index_sequence : make_reverse_index_sequence<Size - 1, Indexes ..., Size - 1> {}; template<size_t ... Indexes> struct make_reverse_index_sequence<0, Indexes ...> : index_sequence<Indexes ...> {};
Sekarang kita mengimplementasikan fungsi yang membalikkan string statis:
template<size_t Size> constexpr auto static_string_reverse(const static_string<Size>& str) { return make_static_string(str, make_reverse_index_sequence<Size - 1>{}); }
Perhitungan String Hash Statis
Kami akan menghitung hash menggunakan rumus berikut:
H (s) = (s 0 + 1) ⋅ 33 0 + (s 1 + 1) ⋅ 33 1 + ... + (s n - 1 + 1) ⋅ 33 n - 1 + 5381 ⋅ 33 n mod 2 64
template<size_t Size> constexpr unsigned long long static_string_hash(const static_string<Size>& str, size_t index) { return index >= Size - 1 ? 5381ULL : static_string_hash(str, index + 1) * 33ULL + str[index] + 1; }
Konversi angka menjadi string statis dan sebaliknya
Dalam bab ini, kita melihat konversi string statis menjadi integer, serta invers. Untuk kesederhanaan, kami mengasumsikan bahwa angka-angka diwakili oleh tipe long long
dan unsigned long long
, ini adalah jenis kapasitas besar, yaitu, mereka cocok untuk kebanyakan kasus.
Ubah angka menjadi string statis
Untuk mengonversi angka menjadi string statis, kita perlu mendapatkan semua digit angka, mengonversinya menjadi karakter yang sesuai dan membuat string karakter-karakter ini.
Untuk mendapatkan semua digit angka, kita akan menggunakan generator yang mirip dengan generator urutan indeks. Tentukan urutan karakter:
template<char ... Chars> struct char_sequence {};
Kami menerapkan generator karakter digit, menyimpan angka saat ini di parameter pertama, dan angka-angka di berikut ini, digit berikutnya ditambahkan ke awal urutan, dan jumlahnya dibagi sepuluh:
template<unsigned long long Value, char ... Chars> struct make_unsigned_int_char_sequence : make_unsigned_int_char_sequence<Value / 10, '0' + Value % 10, Chars ...> {};
Jika angka saat ini adalah 0, maka kami membuangnya, mengembalikan urutan angka, tidak ada lagi yang perlu dikonversi:
template<char ... Chars> struct make_unsigned_int_char_sequence<0, Chars ...> : char_sequence<Chars ...> {};
Anda juga harus mempertimbangkan kasing ketika angka awal nol, dalam hal ini Anda harus mengembalikan karakter nol, jika tidak nol akan dikonversi ke urutan karakter kosong, dan kemudian ke string kosong:
template<> struct make_unsigned_int_char_sequence<0> : char_sequence<'0'> {};
Generator yang diimplementasikan berfungsi baik untuk bilangan positif, tetapi tidak cocok untuk yang negatif. Kami mendefinisikan generator baru dengan menambahkan satu lagi parameter template ke awal - tanda nomor yang dikonversi:
template<bool Negative, long long Value, char ... Chars> struct make_signed_int_char_sequence {};
Kami akan memproses nomor seperti yang ditunjukkan di atas, tetapi dengan mempertimbangkan tanda:
template<long long Value, char ... Chars> struct make_signed_int_char_sequence<true, Value, Chars ...> : make_signed_int_char_sequence<true, Value / 10, '0' + -(Value % 10), Chars ...> {}; template<long long Value, char ... Chars> struct make_signed_int_char_sequence<false, Value, Chars ...> : make_signed_int_char_sequence<false, Value / 10, '0' + Value % 10, Chars ...> {};
Ada satu titik halus di sini, perhatikan -(Value % 10)
. Di sini, Anda tidak dapat -Value % 10
, karena rentang angka negatif adalah satu angka lebih lebar dari kisaran positif dan modulus angka minimum jatuh dari himpunan nilai yang valid.
Kami membuang nomor setelah diproses, jika negatif, tambahkan simbol tanda minus:
template<char ... Chars> struct make_signed_int_char_sequence<true, 0, Chars ...> : char_sequence<'-', Chars ...> {}; template<char ... Chars> struct make_signed_int_char_sequence<false, 0, Chars ...> : char_sequence<Chars ...> {};
Secara terpisah, kami menjaga konversi nol:
template<> struct make_signed_int_char_sequence<false, 0> : char_sequence<'0'> {};
Akhirnya, kami menerapkan fungsi konversi:
template<unsigned long long Value> constexpr auto uint_to_static_string() { return make_static_string(make_unsigned_int_char_sequence<Value>{}); } template<long long Value> constexpr auto int_to_static_string() { return make_static_string(make_signed_int_char_sequence<(Value < 0), Value>{}); }
Ubah string statis menjadi angka
Untuk mengonversi string statis menjadi angka, Anda perlu mengubah karakter menjadi angka, dan kemudian menambahkannya, setelah sebelumnya dikalikan puluhan oleh kekuatan yang sesuai. Kami melakukan semua tindakan secara rekursif, untuk string kosong kami mengembalikan nol:
template<size_t Size> constexpr unsigned long long static_string_to_uint(const static_string<Size>& str, size_t index) { return Size < 2 || index >= Size - 1 ? 0 : (str[index] - '0') + 10ULL * static_string_to_uint(str, index - 1); } template<size_t Size> constexpr unsigned long long static_string_to_uint(const static_string<Size>& str) { return static_string_to_uint(str, Size - 2); }
Untuk mengonversi angka yang ditandatangani, perhatikan bahwa angka negatif dimulai dengan tanda minus:
template<size_t Size> constexpr long long static_string_to_int(const static_string<Size>& str, size_t index, size_t first) { return index < first || index >= Size - 1 ? 0 : first == 0 ? (str[index] - '0') + 10LL * static_string_to_int(str, index - 1, first) : -(str[index] - '0') + 10LL * static_string_to_int(str, index - 1, first); } template<size_t Size> constexpr long long static_string_to_int(const static_string<Size>& str) { return Size < 2 ? 0 : str[0] == '-' ? static_string_to_int(str, Size - 2, 1) : static_string_to_int(str, Size - 2, 0); }
Pertimbangan Kegunaan Perpustakaan
Pada titik ini, sudah dimungkinkan untuk sepenuhnya menggunakan perpustakaan, tetapi beberapa titik tidak nyaman. Dalam bab ini, kita akan melihat bagaimana Anda dapat membuat menggunakan perpustakaan lebih nyaman.
Objek String Statis
Kemas string dan metode yang diimplementasikan ke dalam objek. Ini akan memungkinkan penggunaan nama metode yang lebih pendek, serta mengimplementasikan operator pembanding:
template<size_t Size> struct static_string { constexpr size_t length() const { return Size - 1; } constexpr size_t size() const { return Size; } constexpr size_t begin() const { return 0; } constexpr size_t end() const { return Size - 1; } constexpr size_t rbegin() const { return Size - 2; } constexpr size_t rend() const { return std::numeric_limits<size_t>::max(); } constexpr bool empty() const { return Size < 2; } constexpr auto reverse() const { return static_string_reverse(*this); } template<size_t Begin, size_t End> constexpr auto substring() const { return static_string_substring<Begin, End>(*this); } template<size_t End> constexpr auto prefix() const { return static_string_prefix<End>(*this); } template<size_t Begin> constexpr auto suffix() const { return static_string_suffix<Begin>(*this); } constexpr size_t find(char ch, size_t from = 0, size_t nth = 0) const { return static_string_find(*this, ch, from, nth); } template<size_t SubSize> constexpr size_t find(const static_string<SubSize>& substr, size_t from = 0, size_t nth = 0) const { return static_string_find(*this, substr, from, nth); } template<size_t SubSize> constexpr size_t find(const char (& substr)[SubSize], size_t from = 0, size_t nth = 0) const { return static_string_find(*this, substr, from, nth); } constexpr size_t rfind(char ch, size_t from = Size - 2, size_t nth = 0) const { return static_string_rfind(*this, ch, from, nth); } template<size_t SubSize> constexpr size_t rfind(const static_string<SubSize>& substr, size_t from = Size - SubSize, size_t nth = 0) const { return static_string_rfind(*this, substr, from, nth); } template<size_t SubSize> constexpr size_t rfind(const char (& substr)[SubSize], size_t from = Size - SubSize, size_t nth = 0) const { return static_string_rfind(*this, substr, from, nth); } constexpr bool contains(char ch) const { return static_string_contains(*this, ch); } template<size_t SubSize> constexpr bool contains(const static_string<SubSize>& substr) const { return static_string_contains(*this, substr); } template<size_t SubSize> constexpr bool contains(const char (& substr)[SubSize]) const { return static_string_contains(*this, substr); } template<size_t SubSize> constexpr bool starts_with(const static_string<SubSize>& prefix) const { return static_string_starts_with(*this, prefix); } template<size_t SubSize> constexpr bool starts_with(const char (& prefix)[SubSize]) const { return static_string_starts_with(*this, prefix); } template<size_t SubSize> constexpr bool ends_with(const static_string<SubSize>& suffix) const { return static_string_ends_with(*this, suffix); } template<size_t SubSize> constexpr bool ends_with(const char (& suffix)[SubSize]) const { return static_string_ends_with(*this, suffix); } constexpr size_t count(char ch) const { return static_string_count(*this, ch); } template<size_t Index> constexpr auto split() const { return static_string_split<Index>(*this); } constexpr unsigned long long hash() const { return static_string_hash(*this); } constexpr char operator[](size_t index) const { return data[index]; } std::string str() const { return to_string(*this); } std::array<const char, Size> data; };
. :
template<size_t Size1, size_t Size2> constexpr bool operator<(const static_string<Size1>& str1, const static_string<Size2>& str2) { return static_string_compare(str1, str2) < 0; }
> <= >= == !=, . - .
:
#define ITOSS(x) int_to_static_string<(x)>() #define UTOSS(x) uint_to_static_string<(x)>() #define SSTOI(x) static_string_to_int((x)) #define SSTOU(x) static_string_to_uint((x))
.
:
constexpr auto hello = make_static_string("Hello"); constexpr auto world = make_static_string("World"); constexpr auto greeting = hello + ", " + world + "!";
, :
constexpr int apples = 5; constexpr int oranges = 7; constexpr auto message = static_string_concat("I have ", ITOSS(apples), " apples and ", ITOSS(oranges), ", so I have ", ITOSS(apples + oranges), " fruits");
constexpr unsigned long long width = 123456789ULL; constexpr unsigned long long height = 987654321ULL; constexpr auto message = static_string_concat("A rectangle with width ", UTOSS(width), " and height ", UTOSS(height), " has area ", UTOSS(width * height));
constexpr long long revenue = 1'000'000LL; constexpr long long costs = 1'200'000LL; constexpr long long profit = revenue - costs; constexpr auto message = static_string_concat("The first quarter has ended with net ", (profit >= 0 ? "profit" : "loss "), " of $", ITOSS(profit < 0 ? -profit : profit));
URL:
constexpr auto url = make_static_string("http://www.server.com:8080"); constexpr auto p = url.find("://"); constexpr auto protocol = url.prefix<p>();
:
constexpr auto str = make_static_string("Hello"); for (size_t i = str.begin(); i != str.end(); ++i)
Referensi
, , github
, .
Update
_ss :
template<typename Char, Char ... Chars> constexpr basic_static_string<Char, sizeof ... (Chars) + 1> operator"" _ss() { return {Chars ..., static_cast<Char>('\0')}; };
make_static_string() , :
constexpr auto hello_world = "Hello"_ss + " World"; if ("Hello" < "World"_ss) { ... } constexpr auto hash = "VeryLongString"_ss.hash();
Char char:
template<typename Char, size_t Size> struct basic_static_string {
char whar_t, , concat, :
template<size_t Size> using static_string_t = basic_static_string<char, Size>; template<size_t Size> using static_wstring_t = basic_static_string<wchar_t, Size>; using static_string = basic_static_string<char, 0>; using static_wstring = basic_static_string<wchar_t, 0>;
"" :
constexpr auto wide_string = L"WideString"_ss; constexpr int apples = 5; constexpr int oranges = 7; constexpr int fruits = apples + oranges; constexpr auto str3 = static_wstring::concat(L"I have ", ITOSW(apples), L" apples and ", ITOSW(oranges), L" oranges, so I have ", ITOSW(fruits), L" fruits"); static_assert(str3 == L"I have 5 apples and 7 oranges, so I have 12 fruits", ""); std::wcout << str3 << std::endl;
size(), size() length() , sizeof():
constexpr auto ss1 = "Hello"_ss; static_assert(ss1.length() == 5, ""); static_assert(ss1.size() == 5, ""); static_assert(sizeof(ss1) == 6, "");
github
.
Update 2
AndreySu , :
#include <iostream> using namespace std; template<typename Char, Char ... Chars> struct static_string{}; template<typename Char, Char ... Chars1, Char ... Chars2> constexpr static_string<Char, Chars1 ..., Chars2 ... > operator+( const static_string<Char, Chars1 ... >& str1, const static_string<Char, Chars2 ... >& str2) { return static_string<Char, Chars1 ..., Chars2 ...>{}; } template<typename Char, Char ch, Char ... Chars> std::basic_ostream<Char>& operator<<(std::basic_ostream<Char>& bos, const static_string<Char, ch, Chars ...>& str) { bos << ch << static_string<Char, Chars ... >{}; return bos; } template<typename Char> std::basic_ostream<Char>& operator<<(std::basic_ostream<Char>& bos, const static_string<Char>& str) { return bos; } template<typename Char, Char ... Chars> constexpr static_string<Char, Chars ... > operator"" _ss() { return static_string<Char, Chars ... >{}; }; int main() { constexpr auto str1 = "abc"_ss; constexpr auto str2 = "def"_ss; constexpr auto str = str1 + str2 + str1; std::cout << str << std::endl; return 0; }
, , - .