Календарь на Май 2024 года: calendar2008.ru/2024/may/
Навигация
Главная »  Sql 

Один из методов работы с конфигурационными файлами в С++ (Qt)


Источник: habrahabr
unixod
Практически в каждом проекте, встает задача персистентного чтения/записи конфигурации. Не секрет что существует большое количество уже готовых библиотек для решения этой задачи. Некоторые из-них просты, некоторые чуть сложнее в использовании.
Если же проект разрабатывается с использованием Qt, думаю нет смысла линковать дополнительную библиотеку, так как в Qt есть все средства для создания очень простого, гибкого и кроссплатформенного решения.
Как раз о таком решении хочу расказать вам в этом посте.

Введение


В Qt есть очень удобный класс  QSettings . В принципе он очень прост в использовании:
/* main.cpp */ int main(int argc, char *argv[]){ // эти настройки используются (неявно) классом QSettgins для // определения имени и местоположения конфига QCoreApplication::setOrganizationName("org"); QCoreApplication::setApplicationName("app"); ... return 0; } 

/* some.cpp */ void func(){ QSettings conf; ... // запись в конфиг conf.setValue("section1/key1", someData);   // запись в секцию section1 conf.setValue("key2", someData2);           // запись в секцию General ... // чтение из конфига QString strData = conf.value("section1/key1").toString(); } 

Из приведенного выше примера, обычного использования  QSettings , сразу становятся видны проблемы расширяемости и поддержки кода:
  1. Если имена ключей прописывать явно в коде, то в дальнейшем мы можем столкнуться с ситуацией когда будет сложно удалять/добавлять новые ключи конфигурации. Т.е. при таком подходе, тут проблема в том что на этапе компиляции невозможно выловить инвалидные ключи.
  2. Чтобы избежать проблемы #1 мы могли бы выписать все ключи в отдельный заголовочный файл, и обращаться к ним через строковые константы. Для улучшения модульности кода и очистки глобальной области видимости, также стоило бы поместить все ключи в отдельное пространство имен.
    namespace Settings{ const char * const key1 = "key1"; const char * const section1_key1 = "section1/key1"; const char * const section1_key2 = "section1/key2"; } 

    Но тут у нас появляется другая не очень приятная деталь:
    * во первых слишком многословно, т.е. информация дублируется (key1 -> "key1", и т.д.). В принципе это не удивительно, так как мы же как-то должны описать сериализацию имен ключей. Да мы могли бы написать макрос, но, по известным причинам, макросы стоит избегать, тем более если есть альтернативные варианты.
    * во вторых при достаточном количестве ключей и секций, велика вероятность, что придется прописывать константы для всех комбинаций, что не очень удобно. Конечно же мы можем завести константы для ключей и для секций отдельно, но тогда, при каждом обращении в  QSettings , придется производить объединение строк.

Если внимательно еще раз просмотреть все вышеописанные проблемы, то можно сделать вывод: ключ представлен строкой - это и есть основная проблема. Ведь действительно, если в качестве ключа мы будем использовать перечисления (enums), то все вышеперечисленное разом улетучивается. Перечисления конечно же удобны, но  QSettings  требует, в качестве параметра ключа - строку. Т.е. нам нужен некоторый механизм, который давал бы нам возможность транслировать значения перечислений в строки (извлекать строковые значения элементов перечислений). Например из следующего перечисления:
enum Key{ One, Two, Three }; 

нужно как-то извлечь 3 строки: "One", "Two", "Three".
К сожалению стандартными средствами C++ это сделать невозможно. Но как же быть?
Тут нам на помощь приходит Qt со своей метаобъектной моделью, а если точнее  QMetaEnum . Про QMetaEnum  писать не буду, так как это уже отдельная тема. Могу лишь дать ссылки: раз, два.

Реализация


Имея на вооружении  QMetaEnum , теперь мы можем реализовать класс Settings, лишенный всех вышеперечисленных недостатков, а также предоставляющий возможность задания дефолтных настроек. Класс Settings представляет из себя синглтон Мейерса, это нам дает простоту настройки и его использования:
settings.h (Раскрыть спойлер)

settings.cpp (Раскрыть спойлер)

В данной реализации, класс  QSettings , используется исключительно для кроссплатформенного доступа к настройкам. Конечно же по желанию  QSettgins  может быть заменен любым другим механизмом, например SQLite .

Пример использования


Класс Settings предоставляет очень простой и удобный интерфейс, состоящий всего из трех статических методов:
void setDefaults(const QString &str); - установка параметров поумолчанию
QVariant get(Key, Section); - чтение значения (секция может быть опущена)
ValueRef set(Key, Section); - запись значения (секция может быть опущена)
/* main.cpp */ #include  #include  #include  #include "settings.h"  void doSome(){ //чтение из секции General QString login = Settings::get(Settings::User).toString();    // login == "unixod"      QUrl proxyUrl = Settings::get(Settings::URI, Settings::Proxy).toUrl();    // http://proxy_uri  QString generalUrl = Settings::get(Settings::URI).toString();    // пусто if(generalUrl.isEmpty()) Settings::set(Settings::URI) = "http://some_uri"; }  int main(int argc, char *argv[]){ //данные параметры используются QSettings для определения куда сохранять конфигурацию QCoreApplication::setOrganizationName("unixod"); QCoreApplication::setApplicationName("app");  //по желанию можем установить дефолтную конфигурацию: QFile cfgDefaults(":/config/default.cfg");  // я обычно дефолтовые настройки помещаю в ресурсы cfgDefaults.open(QIODevice::ReadOnly); Settings::setDefaults(cfgDefaults.readAll()); //... doSome(); //... return 0; } 

вот пример синтаксиса описания настроек по умолчанию:
default.cfg (Раскрыть спойлер)

как можно заметить формат - простой:
[section name]/key : value;

Заключение


Стоит заметить что данный класс Settings легко расширяется. Т.е. при желании, добавить/удалить/переименовать какие-нибудь ключи или секции, всего лишь надо изменить соответствующий enum! У читающего может возникнуть вопрос а нельзя ли как нибудь вынести общую логику "за скобки".
Ответ: можно но лучше не стоит. Так как метаобъектная модель Qt не работает с шаблонами, придется использовать макросы, что в свою очередь влечет за собой известные проблемы:
  • Сложность отладки
  • Затруднение анализа кода для IDE
  • Сложность восприятия, читающим, кода
  • и т.д.

При сборке не забываем включить поддержку С++11:
  • GCC:
    -std=с++0x
  • Qt project file:
    QMAKE_CXXFLAGS += -std=c++0x

Спасибо за внимание. )

 

 Блокировки Oracle при неиндексированных внешних ключах.
 Объекты в Oracle - это очень просто.
 Аналитические функции в Oracle (Часть 2).
 Выводимые таблицы с хранимым результатом. Часть 1.
 Docsvision показала 100% совместимость с Microsoft SQL Server 2012..


Главная »  Sql 

© 2024 Team.Furia.Ru.
Частичное копирование материалов разрешено.