Comments 48
А идея крутая и лежит на поверхности. И как я сам не додумался…
Можете почитать — выглядит почти так, как тут.
Вот как, например, у вас обработка списка элементов будет выглядеть? Банальный for_each?
А что касается «пары мегабайт» — за что конкретно мы боремся? Время компиляции по сравнению со многими другими библиотеками у Boost:Hana уменьшено, там на сайте графики есть — хотя, понятно, тут всегда важен баланс между скоростью и возможностями…
Тем, что вы должны прямо «здесь и сейчас» обработать тип. Передать его дальше для обработки нельзя...
Вы имеете ввиду что теряется информация о типе? Если да, то я не понимаю как.
Банальный for_each?
Извините, но я считаю что это разные вещи. Здесь нужен отдельный алгоритм прохода по кортежу, и он у меня есть. Так же как и Hana'вский он принимает на вход кортеж, и лямбду. В лямбде можно уже использовать pattern matching.
Более того — текущий pattern matching можно использовать в Hana::for_each как есть:
hana::for_each(hana::make_tuple(0, '1', "234", 5.5), [&](auto x) {
match(x
,[](std::string value) { cout << "This is string"; }
,[](int i) { cout << "This is int"; }
,[](auto a) { cout << "This is default"; }
);
});
Hana — это библиотека. И мне она не нравится. Прежде всего по тому что там свои кортежи.
Здесь же описана конкретная конструкция. Мне кажется это как сравнивать STL и конкретно взятый алгоритм.
Здесь же описана конкретная конструкция. Мне кажется это как сравнивать STL и конкретно взятый алгоритм.Если под «конкретно взятым алгоритмом» вы понимаете «что-то аналогичное алгоритму из STL'евского <algorithm>а», то да, конечно. Алгоритмы из STL'я удобны не потому, что они реализуют какую-то супермудрость, а потому, что они являются частью большой библиотеки, которые согласованы между собой.
То же самое и здесь: да, вы можете использовать ваш матчер с hana::make_tuple, но… зачем?
Насчёт что лучше — отдельные «алгоритмы» или целые библиотеки можно спорить, но хочу заметить, что даже ваш матчер не существует в вакууме, а тянет за собой FunctionArgs :-)
Практика показывает, что «отдельные маленькие хорошенькие штучки» хороши в теории, но на практике библиотеки — удобнее. До определённого предела, конечно (вряд ли кто захочет использовать библиотеку на терабайт, даже если она будет уметь очень-очень много всего-всего-всего), но в большинстве случаев — это так.
Hana не об этом. Hana позволяет заменить вырвиглазные и тормозные шаблоны Boost Fusion и MPL на читаемые и относительно быстрые constexpr вычисления. Такой минихаскель построенный на C++ constexpr, где вычисления идут в compile-time над типами и константами C++.
Заинлайниться же в C++ может почти все что угодно (кроме не tail рекурсии), даже при раздельной компиляции с LTO.
Паттерн матчинг бывает 2х типов: comile time и run time. Первый — это пример автора и hana (которая позволила бы написать еще короче и понятнее), а так же известный variant. Если же нужен полноценный run time pattern matching почти как в хаскеле (ака dynamic_cast на стероидах, да еще и быстрее), то Mach7 как раз об этом (но десять раз подумайте — может вам просто нужен хаскель?).
P.S. Я экспериментировал с Boost Hana — и она замечательна, планирую написать статьи о ней.
Косвенная проверка:
https://godbolt.org/g/pNIa8n
С -O2 или -O3 вообще результат вычисляется в compile-time.
Пожалуйста, укажите лицензию на ваш код. Формально, использовать код без указанной лицензии нельзя, ибо по умолчанию лицензия на код проприетарная, т. е. использовать код можете только вы, где бы вы его при этом не публиковали.
Не обязательно копипастить весь текст лицензии, укажите хотя бы её название и ваш копирайт. Например:
/* The MIT License (MIT)
* Copyright (c) 2016 tower120
*/
Также было бы очень здорово, если бы вы опубликовали код не только в статье на Хабре, но и, скажем, в виде GitHub Gist. Так вашему коду можно поставить звезду и потом не потерять его. А можно форкнуть и дополнить. И все форки будут в одном месте и тоже не потеряются.
2. Есть и специальные сервисы и люди — но обычно это делается тогда, когда с пользователя кода можно денег содрать. Поймать «дядушку Ляо» на использовании чужого кода можно, конечно — но дальше что? Даже затраты на адвокатов не окупите.
3. А чем чревата продажа пиратских фильмов или аудизаписей? Принцип тот же: до $100'000 за копию в теории, но на практике вряд ли больше нескольких центов. Гораздо более чувствительным может быть требование прекратить новые продажи до того момента пока чужой код не будет изъят — но, опять-таки, «дядушку Ляо» так не остановить.
4. Что значит «а если проект с закрытым исходным кодом»? А какая вообще разница? Суд не интересует какие там у вас в фирме политики. Незаконное копирование == штраф.
4. Это значит, что некая условная корпорация берет из интернета кусок кода, распространяемый, например, под GPL, или вообще без указания лицензии (а значит, по умолчанию она проприетарная), и засовывает его в свой проприетарный же продукт с закрытым кодом. А распространяет она бинарники. Кто и как узнает, что в скомпилированных бинарниках есть кусок чужого кода, вставленный туда без разрешения автора? И какими могут быть реальные последствия этого для корпорации? И как это вообще можно доказать?
И вот этот вот «мир не столь совершенен, а потому познаваем» — очень часто играет в подобного рода вещах.
Кто и как узнает, что в скомпилированных бинарниках есть кусок чужого кода, вставленный туда без разрешения автора?Можно отловить характерные участки кода, строки и т.п. Обычно автору эо сделать несложно: берётся место, которое «давно стоило бы переписать, да руки не доходят» и проверяется его наличие. Так как это место сделано «шероховато», то очень маловероятно что кто-то сделает его так же криво очень мала (все счастливые семьи счастливы одинаково, каждая несчастливая семья несчастлива по-своему).
И как это вообще можно доказать?А как доказывают что Петя убил Васю ледорубом? Следы ищут, «отпечатки пальцев», экспертов привлекают и т.д. и т.п. Потом суд решает — достаточно ли улик. Достаточно должно быть только для того, чтобы заставить код показать экспертам (противной стороне, понятно, его показывать нельзя — секреты фирмы и прочее, но если вы отказываетесь его показать независимым экспертам, ссылаясь на «потерянные флоппи», то вам нужно будет после этого очень сильно постараться, чтобы судья поверил в вашу невиновность).
И какими могут быть реальные последствия этого для корпорации?До сих пор самое страшное что случалось — запрещали продажу конкретных устройств. Что само по себе чревато. До «термоядерной опции» (указать что нарушение, скажем, GPL, лишает вас права на использование соответствуещего кода вообще, навсегда) никто пока не добирался.
Но это все про крупные проекты. Мелкие куски кода (типа 50 строк приведённых в статье) обычно проще выкинуть и переписать заново, чем разбираться с лицензиями. Когда SCO пыталась всех пользователей Linux засудить спорный кусок кода в Linux'е обнаружили, к примеру. На всякий случай его переписали — просто «на всякий случай»…
namespace detail {
template <class ...> struct match;
template <class Head, class ... Tail>
struct match<Head, Tail...> : match<Tail...>, Head {
template <class Head2, class ... Tail2>
match(Head2&& head, Tail2&& ... tail) : match<Tail...>(std::forward<Tail2>(tail)...), Head(std::forward<Head2>(head)) {}
using Head::operator();
};
template <> struct match<> {};
}
template <class T, class ... Cases>
decltype(auto) match(T&& value, Cases&& ... cases) {
return detail::match<typename std::decay<Cases>::type...>{std::forward<Cases>(cases)...}(std::forward<T>(value));
}
namespace detail {
template <class ...> struct match;
template <class Head, class ... Tail>
struct match<Head, Tail...> : match<Tail...>, Head {
template <class Head2, class ... Tail2>
match(Head2&& head, Tail2&& ... tail) : match<Tail...>(std::forward<Tail2>(tail)...), Head(std::forward<Head2>(head)) {}
using Head::operator();
using match<Tail...>::operator();
};
template <class T>
struct match<T>: T {
template <class R>
match(R&& r) : T(std::forward<R>(r)) {}
using T::operator();
};
}
template <class T, class ... Cases>
decltype(auto) match(T&& value, Cases&& ... cases) {
return detail::match<typename std::decay<Cases>::type...>{std::forward<Cases>(cases)...}(std::forward<T>(value));
}
К сожалению, теперь я не могу понять что тут вообще происходит...
Если не повезло — не срабатывает.
Это не совсем эквивалент, так как перебора нет и нельзя передать много «уточняющих» лямб: если будет десколько лямбд, которые, теоретически, могут принять аргумент, то они все будут пытаться примениться и получится ошибка компиляции.
Вангую очень веселое сообщение об ошибке, если что-то не получилось.
Вы говорите про нечто подобное?
А умение читать сообщения об ошибках от компилятора C++ приходит вместе с опытом :-)
Например, нужно получить из функции структуру, в которой нас интересует только одно поле. Пишем:
let MyStruct{field, ..} = my_function();
Или вместо
std::size_t found = str.find(str2);
if (found!=std::string::npos)
std::cout
Это не издевательство, я реально не понимаю как можно практически использовать метапрограммирование и не понимать когда и как вам может пригодиться pattern matching. Я могу у нас в проекте показать сразу пяток мест, где бы это можно было с пользой применить если бы C++14 был разрешён. А так — приходится по старинке, с шаблонами с частичной специализацией…
Посмотрите на ваш любимый пример, использующий метапрограммирование с разными типами, и посмотрите сколько там ненужного boilerplate…
А так — приходится по старинке, с шаблонами с частичной специализацией
Так а чем это плохо?
Посмотрите на ваш любимый пример, использующий метапрограммирование с разными типами, и посмотрите сколько там ненужного boilerplate…
Что вы имеете в виду?
Так а чем это плохо?Тем что на написание и чтение всего это кода уходит время, очевидно. Ваш К.О.
Так-то вообще всё, что можно сделать на C, C++, Haskell'е или там Java можно сделать прямо в машинных кодах… в конце-то-концов ничего больше CPU исполнять не умеет… вопрос только в том, сколько у вас на это времени уйдёт.
Что вы имеете в виду?Возьмите любой пример и посмотрите — сколько вам придётся завести лишних trait'ов, шаблонов и прочего, чтобы сделать хоть что-нибудь.
Возьмите хотя бы те 8 строк с которых началась статья и превратите их в шаблоны метапрограммирования в стиле C++98 — а дальше подумайте — легко ли там будет допустить ошибку и сколько времени вы будете её искать, если ошибётесь.
Вот самый очевидный пример который я придумал. В С++17 введена конструкция constexpr if. Она позволяет выполнять ветвления на этапе компиляции. Например:
template<class T>
auto call_java(T element){
cout << "Start Java Call";
constexpr if (is_same<T, int>){
return jni->CallIntMethod(....);
} else constexpr if (is_same<T, float>) {
return jni->CallFloatMethod(....);
} else {
return jni->CallVoidMethod(....);
}
}
Без constexpr if вам бы пришлось в данном случае три дополнительные функции:
int call_java_helper(int element){
return jni->CallIntMethod(....);
}
float call_java_helper(float element){
return jni->CallFloatMethod(....);
}
void call_java_helper(nullptr_t){
jni->CallVoidMethod(....);
}
template<class T>
auto call_java(T element){
cout << "Start Java Call";
return call_java_helper(element);
}
C pattern matching (как реализовано в статье):
template<class T>
auto call_java(T element){
cout << "Start Java Call";
return match(elment,
,[](int element) { return jni->CallIntMethod(element); }
,[](float element){ return jni->CallFloatMethod(element); }
,[](auto) { jni->CallVoidMethod(); }
);
}
С увеличением сложности разница ещё более увеличивается. Основной плюс как по мне что код получается не размазанным по всему файлу.
...
[](Vec<float, n> element) { return jni->CallFloatVector(element, n); }
...
Но частичная специализация вроде до сих пор только для структур/классов возможна.
Но частичная специализация вроде до сих пор только для структур/классов возможна.Для обычных функций ещё возможна. Для функций членов и лямбд — невозможна. Не знаю — в чём там засада.
Всё-таки без полноценных типов-сумм сопоставлению с образцом очень не хватает юзабельности. То есть в таком виде это скорее static visitor от одного параметра.
Возможно можно как-то переделать на матчинг по нескольким параметрам (вас наверное деструктуризация интересует). Но! Рекомендую к ознакомлению http://cppnow2016.sched.org/event/6Sfb (когда появится конечно).
//какие-то функции
int test1(float a, double b)
{
std::cout << "int test1(float a, double b)\n";
return 1;
}
void test2(std::string a, int b, const char* c)
{
std::cout << "void test2(std::string a, int b, const char* c)\n";
}
...
//создаем "статическую мапу функций"
tagged_functor<
int, decltype(test1),
float, decltype(test2)>
functor(test1, test2);
....
//используем
functor.call<int>()(0.0f, 1.0);
Все же ближе к этому https://habrahabr.ru/post/282630/?reply_to=8875810#comment_8873766
Да, похоже. Насчет вашего "тонкого паттерн матчинга", gcc >=4.9.x и VS поддерживают то что вы хотите уже здесь и сейчас http://coliru.stacked-crooked.com/a/61b8afdb8e54d9af
C++ pattern matching