Bagaimana membuat printf untuk menunjukkan nilai-nilai variabel yang merupakan tipe enum? Misalnya:
typedef enum {Linux, Apple, Windows} OS_type;
OS_type myOS = Linux;
dan apa yang saya butuhkan adalah sesuatu seperti
printenum(OS_type, "My OS is %s", myOS);
yang harus menunjukkan string "Linux", bukan bilangan bulat.
Saya kira, pertama-tama saya harus membuat array string yang terindeks nilai. Tetapi saya tidak tahu apakah itu cara yang paling indah untuk melakukannya. Apakah mungkin?
c++
c
preprocessor
ansi-c
psihodelia
sumber
sumber
Jawaban:
Tidak ada cara yang indah untuk melakukan ini. Cukup atur array string yang diindeks oleh enum.
Jika Anda melakukan banyak output, Anda dapat menentukan operator << yang mengambil parameter enum dan melakukan pencarian untuk Anda.
sumber
Solusi naif, tentu saja, adalah menulis fungsi untuk setiap enumerasi yang melakukan konversi ke string:
Ini, bagaimanapun, adalah bencana pemeliharaan. Dengan bantuan perpustakaan Boost.Preprocessor, yang dapat digunakan dengan kode C dan C ++, Anda dapat dengan mudah mengambil keuntungan dari preprocessor dan membiarkannya menghasilkan fungsi ini untuk Anda. Makro generasi adalah sebagai berikut:
Makro pertama (dimulai dengan
X_
) digunakan secara internal oleh yang kedua. Makro kedua pertama menghasilkan enumerasi, lalu menghasilkan aToString
fungsi yang mengambil objek jenis itu dan mengembalikan nama enumerator sebagai string (implementasi ini, untuk alasan yang jelas, mengharuskan enumerator memetakan ke nilai unik).Dalam C ++ Anda dapat mengimplementasikan
ToString
fungsi sebagaioperator<<
kelebihan beban, tapi saya pikir ini agak lebih bersih untuk membutuhkan eksplisit "ToString
" untuk mengonversi nilai ke bentuk string.Sebagai contoh penggunaan,
OS_type
enumerasi Anda akan didefinisikan sebagai berikut:Sementara makro terlihat pada awalnya seperti itu banyak pekerjaan, dan definisi
OS_type
terlihat agak asing, ingat bahwa Anda harus menulis makro sekali, maka Anda dapat menggunakannya untuk setiap enumerasi. Anda dapat menambahkan fungsionalitas tambahan untuk itu (misalnya, bentuk string untuk enum konversi) tanpa terlalu banyak kesulitan, dan itu sepenuhnya memecahkan masalah pemeliharaan, karena Anda hanya perlu memberikan nama satu kali, ketika Anda menjalankan makro.Pencacahan kemudian dapat digunakan seolah-olah didefinisikan secara normal:
Cuplikan kode dalam pos ini, dimulai dengan
#include <boost/preprocessor.hpp>
baris, dapat dikompilasi sebagai diposting untuk menunjukkan solusinya.Solusi khusus ini adalah untuk C ++ karena menggunakan sintaksis spesifik C ++ (mis., Tidak
typedef enum
) dan fungsi overloading, tetapi akan mudah untuk membuat ini bekerja dengan C juga.sumber
(Windows)
menjadi(Windows, 3)
lalu menggantiBOOST_PP_SEQ_ENUM
dengan yang ditulis sesuaiBOOST_PP_SEQ_FOR_EACH
. Saya tidak punya contoh yang berguna, tetapi saya bisa menulis satu jika Anda mau.Ini adalah blok pra prosesor
Definisi Enum
Panggilan menggunakan
Diambil dari sini . Betapa kerennya itu ? :)
sumber
Gunakan
std::map<OS_type, std::string>
dan isi dengan enum sebagai kunci, dan representasi string sebagai nilai, maka Anda dapat melakukan ini:sumber
Masalah dengan C enum adalah bahwa itu bukan jenis itu sendiri, seperti di C ++. Enum dalam C adalah cara untuk memetakan pengidentifikasi ke nilai integral. Hanya itu saja. Itu sebabnya nilai enum dapat dipertukarkan dengan nilai integer.
Seperti yang Anda tebak dengan benar, cara yang baik adalah membuat pemetaan antara nilai enum dan string. Sebagai contoh:
sumber
enum
adalah tipe C. Konstanta tipe enumerasi integral adalah tipeint
dan bukanenum
tipe yang mendefinisikannya, mungkin itulah yang ingin Anda katakan. Tetapi saya sama sekali tidak mengerti apa hubungannya dengan pertanyaan ini.Saya telah menggabungkan solusi James ' , Howard dan Éder dan menciptakan implementasi yang lebih umum:
Kode lengkapnya ditulis di bawah (gunakan "DEFINE_ENUM_CLASS_WITH_ToString_METHOD" untuk mendefinisikan enum) ( demo online ).
sumber
Contoh sederhana ini berhasil bagi saya. Semoga ini membantu.
sumber
Apakah Anda mencoba ini:
Itu
stringify()
makro dapat digunakan untuk mengubah teks dalam kode Anda ke dalam string, tetapi hanya teks yang tepat antara tanda kurung. Tidak ada variabel dereferencing atau substitusi makro atau hal-hal lain yang dilakukan.http://www.cplusplus.com/forum/general/2949/
sumber
Ada banyak jawaban bagus di sini, tetapi saya pikir beberapa orang mungkin menganggap jawaban saya bermanfaat. Saya menyukainya karena antarmuka yang Anda gunakan untuk mendefinisikan makro sesederhana yang bisa didapat. Ini juga berguna karena Anda tidak harus menyertakan pustaka tambahan - semuanya dilengkapi dengan C ++ dan bahkan tidak memerlukan versi yang sangat terlambat. Saya menarik potongan-potongan dari berbagai tempat secara online sehingga saya tidak dapat mengambil kredit untuk semua itu, tetapi saya pikir itu cukup unik untuk menjamin jawaban baru.
Pertama buat file header ... sebut saja EnumMacros.h atau semacamnya, dan letakkan ini di dalamnya:
Kemudian, dalam program utama Anda, Anda dapat melakukan ini ...
Di mana output akan >> Nilai 'Apple' adalah: 2 dari 4
Nikmati!
sumber
Dengan asumsi bahwa enum Anda sudah ditentukan, Anda dapat membuat array pasangan:
Sekarang, Anda dapat membuat peta:
Sekarang, Anda bisa menggunakan peta. Jika enum Anda diubah, Anda harus menambah / menghapus pasangan dari pasangan array []. Saya pikir itu adalah cara paling elegan untuk mendapatkan string dari enum di C ++.
sumber
bimap
jika seseorang ingin mengurai nama dan mengubahnya menjadi enum (misalnya, dari file XML).Untuk C99 ada
P99_DECLARE_ENUM
di P99 yang memungkinkan Anda mendeklarasikanenum
seperti ini:dan kemudian gunakan
color_getname(A)
untuk mendapatkan string dengan nama warna.sumber
Ini kode C ++ saya:
sumber
Sedikit terlambat ke pesta, tapi inilah solusi C ++ 11 saya:
sumber
error: ‘hash’ is not a class template
->#include <functional>
Preferensi saya sendiri adalah untuk meminimalkan pengetikan berulang dan sulit untuk memahami makro dan untuk menghindari memasukkan definisi makro ke dalam ruang kompilator umum.
Jadi, di file header:
dan implementasi cpp adalah:
Perhatikan #undef dari makro segera setelah kita selesai dengan itu.
sumber
Solusi saya, tidak menggunakan boost:
Dan inilah cara menggunakannya
sumber
Terlambat lain ke pesta, menggunakan preprosesor:
(Saya hanya memasukkan nomor baris sehingga lebih mudah untuk dibicarakan.) Baris 1-4 adalah apa yang Anda edit untuk menentukan elemen enum. (Saya menyebutnya "daftar makro", karena itu makro yang membuat daftar hal-hal. @Lundin memberi tahu saya ini adalah teknik terkenal yang disebut X-makro.)
Baris 7 mendefinisikan makro bagian dalam untuk mengisi deklarasi enum aktual dalam baris 8-11. Baris 12 mendefinisikan makro bagian dalam (hanya untuk membungkam peringatan kompiler).
Baris 14 mendefinisikan makro bagian dalam untuk membuat versi string dari nama elemen enum. Kemudian baris 15-18 menghasilkan array yang dapat mengonversi nilai enum ke string yang sesuai.
Baris 21-27 menghasilkan fungsi yang mengubah string ke nilai enum, atau mengembalikan NULL jika string tidak cocok dengan apa pun.
Ini sedikit rumit dalam cara menangani elemen ke-0. Saya sebenarnya telah mengatasinya di masa lalu.
Saya akui teknik ini mengganggu orang-orang yang tidak ingin berpikir preprocessor itu sendiri dapat diprogram untuk menulis kode untuk Anda. Saya pikir itu sangat menggambarkan perbedaan antara keterbacaan dan pemeliharaan . Kode ini sulit dibaca, tetapi jika enum memiliki beberapa ratus elemen, Anda dapat menambah, menghapus, atau mengatur ulang elemen dan masih memastikan kode yang dihasilkan tidak memiliki kesalahan.
sumber
#define TEST_1 hello #define TEST_2 world
itutypedef enum { TEST_1, TEST_2 } test_t;
dan kemudian membuat tabel look-up string yang menggunakan makro strify:const char* table[]= { STRINGIFY(TEST_1), STRINGIFY(TEST_2), };
Sudah ada beberapa jawaban yang mengisyaratkan solusi yang sama. Jauh lebih mudah dibaca.Inilah metode Old Skool (dulu digunakan secara luas dalam gcc) hanya menggunakan C-prosesor. Berguna jika Anda membuat struktur data diskrit tetapi perlu menjaga urutan konsisten di antara mereka. Entri di mylist.tbl tentu saja dapat diperluas ke sesuatu yang jauh lebih kompleks.
test.cpp:
Dan kemudian mylist.tbl:
sumber
Dalam c ++ seperti ini:
sumber
dari http://www.codeproject.com/Articles/42035/Enum-to-String-and-Vice-Versa-in-C dan setelah
memasukkan
Berfungsi dengan baik jika nilai dalam enum tidak duplikat.
Kode sampel untuk mengonversi nilai enum ke string:
Kode contoh untuk sebaliknya:
sumber
Terima kasih James atas saran Anda. Itu sangat berguna jadi saya menerapkan sebaliknya untuk berkontribusi dalam beberapa cara.
sumber
Untuk memperluas jawaban James, seseorang ingin beberapa kode contoh untuk mendukung enum define dengan nilai int, saya juga memiliki persyaratan ini, jadi inilah cara saya:
Pertama adalah makro penggunaan internal, yang digunakan oleh FOR_EACH:
Dan, inilah makro definisinya:
Jadi saat menggunakannya, Anda mungkin ingin menulis seperti ini:
yang akan diperluas ke:
Ide dasarnya adalah untuk mendefinisikan SEQ, yang setiap elemen adalah TUPLE, sehingga kita dapat memberikan nilai tambahan untuk anggota enum. Dalam loop FOR_EACH, periksa ukuran item TUPLE, jika ukurannya 2, perluas kode ke KEY = VALUE, kalau tidak simpan saja elemen pertama dari TUPLE.
Karena SEQ input sebenarnya adalah TUPLE, jadi jika Anda ingin mendefinisikan fungsi STRINGIZE, Anda mungkin perlu melakukan pra-proses input enumerator terlebih dahulu, inilah makro untuk melakukan pekerjaan:
Makro
DEFINE_ENUM_WITH_STRING_CONVERSIONS_FIRST_ELEM_SEQ
hanya akan mempertahankan elemen pertama di setiap TUPLE, dan kemudian dikonversi ke SEQ, sekarang modifikasi kode James, Anda akan memiliki kekuatan penuh.Implementasi saya mungkin bukan yang paling sederhana, jadi jika Anda tidak menemukan kode bersih, milik saya untuk referensi Anda.
sumber
Solusi bersih, aman dalam standar C murni:
Keluaran
Alasan
Ketika memecahkan masalah inti "memiliki konstanta enum dengan string yang sesuai", seorang programmer yang masuk akal akan datang dengan persyaratan berikut:
Persyaratan pertama, dan mungkin juga yang kedua, dapat dipenuhi dengan berbagai solusi makro yang berantakan seperti trik "x makro" yang terkenal, atau bentuk lain dari sihir makro. Masalah dengan solusi seperti itu adalah mereka meninggalkan Anda dengan kekacauan makro misterius yang sama sekali tidak terbaca - mereka tidak memenuhi persyaratan ketiga di atas.
Satu-satunya hal yang diperlukan di sini adalah memiliki tabel string look-up, yang dapat kita akses dengan menggunakan variabel enum sebagai indeks. Tabel seperti itu secara alami harus berhubungan langsung dengan enum dan sebaliknya. Ketika salah satu dari mereka diperbarui, yang lain harus diperbarui juga, atau itu tidak akan berfungsi.
Penjelasan kode
Misalkan kita memiliki enum like
Ini bisa diubah menjadi
Dengan keuntungan bahwa konstanta makro ini sekarang dapat digunakan di tempat lain, misalnya menghasilkan tabel pencarian string. Mengubah konstanta pra-prosesor ke string dapat dilakukan dengan makro "stringify":
Dan itu saja. Dengan menggunakan
hello
, kita mendapatkan konstanta enum dengan nilai 0. Dengan menggunakantest_str[hello]
kita mendapatkan string "halo".Untuk membuat enum dan tabel pencarian berhubungan secara langsung, kita harus memastikan bahwa mereka mengandung jumlah item yang sama. Jika seseorang akan mempertahankan kode dan hanya mengubah enum, dan bukan tabel pencarian, atau sebaliknya, metode ini tidak akan berfungsi.
Solusinya adalah memiliki enum untuk memberi tahu Anda berapa banyak item yang dikandungnya. Ada trik C yang umum digunakan untuk ini, cukup tambahkan item di bagian akhir, yang hanya memenuhi tujuan untuk memberitahukan berapa banyak item yang dimiliki oleh enum:
Sekarang kita dapat memeriksa pada waktu kompilasi bahwa jumlah item dalam enum adalah sebanyak jumlah item dalam tabel pencarian, lebih disukai dengan pernyataan statis C11:
(Ada cara-cara yang jelek tapi berfungsi penuh untuk membuat pernyataan statis dalam versi lama dari standar C juga, jika seseorang bersikeras menggunakan kompiler dinosaurus. Sedangkan untuk C ++, itu mendukung penegasan statis juga.)
Sebagai catatan tambahan, di C11 kita juga dapat mencapai keamanan tipe yang lebih tinggi dengan mengubah makro stringify:
(
int
karena konstanta enumerasi sebenarnya bertipeint
, bukantest_t
)Ini akan mencegah kode seperti
STRINGIFY(random_stuff)
dari kompilasi.sumber
#define
, tambahkan referensi ke yang didefinisikan dalam deklarasi enum dan tabel pencarian. Jika Anda salah saat menambahkan baris-baris itu, program tidak akan dikompilasi. Angka-angka yang saya tambahkan ke pengidentifikasi sama sekali tidak wajib, Anda juga bisa menulis#define APPLES hello
dan#define ORANGES world
diikuti olehtypedef enum { APPES, ORANGES, TEST_N } test_t;
dan seterusnya.Apa yang saya buat adalah kombinasi dari apa yang saya lihat di sini dan pertanyaan serupa di situs ini. Saya membuat ini adalah Visual Studio 2013. Saya belum mengujinya dengan kompiler lain.
Pertama-tama saya mendefinisikan satu set makro yang akan melakukan trik.
Selanjutnya, tentukan makro tunggal yang akan membuat kelas enum dan fungsi untuk mendapatkan string.
Sekarang mendefinisikan jenis enum dan memiliki string untuk itu menjadi sangat mudah. Yang perlu Anda lakukan adalah:
Baris berikut dapat digunakan untuk mengujinya.
Ini akan menampilkan:
Saya percaya ini sangat bersih dan mudah digunakan. Ada beberapa batasan:
Jika Anda bisa mengatasi ini. Saya pikir, terutama cara menggunakannya, ini bagus dan ramping. Keuntungan:
sumber
Solusi bersih untuk masalah ini adalah:
Hal yang baik tentang solusi ini adalah sederhana dan juga membangun fungsi dapat dilakukan dengan mudah melalui salin dan ganti. Perhatikan bahwa jika Anda akan melakukan banyak konversi dan enum Anda memiliki terlalu banyak nilai yang mungkin, solusi ini mungkin menjadi intensif CPU.
sumber
Saya agak terlambat tapi inilah solusi saya menggunakan g ++ dan hanya perpustakaan standar. Saya sudah mencoba untuk meminimalkan polusi namespace dan menghapus kebutuhan untuk mengetik ulang nama enum.
File header "my_enum.hpp" adalah:
Contoh penggunaan:
Ini akan menampilkan:
Anda hanya perlu mendefinisikan semuanya sekali saja, namespace Anda seharusnya tidak tercemar, dan semua perhitungan hanya dilakukan satu kali (sisanya hanya pencarian). Namun, Anda tidak mendapatkan jenis-keamanan kelas enum (mereka masih hanya bilangan bulat pendek), Anda tidak dapat menetapkan nilai ke enum, Anda harus mendefinisikan enum di suatu tempat Anda dapat menentukan ruang nama (misalnya global).
Saya tidak yakin seberapa bagus kinerjanya dalam hal ini, atau apakah itu ide yang bagus (saya belajar C sebelum C ++ sehingga otak saya tetap bekerja seperti itu). Jika ada yang tahu mengapa ini adalah ide yang buruk jangan ragu untuk menunjukkannya.
sumber
Ini tahun 2017 tetapi pertanyaannya masih hidup
Namun cara lain:
Output:
sumber
Ini adalah versi enum yang diperluas dengan kelas yang diuraikan ... itu tidak menambah nilai enum lain selain yang disediakan.
Penggunaan: DECLARE_ENUM_SEQ (CameraMode, (3), Fly, FirstPerson, PerspectiveCorrect)
sumber
Saya membutuhkan ini untuk bekerja di kedua arah DAN saya sering memasukkan enum saya di dalam kelas yang berisi, dan jadi saya mulai dengan solusi dengan cara James McNellis, jauh di atas jawaban ini, tetapi saya membuat solusi ini. Perhatikan juga saya lebih suka kelas enum daripada hanya enum, yang agak mempersulit jawaban.
Untuk menggunakannya di dalam kelas, Anda bisa melakukan sesuatu seperti ini:
Dan saya menulis tes CppUnit, yang menunjukkan cara menggunakannya:
Anda harus memilih makro mana yang akan digunakan, DEFINE_ENUMERATION atau DEFINE_ENUMERATION_INSIDE_CLASS. Anda akan melihat saya menggunakan yang terakhir ketika mendefinisikan ComponentStatus :: Status tetapi saya menggunakan yang pertama ketika hanya mendefinisikan Status. Perbedaannya sederhana. Di dalam kelas, saya awali metode to / from sebagai "statis" dan jika tidak di dalam kelas, saya menggunakan "inline". Perbedaan sepele, tetapi perlu.
Sayangnya, saya tidak berpikir ada cara yang bersih untuk menghindari keharusan melakukan ini:
walaupun Anda bisa secara manual membuat metode inline setelah definisi kelas Anda yang hanya menghubungkan ke metode kelas, sesuatu seperti:
sumber
Jawaban saya sendiri, tidak menggunakan boost - menggunakan pendekatan saya sendiri tanpa sihir berat, dan solusi ini memiliki batasan tidak dapat mendefinisikan nilai enum tertentu.
Versi terbaru dapat ditemukan di github di sini:
https://github.com/tapika/cppscriptcore/blob/master/SolutionProjectModel/EnumReflect.h
sumber
Ada banyak jawaban lain untuk ini tetapi saya pikir cara yang lebih baik adalah dengan menggunakan fitur C ++ 17 dan menggunakan constexpr sehingga terjemahan dilakukan pada waktu kompilasi. Ini jenis aman dan kita tidak perlu macam-macam dengan makro. Lihat di bawah:
Ini kemudian dapat dengan mudah digunakan sehingga kesalahan kunci string terdeteksi pada waktu kompilasi:
Kode lebih verbose daripada beberapa solusi lain, tetapi kita dapat dengan mudah melakukan konversi Enum ke String dan konversi String ke Enum pada waktu kompilasi dan mendeteksi kesalahan tipe. Dengan beberapa fitur C ++ 20 yang akan datang, ini mungkin dapat disederhanakan sedikit lagi.
sumber