• Advertisement
Sign in to follow this  

Fun stuff with anonymous structs and unions

This topic is 4485 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

'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?

Share this post


Link to post
Share on other sites
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 file

Settings::Settings() : settingA(settings[0]), settingB(settings[1]), settingC(settings[2])
{
}

void Settings::LoadSettings(const char* file)
{
// Stuff
}

// Legacy code

float foo = Settings.settingA;
float bar = Settings.settingB;

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement