Fun stuff with anonymous structs and unions

Started by
4 comments, last by ElectroDruid 18 years, 6 months ago
'Ello, I'm reworking a bit of code which was held together with far too many (150+) #defines, which meant recompiling every time someone wants to tweak a value. What I'm trying to do is turn all of the #defines into actual variables which read their values from a text file at runtime without changing the code that uses these values. The idea was to use an anonymous union containing a couple of anonymous structs, like this:
static union
{
    struct
    {
        float SOME_VALUE;
        float SOME_OTHER_VALUE;
        // More float variable names...

        int   SOME_INTEGER_VALUE;
        // More int variable names
    };

    struct
    {
        float anArrayOfFloats[NUMBER_OF_FLOAT_VARIABLES];
        int   anArrayOfInts[NUMBER_OF_INT_VARIABLES];
    };
};
The idea being that the initialisation code can just iterate through the text file containing the values (which are in the same order that the variables are declared in the first anonymous struct), and plug them into the arrays, and then the rest of the code can use the actual variable names as if they were the #defines they used to be. So far so good. It seems to work fine under Visual Studio, with the one irritating feature that the watch window in the debugger refuses to print values for either the names themselves (SOME_VALUE, etc) or via the arrays (anArrayOfFloats[0], etc). Is there any way to persuade the debugger to do this? Also, are other C++ compilers likely to struggle with this sort of code for any reason? If so, what's the cleanest way I can convert this code from using #defines to reading values from a file?
"We two, the World and I, are stubborn fellows at loggerheads, and naturally whichever has the thinner skull will get it broken" - Richard Wagner
Advertisement
A cleaner, more general-purpose solution would be to create a Settings class that reads in key/value pairs from a file and stores them in some sort of internal structure (like an std::map). Then you just access the map to get the values you need. The downside here is that you need to store the data in a flexible representation, such as strings, so that they can be converted down into the type you want (based on a template member function for instance). But then you incur overhead every time you want to access a value since it needs to be converted. If you have a bunch of values of the same type, that's inefficient. A second solution is to template the Settings class so that it only stores data of a single type, but I personally don't like the idea of requiring a separate object for each data type, since an object that supposedly contains "settings" should contain all the settings. But that's just me. Finally, if you know exactly what data and values is to be stored (which sounds like the case here if you're just converting your #define list into something nicer), then you can create the Settings class to utilize tag dispatching. This way, you can still have the data loaded at run-time, but have references into the Settings object will be evaluated at compile-time.

Or, for simplicities sake, just create a wrapper class around all those values that also has file-loading services. You will have the arrays in addition to individual members like you have have now, but only not in anonymous struct's or union's. Load the data into the arrays, but have individual data members be references into the array. Then you don't need any template magic at all, just an amazingly huge initializer list [smile]

class Settings{public:  Settings();  float& settingA;  float& settingB;  float& settingC;  void LoadSettings(const char* file);private:  float settings[3];};// Another fileSettings::Settings() : settingA(settings[0]), settingB(settings[1]), settingC(settings[2]){}void Settings::LoadSettings(const char* file){   // Stuff}// Legacy codefloat foo = Settings.settingA;float bar = Settings.settingB;
How about something like:

class Setting{public:    /* Various get/set methods, constructor and destructor */private:    std::string name;    int type; /* Probably an enum really */    union    {        int integer;        double real;        std::string *string;        /* etc. */    } value;};


Edit: sorry I don't think that was very helpful. I didn't read it very carefully.

Try:

int *int_setting_list[] ={&SOME_INTEGER_VALUE,&SOME_OTHER_INTEGER_VALUE};float *float_setting_list[] ={&SOME_FLOAT_VALUE,&SOME_OTHER_FLOAT_VALUE};


It's a bit of a C hack, but it's better style than your solution. As for compilers struggling with your original code alignment won't come into consideration - the only possibility would be an aliasing issue but I can't immediately think of code to make it happen if you only read them in once. You'd have to do something like:

anArrayOfInts[0] = 0;int a = anArrayOfInts[0];SOME_INTEGER_VALUE = 1;int b = anArrayOfInts[0];


In that case b could well contain the value 0 instead of 1, but I don't think you're going to be doing this.
Quote:Original post by ElectroDruid
Also, are other C++ compilers likely to struggle with this sort of code for any reason?


Yes, anonymous struct/class/unionses are a non-standard compliant language extension, that code is not portable between C++ compilers other than the ones that do support this language extension.

Quote:Original post by ElectroDruid
If so, what's the cleanest way I can convert this code from using #defines to reading values from a file?


I didn't find what you are trying to do very clear but it seems like you want some kind of property map if that is the case then check this.

Quote:Original post by ZQJ
How about something like:
*** Source Snippet Removed ***


You may aswell use boost::variant (which is a discriminated union) for this. union members can only be of POD-type (not the case for boost::variant) and it can be error prone manually managing type tags.
Quote:Original post by snk_kid
Quote:Original post by ElectroDruid
Also, are other C++ compilers likely to struggle with this sort of code for any reason?


Yes, anonymous struct/class/unionses are a non-standard compliant language extension, that code is not portable between C++ compilers other than the ones that do support this language extension.

Not to mention variable length arrays (C99, but not C++).

Enigma
ZQJ's "C hack" did the job rather nicely, after a bit of playing about. The only slightly annoying thing is that (unless I've misunderstood something) you have to declare all the variable names, then list them again as references in the arrays, and also extern a bunch of them in the header file (for other modules that use some of the values too). The system works but I can picture it becoming a bit of a maintenance headache further on down the line. Still, losing those anonymous structs and union means the debugger copes with it all properly now, and the code should be more portable at least.
"We two, the World and I, are stubborn fellows at loggerheads, and naturally whichever has the thinner skull will get it broken" - Richard Wagner

This topic is closed to new replies.

Advertisement