Reading Application Configuration Best Practices

In my desktop application, I am using QSettings to store various application values. For example, basic information about the form, recent documents, connection paragraphs and some things that change the behavior of the application, for example, "do not show this message again" in informational dialogs.

My usual way of accessing it is to read all settings in a structure or object when the application starts and save them when the application is closed.

I also have a dialog that allows me to edit many of the values ​​in the settings object and save them when the dialog is closed.

The values ​​in the settings object will be required by many windows and possibly non-visual objects.

What's the best way to do this?

I started with the settings object being included in the main window, but then I have a problem with other windows that need to be able to access the elements of the main window.

I really thought I could have a settings object created in its own cpp file and just #include where needed. But I'm not sure if this is possible or the syntax for this.

What's the best fit for this scenario?

+3


source to share


3 answers


I suggest to always use QSettings , avoiding the extra structure or class. You can include application name, organization name and domanin organization in your main.

From QtDocs:

If you are using QSettings from many places in your application, you can specify the organization name and application name using QCoreApplication :: setOrganizationName () and QCoreApplication :: setApplicationName (), and then use the default QSettings constructor:

QCoreApplication::setOrganizationName("MySoft");
QCoreApplication::setOrganizationDomain("mysoft.com");
QCoreApplication::setApplicationName("Star Runner");

      

And then use the default constructor where you need to activate the properties:

QSettings settings;

      



QSettings objects can be created either on the stack or on the heap (i.e. using a new one). The construction and destruction of a QSettings object is very fast.

You can set the setting wherever in the program:

If a parameter already exists with the same key, the existing value will be overwritten with the new value. For efficiency, changes cannot be immediately saved to persistent storage. (You can always call sync () to commit changes.)

You can also use it on different threads without issue:

QSettings is reentrant. This means that you can use a separate QSettings object in different threads at the same time. This guarantee is maintained even when QSettings objects refer to the same files on disk (or to the same entries in the system registry). If a setting is changed through a single QSettings object, the change will be immediately reflected in any other QSettings objects that run in the same place and that live in the same process.

QSettings can be safely used from different processes (which can be different instances of your application running at the same time or in different applications in general) to read and write to the same system locations. It uses message blocking and an intelligent merge algorithm to ensure data integrity. Note that sync () will import changes made by other processes (in addition to recording changes from this QSettings).

+5


source


QSettings is not thread safe. The documentation clearly states:

QSettings is reentrant. This means that you can use different QSettings objects on different threads at the same time. This guarantee is valid when QSettings objects refer to the same files on disk (or to the same entries in the system registry).



It is reentrant, but not thread safe. You cannot safely use the global QSettings object from multiple threads. If a thread calls beginGroup () and then the second thread also calls beginGroup (), then you have a problem because your QSettings object is prefixed incorrectly.

In streaming mode, you can use multiple QSettings objects for the same file. Therefore, you should create QSettings objects locally on the stack instead of sharing a single object globally to avoid race conditions.

+5


source


I see no problem.

There are two main options:

  • Create the object once and pass it to all windows: For simplicity, let's assume your settings object is of type Qsettings. Also, assume your application is not multithreaded. You create this object in your main function and pass it as a parameter for each window you create. All windows will #include <QSettings>

    and will know how to access it. The same happens if you have your own custom settings class. The interface has its own header and implementation of its own cpp. You create it alone and pass it to all windows.
  • As mentioned in the comments, you can also use singelton. Just create a static function for the settings class that returns the same instance that was declared static inside the function. eg:

class Settings
{
public:
    static Settings & TheSettings()
    {
        static Settings theSettings_;
        return theSettings_;
    }
    // Here go all methods for accessing the data
    // You will also want:
    ~Settings();
    Settings(const Settings &);
    Settings & operator=(const Settings &);
    // If you are using c++11 you may also want to add move constructor and assignment. 
    // Or you can just use the defaults for all the above if all your private members can destruct themselves  
private:
    Settings();
};

      

Here's the above with QSettings as a settings object:


class Settings
{
public:
    static QSettings & TheSettings()
    {
        static QSettings theSettings_;
        return theSettings_;
    }

private:
    Settings();
    Settings(const Settings &);
    Settings & operator=(const Settigns &);
};

      

Hope this puts you on the right track

PS. ... For multi-threaded applications, make sure that all internal data inside the "Settings" object is available in the critical section (or protection against mutual access)

PPS. Note that Miki mentions in the comments that QSettings is thread safe, so you don't need to wrap its access in a critical section. However, if you ever use a different underlying storage (be it a file, a different container), this will probably be necessary.

+1


source







All Articles