Re[4]: [ANN] Опыт генерации C++ с помощью Ruby
От: eao197 Беларусь http://eao197.blogspot.com
Дата: 20.03.08 11:21
Оценка:
Здравствуйте, remark, Вы писали:

R>Не знаю в тему это или нет, но я делал так:


R>
R>void write_generated_file(std::string const& filename, std::ostringstream const& contents)
R>{
R>  std::string const previous_contents = load_file(filename); 
R>  if (previous_contents == contents.str())
R>    return;
R>  store_file(filename, contents.str());
R>}
R>


Т.е. ты выполнял генерацию, а затем смотрел, отличается ли результат генерации от предшествующего результата?

Имхо, такой способ хорош, когда скоростью самой генерации можно пренебречь. И когда результат генерации не размазывается по нескольким файлам. А так же хорош, когда сам генератор меняется -- тогда при неизменной конфигурации все равно получается новое содержимое и файлы перезаписываются.


SObjectizer: <микро>Агентно-ориентированное программирование на C++.
Re[5]: [ANN] Опыт генерации C++ с помощью Ruby
От: remark Россия http://www.1024cores.net/
Дата: 20.03.08 11:57
Оценка:
Здравствуйте, eao197, Вы писали:

E>Здравствуйте, remark, Вы писали:


R>>Не знаю в тему это или нет, но я делал так:


R>>
R>>void write_generated_file(std::string const& filename, std::ostringstream const& contents)
R>>{
R>>  std::string const previous_contents = load_file(filename); 
R>>  if (previous_contents == contents.str())
R>>    return;
R>>  store_file(filename, contents.str());
R>>}
R>>


E>Т.е. ты выполнял генерацию, а затем смотрел, отличается ли результат генерации от предшествующего результата?


Да. Тупо в лоб.

E>Имхо, такой способ хорош, когда скоростью самой генерации можно пренебречь.


В принципе да. У меня как-то всё быстро генерировалось, хотя там из базы данных много инфы забиралось.

E>И когда результат генерации не размазывается по нескольким файлам.


Я во все функции генерации передавал std::ostream&, и изначально он указывал на std::ofstream, потом переделал на std::ostringstream для борьбы с ошибками в середине генерации, а потом ещё доделал проверку, что содержимое реально изменилось.
Я не вижу вариантов, как может быть размазана генерация, т.ч. нельзя было применить такой приём. В любом случае сгенерированный файл должен записываться атомарно и целиком, что бы не было частично сгенерированных файлов.

E>А так же хорош, когда сам генератор меняется -- тогда при неизменной конфигурации все равно получается новое содержимое и файлы перезаписываются.


Меня результат более чем устроил. Максимально просто и никаких засад.



1024cores &mdash; all about multithreading, multicore, concurrency, parallelism, lock-free algorithms
Re[5]: Делов-то!
От: Roman Odaisky Украина  
Дата: 22.03.08 17:43
Оценка: 5 (2)
Здравствуйте, Left2, Вы писали:

R>>Но вот сейчас я подумал, что С++ в простейшем случае легко придать свойство скриптовости:

L>Можно, но зачем нам искуственно делать то что скрипты умеют делать естественным образом?
R>>Было бы неплохо. Можно даже не задачку, а просто кусочек задачи.
L>Если хочешь — давай попробуем. Чтобы было совсем кратко и понятно предлагаю след. задачу:
L>Название — "именованые enum-ы".
L>Содержание — входной XML-файл содержит структуру след. вида:
L>На выходе — h-файл вида
L>и cpp-файл вида
L>Думаю, задачка как раз для того чтобы сравнить удобство подходов к кодогенерации :) Небольшая (я намеренно ничего не усложнял) и при этом ИМХО вполне показательная.

За неимением под рукой парсера XML на C++, предположу, что кто-то добрый уже превратил XML-код в что-то вроде std::map<std::string, std::map<std::string, int> > (или в нечто, по чему можно так же ходить итераторами).
typedef std::vector<std::pair<std::string, int> > EnumValues;
typedef std::vector<std::pair<std::string, EnumValues> > Enums;

Осталось только написать шаблоны
    static boost::format const cppEnumEntryFromNameValue("\te%1% = %2%,\n");
    static boost::format const cppEnumDefFromNameEntries(
        "enum E%1%\n"
        "{\n"
        "%2%"
        "\t" "e%1%Pte\n"
        "};\n"
        "std::string toString(E%1%);\n"
        "\n"
    );

    static boost::format const cppToStringEntryFromName(
        "\t" "else if(value == e%1%)\n"
        "\t" "{\n"
        "\t\t" "return \"%1%\";\n"
        "\t" "}\n"
    );

    static boost::format const cppToStringDefFromNameEntries(
        "std::string toString(E%1% value)\n"
        "{\n"
        "\t" "if(0);\n"
        "%2%"
        "\t" "throw std::logic_error(\"Bad E%1% value!\");\n"
        "}\n"
        "\n"
    );

Код-то простой:
    std::string cppHeader, cppSource;
    foreach(Enums::const_reference i, enums)
    {
        std::string cppEnumEntries, cppToStringEntries;
        foreach(EnumValues::const_reference j, i.second)
        {
            cppEnumEntries     += str(tmp(cppEnumEntryFromNameValue) %j.first %j.second);
            cppToStringEntries += str(tmp(cppToStringEntryFromName) %j.first);
        }
        cppHeader += str(tmp(cppEnumDefFromNameEntries) %i.first %cppEnumEntries);
        cppSource += str(tmp(cppToStringDefFromNameEntries) %i.first %cppToStringEntries);
    }

Тест:
int main()
{
    EnumValues rgb;
    rgb.push_back(std::make_pair("Red", 0));
    rgb.push_back(std::make_pair("Green", 1));
    rgb.push_back(std::make_pair("Blue", 2));
    EnumValues cmyk;
    cmyk.push_back(std::make_pair("Cyan", 0));
    cmyk.push_back(std::make_pair("Magenta", 1));
    cmyk.push_back(std::make_pair("Yellow", 2));
    cmyk.push_back(std::make_pair("Key", 3));
    Enums enums;
    enums.push_back(std::make_pair("Rgb", rgb));
    enums.push_back(std::make_pair("Cmyk", cmyk));

    std::pair<std::string, std::string> const code = cppCode(enums);
    std::cout << "---%<--- header ---%<---" << std::endl;
    std::cout << code.first;
    std::cout << "---%<--- source ---%<---" << std::endl;
    std::cout << code.second;
}

Для полноты картины:
#include <iostream>
#include <string>
#include <vector>
#include <utility>
#include <boost/format.hpp>
#include <boost/foreach.hpp>

#define foreach BOOST_FOREACH

typedef std::vector<std::pair<std::string, int> > EnumValues;
typedef std::vector<std::pair<std::string, EnumValues> > Enums;

template <class X>
inline X tmp(X x)
{
    return x;
}
До последнего не верил в пирамиду Лебедева.
Re[6]: Делов-то!
От: Qbit86 Кипр
Дата: 22.03.08 18:01
Оценка:
Здравствуйте, Roman Odaisky, Вы писали:

RO>
RO>template <class X>
RO>inline X tmp(X x)
RO>{
RO>    return x;
RO>}
RO>


Ух ты... Ну да, прикольно, нужно взять на вооружение :) Мелочь, а приятно!

RO>Для полноты картины:


А функция str() — это, вероятно, аналогичный type inferring wrapper над boost::lexical_cast<>()?
Глаза у меня добрые, но рубашка — смирительная!
Re[7]: Делов-то!
От: Qbit86 Кипр
Дата: 22.03.08 18:04
Оценка:
Q>А функция str() — это, вероятно, аналогичный type inferring wrapper над boost::lexical_cast<>()?

А, сообразил, это я фигню сказал.
Глаза у меня добрые, но рубашка — смирительная!
Re[4]: [ANN] Опыт генерации C++ с помощью Ruby
От: Sni4ok  
Дата: 22.03.08 19:10
Оценка:
Здравствуйте, eao197, Вы писали:

E>Как это сделать с помощью макросов и шаблонов -- я не знаю.


ну вот видите, свои недостатки в квалификации восполняете весьма сомнительным образом, но пытаетесь убедить при этом, что предложенное вами решение офигенно правильное и безгрешное.
Re[7]: Делов-то!
От: Roman Odaisky Украина  
Дата: 22.03.08 20:47
Оценка:
Здравствуйте, Qbit86, Вы писали:

Q>А функция str()


namespace boost {

    template<class Ch, class Tr, class Alloc> inline
    std::basic_string<Ch, Tr, Alloc> str(const basic_format<Ch, Tr, Alloc>& f) {
        // adds up all pieces of strings and converted items, and return the formatted string
        return f.str();
    }
До последнего не верил в пирамиду Лебедева.
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.