(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 Office 365 Персональный 32-bit/x64. 1 ПК/MAC + 1 Планшет + 1 Телефон. Все языки. Подписка на 1 год.
Microsoft Office 365 Бизнес. Подписка на 1 рабочее место на 1 год
Microsoft Office 365 Профессиональный Плюс. Подписка на 1 рабочее место на 1 год
Microsoft 365 Business Standard (corporate)
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-технологии
Delphi - проблемы и решения
Краткие описания программ и ссылки на них
 
Статьи по теме
 
Новинки каталога Download
 
Исходники
 
Документация
 
 



    
rambler's top100 Rambler's Top100