(495) 925-0049, ITShop интернет-магазин 229-0436, Учебный Центр 925-0049
  Главная страница Карта сайта Контакты
Поиск
Вход
Регистрация
Рассылки сайта
 
 
 
 
 

Один из методов работы с конфигурационными файлами в С++ (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 <QtCore/QCoreApplication>
#include <QUrl>
#include <QFile>
#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

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

Ссылки по теме


 Распечатать »
 Правила публикации »
  Написать редактору 
 Рекомендовать » Дата публикации: 07.08.2012 
 

Магазин программного обеспечения   WWW.ITSHOP.RU
Microsoft 365 Business Standard (corporate)
Microsoft 365 Business Basic (corporate)
Delphi Professional Named User
Microsoft Windows Professional 10, Электронный ключ
Enterprise Connectors (1 Year term)
 
Другие предложения...
 
Курсы обучения   WWW.ITSHOP.RU
 
Другие предложения...
 
Магазин сертификационных экзаменов   WWW.ITSHOP.RU
 
Другие предложения...
 
3D Принтеры | 3D Печать   WWW.ITSHOP.RU
 
Другие предложения...
 
Новости по теме
 
Рассылки Subscribe.ru
Информационные технологии: CASE, RAD, ERP, OLAP
Безопасность компьютерных сетей и защита информации
Новости ITShop.ru - ПО, книги, документация, курсы обучения
Программирование на Microsoft Access
CASE-технологии
3D и виртуальная реальность. Все о Macromedia Flash MX.
Новые программы для Windows
 
Статьи по теме
 
Новинки каталога Download
 
Исходники
 
Документация
 
 



    
rambler's top100 Rambler's Top100