[Pure С++] портабильная читалка-писАлка INI
От: ssm Россия  
Дата: 07.04.03 17:24
Оценка: 12 (1) -1
На оригинальность не претендую, так что простите за банальность .
Появилась идея написать портабильный сабж, с применением полета програмистской мысли, а именно разделения "данные-представление" и реализации при помощи стратегий. Хотелось бы услышать доводы за и против, со следующих точек зрения : проектирования, реализации, понятности, простоты

Используется все это хозяйство следующим образом:

//test.cpp
#include "conf.h"
#include <iostream>


int main(){

    Conf conf("test.cfg");

    //загрузка данных 
    conf.load();

    //вывод в std::cout
    conf.show();
   
    //читалка
    std::string val = conf.get_string("Test section", "testName");
    std::cout << val << endl;   

    //писАлка  
    conf.put_string("test1", "12", "12");

    //сохранялка
    conf.save();

    //возвращалка  :) 
    return 0;

}



А вот собственно и мои потуги:

ConfCfgView — стратегия представления
ConfImpl<typename View> — шаблон, отвечающий за данные, представляемые в View



//conf.h
#ifndef __CONF_H__
#define __CONF_H__


#include <set>
#include <string>
#include <fstream>
#include <algorithm>


//Key structure:
//  section, name, value
template<typename View>
class ConfImpl{
    struct ConfData{
        std::string sect, name;
        mutable std::string val;
        ConfData(){}
        ConfData(const std::string sect, const std::string &name, const std::string &val)
            :sect(sect), name(name), val(val){}
        struct Less{
            bool operator()(const ConfData &l, const ConfData &r) const{         
                if(l.sect == r.sect)
                   return l.name < r.name;
                else
                   return l.sect < r.sect;
            }
        };
    };

    typedef std::set<ConfData, ConfData::Less> Set;
    
    Set data_;
    string fileName_;
    
    void put_to_stream(std::ostream &os){
        string sect = "";
        bool write_sect;
        for (Set::iterator i = data_.begin(); i != data_.end(); ++i){
            write_sect = (sect != i->sect) || (i == data_.begin()); 
            View::write(os, write_sect, i->sect, i->name, i->val);
            if(write_sect)
                sect = i->sect;
        }
    };
public:
    ConfImpl(const std::string &fileName) :fileName_(fileName){};

    void load(){
        std::ifstream is(fileName_.c_str());
        if(!is)
            return;
        
        data_.clear();
   
        std::string sect, name, val;
        while(View::read(is, sect, name, val))
            put_string(sect, name, val);
    };
    
    bool save(){
        std::ofstream os(fileName_.c_str());
        if (!os)
            return false;
        else{
            put_to_stream(os);
            return os;
        }
    };
    
    void show(){
        put_to_stream(std::cout);
    }
    
    std::string get_string(const std::string &sect, const std::string &name){
        Set::iterator i = data_.find(ConfData(sect, name, ""));
        return (i != data_.end()) ? i->val : "";    
    }
    
    void put_string(const std::string &sect, 
            const std::string &name, const std::string &val) {
        std::pair<Set::iterator, bool> p = data_.insert(ConfData(sect, name, val));
        string s(p.first->val);
        if (!p.second);
            p.first->val = val;
    }
};


//View structure:
//[section]
//name=value
class ConfCfgView{
    static void skip_ws(std::istream &is){
        char ch; 
        while(is.get(ch)){
            if (!isspace(ch)){
                is.putback(ch);
                break;
            }
        }
    }
public:
    static bool read(std::ifstream &is, std::string &sect, std::string &name, std::string &val){
        char ch;  
        
        skip_ws(is);
        if(!is)
            return false;
        
        //read section name
        is.get(ch);
        if (ch == '['){
            sect.erase();
            while(is.get(ch) && (ch != ']'))
                sect.push_back(ch);
            skip_ws(is);
        } else
            is.putback(ch);
        
        //read name
        name.erase();
        while(is.get(ch) && (ch != '='))
            name.push_back(ch);

        //read value
        val.erase();
        while(is.get(ch) && (ch != '\n'))
            val.push_back(ch);
    }

    static void write(std::ofstream &os, bool write_sect, const std::string &sect, 
            const std::string &name, const std::string &val){
        if (write_sect)
            os << '[' << sect << ']' << std::endl;
        os << name << "=" << val << std::endl;
    }
};



typedef ConfImpl<ConfCfgView > Conf;



