Enum türlerinin değişkenlerini C'de dizge olarak kullanmanın kolay yolu?


89

İşte yapmaya çalıştığım şey:

Aşağıdakine benzer bir geçiş durumu yapacak bir işlev yazmaya çalışıyorum:

Her durumda tanımlamak yerine, yukarıda yapmaya çalıştığım gibi enum değişkenini kullanarak onu ayarlamanın bir yolu var mı?

Yanıtlar:


16

Yerleşik bir çözüm yok. En kolay yol, char*numaralandırmanın int değerinin o numaralamanın açıklayıcı adını içeren bir dizeye dizin oluşturduğu bir dizidir. enumBazı inteşlemelerin dizi tabanlı bir eşlemeyi kullanışsız hale getirecek kadar yüksek olduğu bir seyrek (0'da başlamayan veya numaralandırmada boşlukları olan) varsa, bunun yerine bir karma tablo kullanabilirsiniz.


Bunu genişleterek, eğer gerçekten doğrusal olarak artan bir liste ise, her bir ismi bir dizeye kaydetmek ve çözmek için editörünüzün makro aracını kullanabilirsiniz. Ekstra küçük bir yazıya ihtiyaç duyulur ve ilk etapta tanımlama ihtiyacını ortadan kaldırırsınız. Kopyalanan makroların sonundaki kaydı tıklıyorum, sonra bir teklif ekliyorum ve bir sonraki satırda aynı yere ilerliyorum. Durmaya basıyorum. X kez run tuşuna basıyorum ve ne kadar çok olursa (veya sadece tek adım) yapıyorum. Daha sonra onu bir dizge dizisine kaydırabilirim.
user2262111

70

Bir şeyi hem bir C tanımlayıcısı hem de bir dizge yapma tekniği ?burada kullanılabilir.

Her zamanki gibi, bu tür önişlemci malzemelerinde, önişlemci parçasını yazmak ve anlamak zor olabilir ve makroları diğer makrolara aktarmayı içerir ve # ve ## operatörlerini kullanmayı içerir, ancak kullanmak gerçekten kolaydır. Bu stili, aynı listeyi iki kez tutmanın gerçekten zahmetli olabileceği uzun numaralandırmalar için çok yararlı buluyorum.

Fabrika kodu - yalnızca bir kez yazılır, genellikle başlıkta gizlenir:

enumFactory.h:

Fabrika kullanılmış

someEnum.h:

someEnum.cpp:

Teknik, XX makrolarının daha fazla argüman kabul etmesini sağlayacak şekilde kolayca genişletilebilir ve bu örnekte sağladığım üçe benzer şekilde, farklı ihtiyaçlar için XX yerine başka makrolar da hazırlayabilirsiniz.

#İnclude / #define / #undef kullanarak X-Makrolarla karşılaştırma

Bu, diğerlerinin bahsettiği X-Macros'a benzer olsa da, bu çözümün #undefing hiçbir şey gerektirmediği için daha zarif olduğunu düşünüyorum, bu da karmaşık şeylerin çoğunu gizlemenize olanak tanıyan fabrikada başlık dosyası - başlık dosyası yeni bir enum tanımlamanız gerektiğinde hiç dokunmadığınız bir şeydir, bu nedenle yeni enum tanımı çok daha kısa ve daha temizdir.


2
Eminim bu daha iyi olduğunu söylemek ne kadar değilim / x-makroları daha kötü - bu olduğunu x-makrolar. SOME_ENUM(XX)Tam olarak, bir X-makro (kesin olarak geçer "kullanıcı formu" XXkullanmak yerine fonksiyonu #def #undef) ve daha sonra tüm X-MAKRO sonra kullanır DEFINE_ENUM geçirilir çevirin. Çözümden hiçbir şey almamak - iyi çalışıyor. Sadece bunun X makrolarının bir kullanımı olduğunu açıklığa kavuşturmak için.
BeeOnRope

1
@BeeOnRope Not ettiğiniz fark önemlidir ve bu çözümü deyimsel X makrolarından ( Wikipedia örnekleri gibi ) ayırır . Alanın XXüzerinden geçmenin avantajı #define, önceki modelin makro genişletmelerde kullanılabilmesidir. Bu kadar kısa olan diğer çözümlerin hepsinin yeni bir numaralandırma tanımlamak için ayrı bir dosyanın oluşturulmasını ve çoklu dahil edilmesini gerektirdiğine dikkat edin.
pmttavara

1
Diğer bir numara da enum adını makro adı olarak kullanmaktır . Sadece yazabilir #define DEFINE_ENUM(EnumType) ..., yerini ENUM_DEF(...)ile EnumType(...)ve kullanıcı söz sahibi #define SomeEnum(XX) .... C ön işlemcisi, SomeEnumparantezle takip edildiğinde bağlamsal olarak makro çağırmaya, aksi takdirde normal bir simgeye genişleyecektir . (Elbette, kullanıcı veya SomeEnum(2)yerine enum türünü kullanmayı (SomeEnum)2static_cast<SomeEnum>(2)
seviyorsa

1
@pmttavara - eğer hızlı arama herhangi bir gösterge ise, x-makrolarının en yaygın kullanımı, #defineve ile birlikte sabit bir iç makro adı kullanır #undef. "Kullanıcı formu" nun (örneğin, bu makalenin altında önerilen ) bir tür x-makrosu olduğuna katılmıyor musunuz ? Kesinlikle ona her zaman x-makro da dedim ve son zamanlarda bulunduğum C kod tabanlarında bu en yaygın biçimdir (bu açıkçası önyargılı bir gözlem). Yine de OP'yi yanlış ayrıştırıyor olabilirim.
BeeOnRope

2
@BeeOnRope Şu anki ifade, düzenlemenin bir sonucudur, beni geri ikna ettiğiniz için, o zamanlar belki daha az kullanılmış bir form (veya makalelerde en az bir tane daha az bahsedilmiş) olsa bile, bu x-makro.
Suma

62

3
Bu, cpp'nin yapıldığı türden bir şey. +1.
Derrick Turk

6
Bu iyi bir cevap, birinin özel aletler kullanmadan yapabileceği en iyisi gibi görünüyor ve ben daha önce bu tür şeyleri yaptım; ama yine de hiçbir zaman gerçekten 'doğru' hissettirmiyor ve bunu yapmaktan hiç hoşlanmıyorum ...
Michael Burr

Küçük değişimi: #define ENUM_END(typ) }; extern const char * typ ## _name_table[];in defs.hdosyası - bu Kullanmaya dosyalarda adınız tablosunu ilan edecek. (Yine de tablo boyutunu bildirmenin iyi bir yolunu bulamıyorum.) Ayrıca şahsen ben son noktalı virgülü bırakırım, ancak esaslar her iki şekilde de tartışılabilir.
Chris Lutz

1
@Bill, niye rahatsız typdoğrultusunda #define ENUM_END(typ) };?
Pacerier

Bu,
makromun

13

Bunu yapmanın kesinlikle bir yolu var - X () makrolarını kullanın . Bu makrolar, kaynak veri listesinden numaralandırmalar, diziler ve kod blokları oluşturmak için C ön işlemcisini kullanır. Yalnızca X () makrosunu içeren #define öğesine yeni öğeler eklemeniz gerekir. Switch ifadesi otomatik olarak genişler.

Örneğiniz şu şekilde yazılabilir:

Daha verimli yollar vardır (örn. Bir dizgi dizisi ve enum indeksi oluşturmak için X Makrolarını kullanmak), ancak bu en basit demodur.


8

Birkaç sağlam cevabınız olduğunu biliyorum, ancak C ön işlemcisindeki # işlecini biliyor musunuz?

Bunu yapmanıza izin verir:


char const *kConstStr[]
Anne van Rossum

6

Sık sık ihtiyacım olmasına rağmen C veya C ++ bu işlevi sağlamaz.

Aşağıdaki kod çalışır, ancak en iyisi seyrek olmayan numaralandırmalar için uygundur.

Seyrek olmayan derken, formda değil demek istiyorum

çünkü içinde büyük boşluklar var.

Bu yöntemin avantajı, numaralandırmaların ve dizelerin tanımlarını yan yana koymasıdır; bir işlevde switch deyimine sahip olmak onlara öncülük eder. Bu, birini diğeri olmadan değiştirme olasılığınızın daha düşük olduğu anlamına gelir.


6

ÖPÜCÜK. Numaralandırmalarınızla her türlü diğer anahtar / durum işlerini yapacaksınız, öyleyse yazdırma neden farklı olsun? Bir vakayı unutabileceğiniz yaklaşık 100 başka yer olduğunu düşündüğünüzde, baskı rutininizde bir vakayı unutmak çok büyük bir sorun değildir. Sadece ayrıntılı olmayan durum eşleşmeleri konusunda uyarı verecek olan -Wall'ı derleyin. "Varsayılan" seçeneğini kullanmayın çünkü bu, anahtarı kapsamlı hale getirir ve uyarılar almazsınız. Bunun yerine, anahtarın çıkmasına izin verin ve varsayılan durumla şu şekilde ilgilenin ...



4

Boost :: ön işlemcisinin kullanılması , aşağıdaki gibi zarif bir çözümü mümkün kılar:

1.Adım: başlık dosyasını ekleyin:

Adım 2: Numaralandırma nesnesini aşağıdaki sözdizimi ile bildirin:

3. Adım: Verilerinizi kullanın:

Eleman sayısını almak:

İlişkili dizeyi almak:

İlişkili dizeden enum değerini alma:

Ekstra dosya içermeyen, temiz ve kompakt görünüyor. EnumUtilities.h içinde yazdığım kod şu:

Bazı sınırlamalar vardır, yani boost :: preprocessor olanları. Bu durumda, sabitlerin listesi 64 öğeden büyük olamaz.

Aynı mantığı takip ederek, seyrek enum oluşturmayı da düşünebilirsiniz:

Bu durumda sözdizimi şöyledir:

Kullanım yukarıdakiyle benzerdir (eksi eName ## 2Enum işlevi, önceki sözdiziminden çıkarım yapmayı deneyebilirsiniz).

Mac ve linux üzerinde test ettim, ancak boost :: preprocessor'ın tam olarak taşınabilir olmayabileceğini unutmayın.


3

Buradaki bazı teknikleri birleştirerek en basit formu buldum:


2

Gcc kullanıyorsanız, şunları kullanmak mümkündür:

O zaman örneğin arayın


1

Mu Dynamics Araştırma Laboratuvarları - Blog Arşivi'ndeki fikirlere göz atın . Bunu bu yılın başlarında buldum - tam olarak karşılaştığım bağlamı unutuyorum - ve onu bu koda uyarladım. Ön tarafa bir E eklemenin yararlarını tartışabiliriz; ele alınan özel soruna uygulanabilir ancak genel bir çözümün parçası değildir. Bunu daha sonra istersem diye ilginç kod parçalarını sakladığım 'vinyetler' klasörümde sakladım. O sırada bu fikrin nereden geldiğine dair bir not tutmadığımı söylemekten utanıyorum.

Üstbilgi: paste1.h

Örnek kaynak:

C ön işlemcisinin dünyanın en temiz kullanımı olması zorunlu değildir - ancak materyalin birden çok kez yazılmasını engeller.



0

Enum dizini 0 tabanlıysa, adları bir char * dizisine koyabilir ve enum değeriyle dizine ekleyebilirsiniz.



0

Ben basit bir şablon sınıfı oluşturduk streamable_enumkullandığı operatörleri akışı olduğunu <<ve >>ve dayanmaktadır std::map<Enum, std::string>:

Kullanım:


0

Aşağıdaki özelliklere sahip makroları kullanan bir çözüm:

  1. numaralandırmanın her bir değerini yalnızca bir kez yazın, böylece sürdürülecek çift liste kalmaz

  2. enum değerlerini daha sonra #included olacak şekilde ayrı bir dosyada tutmayın, böylece istediğim yere yazabilirim

  3. numaralandırmanın kendisini değiştirmeyin, yine de enum türünün tanımlanmasını istiyorum, ancak buna ek olarak her bir enum adını karşılık gelen dizeyle eşleyebilmek istiyorum (eski kodu etkilememek için)

  4. arama hızlı olmalıdır, bu nedenle bu büyük numaralandırmalar için tercihen geçiş durumu olmamalıdır

https://stackoverflow.com/a/20134475/1812866


0

Yapıları ve sınıfları uyarlamak için Boost.Fusion gibi bir çözümün güzel olacağını düşündüm, hatta bir noktada numaralandırmaları füzyon dizisi olarak kullanmak için bile vardı.

Bu yüzden, numaralandırmaları yazdırmak üzere kodu oluşturmak için sadece bazı küçük makrolar yaptım. Bu mükemmel değildir ve Boost.Fusion tarafından oluşturulan standart kod ile görülecek hiçbir şey yoktur, ancak Boost Fusion makroları gibi kullanılabilir. Boost.Fusion'ın, yapı üyelerinin adlarını yazdırmaya izin veren bu altyapıya entegre olması için ihtiyaç duyduğu türleri gerçekten oluşturmak istiyorum, ancak bu daha sonra gerçekleşecek, şimdilik sadece makrolar:

#ifndef SWISSARMYKNIFE_ENUMS_ADAPT_ENUM_HPP
#define SWISSARMYKNIFE_ENUMS_ADAPT_ENUM_HPP

#include <swissarmyknife/detail/config.hpp>

#include <string>
#include <ostream>
#include <boost/preprocessor/cat.hpp>
#include <boost/preprocessor/stringize.hpp>
#include <boost/preprocessor/seq/for_each.hpp>


#define SWISSARMYKNIFE_ADAPT_ENUM_EACH_ENUMERATION_ENTRY_C(                     \
    R, unused, ENUMERATION_ENTRY)                                               \
    case ENUMERATION_ENTRY:                                                     \
      return BOOST_PP_STRINGIZE(ENUMERATION_ENTRY);                             \
    break;                                                                      

/**
 * \brief Adapts ENUM to reflectable types.
 *
 * \param ENUM_TYPE To be adapted
 * \param ENUMERATION_SEQ Sequence of enum states
 */
#define SWISSARMYKNIFE_ADAPT_ENUM(ENUM_TYPE, ENUMERATION_SEQ)                   \
    inline std::string to_string(const ENUM_TYPE& enum_value) {                 \
      switch (enum_value) {                                                     \
      BOOST_PP_SEQ_FOR_EACH(                                                    \
          SWISSARMYKNIFE_ADAPT_ENUM_EACH_ENUMERATION_ENTRY_C,                   \
          unused, ENUMERATION_SEQ)                                              \
        default:                                                                \
          return BOOST_PP_STRINGIZE(ENUM_TYPE);                                 \
      }                                                                         \
    }                                                                           \
                                                                                \
    inline std::ostream& operator<<(std::ostream& os, const ENUM_TYPE& value) { \
      os << to_string(value);                                                   \
      return os;                                                                \
    }

#endif

Aşağıdaki eski cevap oldukça kötü, lütfen bunu kullanmayın. :)

Eski cevap:

Enums bildirimi sözdizimini çok fazla değiştirmeden bu sorunu çözen bir yol arıyordum. Dizgeli bir enum bildiriminden bir dizge almak için önişlemciyi kullanan bir çözüme geldim.

Seyrek olmayan numaralandırmaları şu şekilde tanımlayabilirim:

Ve onlarla farklı şekillerde etkileşim kurabilirim:

Aşağıdaki tanımlara göre:

Seyrek enum için desteğe ihtiyacım olduğunda ve daha fazla zamanım olduğunda to_string ve from_string uygulamalarını boost :: xpressive ile geliştireceğim, ancak bu, gerçekleştirilen önemli şablonlama ve oluşturulan yürütülebilir dosya nedeniyle derleme süresine mal olacak gerçekten daha büyük olması muhtemel. Ancak bunun avantajı, bu çirkin manuel dize işleme kodundan daha okunabilir ve bakımı yapılabilir.: D

Aksi takdirde, enums değeri ve string arasında bu tür eşleştirmeleri gerçekleştirmek için her zaman boost :: bimap kullandım, ancak manuel olarak bakımı gerekiyor.


0

Tüm olağan nedenlerden dolayı makroları kullanmamayı tercih ettiğim için, enum bildirim makrosunu serbest tutma avantajına sahip daha sınırlı bir makro çözümü kullandım. Dezavantajları, her bir numaralandırma için makro tanımını kopyalayıp yapıştırmak ve numaralandırmaya değer eklerken açıkça bir makro çağrısı eklemek zorunda kalmayı içerir.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.