Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > C/C++: Для новичков > Удобных конфиг


Автор: Тигер 23.10.2010, 15:52
Как на С++ огранизовать удобный оффлайн конфиг. тобишь ini и пр. рантайм не предлогать =)
хочется что-то вроде

cnfMap["file_name"]="My file";
cnfMap["count"]=5;
cnfMap["zlo"]=0.0006;

структуры и union не подходят. точнее не совсем подходят. нужен именно маппер с ключами, что бы можно было вывести конфиг в цикле как
key => value

Автор: boostcoder 23.10.2010, 16:43
конечно же http://www.boost.org/doc/libs/1_44_0/doc/html/property_tree.html.

Автор: Тигер 23.10.2010, 16:50
спасибо, это идеальное решение.
но у меня программа не большая, там поминимому библиотек, тоесть хотелось бы вариант максимум с STL, WinAPI

Автор: boostcoder 23.10.2010, 16:59
изврат конечно, но сам когда-то писал.
Код


#ifndef CONFIGFILE_HPP
#define CONFIGFILE_HPP

#include <string>
#include <fstream>
#include <iterator>
#include <algorithm>
#include <stdexcept>

/***************************************************************************/

class config_file {
public:
    class variant {
        public:
            variant(const std::string& s):str(s) {}
            ~variant() {}
            /**  */
         template<typename T>
            T as() const {
                T result;
                std::stringstream ss(str);
                ss >> result;
                return result;
            }
         operator std::string () const { return str; }
        private:
            variant();
            std::string str;
    };
    /**  */
   config_file(const std::string& fn):fname(fn) {
      std::ifstream file(fname);
      if ( !file ) {
         throw std::runtime_error("config file not exists!");
      }
   }
    /**  */
    variant operator[] (const std::string& key) const {
        std::string temp, result;
        std::ifstream file(fname.c_str());
        if ( !file ) {
            throw std::invalid_argument("can`t open config file!");
        }
        while ( !file.eof() ) {
            std::getline(file, temp);
            if ( !temp.size() || temp[0] == '#' ) continue;
            if ( std::find(temp.begin(), temp.end(), '\r') != temp.end() ) {
                temp.erase(std::remove(temp.begin(), temp.end(), '\r'), temp.end());
            }
            if ( parse_string(result, key, temp) ) {
                break;
            }
        }
        if ( result.empty() ) {
            throw std::invalid_argument("value of string is empty for: " + key);
        }
        return result;
    }
private:
    bool parse_string(std::string& path, const std::string& key, const std::string& str) const {
        std::string::const_iterator it = std::search(str.begin(), str.end(), key.begin(), key.end());
        if ( it == str.end() ) return false;

        std::advance(it, key.length());

        while ( *it++ != '=' ) {}
        while ( *++it == ' ' ) {}
        path.assign(it, str.end());
 
       return true;
    }

private:
    std::string fname;
};

/***************************************************************************/

#endif /** CONFIGFILE_HPP */


Автор: Тигер 23.10.2010, 19:20
идея интересная, но что-то я не пойму почему у меня не работает

map<string,variant*> cnf;
cnf["test"]=new variant("5");
int num=cnf["test"]->as<int>();

и даже так

variant is("5");
int num=is.as<int>();

Автор: boostcoder 23.10.2010, 19:32
что-то вы нагородили..

так:
Код

config_file config("file.conf");
...
int i = config["key1"].as<int>();
std::string s = config["key2"].as<std::string>();

Автор: Леопольд 23.10.2010, 19:36
Цитата(Тигер @  23.10.2010,  19:20 Найти цитируемый пост)
и даже так
сделай как-то так:
config.h
Код
map<string,int> cnf;

config.cpp
Код
namespace
{
    class InitConfig{
        InitConfig() 
        {
            cnf["test"]=5;
        }
    } initConfig;
}


правда смысла от этого не вижу, лучше констант наплодить...

Автор: Тигер 23.10.2010, 19:38
ну мне файл просто не нужен.  тоесть мне каг бы нужeн оффлайн глобальный массив конфигурации

чтобы я сделал так
Код

map<...> cnf;
cnf[...]=...;
/* а потом */
some function(){ cnf["some"]->as<int>(); }
/* и тд */

тобишь просто массив с "разными типами" структуры и объединения не подходят
из-за неудобной иницаилизации в случае с контейнером map
из-за невозможности вывести  содержимое как key => value; в случае простой конфигурационной структуры без map =)

Автор: Леопольд 23.10.2010, 19:40
Цитата(Тигер @  23.10.2010,  19:38 Найти цитируемый пост)
ну мне файл просто не нужен.  тоесть мне каг бы нужeн оффлайн глобальный массив конфигурации
Это называется хардкод, когда что-то вшивается в исполняемый файл.

Автор: Тигер 23.10.2010, 19:45
пусть так, главное что рантайм не подходит.

Автор: Леопольд 23.10.2010, 19:51
Цитата(Тигер @  23.10.2010,  19:45 Найти цитируемый пост)
пусть так, главное что рантайм не подходит
И то и другое рантайм.
Лучше вынести константы в отдельный namespace:
Код
namespace Cfg
{
    char const* key1 = "key1";
    int const key2 = 5;
    //...
}


Автор: boostcoder 23.10.2010, 19:52
в извращениях не участвую smile 

Автор: Тигер 23.10.2010, 19:56
Леопольд
namespace нельзя вывести как key=>value в цикле =)
boostcoder
это называется удобный конфиг,а не извращение ;)

Автор: Леопольд 23.10.2010, 19:57
Цитата(Тигер @  23.10.2010,  19:56 Найти цитируемый пост)
называется удобный конфиг
Это точно не конфиг, его нельзя изменить, а конфиг подразумевает настройку bез перекомпиляции.

Автор: Тигер 23.10.2010, 19:58
можно - при компиляции
Цитата

а конфиг подразумевает настройку bез перекомпиляции.

нет

Автор: Леопольд 23.10.2010, 19:59
Цитата(Тигер @  23.10.2010,  19:58 Найти цитируемый пост)
можно - при коммпиляции 
Это хардкод... smile

Автор: Тигер 23.10.2010, 20:02
это зависит от поставленной задачи. в рамках данной задачи это удобный кофиг =)
и да.

variant s("5");
int i=s.as<int>();
error C2062: type 'int' unexpected

что значит тип неожиданный..

Автор: mes 23.10.2010, 20:15
Цитата(Тигер @  23.10.2010,  19:02 Найти цитируемый пост)
что значит тип неожиданный..

значит в данном контексте он не ожидается.. 
возможно у вас функция as не шаблоная..
в общем не глядя на код трудно сказать.. 

Автор: Тигер 23.10.2010, 20:18
Код

class variant {
        public:
            variant(const std::string& s):str(s) {}
         template<typename T>
            T as() const {
                T result;
                std::stringstream ss(str);
                ss >> result;
                return result;
         }
        private:
            std::string str;
};

Автор: Тигер 23.10.2010, 20:34
как я понял есть такой паттерн - Variant или Any
нашёл несколько реализация, но мне нравится реализация которая тут выше, а она не работает.

Автор: mes 23.10.2010, 20:35
или то место где ошибка не показали , или компилятор у Вас староват :

http://liveworkspace.org/code/deae7dfc1c3e2913ece4541a4b2421c1

Автор: Тигер 23.10.2010, 20:40
VC6 =)
у меня необходимость использовать имено VC6 к сожелению. что посовутуете?

Добавлено через 5 минут и 16 секунд
вот так ошибку не выдает но и не работает
исключение вылетает при запуске программы
Код

class variant {
        public:
            variant(const std::string& s):str(s) {}
         template<class T>
             T as() const {
                T result;
                std::stringstream ss(str);
                ss >> result;
                return result;
         }
         template <class T>
             operator T () {
                return this->as<T>();
         }
        private:
            std::string str;
};


