Здравствуйте, alex_public, Вы писали:
_>Что-то там код слегка разный вроде. Ну да не принципиально, главное же идея обхода. ) Ну и да, на C++ тут самый симпатичный вариант.
Вот только он не решает поставленную задачу: http://rsdn.ru/forum/philosophy/5341291.1
Здравствуйте, kurchatov, Вы писали:
K>Откуда пошла эта мода на обмазывание шаблонами к месту и не к месту? Нет, я ничего не имею против шаблонов, если действительно надо — только приветствую. K>Но в последние годы появился какой-то особенный класс С++ программистов, которые пихают шаблоны везде, делая код и ошибки компиляции нечитаемыми. K>Такие люди пишут код, обмазанный шаблонами — и говорят "смотрите, как просто и красиво!". Да нихрена не просто и не красиво! Разбор такого кода вызывает у меня только головную боль.
Думаю, это началось с того момента когда Александреску отрыл тайны шаблонной магии. Получив чудесные знания многие просто не удерживаются от того, чтобы применить их на практике где нужно и где нет (что чаще всего). Со временем, конечно, желание баловаться с шаблонами отпадает. Но из-за того, что постоянно появляются молодые подаваны верующие в то, что путь джедая С++ лежит через шаблонный мазохизм, "мода на обмазывание шаблонами к месту и не к месту", скорее всего, умрет не раньше самого С++...
Здравствуйте, alex_public, Вы писали:
EP>>Недостаток языка — это отсутствие compile-time reflection, Boost.Fusion для этого предлагает макросы BOOST_FUSION_DEFINE_STRUCT и подобные. EP>>А вот те методы работы, которые предлагает Boost.Fusion с уже адаптированными структурами (то есть для которые есть необходимые гетерогенные итераторы) — вполне себе, ничего костыльного. _>Нуу тут вопрос вот в чём — если бы у нас уже была интроспекция в языке, то стали бы мы использовать подобное? Соответственно все варианты где ответ "нет не стали бы, но пока приходится" являются сильно костыльным. На мой вкус конечно же...
Не, так о том же и речь — compile-time reflection будет скорей всего в виде специализаций шаблонов для интроспектируемых типов.
Например, что-то типа:
То есть по сути задаётся гетерогенная последовательность. Обходить её "вручную", то есть какой-нибудь рекурсией по std::fields_count не удобно.
С другой стороны, в Boost.Fusion уже есть готовые высокоуровневые алгоритмы для работы с подобными гетерогенными последовательностями — for_each, fold, filter, transform, join, zip, etc. Для их использования достаточно написать адаптер для traits предоставленных compile-time reflection.
То есть даже когда появится compile-time reflection — Boost.Fusion будет удобным способом его использования (если конечно в стандарт не войдёт ещё и аналог Fusion).
Здравствуйте, kurchatov, Вы писали:
K>Откуда пошла эта мода на обмазывание шаблонами к месту и не к месту?
Это засилье в отрасли понтарезов и олимпиадников, которые не осилили разработку на функциональных ЯП,
но которым выпендренуться, по разным причинам, требуется. Они и уцепились за сущности aka "Метапрограммирование,
Boost.Fusion, Boost.Mpl...". Но выглянуть из песочницы у них кругозора не хватает.
В Lisp красиво, без того уродства, коим являются шаблоны C++, любая, и самая сложная, грамматика, парсится в несколько десятков строк.
Здравствуйте, smeeld, Вы писали:
S>В Lisp красиво, без того уродства, коим являются шаблоны C++, любая, и самая сложная, грамматика, парсится в несколько десятков строк.
Ну так в LISP уродливо всё остальное.
WH>В том треде Evgeny.Panasyuk накидал кучу гнилых отмазок и слился.
Нуу на самом деле не помню каких-то особых претензий там к нему. Основное, что он реализовал отдельную функцию, а не функцию-член. Ну так это в C++ это как раз не проблема поправить, благодаря множественному наследованию.
Проблема в том, что модность обсуждаемого кода идёт уже как бы поверх эмуляции интроспекции. И в этом смысле код действительно красив. Но если взглянуть на эту эмуляцию, то естественно ужас ужас ужас.
В общем моё мнение тут такое: как только в C++ появится нормальная интроспекция (естественно времени компиляции), то пример Евгения честно можно будет позиционировать как полноценное решение, причём оно возможно будет ещё и самым красивым. Ну а пока...
Здравствуйте, Evgeny.Panasyuk, Вы писали:
EP>С другой стороны, в Boost.Fusion уже есть готовые высокоуровневые алгоритмы для работы с подобными гетерогенными последовательностями — for_each, fold, filter, transform, join, zip, etc. Для их использования достаточно написать адаптер для traits предоставленных compile-time reflection.
Да, удобные штуки. Кстати, в том же D я помнится так и не нашёл готового "static_foreach", так что для некоторых случаев приходилось писать ту самую рекурсию на шаблонах, в лучших традициях C++. )))
EP>То есть даже когда появится compile-time reflection — Boost.Fusion будет удобным способом его использования (если конечно в стандарт не войдёт ещё и аналог Fusion).
Да уж не важно как, хорошо бы только побыстрее. А то это уж очень заметный недостаток, причём для целых областей)
Здравствуйте, WolfHound, Вы писали:
_>>Что-то там код слегка разный вроде. Ну да не принципиально, главное же идея обхода. ) Ну и да, на C++ тут самый симпатичный вариант. WH>Вот только он не решает поставленную задачу: http://rsdn.ru/forum/philosophy/5341291.1
Эту "Помечаем его атрибутом/аннотацией [...] добавленную в класс"? Если дословно, то да — не решает.
Но конкретно в данном топике мы обсуждаем алгоритмы обхода гетерогенных последовательностей и Boost.Fusion в частности.
Если в Nemerle есть что-то готовое на эту тему, то можешь показать. Например, аналог чего-то вот такого:
auto println = [](auto... xs)
{
initializer_list<int>({(cout << xs << " ", 0)...});
cout << endl;
};
BOOST_FUSION_DEFINE_STRUCT
(
(), Foo,
(int, x)
(double, y)
)
BOOST_FUSION_DEFINE_STRUCT
(
(), Bar,
(complex<double>, u)
(string, v)
)
auto compose = [](auto f, auto g)
{
return [=](auto... xs)
{
return f(g(xs...));
};
};
auto apply = [](auto f, auto g, auto... xs)
{
auto z = zip(xs..., transform(xs, compose(g, g))...);
for_each(z, [=](auto x)
{
invoke(f, x);
});
};int main()
{
Foo a = {1, .1};
a.x *= 2;
Bar b = {{1, 2}, "a"};
b.v += 'b';
auto double_it = [](auto x) { return x + x; };
apply(println, double_it, a, b, make_vector(b.v + 'c', cos(b.u)));
}
Естественно без потери гибкости — например apply можно передать в стороннюю библиотеку, которая может вызывать его с разными наборами аргументов и т.п.
Где: zip — zip'ует несколько гетерогенных последовательностей в одну. transform, соответственно, трансформирует а-ля map. Причём трансформация может переводить в другой тип. for_each — обходит гетерогенную последовательность. invoke — вызывает "функцию" аргументами из гетерогенной последовательности, т.е. explode или распаковка.
Причём это всё работает со структурами (Foo, Bar), с кортежами/tuples (make_vector), да и вообще со всем что удовлетворяет необходимым концепциям (например "виртуальная" последовательность, не хранящая значений внутри).
WH>В том треде Evgeny.Panasyuk накидал кучу гнилых отмазок и слился.
Кстати, стандартное возражение, что IDE перестает работать на таких макросах, обходится довольно просто — у меня в команду, которая зовет ctags, добавлен простой перловый скрипт, который генерит правильные ссылки на структуры/поля, объявленные подобными макросами, так что все среды, которые основаны на работе с ctags (а это, в частности, vi и emacs, ну и мой любимый NEdit), видят эти макросы как обычные структуры с обычными полями.
(естественно, всякие автоматические рефакторинги для них не работают без дополнительного программирования. Но с подобными структурами вообще опасно какие-либо автоматические рефакторинги проводить, так как они предоставляют доступ "в обход" нормального через имена полей. Хотя, конечно, нет ничего невозможного.)
Здравствуйте, PM, Вы писали:
PM>>>Для добавления экстремальности я бы еще добавил boost.preprocessor в эту компанию X>>замечательная либа позволяющая делать замечательные вещи PM>Наверно иногда да. Но на мой взгляд, препроцессор, как он сейчас есть в С/С++ довольно опасная вещь.
Самая безобидная вещь в с++, можно легко получить результат препроцессинга и понять где ошибся. Очень мощный плюс, где вы ещё такой в с++ найдёте?
[In theory there is no difference between theory and practice. In
practice there is.]
[Даю очевидные ответы на риторические вопросы]
Здравствуйте, alex_public, Вы писали:
_>Хыхы, кстати optional — это считай уже не boost, а стандарт языка. )))
Ну в стандарте много всякого было — и неоднократно проклятый vector<bool>, и спецификации исключений, и встроенные массивы для облегчения членовредительства ... Что касается optional, то вот (просто например) объясни, зачем в нем определены operator-> и operator* : optional — не указатель, и предназначен не для того совсем, а с ним используется синтаксис указателей. "Похожие вещи должны выглядеть похоже, а разные — по-разному". Это все равно, как если бы в vector были бы определены operator+ (для дублирования push_back) и оператор-- (для дублирования pop_back).
Люди! Люди, смотрите, я сошел с ума! Люди! Возлюбите друг друга! (вы чувствуете, какой бред?)
Здравствуйте, Cyberax, Вы писали:
S>>В Lisp красиво, без того уродства, коим являются шаблоны C++, любая, и самая сложная, грамматика, парсится в несколько десятков строк. C>Ну так в LISP уродливо всё остальное.
Здравствуйте, alex_public, Вы писали:
_>Нужен/ненужен — это не разговор в мире разработки. Формально можно вообще всё на ассемблере написать и сказать что остальное не нужно. Разговор у нас идёт о минимизации усилий по написанию кода.
. Покажи где там нечитаемый код. F>но там же нет шаблонов.
Там есть шаблоны, но не в смысле TMP (хотя если учитывать внутренности Spirit'а, то и в TMP смысле в том числе).
А вообще, выше же по ветке было:
K>>>Да вот только не EBNF она предоставляет, а нечитаемую пародию на него.
P>>Дело скорее вкуса. Достаточно быстро адаптируешься, а для задач, под которые, как я считаю он более всего подходит (см. ниже) — большего и не надо.
ОК>Это не дело вкуса, а дело инженеринга. Мне довелось поддерживать проект в который такие любители запихали Спирит. Абсолютно нечитаемый и неподдерживаемый код был в тех местах.
Здравствуйте, kurchatov, Вы писали:
K>Такие люди пишут код, обмазанный шаблонами — и говорят "смотрите, как просто и красиво!". Да нихрена не просто и не красиво! Разбор такого кода вызывает у меня только головную боль.
K>Если все еще непонятно, о чем говорю — приведу пример — boost::spirit. Это просто экстремум шаблонофилии.
Проблема с метапрограммированием не в спирите. Спирит это пример относительно внятного применения шаблонов. Если знаешь про парсеры, то спирит достаточно понятный.
Мулька в том, что такие рафинированые случаи применения скорее исключения. Большей частью в мета-код приходится влазить по уши, потому что автор реализовал идейку одному ему понятную. Метакод, что характерно, затрудняет исследование целевого результата. То есть, конечный результат хуже просматривается.
С шаблонами навроде T4 еще куда ни шло, хотя бы структура видна. Более сильные техники равносильны практически обфускации.
Пример:
var sb = new StringBuilder();
GetSomeText(sb); // нативная функция
Код содержит большую дыру. Такие дыры хочется исключать всеми способами, скажем, очень эффективный — code review. Но вот фокус — этот код спрятан унутре макроса примерно так
body.NativeCall(GetSomeText);
Вопрос — каким образом ревьюеру надо догадываться про такие вот детали ?
Похожая проблема — слияние версией. При слиянии приходится выполнять двойную работу, т.к. корректное слияние макро-кода не гарантирует корректного выхлопа, который собтсвенно и не виден.
И вот на ровном месте появляются те самые паттерны, от которых, по идее, макры должы избавлять. То есть, годится не любой говнопарсинг, запиханый в макры, а только православный, с четкой формализацией, прозрачной моделью и тд и тд. Произвольная схема взаимодействия окажется скорее всего говном, а вот широко известный паттерн Property Changed вполне сгодится.
Прятать эвристики, разные детали, которые меняют важность в зависимости от рассматриваемого аспекта, практически преступление.
Здравствуйте, Vain, Вы писали:
PM>>Наверно иногда да. Но на мой взгляд, препроцессор, как он сейчас есть в С/С++ довольно опасная вещь. V>Самая безобидная вещь в с++, можно легко получить результат препроцессинга и понять где ошибся. Очень мощный плюс, где вы ещё такой в с++ найдёте?
Угу, промотав пару мегабайт включенных заголовков. Вам когда-нибудь приходилось вызывать функции типа Sleep, GetMessage из каких-нибудь сторонних библиотек? При компиляции под Windows на ровном месте вываливается ошибка о несоответствии типов аргументов. И по неопытности можно долго ломать голову, глядя на абсолютно корректный вызов, имея #define GetMessage GetMessageW где-то в неявно включенном windows.h
Здравствуйте, alex_public, Вы писали:
_>Нуу на самом деле не помню каких-то особых претензий там к нему. Основное, что он реализовал отдельную функцию, а не функцию-член. Ну так это в C++ это как раз не проблема поправить, благодаря множественному наследованию.
И как ты множественным наследованием собрался реализовать абстрактный метод?
error C2259: 'Foo' : cannot instantiate abstract class
_>В общем моё мнение тут такое: как только в C++ появится нормальная интроспекция (естественно времени компиляции), то пример Евгения честно можно будет позиционировать как полноценное решение, причём оно возможно будет ещё и самым красивым. Ну а пока...
Нельзя. Плюс я могу придумать ещё миллион задач которые на шаблонах С++ не решить.
... << RSDN@Home 1.2.0 alpha 5 rev. 62>>
Пусть это будет просто:
просто, как только можно,
но не проще.
(C) А. Эйнштейн
Здравствуйте, Evgeny.Panasyuk, Вы писали:
EP>Если в Nemerle есть что-то готовое на эту тему, то можешь показать. Например, аналог чего-то вот такого:
Готового нет. При желании можно сделать. Но никто это делать не будет, ибо макросы всё равно мощнее.
Но главная проблема этого примера в том, что он не решает осмысленную задачу.
Осмысленная задача звучит примерно так: Добавить во все методы классов находящихся в заданном пространстве имён логирование.
"?
Именно это ты и сделал.
EP>А вообще, градус озлобленности у евангелистов Nemerle поражает
Поражает привычка людей решать задачу, которую не просили, и делать далеко идущие выводы.
... << RSDN@Home 1.2.0 alpha 5 rev. 62>>
Пусть это будет просто:
просто, как только можно,
но не проще.
(C) А. Эйнштейн