Application Settings - c++

What is a fairly standard way for storing application settings, mainly for windows but also easy to move over to other platforms.
There's basically 4 groups of settings I want to have:
Global settings, affects all users, and may be moved between machines
Global system settings, affects all users, but specific to that system (eg defaults for that system, eg graphics options)
User settings, user settings that are moved between systems (eg sound volume)
User system settings, user settings specific to that system (eg graphics options that are hardware dependent)
Each level overrides the previous level, allowing for "global settings" to be largly the applications defaults, with user settings storing the options the user chose.
The first two will basically be defaults where there is no user setting (eg for a new user).
I considered implementing a set of functions, which I could then implement for the different systems (likely to be through ini files), but is this the best way?
(c++)
namespace config
{
void Init(const std::string &AppName);
//updates config for keys/sections that don't exist (ie don't overwrite changes by advanced users by rewriting the entire file)
void Defaults (std::map<std::string,std::map<std::string,std::string> > &Map);
void SystemDefaults (std::map<std::string,std::map<std::string,std::string> > &Map);
void Set (const std::string &Section, const std::string &Key, const std::string &Value);
void SetSystem (const std::string &Section, const std::string &Key, const std::string &Value);
void SetUser (const std::string &Section, const std::string &Key, const std::string &Value);
void SetUserSystem (const std::string &Section, const std::string &Key, const std::string &Value);
std::string GetValue (const std::string &Section, const std::string &Key);
}
I know windows has a set of directories for such settings, but are these the correct dirs for my needs?
EDIT: I would rather go with files (ini or xml), rather than using say the windows registery. However wheres the best places to put these config files under each OS?
Under Vista I found these, which seem to fit my groups, however what of older windows versions (I need to support win2000, XP, etc), and does mac/linux have there own simelar folders?
Global settings - <SYSDRIVE>\Users\Default\Appdata\Roaming
Global system settings - <SYSDRIVE>\Users\Default\Appdata\Local
User settings - <SYSDRIVE>\Users\<USER>\AppData\Roaming
User system settings - <SYSDRIVE>\Users\<USER>\AppData\Local

If you are a boost user, you might take a look at the program options library, it supports using config files as well as environment variables and (of course) command line options.
It is designed to be portable, so that should ease your cross-platform headaches.

See also Where should cross-platform apps keep their data?

There are (at least) three reasonable choices:
Registry: This is my least favorite because of portability and relative opacity.
Environment variables: I recommend using one (just one) that points to a place where your material is kept - an "installation directory" or some such.
Files: Both a/the user-home directory (or in a subdirectory thereof) and project/product directory are suitable for storing things.
You might want to use a simple keyword=value paradigm, and basic rules so your variables - settings - can be read by more than one type of code very easily. For example, I typically use the Java paradigm for Property files and use matching behavior C code so both my codelines can easily read the settings.

Related

How to get a settings storage path in a cross-platform way in Qt?

My program needs to read/write a text (just a few lines) file with its settings to disk. To specify a path in code may work well on one platform, like windows, but if runs it on Linux, the path is not cross platform.
I am looking for a similar solution to QSettings that saves settings to different paths or has its native ways to handle this. Programmers don't need to do with the details. But the text file is not suitable to be saved as a value in QSettings.
No user interaction should be needed to obtain such path. The text file should persist across application restarts. It can't be a temporary file.
Is there an existing solution in Qt and what is the API that should be used?
The location of application-specific settings storage differs across platforms. Qt 5 provides a sensible solution via QStandardPaths.
Generally, you'd store per-user settings in QStandardPaths::writableLocation(QStandardPaths::AppDataLocation). If you wish the settings not to persist in the user's roaming profile on Windows, you can use QStandardPaths::AppLocalDataLocation, it has the meaning of AppDataLocation on non-Windows platforms.
Before you can use the standard paths, you must set your application name via QCoreApplication::setApplicationName, and your organization's name using setOrganizationName or setOrganizationDomain. The path will depend on these, so make sure they are unique for you. If you ever change them, you'll lose access to old settings, so make sure you stick with name and domain that makes sense for you.
The path is not guaranteed to exist. If it doesn't, you must create it yourself, e.g. using QDir::mkpath.
int main(int argc, char ** argv) {
QApplication app{argc, argv};
app.setOrganizationDomain("stackoverflow.com");
app.setApplicationName("Q32525196.A32535544");
auto path = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation);
if (path.isEmpty()) qFatal("Cannot determine settings storage location");
QDir d{path};
if (d.mkpath(d.absolutePath()) && QDir::setCurrentPath(d.absolutePath())) {
qDebug() << "settings in" << QDir::currentPath();
QFile f{"settings.txt"};
if (f.open(QIODevice::WriteOnly | QIODevice::Truncate))
f.write("Hello, World");
}
}
If you want to save some user related data, you can get user home directory path using QDir::homePath().
There is QDir to handle paths to dirs, QFileInfo for platform independent file information and QDir's homePath()
My proposal is to use these classes and use QDir::home() or QDir::homePath() to find a directory where to write to, since the user has write permissions in his homedir and it exists on each platform.
You can store file in application directory. Take a look at QCoreApplication::applicationDirPath().
From Qt documentation:
Returns the directory that contains the application executable.