Автор: mes 23.10.2010, 20:55
Цитата(Тигер @  23.10.2010,  19:40 Найти цитируемый пост)
 имено VC6 к сожелению.

а так работает ?  :
Код

template<typename T>
 T as (std::string & str) {
     T result;
     std::stringstream ss(str);
     ss >> result;
     return result;
  }
...
  std::cout << as<int>("5");

Автор: Тигер 23.10.2010, 21:02
вот так работает...
    
Код

string str("5");
      std::cout << as<int>(str);

но это не вариант =) всмысле неудобно и в массив не загонишь никак

Автор: mes 23.10.2010, 21:11
Цитата(Тигер @  23.10.2010,  20:02 Найти цитируемый пост)
всмысле неудобно и в массив не загонишь никак

а если подумать ? smile

Автор: Тигер 23.10.2010, 21:18
хз =)
статику в функции заюзать и хранить указатель на функцию? а как различать присвоение от вывода?

меня вот такой вариант устраивает =)
http://liveworkspace.org/code/96e9c718403326441e0d119a670e125f
только не понятно почему не работает такой
http://liveworkspace.org/code/fd2324d1a3fb4ff4640cf61e2d6d2796

Автор: mes 23.10.2010, 21:24
Цитата(Тигер @  23.10.2010,  20:18 Найти цитируемый пост)
только не понятно почему не работает такой

потому что cnf["key1"] = .. вне функции 


Автор: Тигер 23.10.2010, 21:28
мм. а разве нельзя в глобальной области так делать? тоесть мне впринципе нужен глобальный контейнер

Автор: mes 23.10.2010, 21:29
Цитата(Тигер @  23.10.2010,  20:18 Найти цитируемый пост)
а как различать присваение от вывода?

если нельзя определять шаблоные функции внутри класса, то придется сделать шаблонную функцию внешней, а в классе прокси функции для удобного пользования.. 

т.е. 
Код

class variant {
  int as_int () { return lex_cast<int>(str); }
...

а если применить в дополнение паттерн pimpl, точнее его разновидность _виртуальный конструктор_ то, можно избавиться и от лишних new, т.е. будет просто:  variant v1 (5), v2 ("string");

Добавлено через 3 минуты и 11 секунд
Цитата(Тигер @  23.10.2010,  20:28 Найти цитируемый пост)
мм. а разве нельзя в глобальной области так делать? тоесть мне впринципе нужен глобальный контейнер 

определить и инициализировать можно, а присвоение уже должно быть внутри функции, которую к примеру можно вызвать первую в main.. или написать класс в конструкторе которого идет нужная инициализация и создать его объект пред маин.. но лучше смотреть тогда паттерн singleton.. 

Автор: Тигер 23.10.2010, 21:42
плюнул и сделал так...
Код

template<class T>
class variant {
        public:
            variant(const std::string& s):str(s) {}
             T as() const {
                T result;
                std::stringstream ss(str);
                ss >> result;
                return result;
             }
             operator T () {
                return this->as();
             }
        private:
            std::string str;
};

мм а зачем тут singleton? нам же впринципе без разницы сколько экземпляров мы будем создавать, только память правда расходуется...

Автор: mes 23.10.2010, 21:58
Цитата(Тигер @  23.10.2010,  20:42 Найти цитируемый пост)
 нам же впринципе без разницы сколько экземпляров мы будем создавать

предполагалось что Вы хотите, чтоб у Вас был один конфиг на все программу smile

Автор: Тигер 23.10.2010, 22:00
ОБлом. В массив то теперь не загонишь...
Цитата

предполагалось что Вы хотите, чтоб у Вас был один конфиг на все программу

ну если бы инициализация в конструкторе была статичная, то по сути так бы оно и было =)
если я конечно правильно понял что вы предложили...