#endif //__CONF_H__
Re: [Pure С++] портабильная читалка-писАлка INI
От: Tom Россия http://www.RSDN.ru
Дата: 08.04.03 07:12
Оценка: -2
Поставил нолик за стилистику кода
... << RSDN@Home 1.0 beta 6a >>
Народная мудрось
всем все никому ничего(с).
Re[2]: [Pure С++] портабильная читалка-писАлка INI
От: ssm Россия  
Дата: 08.04.03 08:33
Оценка:
Здравствуйте, Tom, Вы писали:

Tom>Поставил нолик за стилистику кода


Спасибо за отклик, но хотелось бы более потробно узнать, что ты подразумеваешь под стилистикой кода(отступы, и т.д. ?). Если можно, хотелось бы увидеть несколько примеров "хорошой" и "плохой" стилистики кода. Хотелось бы услышать предложения(рекомендации) по улучшению кода на примере моего предыдущего постинга. Заранее — спасибо
Re[3]: [Pure С++] портабильная читалка-писАлка INI
От: Tom Россия http://www.RSDN.ru
Дата: 08.04.03 09:34
Оценка:
допустим посмотрим на это
static void write(std::ofstream &os, bool write_sect, const std::string &sect, 
            const std::string &name, const std::string &val){
        if (write_sect)
            os << '[' << sect << ']' << std::endl;
        os << name << "=" << val << std::endl;
    }


я бы оформил так

/*
 * Описание функции
 */
static void write(std::ofstream &os,        //описание параметра
                  bool write_sect,            //..
                  const std::string &sect,    //..
                  const std::string &name,    //..
                  const std::string &val)    //описание параметра
{
        if (write_sect)
            os << '[' << sect << ']' << std::endl;
        //рекомендуется вставлять пустую строку после и до if
        os << name << "=" << val << std::endl;
}


Главное запомнить и заучить наизусть, что плохо документированный код никому не нужен. Хороший стиль программирования и именования позволяет программе быть самодокументированной. Так же стиль комментариев желательно делать таким, что бы их могли поднимать программы автоматической генерации документации типа doxygen.
... << RSDN@Home 1.0 beta 6a >>
Народная мудрось
всем все никому ничего(с).
Re[4]: [Pure С++] портабильная читалка-писАлка INI
От: ssm Россия  
Дата: 08.04.03 09:41
Оценка:
Здравствуйте, Tom, Вы писали:

Tom>Главное запомнить и заучить наизусть, что плохо документированный код никому не нужен. Хороший стиль программирования и именования позволяет программе быть самодокументированной. Так же стиль комментариев желательно делать таким, что бы их могли поднимать программы автоматической генерации документации типа doxygen.


Спасибо, приму на вооружение
Re[4]: [Pure С++] портабильная читалка-писАлка INI
От: Аноним  
Дата: 08.04.03 10:36
Оценка: +1
Здравствуйте, Tom, Вы писали:

Tom>допустим посмотрим на это

<skip>
Tom>я бы оформил так
Tom>
Tom>/*
Tom> * Описание функции
Tom> */
Tom>static void write(std::ofstream &os,        //описание параметра
Tom>                  bool write_sect,            //..
Tom>                  const std::string &sect,    //..
Tom>                  const std::string &name,    //..
Tom>                  const std::string &val)    //описание параметра
Tom>{
Tom>        if (write_sect)
Tom>            os << '[' << sect << ']' << std::endl;
Tom>        //рекомендуется вставлять пустую строку после и до if
Tom>        os << name << "=" << val << std::endl;
Tom>}
Tom>


Мои 2 цента.

1. чем вставлять пустую строку после if, проще и правильнее всегда обрамлять if в {}
2. чем стараться подогнать начало второго параметра так, чтобы он начинался после названия функции на предыдущей строке, проще и более обще будет всегда отступать фиксированное количество tab'ов от начала, т.е.
не
static void write(std::ofstream &os,
                  bool write_sect,
                  const std::string &sect,

но
static void write(
            std::ofstream &os,
            bool write_sect,
            const std::string &sect,

это очень облегчает жизнь, когда надо написать метод у вложенного template-класса, а MSVC отказывается правильно позиционировать курсор.
Re[4]: [Pure С++] портабильная читалка-писАлка INI
От: peterbes Россия  
Дата: 08.04.03 11:39
Оценка: 2 (1) +2
Здравствуйте, Tom, Вы писали:


Tom>Главное запомнить и заучить наизусть, что плохо документированный код никому не нужен. Хороший стиль программирования и именования позволяет программе быть самодокументированной. Так же стиль комментариев желательно делать таким, что бы их могли поднимать программы автоматической генерации документации типа doxygen.



заучить наизусть — будем знать. Парнишка алгоритм свой выложил, а вы нотации изволите читать. А меня Сям никто не учил, а я по чужим распечаткам учился. Рулон берешь, нарезаешь страницы и изучаешь. Почему у меня не возникали претензии к стилю. А?
Re: [Pure С++] портабильная читалка-писАлка INI
От: ssm Россия  
Дата: 08.04.03 12:05
Оценка: 16 (3)
Здравствуйте, ssm, Вы писали:

ssm>На оригинальность не претендую, так что простите за банальность .


Доработанная версия выглядит так:


#ifndef __CONF_H__
#define __CONF_H__


/*
 *Имя модуля  : conf.h
 *Назначение  : 
 *  чтение, запись, изменение конфигурационных файлов. 
 *  так как фактически записью занимается стратегия View можно
 *  изменять формат записи-чтения самого файла изменяя только эту
 *  стратегию
 *Особенности : 
 *   - вроде как Pure C++
 *   - при записи терятся исходное форматирование 
 *   - запись ведется в отсортированном виде (секция, параметр)
 */


#include <set>
#include <string>
#include <fstream>
#include <algorithm>

/*
 * Используется для чтение-изменения значение параметров
 * конфигурационных файлов.
 * При инициализации объекта данного класса получает имя
 * конф. файла в виде строки.
 * Чтение и запись параметров производится в виде строк
 * Запись всегда ведется в отсортированном виде, в независимости
 * от первоначального формата файла.
 * Для записи и чтении из(в) поток используется стратегия View
 * */
template<typename View>
class ConfImpl{
    // Вспомогательная структура для хранения ключа и его значения     
    struct ConfData{
        std::string sect, name;             //Секция, параметр
        mutable std::string val;            //Значение параметра
        
        ConfData(){}                        //Используется для контейнера
        
        ConfData(                           //Инициализация членов 
                const std::string sect,     //Секция
                const std::string &name,    //Параметр
                const std::string &val)     //Значение
            :sect(sect), name(name), val(val){}
        
        //Предикат для сравнения двух ключей
        struct Less{
            bool operator()(
                    const ConfData &l, 
                    const ConfData &r) const
            {         
                if(l.sect == r.sect)
                   return l.name < r.name;
                else
                   return l.sect < r.sect;
            }
        };
    };
    
    //Тип контейнера ключей(секция, параметр, значение)
    typedef std::set<ConfData, ConfData::Less> Set;
    
    Set data_;                              //Ключи
    string fileName_;                       //Имя конфигурационного файла
        
    /*
     * Запись содержимого контейнера в поток-приемник.
     * Для записи используется стратегия View
     */
    void put_to_stream(                     
            std::ostream &os)               //поток - приемник
    {
        string sect = "";
        bool write_sect;
        for (Set::iterator i = data_.begin(); i != data_.end(); ++i){
            write_sect = (sect != i->sect) || (i == data_.begin()); 
            View::write(os, write_sect, i->sect, i->name, i->val);
            if(write_sect)
                sect = i->sect;
        }
    };
public:
    //Инициализирует объект именем файла конфига
    ConfImpl( const std::string &fileName ) :fileName_(fileName){};
    
    /*
     * Загрузка данных из конфигурационного файла
     * Старые данные очищаются
     */
    void load(){
        std::ifstream is(fileName_.c_str());
        if(!is)
            return;
        
        data_.clear();
   
        std::string sect, name, val;
        while(View::read(is, sect, name, val))
            put_string(sect, name, val);
    };
    
    //Созранение буфера данных в конфигурационный файл
    bool save(){
        std::ofstream os(fileName_.c_str());
        if (!os)
            return false;
        else{
            put_to_stream(os);
            return os;
        }
    };
    
    //Вывод данных в std::iostream
    void show(){
        put_to_stream(std::cout);
    }
    
    //Чтение из буфера значения по секции + параметр
    std::string get_string(
            const std::string &sect,    //имя секции
            const std::string &name)    //имя параметра
    {
        Set::iterator i = data_.find(ConfData(sect, name, ""));
        return (i != data_.end()) ? i->val : "";    
    }
    
    //Сохранение значения в буфере
    void put_string(
            const std::string &sect,    //секция
            const std::string &name,    //имя параметра
            const std::string &val)     //значение 
    {
        std::pair<Set::iterator, bool> p = data_.insert(ConfData(sect, name, val));
        string s(p.first->val);
        if (!p.second);
            p.first->val = val;
    }
};


/*
 *Стратегия отображения конфигурационного файла
 *Должна содержать методы чтения(записи) ключа из(в) поток
 */
class ConfCfgView{
    //Очистка незначащих значений(пробелов, переводов каретки, etc.)
    static void skip_ws(std::istream &is){
        char ch; 
        while(is.get(ch)){
            if (!isspace(ch)){
                is.putback(ch);
                break;
            }
        }
    }

public:
    //Чтение ключа из потока. В случае успешного чтения
    //возвращаем true. Дальнейший контроль ошибок - на совести 
    //составителя конфигурационного файла :) 
    static bool read(
            std::istream &is,           //[in] - поток, из которого происходит чтение
            std::string &sect,          //[in] - предыдущее имя секции [out] - текущее имя секции
            std::string &name,          //[out] - имя параметра
            std::string &val)           //[out] - значение параметра
    {
        char ch;  
        
        skip_ws(is);
        if(!is)
            return false;
        
        //read section name
        is.get(ch);
        if (ch == '['){
            sect.erase();
            while(is.get(ch) && (ch != ']'))
                sect.push_back(ch);
            skip_ws(is);
        } else
            is.putback(ch);
        
        //read name
        name.erase();
        while(is.get(ch) && (ch != '='))
            name.push_back(ch);

        //read value
        val.erase();
        while(is.get(ch) && (ch != '\n'))
            val.push_back(ch);

        return true;
    }

    //запись ключа в поток
    static void write(
            std::ostream &os,           //выходной поток 
            bool write_sect,            //писать ли в поток секцию?
            const std::string &sect,    //секция
            const std::string &name,    //имя
            const std::string &val)     //значение
    {
        if (write_sect)
            os << '[' << sect << ']' << std::endl;
        
        os << name << "=" << val << std::endl;
    }
};


//Используется для файлов со след. структурой
//[Имя секции] 
//параметр=значение
typedef ConfImpl<ConfCfgView > Conf;



#endif //__CONF_H__
Re[5]: [Pure С++] портабильная читалка-писАлка INI
От: WolfHound  
Дата: 09.04.03 09:21
Оценка:
Здравствуйте, peterbes, Вы писали:

P>заучить наизусть — будем знать. Парнишка алгоритм свой выложил, а вы нотации изволите читать.

Ну 0 это лишка. А вот обьяснения почему его стиль плохой справедливы.
P>А меня Сям никто не учил, а я по чужим распечаткам учился. Рулон берешь, нарезаешь страницы и изучаешь.
Меня тоже. Тем не мение мне оформление не понравилось.
P>Почему у меня не возникали претензии к стилю. А?
Могу только догадываться.
... << RSDN@Home 1.0 beta 5 >>
Пусть это будет просто:
просто, как только можно,
но не проще.
(C) А. Эйнштейн
Re[6]: [Pure С++] портабильная читалка-писАлка INI
От: ssm Россия  
Дата: 09.04.03 09:35
Оценка:
Здравствуйте, WolfHound, Вы писали:

P>А меня Сям никто не учил, а я по чужим распечаткам учился. Рулон берешь, нарезаешь страницы и изучаешь.

WH>Меня тоже. Тем не мение мне оформление не понравилось.

Честно говоря меня больше инетересовало:

-"правильность" выделения стратегии
-"правильность" выбора std::set(вместо того же std::map, или скажем сортированного вектора или списка)
-"правильность" самого дизайна в целом и мысли участников форума на эту тему


Хотя может моя "стилистика кода" отбила все желание вникать в код
Re[7]: [Pure С++] портабильная читалка-писАлка INI
От: WolfHound  
Дата: 09.04.03 18:39
Оценка:
Здравствуйте, ssm, Вы писали:

ssm>Хотя может моя "стилистика кода" отбила все желание вникать в код


Рад что ты сделал правильный вывод.
... << RSDN@Home 1.0 beta 5 >>
Пусть это будет просто:
просто, как только можно,
но не проще.
(C) А. Эйнштейн
Re[8]: [Pure С++] портабильная читалка-писАлка INI
От: ssm Россия  
Дата: 10.04.03 06:22
Оценка:
Здравствуйте, WolfHound, Вы писали:


WH>Рад что ты сделал правильный вывод.


Ну "стилистику" я поправил здесь
Автор: ssm
Дата: 08.04.03
, хотелось бы все таки получить ответы
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.