What's the standard approach for configuration in Qt console apps?

In .Net you typically have an app.config file and built in ways to access the configuration.
Is there an equivalent standard approach to configuration using Qt?
For example, lets say my application connects to an online server, I want the ability to store the connection details (user defined).
Is this a case of "roll your own", or is there a way to store and read these configurations using XML, or any other format with easy read/write methods provided by Qt?
Edit: To add some complication to the question. This is a Linux console app, so looking specifically for file based and transparent config please.
You could use QSettings for this. Please refer to the documentation for details:
http://qt-project.org/doc/qt-5.1/qtcore/qsettings.html
You could always use other formats as well like XML, Json, and so forth, but generically speaking, QSettings is the way, or if you are writing a KDE application, then probably KConfig.
These are the two important methods you need to be aware of when dealing with QSettings for reading and writing:
Reading
QVariant QSettings::value(const QString & key,
const QVariant & defaultValue = QVariant()) const
Writing
void QSettings::setValue(const QString & key, const QVariant & value)
Then, you can simply stick to the native format (or even ini on your Linux if you prefer):
QSettings::NativeFormat 0 Store the settings using the most
appropriate storage format for the platform. On Windows, this means
the system registry; on Mac OS X, this means the CFPreferences API; on
Unix, this means textual configuration files in INI format.
Here you can find an example for your convenience:
#include <QSettings>
int main()
{
....
QSettings settings("Foo", "Bar");
// settings.beginGroup("application");
QString string = settings.value("foo", "bar");
// settings.endGroup();
....
}
Note, the groups are optional, and it depends on your exact purpose. You can group settings that way to keep certain ones encapsulated.
This may also be important for you to know as per documentation:
On Unix systems, if the file format is NativeFormat, the following files are used by default:
$HOME/.config/MySoft/Star Runner.conf (Qt for Embedded Linux: $HOME/Settings/MySoft/Star Runner.conf)
$HOME/.config/MySoft.conf (Qt for Embedded Linux: $HOME/Settings/MySoft.conf)
/etc/xdg/MySoft/Star Runner.conf
/etc/xdg/MySoft.conf

Can you use multiple message domains in boost::locale?

I have a number of applications that share a number of general libraries. I am trying to internationalize my applications using boost::locale. It will be easy for me to create a separate .mo file for each general library and for each specific application. I was wandering if it is possible to simultaneously use multiple message domains like this:
boost::locale::generator gen;
gen.add_messages_path(".");
gen.add_messages_domain("lib1");
gen.add_messages_domain("lib2");
std::locale::global(gen("zh_CN.UTF-8"));
.
.
.
boost::locale::gettext("Show image");
I was expecting boost::locale to search in both lib1.mo and lib2.mo, however this doesn't seem to work. Only messages from the first domain added are found, in this case from lib1.mo. If I add lib2 before lib1, then only messages from lib2 are found.
I know you can use a domain explicitly in the call like this:
boost::locale::dgettext("lib2", "Show image");
This does work, but I would like to avoid specifying the domain for every call. I am also not sure that this will work well with extracting the strings with xgettext.
Is it possible what I am trying to do? Am I missing something?
Please suggest any alternative if you know one.
I use msvc 9.0 (2008) and boost 1.48.
Since there were no answers posted to this question I assume that this is not possible with boost::locale. I will shortly therefore outline what I did to achieve my required functionality:
I created a singleton class with the following interface
class MY_GETTEXT
{
public:
void SetPath(const std::string& i_path);
void AddDomain(const std::string& i_domain);
void ChangeLocale(const std::string& i_locale);
std::string gettext(const std::string i_msg_id);
};
AddDomain is called for each domain you want to use, and adds it to a member set m_language_domains_a. ChangeLocale does some locale manipulation and stores a locale in the member m_locale, I will ignore its implementation here.
To translate you should simply call MY_GETTEXT::gettext. Its implementation looks like this:
std::string MY_GETTEXT::gettext(const std::string i_msg_id)
{
BOOST_FOREACH(const std::string& domain , m_language_domains_a)
{
if (boost::locale::translate(i_msg_id).str(m_locale, domain) != i_msg_id)
{
return boost::locale::translate(i_msg_id).str(m_locale, domain);
}
}
return i_msg_id;
}

C++ - QSettings question

Does Qt has something like QSettings, but for local scopes?
I am seeking for a data structure with the same methods, but not specific for APPLICATION.
I mean, I want to construct local (for example, exporting settings) settings from file (xml, for example) and use them in local scope - without polluting global application settings.
Is that possible (with QSettings or some other class)? How should I construct the object then?
You can use
void QSettings::setPath ( Format format, Scope scope, const QString & path )
to set the format (as specified in the doc)
QSettings::NativeFormat 0 Store the
settings using the most appropriate
storage format for the platform. On
Windows, this means the system
registry; on Mac OS X, this means the
CFPreferences API; on Unix, this means
textual configuration files in INI
format.
QSettings::IniFormat 1 Store the
settings in INI files.
QSettings::InvalidFormat
the scope:
QSettings::UserScope 0 Store settings
in a location specific to the current
user (e.g., in the user's home
directory).
QSettings::SystemScope 1 Store
settings in a global location, so that
all users on the same machine access
the same set of settings.
So if you are on Windows and want to write User-specific settings, you would use the IniFormat and the UserScope values and specify the path where you want to write your settings in the path variable.
Hope this helps.
You create a datastream and write the data into the file in member by member fashion.

How do I use the registry?

In the simplest possible terms (I'm an occasional programmer who lacks up-to-date detailed programming knowledge) can someone explain the simplest way to make use of the registry in codegear C++ (2007).
I have a line of code in an old (OLD!) program I wrote which is causing a significant delay in startup...
DLB->Directory=pIniFile->ReadString("Options","Last Directory","no key!");
The code is making use of an ini file. I would like to be able to use the registry instead (to write variables such as the last directory the application was using)
But the specifics are not important. I'd just like a generic how-to about using the registry that's specific to codegear c++ builder.
I've googled this, but as usual with this type of thing I get lots of pages about c++ builder and a few pages about the windows registry, but no pages that explain how to use one with the other.
Use the TRegistry class... (include registry.hpp)
//Untested, but something like...
TRegistry *reg = new TRegistry;
reg->RootKey = HKEY_CURRENT_USER; // Or whatever root you want to use
reg->OpenKey("theKey",true);
reg->ReadString("theParam",defaultValue);
reg->CloseKey();
Note, opening and reading a ini file is usually pretty fast, so maybe you need to test your assumption that the reading of the ini is actually your problem, I don't think that just grabbing your directory name from the registry instead is going to fix your problem.
Include the Registry.hpp file:
#include <Registry.hpp>
Then in any function you have, you can write the following to read the value:
String __fastcall ReadRegistryString(const String &key, const String &name,
const String &def)
{
TRegistry *reg = new TRegistry();
String result;
try {
reg->RootKey = HKEY_CURRENT_USER;
if (reg->OpenKeyReadOnly(key)) {
result = reg->ReadString(name, def);
reg->CloseKey();
}
}
__finally {
delete reg;
}
return result;
}
So reading the value should be as easy as:
ShowMessage(ReadRegistryString("Options", "Last Directory", "none"));
You can use the following to write the value:
void __fastcall WriteRegistryString(const String &key, const String &name,
const String &value)
{
TRegistry *reg = new TRegistry();
try {
reg->RootKey = HKEY_CURRENT_USER;
if (reg->OpenKey(key, true)) {
reg->WriteString(name, value);
reg->CloseKey();
}
}
__finally {
delete reg;
}
}
Should be self explaining, remembering the try ... finally is actually really helpful when using the VCL TRegistry class.
Edit
I've heard that .ini files are stored in the registry in Windows, so if you want the speed advantage of ini files you should call them something else - like .cfg
This is something I've heard from an although reliable source, I haven't tested it myself.
Tim is right but an even simpler class to use is TIniRegFile but it is also more limited in what you can do.
Please see the documentation for the QSettings class from the Qt 4.5 library. It will allow you to load and store your program's configuration settings easily and in a cross-platform manner. The Windows implementation uses the Windows registry for loading and storing your program's configuration data. On other platforms, the platform's preferred, native mechanism for storing configuration data will be used. This is far better than interacting with the Windows registry directly, as you will not be tied to a specific platform.