TransWikia.com

In C++, is it possible to make a variable simultaneously easy to access and easy to iterate over?

Stack Overflow Asked by nintendoeats on December 15, 2020

I am refactoring a "profiles" system with the goal of reducing the amount of repeated code required to save, load, and access settings. However, I am finding that I am just shuffling the repeated code around (decreasing it in one place increases it in another). I feel like I am missing something.

Note, in this scheme there is a concept of the "default" profile, which is selectively masked by the "loaded" profile. The user can configure all settings in the default profile, then just change specific settings for other profiles. For example, the user might want to use the same port number for all profiles (defined in the default profile), but specify a different IP address for connecting to different computers (defined in the loaded profile).
This functionality is not actually implemented yet, it is part of the reason I am refactoring.

Each setting has to be associated with the following information:

  1. A string used to identify it in XML.
  2. An int, bool, or string, which specifies the actual setting value.
  3. A boolean, which specifies whether to use the above value or the default.

Initially, I defined all of these as member variables of a struct. This is very easy to use for accessing settings. I can just pass the profile struct to any function that needs the settings, then access a particular setting using profile.thingy.

Unfortunately, saving and loading settings in this scheme requires adding separate save/load code for each setting. Similarly, to generate a "merged" settings profile that combines the default and current profiles will require manually coding merging for each setting. I also needed to add separate "useDefault" and "xmlName" variables for each setting. It was all a bit silly.

To start with, I implemented a class template SerfSetting which contains the 3 pieces of data mentioned earlier.

    template <class T>
class SerfSetting {
public: 
    T value;
    string xmlName;
    bool useDefault = false;

This is a little bit more annoying to access, since now I need to write profile.setting.value everywhere, but that’s not so bad. I also overloaded = and == to operate on value instead of the object itself, so sometimes this is transparent.

The real problem is when I try to turn this into something iterable, to save myself from having to repeat S/L/M (saving/loading/merging) code for every new setting.

What I’m looking at right now is using std::any, or a separate std::vector or std::map for each type of data. For S/L/M these would be perfect, very simple to iterate over. However, accessing a setting then becomes profile.listOfSettings[SETTING_INDEX_CONSTANT].value (or similar), which is pretty absurd for a common task and adds a potential for runtime bugs that might be difficult to catch (since I need to manually maintain the setting index constants, and manually ensure that the correct setting is being accessed).

Ideally I would be able to just iterate over the member variables of the profile struct, but the internet is quite clear that "C++ does not support reflection" and the alternatives I have seen are just as contrived and ugly as what I described above.

Is there a sensible solution to this problem, or am I resigned to having something ugly somewhere?

Thanks!

One Answer

I appreciate your comments, and am happy to now be aware of those tools (though I'm not sure std::optional applies in this case, since all values are expected to be non-null when they are accessed).

As often happens, taking the time to clearly state the problem in words helped me to solve it. I realized after posting, I was making the false assumption that I needed to access and iterate over the settings using the same object.

I believe that the simple solution is this:

struct SerfProfile {

    vector<SerfSetting<string>*> stringPtrList;

    SerfSetting<string> IPaddress = SerfSetting<string>();

    SerfProfile() {
        stringPtrList.push_back(&IPaddress);
    }
};

I can access the setting using IPaddress.value and iterate over the settings using stringPtrList . This also makes it easy to make profile settings that are not S/L/M, if I ever need to do that for some reason.

I appreciate you taking the time to look over this problem.

Answered by nintendoeats on December 15, 2020

Add your own answers!

Ask a Question

Get help from others!

© 2024 TransWikia.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP