Java Enum emulation in C ++

I ported an application that I wrote some time ago from Java to C ++. One thing that I quickly realized was that the rich Java Enum (which were introduced in Java 5) were far superior to the Java resources presented above in C ++. C ++ 0x and later versions of C ++ 11 Strongly Typed Enumerations (the so-called enum classes) still do not provide the richness provided by Java Enums, and I could not find anything to emulate this object here.

I decided to try to imitate some functions like standing by classes, and I would like some of them to help implement this, perhaps using templates if necessary (it just seems like there should be a more general way to implement this), you will see that the ability to search for a specific Enum using a string name is quite fully implemented - (this is an emulation of the Java ENUM valueOf (String str) method - it works, but I'm sure this is far from optimal. The way I implemented Enum instances uses static const instances inside class - I'm on I ate it somewhere in Stack Overflow, but I can not remember exactly where - sorry.

The FYI application is an NMEA String parser, and here are a few of the most interesting Enum classes:

Here is the title

#ifndef _NMEASentence_h_ #define _NMEASentence_h_ // SYSTEM INCLUDES #include <stdint.h> #include <string> // APPLICATION INCLUDES // DEFINES // MACROS // EXTERNAL FUNCTIONS // EXTERNAL VARIABLES // CONSTANTS // STRUCTS // TYPEDEFS // FORWARD DECLARATIONS /** * Name: NMEASentence */ class NMEASentence { public: static const int MAX_LEN; static const char START; static const char CKSM_DELIM; static const char CR; static const char LF; NMEASentence( const std::string rPrefix, const std::string& rParams) : mPrefix(rPrefix) , mParams(rParams) {}; // make the class abstract virtual ~NMEASentence() = 0; protected: std::string mPrefix; std::string mParams; }; #endif // _NMEASentence_h_ 

Here is the CPP

 // SYSTEM INCLUDES // APPLICATION INCLUDES #include "vcdu/NMEASentence.h" // EXTERNAL FUNCTIONS // EXTERNAL VARIABLES // CONSTANTS // STATIC VARIABLE INITIALIZATIONS const int NMEASentence::MAX_LEN = 82; const char NMEASentence::START = '$'; const char NMEASentence::CKSM_DELIM = '*'; const char CR = '\r'; const char LF = '\n'; // implementation of the pure virtual dtor allowed // its a trick to allow class to be abstract NMEASentence::~NMEASentence() {}; 

Here is a subclass of the general class NMEASentence

 #ifndef _CDUMessage_h_ #define _CDUMessage_h_ // SYSTEM INCLUDES //#include <...> // APPLICATION INCLUDES #include "vcdu/NMEASentence.h" #include "vcdu/CDUEnumConstants.h" // DEFINES // MACROS // EXTERNAL FUNCTIONS // EXTERNAL VARIABLES // CONSTANTS // STRUCTS // TYPEDEFS // FORWARD DECLARATIONS /** * CDUMessage */ class CDUMessage : public NMEASentence { public: /** * 5 classifications of message types, The type specifies the * number and type of each parameter */ typedef enum CDUMessageSubType { Alive, Display, XYDisplay, Status, Keyboard, Configuration } CDUMessageSubType; /** * enumeration of the supported message types & their arg count */ static class CDUMessageType { public: static const CDUMessageType CDUAlive; // the following 3 messages are associated with the title line static const CDUMessageType CDUDisplayDataStatusBlock; static const CDUMessageType CDUDisplayTitle; static const CDUMessageType CDUDisplayPageNumber; // these messages are associated with the active display area static const CDUMessageType CDUDisplayScratchPad; static const CDUMessageType CDUDisplayLS1Text; static const CDUMessageType CDUDisplayLS2Text; static const CDUMessageType CDUDisplayLS3Text; static const CDUMessageType CDUDisplayLS4Text; static const CDUMessageType CDUDisplayLS5Text; static const CDUMessageType CDUDisplayLS6Text; static const CDUMessageType CDUDisplayLS1SText; static const CDUMessageType CDUDisplayLS2SText; static const CDUMessageType CDUDisplayLS3SText; static const CDUMessageType CDUDisplayLS4SText; static const CDUMessageType CDUDisplayLS5SText; static const CDUMessageType CDUDisplayLS6SText; static const CDUMessageType CDUDisplayRS1Text; static const CDUMessageType CDUDisplayRS2Text; static const CDUMessageType CDUDisplayRS3Text; static const CDUMessageType CDUDisplayRS4Text; static const CDUMessageType CDUDisplayRS5Text; static const CDUMessageType CDUDisplayRS6Text; static const CDUMessageType CDUDisplayRS1SText; static const CDUMessageType CDUDisplayRS2SText; static const CDUMessageType CDUDisplayRS3SText; static const CDUMessageType CDUDisplayRS4SText; static const CDUMessageType CDUDisplayRS5SText; static const CDUMessageType CDUDisplayRS6SText; // this is a special message to clear the screen buffer static const CDUMessageType CDUDisplayCLS; static const CDUMessageType CDUDisplayPutString; static const CDUMessageType CDUStatus; static const CDUMessageType CDUKeyboard; static const CDUMessageType CDUSet; static const CDUMessageType CDUGet; inline std::string getPrefix() const { return mPrefix; } inline CDUMessageSubType getMesageSubType() const { return mSubType; } inline virtual int getTextRowIndex() const { return mTextRowIndex; } inline JustifyStyle getJustifyStyle() const { return mJustifyStyle; } static std::vector<CDUMessageType>& getValues() { static std::vector<CDUMessageType> gValues; if (gValues.empty()) { gValues.push_back(CDUAlive); gValues.push_back(CDUDisplayDataStatusBlock); gValues.push_back(CDUDisplayTitle); gValues.push_back(CDUDisplayPageNumber); gValues.push_back(CDUDisplayScratchPad); gValues.push_back(CDUDisplayLS1Text); gValues.push_back(CDUDisplayLS2Text); gValues.push_back(CDUDisplayLS3Text); gValues.push_back(CDUDisplayLS4Text); gValues.push_back(CDUDisplayLS5Text); gValues.push_back(CDUDisplayLS6Text); gValues.push_back(CDUDisplayLS1SText); gValues.push_back(CDUDisplayLS2SText); gValues.push_back(CDUDisplayLS3SText); gValues.push_back(CDUDisplayLS4SText); gValues.push_back(CDUDisplayLS5SText); gValues.push_back(CDUDisplayLS6SText); gValues.push_back(CDUDisplayRS1Text); gValues.push_back(CDUDisplayRS2Text); gValues.push_back(CDUDisplayRS3Text); gValues.push_back(CDUDisplayRS4Text); gValues.push_back(CDUDisplayRS5Text); gValues.push_back(CDUDisplayRS6Text); gValues.push_back(CDUDisplayRS1SText); gValues.push_back(CDUDisplayRS2SText); gValues.push_back(CDUDisplayRS3SText); gValues.push_back(CDUDisplayRS4SText); gValues.push_back(CDUDisplayRS5SText); gValues.push_back(CDUDisplayRS6SText); gValues.push_back(CDUDisplayCLS); gValues.push_back(CDUDisplayPutString); gValues.push_back(CDUStatus); gValues.push_back(CDUKeyboard); gValues.push_back(CDUSet); gValues.push_back(CDUGet); } return gValues; } private: CDUMessageType(const std::string& rPrefix, const CDUMessageSubType& rSubType, const JustifyStyle& rJustifyStyle, const int& rTextRowIndex) : mPrefix (rPrefix) , mSubType (rSubType) , mJustifyStyle(rJustifyStyle) , mTextRowIndex(rTextRowIndex) {} std::string mPrefix; CDUMessageSubType mSubType; JustifyStyle mJustifyStyle; int mTextRowIndex; }; CDUMessageType getMessageType() const { return mMessageType; }; virtual ~CDUMessage(){}; protected: /** * Alternative Simplified Constructor * @param aMessageType * @param aParams */ CDUMessage(const CDUMessageType& rMessageType, const std::string& rParams) : NMEASentence (rMessageType.getPrefix(), rParams) , mMessageType (rMessageType) {}; CDUMessageType mMessageType; }; #endif // _CDUMessage_h_ 

And the corresponding CPP

 // SYSTEM INCLUDES //#include <...> // APPLICATION INCLUDES #include "vcdu/CDUMessage.h" // EXTERNAL FUNCTIONS // EXTERNAL VARIABLES // CONSTANTS // STATIC VARIABLE INITIALIZATIONS // this is the heartbeat message (not associated with any line => -1 for last paramter) const CDUMessage::CDUMessageType CDUMessage::CDUMessageType::CDUAlive ("PCDUALIVE", CDUMessage::Alive, JustifyStyle::Left, -1); // the following 3 messages are associated with the title line const CDUMessage::CDUMessageType CDUMessage::CDUMessageType::CDUDisplayDataStatusBlock("PCDUDSB", CDUMessage::Display, JustifyStyle::Left, 0); const CDUMessage::CDUMessageType CDUMessage::CDUMessageType::CDUDisplayTitle("PCDUTIT", CDUMessage::Display, JustifyStyle::Center, 0); const CDUMessage::CDUMessageType CDUMessage::CDUMessageType::CDUDisplayPageNumber("PCDUPGE", CDUMessage::Display, JustifyStyle::Right, 0); // these messages are associated with the active display area const CDUMessage::CDUMessageType CDUMessage::CDUMessageType::CDUDisplayScratchPad("PCDUSPD", CDUMessage::Display, JustifyStyle::Left, 13); const CDUMessage::CDUMessageType CDUMessage::CDUMessageType::CDUDisplayLS1Text("PCDUL1T", CDUMessage::Display, JustifyStyle::Left, 2); const CDUMessage::CDUMessageType CDUMessage::CDUMessageType::CDUDisplayLS2Text("PCDUL2T", CDUMessage::Display, JustifyStyle::Left, 4); const CDUMessage::CDUMessageType CDUMessage::CDUMessageType::CDUDisplayLS3Text("PCDUL3T", CDUMessage::Display, JustifyStyle::Left, 6); const CDUMessage::CDUMessageType CDUMessage::CDUMessageType::CDUDisplayLS4Text("PCDUL4T", CDUMessage::Display, JustifyStyle::Left, 8); const CDUMessage::CDUMessageType CDUMessage::CDUMessageType::CDUDisplayLS5Text("PCDUL5T", CDUMessage::Display, JustifyStyle::Left, 10); const CDUMessage::CDUMessageType CDUMessage::CDUMessageType::CDUDisplayLS6Text("PCDUL6T", CDUMessage::Display, JustifyStyle::Left, 12); const CDUMessage::CDUMessageType CDUMessage::CDUMessageType::CDUDisplayLS1SText("PCDUL1S", CDUMessage::Display, JustifyStyle::Left, 1); const CDUMessage::CDUMessageType CDUMessage::CDUMessageType::CDUDisplayLS2SText("PCDUL2S", CDUMessage::Display, JustifyStyle::Left, 3); const CDUMessage::CDUMessageType CDUMessage::CDUMessageType::CDUDisplayLS3SText("PCDUL3S", CDUMessage::Display, JustifyStyle::Left, 5); const CDUMessage::CDUMessageType CDUMessage::CDUMessageType::CDUDisplayLS4SText("PCDUL4S", CDUMessage::Display, JustifyStyle::Left, 7); const CDUMessage::CDUMessageType CDUMessage::CDUMessageType::CDUDisplayLS5SText("PCDUL5S", CDUMessage::Display, JustifyStyle::Left, 9); const CDUMessage::CDUMessageType CDUMessage::CDUMessageType::CDUDisplayLS6SText("PCDUL6S", CDUMessage::Display, JustifyStyle::Left, 11); const CDUMessage::CDUMessageType CDUMessage::CDUMessageType::CDUDisplayRS1Text("PCDUR1T", CDUMessage::Display, JustifyStyle::Right, 2); const CDUMessage::CDUMessageType CDUMessage::CDUMessageType::CDUDisplayRS2Text("PCDUR2T", CDUMessage::Display, JustifyStyle::Right, 4); const CDUMessage::CDUMessageType CDUMessage::CDUMessageType::CDUDisplayRS3Text("PCDUR3T", CDUMessage::Display, JustifyStyle::Right, 6); const CDUMessage::CDUMessageType CDUMessage::CDUMessageType::CDUDisplayRS4Text("PCDUR4T", CDUMessage::Display, JustifyStyle::Right, 8); const CDUMessage::CDUMessageType CDUMessage::CDUMessageType::CDUDisplayRS5Text("PCDUR5T", CDUMessage::Display, JustifyStyle::Right, 10); const CDUMessage::CDUMessageType CDUMessage::CDUMessageType::CDUDisplayRS6Text("PCDUR6T", CDUMessage::Display, JustifyStyle::Right, 12); const CDUMessage::CDUMessageType CDUMessage::CDUMessageType::CDUDisplayRS1SText("PCDUR1S", CDUMessage::Display, JustifyStyle::Right, 1); const CDUMessage::CDUMessageType CDUMessage::CDUMessageType::CDUDisplayRS2SText("PCDUR2S", CDUMessage::Display, JustifyStyle::Right, 3); const CDUMessage::CDUMessageType CDUMessage::CDUMessageType::CDUDisplayRS3SText("PCDUR3S", CDUMessage::Display, JustifyStyle::Right, 5); const CDUMessage::CDUMessageType CDUMessage::CDUMessageType::CDUDisplayRS4SText("PCDUR4S", CDUMessage::Display, JustifyStyle::Right, 7); const CDUMessage::CDUMessageType CDUMessage::CDUMessageType::CDUDisplayRS5SText("PCDUR5S", CDUMessage::Display, JustifyStyle::Right, 9); const CDUMessage::CDUMessageType CDUMessage::CDUMessageType::CDUDisplayRS6SText("PCDUR6S", CDUMessage::Display, JustifyStyle::Right, 11); // these messages are not associated with a paricular line# which is why we specify -1 for the last parameter const CDUMessage::CDUMessageType CDUMessage::CDUMessageType::CDUDisplayCLS("PCDUCLS", CDUMessage::Display, JustifyStyle::Left, -1); const CDUMessage::CDUMessageType CDUMessage::CDUMessageType::CDUDisplayPutString("PCDUPUTS", CDUMessage::XYDisplay, JustifyStyle::None, -1); const CDUMessage::CDUMessageType CDUMessage::CDUMessageType::CDUStatus("PCDUSID", CDUMessage::Status, JustifyStyle::Left, -1); const CDUMessage::CDUMessageType CDUMessage::CDUMessageType::CDUKeyboard("PCDUKEY", CDUMessage::Keyboard, JustifyStyle::Left, -1); const CDUMessage::CDUMessageType CDUMessage::CDUMessageType::CDUSet("PCDUSETV", CDUMessage::Configuration, JustifyStyle::Left, -1); const CDUMessage::CDUMessageType CDUMessage::CDUMessageType::CDUGet("PCDUGETV", CDUMessage::Configuration, JustifyStyle::Left, -1); 

And just to show a general example of using Enums here, we have some more Enum C ++ classes that I need to use throughout the application. They all look pretty similar, and I can't help but feel that there should be an easier and less detailed way to implement this. Any help or ideas would really be welcome.

 class JustifyStyle { public: static const JustifyStyle Left, Center, Right, None; inline std::string getName() const { return mName; } private: JustifyStyle(const std::string& rName) : mName(rName) {} std::string mName; }; class FontSize { public: static const FontSize F1, F2, F3, F4, F5, F6; inline std::string getName() const { return mName; } static std::vector<FontSize>& getValues() { static std::vector<FontSize> gValues; if (gValues.empty()) { gValues.push_back(F1); gValues.push_back(F2); gValues.push_back(F3); gValues.push_back(F4); gValues.push_back(F5); gValues.push_back(F6); } return gValues; } private: FontSize(const std::string& rName) : mName(rName) {} std::string mName; }; class FontStyle { public: static const FontStyle S, B, I, U, N; inline std::string getName() const { return mName; } static std::vector<FontStyle>& getValues() { static std::vector<FontStyle> gValues; if (gValues.empty()) { gValues.push_back(S); gValues.push_back(B); gValues.push_back(I); gValues.push_back(U); gValues.push_back(N); } return gValues; } inline bool operator<(const FontStyle& rhs) const { return mName < rhs.mName; } private: FontStyle(const std::string& rName) : mName(rName) {} std::string mName; }; class FontColor { public: static const FontColor BLACK, CYAN, RED, YELLOW, GREEN, MAGENTA, AMBER, WHITE; inline int getValue() const { return mValue; } inline std::string getValueStr() const { return UtlStringUtils::integerToString(mValue); } static std::vector<FontColor>& getValues() { static std::vector<FontColor> gValues; if (gValues.empty()) { gValues.push_back(BLACK); gValues.push_back(CYAN); gValues.push_back(RED); gValues.push_back(YELLOW); gValues.push_back(GREEN); gValues.push_back(MAGENTA); gValues.push_back(AMBER); gValues.push_back(WHITE); } return gValues; } private: // constructor FontColor(const int& rValue) : mValue(rValue) {} int mValue; }; class CDUFontChar { public: // constructor CDUFontChar ( const char cduChar = '\0', const FontSize& rSize = FontSize::F3, const std::set<FontStyle>& rStyles = std::set<FontStyle>(), const FontColor& rColor = FontColor::WHITE) : mCDUChar (cduChar) , mSize (rSize) , mFontStyles(rStyles) , mColor(rColor) {} inline char getCDUChar() const { return mCDUChar; } inline FontSize getSize() const { return mSize; } inline std::set<FontStyle> getStyles() const { return mFontStyles; } inline FontColor getColor() const { return mColor; } private: char mCDUChar; FontSize mSize; std::set<FontStyle> mFontStyles; FontColor mColor; }; 
+7
source share
2 answers

I had the opportunity to clarify the approach that I used to model Java as enumerations in C ++, since I raised the question of stack overflow. The code shown below (in the header and implementation cpp file), as well as a code snippet showing how the enumerations can be used in the switch statement, is what I used in my code. Subtleties lie in the use of the integral casting operator (which in this case is carried out using the user-defined Value operator, used as follows.

 // integral operator cast for switch statements (cast to named enum) inline operator const Value() const { return mValue; } 

In the header file CDUEnumConstants.h :

 #ifndef _cduEnumConstants_h_ #define _cduEnumConstants_h_ // SYSTEM INCLUDES #include <set> #include <map> #include <vector> #include <string> #include <algorithm> #include <iostream> // APPLICATION INCLUDES #include "util/UtlExceptions.h" #include "util/UtlStringUtils.h" // DEFINES // MACROS // EXTERNAL FUNCTIONS // EXTERNAL VARIABLES // CONSTANTS // STRUCTS // TYPEDEFS class Annunciator { public: enum Value { // these are all undefined in the discrete sense undefined=-1, power=-2, fail=-3, // these are discrete bits exec=(1<<6), dspy=(1<<7), msg=(1<<8), ofst=(1<<9) }; static const Annunciator AnnunciatorUNDEFINED; static const Annunciator AnnunciatorPOWER; static const Annunciator AnnunciatorFAIL; static const Annunciator AnnunciatorEXEC; static const Annunciator AnnunciatorDSPY; static const Annunciator AnnunciatorMSG; static const Annunciator AnnunciatorOFST; // used to store in a set inline bool operator<(const Annunciator& rhs) const { return mValue < rhs.mValue; } // used for comparisons inline bool operator==(const Annunciator& rhs) const { return mValue == rhs.mValue; } inline bool supported() const { return mValue > 0; } // integral operator cast for switch statements (cast to named enum) inline operator const Value() const { return mValue; } inline std::string getStringVal() const { return mStringVal; } static const std::set<Annunciator>& getValues() { static std::set<Annunciator> gValues; if (gValues.empty()) { gValues.insert(AnnunciatorUNDEFINED); gValues.insert(AnnunciatorPOWER); gValues.insert(AnnunciatorFAIL); gValues.insert(AnnunciatorEXEC); gValues.insert(AnnunciatorDSPY); gValues.insert(AnnunciatorMSG); gValues.insert(AnnunciatorOFST); } return gValues; } static Annunciator valueOf(const std::string& rStringVal) { for (const auto& next : getValues()) { if (next.getStringVal() == rStringVal) { return next; } } throw new UtlIllegalArgumentException( "Illegal Argument: " + rStringVal); } private: Annunciator(const Value& rValue, const std::string& rStringVal) : mValue(rValue) , mStringVal(rStringVal) {} Value mValue; std::string mStringVal; }; // FORWARD DECLARATIONS // Extendable in the future class CDUVariable { public: enum Value { baud, firmware, type, serial }; static const CDUVariable Baud, Firmware, Type, Serial; // used to store in a set inline bool operator<(const CDUVariable& rhs) const { return mValue < rhs.mValue; } // used for comparisons inline bool operator==(const CDUVariable& rhs) const { return mValue == rhs.mValue; } // integral operator cast for switch statements (cast to named enum) inline operator const Value() const { return mValue; } inline std::string getStringVal() const { return mStringVal; } static const std::set<CDUVariable>& getValues() { static std::set<CDUVariable> gValues; if (gValues.empty()) { gValues.insert(Baud); gValues.insert(Firmware); gValues.insert(Type); gValues.insert(Serial); } return gValues; } static CDUVariable valueOf(const std::string& rStringVal) { for (const auto& next : getValues()) { if (next.getStringVal() == rStringVal) { return next; } } throw new UtlIllegalArgumentException( "Illegal Argument: " + rStringVal); } private: CDUVariable(const Value& rValue, const std::string& rStringVal) : mValue(rValue) , mStringVal(rStringVal) {} Value mValue; std::string mStringVal; }; #endif // _cduEnumConstants_h_ 

and in the CDUEnumConstants.cpp file, where initial enumeration values ​​are initialized.

 const Annunciator Annunciator::AnnunciatorUNDEFINED(Annunciator::undefined, "UNDEFINED"); const Annunciator Annunciator::AnnunciatorPOWER(Annunciator::power, "POWER"); const Annunciator Annunciator::AnnunciatorFAIL(Annunciator::fail, "FAIL"); const Annunciator Annunciator::AnnunciatorEXEC(Annunciator::exec, "EXEC"); const Annunciator Annunciator::AnnunciatorDSPY(Annunciator::dspy, "DSPY"); const Annunciator Annunciator::AnnunciatorMSG(Annunciator::msg, "MSG"); const Annunciator Annunciator::AnnunciatorOFST(Annunciator::ofst, "OFST"); const CDUVariable CDUVariable::Baud(CDUVariable::baud, "0"); const CDUVariable CDUVariable::Firmware(CDUVariable::firmware, "1"); const CDUVariable CDUVariable::Type(CDUVariable::type, "2"); const CDUVariable CDUVariable::Serial(CDUVariable::serial, "3"); 

Now, to use the CDUVariable enumeration in the switch statement in the user code (ignore the other method data), use the following.

 std::unique_ptr<EngravityMessage> VCDUManager::getConfigValue( const CDU_ID& rCduID, const std::unique_ptr<EngravityMessage>& rVarNameMessage) { EngravityConfigMessage* pVariableName = static_cast<EngravityConfigMessage*>(rVarNameMessage.get()); gpLogManager->add(FAC_PROCESS, PRI_NOTICE, "CDU_%d: getConfigValue\n", rCduID); CDUVariable variable = pVariableName->getCDUVariable(); std::unique_ptr<EngravityMessage> variableValue; switch (variable) { case CDUVariable::baud: variableValue = std::unique_ptr<EngravityMessage>( new EngravityConfigMessage ( EngravityMessageType::SetV, variable, "38400")); break; case CDUVariable::firmware: variableValue = std::unique_ptr<EngravityMessage>( new EngravityConfigMessage ( EngravityMessageType::SetV, variable, "1.04")); break; case CDUVariable::type: variableValue = std::unique_ptr<EngravityMessage>( new EngravityConfigMessage ( EngravityMessageType::SetV, variable, "737-NG")); break; case CDUVariable::serial: variableValue = std::unique_ptr<EngravityMessage>( new EngravityConfigMessage ( EngravityMessageType::SetV, variable, "0074")); break; default: break; } return variableValue; } 

I presented 2 Enums examples for two reasons, firstly, I wanted to show how to search for the Enum value from a string that is optimized for using a sorted set, and secondly, here, it seems that the template prompts template programming - but I don’t it was possible to implement it as a template - in fact, I'm not sure if this is possible.

+2
source

Basically, Java enum is a language function designed to cross the bridge between good practice, constant and bad practice, or the singleton antipattern. C ++ makes enum into boring integer constants, and Java makes them fully object oriented singleton, but hopes the developer will keep their const companies. So yes, you are exactly right - the path to porting is to use C ++ global constants. Strictly speaking, Java enums do not have to be constants, for example. they may have non-final fields, but I would (and think so) consider this as bad practice, so all methods in your C ++ enumerations should be semantically const .

+5
source

All Articles