Converting Enums to Strings for QPrinter



  • I want to be able to convert the QPrinter enums to strings so I can pass them to lp. There are lots of examples using QMetaObject to do this. Unfortunately "'staticMetaObject' is not a member of 'QPrinter'". Any ideas?


  • Lifetime Qt Champion

    Hi,

    Indeed QPrinter is not a QObject based class. One thing you can do is to write conversion functions that maps the enum you need to QStrings.



  • Hi
    I've created the following code to enable easy conversion between enum values and strings:

    #define DECLARE_ENUM_TYPE(NAME, ...) \
    	class NAME { \
    	public: \
    		typedef enum { \
    			Invalid ## NAME = -1, \
    			___LOWER_BOUND___ = -1, \
    			__VA_ARGS__,\
    			___UPPER_BOUND___, \
    			___VALUES_COUNT___ = ___UPPER_BOUND___, \
    		} enum_t; \
    		\
    		NAME () \
    			: _value (Invalid ## NAME) {} \
    		explicit NAME (enum_t i_value): \
    			_value (i_value) {} \
    		\
    		NAME (const NAME& i_other): \
    			_value (i_other._value) {} \
    		\
    		NAME (const char* i_name): \
    			_value (Invalid ## NAME) { \
    			std::string name (i_name); \
    			\
    			if (textValueMap ().count (name)) { \
    				_value = textValueMap ()[name]; \
    			} \
    		} \
    		\
    		operator const char*() const { \
    			char* text = "Invalid";\
    			\
    			if (Invalid ## NAME != _value) { \
    				text = (char*)(&(valueTexts ()[valueTextIndexes () [_value]])); \
    			} \
    			\
    			return text; \
    		} \
    		\
    		operator enum_t() const { \
    			return _value; \
    		} \
    		\
    		NAME& operator= (const NAME& i_other) { \
    			_value = i_other._value; \
    			return *this; \
    		} \
    		NAME& operator= (const enum_t& i_value) { \
    			_value = i_value; \
    			return *this; \
    		} \
    		\
    		\
    	private: \
    		const char* valueTexts () const { \
    			static const char* s_valueTexts = buildValueTexts (); \
    			\
    			return s_valueTexts; \
    		} \
    		\
    		const size_t* valueTextIndexes () const { \
    			static const size_t* s_valueTextIndexes = buildValueTextIndexes (); \
    			\
    			return s_valueTextIndexes; \
    		} \
    		\
    		std::map< std::string, enum_t > textValueMap () const { \
    			static std::map< std::string, enum_t > s_textValueMap (buildTextValueMap ()); \
    			\
    			return s_textValueMap; \
    		}\
    		\
    		size_t* buildValueTextIndexes () const { \
    			static size_t s_valueTextIndexes[___VALUES_COUNT___]; \
    			\
    			s_valueTextIndexes [0] = 0; \
    			\
    			char comaSeparatedValueText[] = #__VA_ARGS__; \
    			\
    			for (size_t \
    					charIndex (1), \
    					maxIndex (strlen (comaSeparatedValueText)), \
    					valueForText (1); \
    				charIndex < maxIndex; \
    				++charIndex) { \
    				if (((',' == comaSeparatedValueText [charIndex - 1]) || \
    					(' ' == comaSeparatedValueText [charIndex - 1])) && \
    					((',' != comaSeparatedValueText [charIndex]) && \
    					(' ' != comaSeparatedValueText [charIndex]))) { \
    					s_valueTextIndexes [valueForText] = charIndex; \
    					valueForText += 1; \
    				} \
    			} \
    			\
    			return s_valueTextIndexes; \
    		} \
    		\
    		char* buildValueTexts () const { \
    			static char s_valueTexts[] = #__VA_ARGS__; \
    			\
    			for (size_t \
    					charIndex (0), \
    					maxIndex (strlen (s_valueTexts)); \
    				charIndex < maxIndex; \
    				++charIndex) { \
    				if (',' == s_valueTexts [charIndex]) { \
    					s_valueTexts [charIndex] = 0; \
    				} \
    			} \
    			\
    			return s_valueTexts; \
    		} \
    		\
    		std::map< std::string, enum_t > buildTextValueMap () const { \
    			static std::map< std::string, enum_t > s_textValueMap; \
    			\
    			for ( \
    				size_t value = 0; \
    				value < ___VALUES_COUNT___; \
    				++value) { \
    				s_textValueMap [&(valueTexts ()[valueTextIndexes () [value]])] = enum_t (value); \
    			} \
    			\
    			return s_textValueMap; \
    		} \
    		\
    		\
    		enum_t _value; \
    	};
    Obviously, it does not do exactly what you want, but you may try to use it as startup point for your solution.
    


  • Looks fascinating, but I'm having a hard time seeing the forest for the trees. Do you have sample code which calls this macro?


  • Qt Champions 2016

    @DougyDrumz
    Maybe try something simpler than generating classes with macros? This should suffice:

    // These two statics you could also put in your class if you wish (it'd be better even)
    static QPair<EnumType, QString> nameTable[] = {
        qMakePair(EnumValue1, QStringLiteral("EnumValue1")),
        qMakePair(EnumValue2, QStringLiteral("EnumValue2"))
    };
    static const qint32 EnumTypeValuesNumber = 2;
    
    class MyClass
    {
        QHash<EnumType, QString> enumToNameHash;
        QHash<QString, EnumType> nameToEnumHash;
    
        QString enumToString(EnumType enumValue)
        {
            if (enumToNameHash.isEmpty())  {
                // Lazy init the hash table
                enumToNameHash.reserve(EnumTypeValuesNumber);
                for (qint32 i = 0; i < EnumTypeValuesNumber; i++)
                    enumToNameHash.insert(nameTable[i].first, nameTable[i].second);
            }
    
            return enumToNameHash.value(enumValue);
        }
    
        EnumType stringToEnum(QString enumName)
        {
            if (nameToEnumHash.isEmpty())  {
                // Lazy init the hash table
                nameToEnumHash.reserve(EnumTypeValuesNumber);
                for (qint32 i = 0; i < EnumTypeValuesNumber; i++)
                    nameToEnumHash.insert(nameTable[i].second, nameTable[i].first);
            }
    
            return nameToEnumHash.value(enumName);
        }
    
        // ...
    };
    

    As a side note, you could also simplify it a bit if your enums are ordered and are starting from 0. Then one of the hash tables isn't needed. Additionally, you could put one value at the end of the enum to serve as the number of values constant instead of creating its own variable, e.g:

    enum MyEnumType  {
        EnumValue1 = 0, EnumValue2, EnumTypeValuesNumber
    }
    

    Kind regards.



  • I'm a little confused here. What both of you appear to be doing is making a mapping between an enum value and a string representing that value when a class is created. Please correct me if I'm wrong. What I want to do is extract the string representation of an enum value for a class that already exists - in my case QPrinter. The staticMetaObject is a great paradigm for this. Unfortunately there is no staticMetaObject for QPrinter. Do I need to parse the OPrinter header file and grab the strings there? I've seen examples similar to that around.

    I was hoping for some simple magic, but as we know, there is no magic.


  • Qt Champions 2016

    @DougyDrumz
    Well, I'm not aware of any other way ... an enum is an integer wrapped in a nice language construct, nothing else. In the end the moc will do it in similar fashion, that's why you need to use all that macros for the meta-object system ...



  • @DougyDrumz Sorry for making you wait: yesterday forum was down right after your question, and today I've just got to computer.

    // Definition of enum class
    DECLARE_ENUM_TYPE (
    	DatasetElementType,
    	int8_t,
    	uint8_t,
    	int16_t,
    	uint16_t,
    	int32_t,
    	uint32_t,
    	int64_t,
    	uint64_t,
    	float_t,
    	double_t
    )
    

    Then you can use it anewhere in your code:

    DatasetElementType_t value (DatasetElementType_t::uint8_t);
    
    qDebug () << value; // Output: uint8_t
    
    value = DatasetElementType ("uint16_t"); // Create by enum element name, for example, during deserialization.
    
    qDebug () << value; // Output: uint16_t
    

    I've undestud that you want to convert to string and vise versa values from existing enum. Ofcourse, the solution that I've proposed is not exactly the thing you want. However, you may try to implement something like this to be able to wrap QPrinter enum. I don't know any other sane solution for this problem.



  • @kshegunov Hi.
    Basicaly you are wrong and my solution is much easier. The reason for this is pretty simple: proposed macros may be created and debuged once and then anyone may use it for as much as he wants. And addition of new values in enum does not require to change some other code to support it - in you solution you have to edit nameTable every time you add enum members. However my solution lacks ability to create enums where values start not from 0.


  • Qt Champions 2016

    @Wilk

    Basicaly you are wrong and my solution is much easier. The reason for this is pretty simple: proposed macros may be created and debuged once and then anyone may use it for as much as he wants.

    Surely, why bother with functions if you can use macros ...



  • @kshegunov
    I don't understand you. Macros is a powerfull tool which can be reliable is used in a right way. Just take a look at Qt itself and you will find out quite a lot of macros.


  • Qt Champions 2016

    @Wilk
    This is beside the scope of this thread and to be honest I don't want to enter in fruitless discussions. Whatever the reason is that you think using macros is best in this case I don't have a problem with, in any case it's your choice how you will do your coding.



  • Let me tone down this discussion by saying that the original reason for my question has disappeared. I misunderstood how QPrinter worked. I thought I would need the string versions of the QPrintDialog enums to pass on to lp. Turns out QPrinter does what I need to do automatically and I don't need to call lp. Thanks for the lively discussion though.


Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.