Автор: mes 23.10.2010, 22:04
Цитата(Тигер @  23.10.2010,  21:00 Найти цитируемый пост)
ОБлом. В массив то теперь не загонишь...

кого не загонишь ?
 smile 

Цитата(Тигер @  23.10.2010,  21:00 Найти цитируемый пост)
у если бы инициализация в конструкторе была статичная, то по сути так бы оно и было =)

если бы тут были телепаты, то уже тема давно была бы решена  smile 

Автор: Тигер 23.10.2010, 22:06
ну вот такая штука теперь не будет рабоать =)
Код

template<class T>
class variant {
        public:
            variant(const std::string& s):str(s) {}
             T as() const {
                T result;
                std::stringstream ss(str);
                ss >> result;
                return result;
             }
             operator T () {
                return this->as();
             }
        private:
            std::string str;
};

map<string,variant*> cnf;

void InitCNF(){
   cnf["file_out_name"]=new variant<char*>("blabla");
  /*....*/
}

Автор: mes 23.10.2010, 22:12
Цитата(Тигер @  23.10.2010,  21:06 Найти цитируемый пост)
ну вот такая штука теперь не будет рабоать =)

ну так естественно.. 
у Вас кол-во же вариантов типа вариант ограничено ? что ж мешает избавиться от шаблона ?

Автор: Тигер 23.10.2010, 22:30
хочется похорошему сделать...
наверно надо забить и  скачать студию всё таки..

Автор: boostcoder 23.10.2010, 22:54
Цитата(Тигер @  23.10.2010,  19:56 Найти цитируемый пост)
это называется удобный конфиг,а не извращение ;) 

лжец! это называется хардкодинг.
Цитата(Тигер @  23.10.2010,  20:02 Найти цитируемый пост)
в рамках данной задачи это удобный кофиг =)

лжец! и это называется хардкодинг.
Цитата(Тигер @  23.10.2010,  20:34 Найти цитируемый пост)
мне нравится реализация которая тут выше, а она не работает. 

лжец! все работает.

а почему бы не взять мой класс, и переделать его так, чтоб он использовал std::istringstream вместо файла? smile

Добавлено через 1 минуту и 21 секунду
Цитата(Тигер @  23.10.2010,  20:34 Найти цитируемый пост)
но мне нравится реализация которая тут выше

может стОило сказать спасибо?

Автор: Тигер 23.10.2010, 23:08
я сказал да =)
но в добавок это называется удобный конфиг и рациональная реализация.
я и взял по сути ваш класс =)
в VC6 не работает.
и спасибо за паттерн.

Автор: artikov 24.10.2010, 21:30
всю тему не читал.
http://www.gamedev.ru/flame/forum/?id=134697 кто-нибудь предлагал?

Автор: djamshud 24.10.2010, 21:50
Аналогично, все не читал. Сам использую конфиги на макросах и в зависимости от настроек сборки проекта подключаю его статически в компайл-тайм или распарсиваю в рантайме.

Для очень сложных конфигов - XML, рантайм разумеется.

Автор: bsa 25.10.2010, 09:17
Цитата(Тигер @  24.10.2010,  00:08 Найти цитируемый пост)
но в добавок это называется удобный конфиг и рациональная реализация.
У тебя видимо недостаточно опыта, поэтому ты называешь это "удобным" и "рациональным". На самом деле, конфигурация, реализуемая подобным образом, нужна именно для того, чтобы можно было задать новую без перекомпиляции. Так называемый "ХардКодинг", который ты фактически делаешь, лучше делать через макросы или константы из особого пространства имен. Если тебе нужен удобный набор начальных настроек, то я бы использовал связку перечисления, структуры и статического массива. Перечислением задается номер параметра (индекс в массиве), структура содержим значение и название параметра (если второе не нужно, то можно ограничиться просто значением), а массив уже содержит весь набор. Правда, у этого метода есть и недостатки - при добавлении новых параметров нужно быть очень аккуратным, так как вносить изменения необходимо в 2 места (в перечисление и массив).

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)