У меня вообще-то тоже только буст
I>На две строки длиннее, зато комментарии не нужны, поскольку нет никаких ничего не говорящих индексов 0, 1, 2, 3.
Т.е. вместо 4 простых понятных функций мы имеем 6 делающих не очень понятно что
По крайней мере, я не понял, что каждая делает. Но вполне допускаю, что разобраться можно, если аккуратно нарисовать на листочке граф зависимостей.
Разбираться с enable/disable_if — это как раз то, от чего хотелось уйти, а ты предлагаешь к этому вернуться.
Если ты по-прежнему считаешь, что твое решение проще, добавь пару перегрузок, скажем, для is_floating (или лучше для double через is_same, а то у тебя is_arithmetic никогда ни сматчится) и для is_array. У меня добавятся две очевидные функции.
I>И можно не только последовательность, но и дерево условий использовать. Ну и Бритва Оккама к тому же.
Не очень понял, к чему бритва Оккама конкретно здесь и в программировании вообще.
Любая функция, кроме main, противоречит этому принципу.
Здравствуйте, jazzer, Вы писали:
J>Т.е. вместо 4 простых понятных функций мы имеем 6 делающих не очень понятно что
Это не просто 6 функций (точнее шаблона функций), а три раза по две (два), у каждой пары свое имя. f выбирает между arithmetic и не-arithmetic, f_arithmetic — между integral и не-integral, а f_integral — между char и не-char.
J>Если ты по-прежнему считаешь, что твое решение проще, добавь пару перегрузок, скажем, для is_floating (или лучше для double через is_same, а то у тебя is_arithmetic никогда ни сматчится) и для is_array. У меня добавятся две очевидные функции.
У тебя добавятся две очевидные функции и два условия, у меня — то же самое, смотри:
#include <iostream>
#include <string>
using namespace std;
#include <boost/mpl/or.hpp>
#include <boost/type_traits.hpp>
#include <boost/utility/enable_if.hpp>
using namespace boost;
using namespace mpl;
template < class T >
typename disable_if< is_same< T, char >, string >::type
f_integral(T x)
{
return"integral";
}
template < class T >
typename enable_if< is_same< T, char >, const char* >::type
f_integral(T x)
{
return"char";
}
template < class T >
typename disable_if< or_< is_integral< T >, is_same< T, double > >, string >::type
f_arithmetic(T x)
{
return"arithmetic";
}
template < class T >
typename enable_if< is_integral< T >, string >::type
f_arithmetic(T x)
{
return f_integral(x);
}
template < class T >
typename enable_if< is_same< T, double >, string >::type
f_arithmetic(T x)
{
return"double";
}
template < class T >
typename disable_if< or_<is_arithmetic< T >, is_array< T > >, string >::type
f(T const& x)
{
return"generic";
}
template < class T >
typename enable_if< is_arithmetic< T >, string >::type
f(T x)
{
return f_arithmetic(x);
}
template < class T >
typename enable_if< is_array< T >, string >::type
f(T const& x)
{
return"array";
}
int main()
{
cout
<< f('a') << '\n'
<< f(1) << '\n'
<< f(1.) << '\n'
<< f(1.f) << '\n'
<< f("") << '\n'
<< f(main) << '\n'
;
}
J>У меня вообще-то тоже только буст
Ну тут enable_cond_c либо относится к используемым "библиотечным" классам, и тогда у тебя не только буст; либо является частью решения, тогда оно в два раза больше моего.
Здравствуйте, igna, Вы писали:
I>Здравствуйте, jazzer, Вы писали:
J>>Т.е. вместо 4 простых понятных функций мы имеем 6 делающих не очень понятно что
I>Это не просто 6 функций (точнее шаблона функций), а три раза по две (два), у каждой пары свое имя. f выбирает между arithmetic и не-arithmetic, f_arithmetic — между integral и не-integral, а f_integral — между char и не-char.
выделенное звучит очень логично
а связь genericовой функции с массивами и арифметическими типами вообще самоочевидна
Здравствуйте, igna, Вы писали:
I>Здравствуйте, jazzer, Вы писали:
J>>Т.е. вместо 4 простых понятных функций мы имеем 6 делающих не очень понятно что
I>Это не просто 6 функций (точнее шаблона функций), а три раза по две (два), у каждой пары свое имя. f выбирает между arithmetic и не-arithmetic, f_arithmetic — между integral и не-integral, а f_integral — между char и не-char.
хм... Ладно, вернемся к этому чуть ниже.
J>>Если ты по-прежнему считаешь, что твое решение проще, добавь пару перегрузок, скажем, для is_floating (или лучше для double через is_same, а то у тебя is_arithmetic никогда ни сматчится) и для is_array. У меня добавятся две очевидные функции.
I>У тебя добавятся две очевидные функции и два условия, у меня — то же самое, смотри:
То же самое? Очевидные условия? Ну давай посмотрим на твои условия повнимательнее: I>
I>template < class T >
I>typename disable_if< or_< is_integral< T >, is_same< T, double > >, string >::type
I>f_arithmetic(T x)
I>{
I> return"arithmetic";
I>}
I>template < class T >
I>typename disable_if< or_<is_arithmetic< T >, is_array< T > >, string >::type
I>f(T const& x)
I>{
I> return"generic";
I>}
I>
Что ты там говорил, "f_arithmetic — между integral и не-integral" — не говоря уже об очевидном несовпадении имени и смысла, на которое указал zaufi, что тут делает is_same< T, double >?
И заодно скажи мне, в сколько функций тебе пришлось внести изменения по сравнению с твоим исходным вариантом с 4 условиями (мне самому лень считать).
Не говоря уже о засорении пространства имен — у меня одна единственная фукнция f, а у тебя целый зоопарк: f, f_arithmetic, f_integral...
Причем три f, три f_arithmetic и почему-то всего две f_integral
Боюсь, что ты погорячился насчет простейших, очевидных и прочих эпитетов.
J>>У меня вообще-то тоже только буст I>Ну тут enable_cond_c либо относится к используемым "библиотечным" классам, и тогда у тебя не только буст; либо является частью решения, тогда оно в два раза больше моего.
в 2 раза? это ты как посчитал?
ЗЫ ты не пишешь библиотечных функций/классов? Или пишешь, но тебе стыдно?
Или это религия новая такая — использовать только то, что дано свыше, и не писать самим всяких там отсутствующих в STL copy_if, а вместо этого либо использовать имеющийся remove_copy_if, либо ждать C++0x?
А если я enable_cond предложу в Boost.Utility и его включат, что ты будешь делать?
Здравствуйте, jazzer, Вы писали:
J>Боюсь, что ты погорячился насчет простейших, очевидных и прочих эпитетов.
складывается ощущение что автор сам слегка запутался в накопипастеных функциях демонстрируя простоту своего решения
Re[2]: template pattern matching - в защиту отдельных услови
Здравствуйте, Кодт, Вы писали:
К>А интересно, можно ли вообще раздавать статические приоритеты сигнатурам, не вытаскивая таблицу принятия решений в отдельное место?
На самом деле, таблица принятия решений в одном месте — довольно удобная штука.
Например, в моем проекте без нее не обойтись было никак, и решение с номерами строк и упоминанием условий непосредственно перед функциями не сработало бы по трем причинам:
1) У меня два набора связанных функций (а может быть и больше), которые рулятся одними и теми же (или похожими, см. п.2) условиями. Сам понимаешь, повторять эти условия непосредственно в каждом наборе функций чревато стандартными проблемами копи-пейста и кошмаром в поддержке.
Когда у тебя есть таблица принятия решений, и у этой таблицы, что немаловажно, есть имя, то ты можешь просто запустить поиск по использованию этого имени и увидеть сразу все функции, которые от этой таблицы зависят.
2) Я могу работать с набором условий алгоритмически именно как с набором, т.е. использовать mpl::transform и прочие приятные вещи, если мне нужно сгенерировать сложные условия по имеющемуся списку простых (в моем случае у меня условия на нескольких аргументах, но условие на одном из аргументов всегда одно и то же, поэтому я его могу за один вызов mpl::transform с mpl::and_ прицепить к условиям по остальным аргументам, которые сами по себе выглядят достаточно просто, что сильно повышает читабельность), причем я могу это делать на лету непосредственно при определении конкретной функции.
Выглядит это примерно так (using namespace boost::mpl):
// исходные условия без учета специального ограничения на первый аргументtemplate < class T1, class T2 >
struct Conditions {...};
// с добавкой на первый аргумент в каждом условииtemplate < class T1, class T2 >
struct ConditionsWithPtr
: transform< Conditions< T1, T2 >
, and_< is_pointer< T1 >, _ >
>::type
{};
3) У меня бывает так, что одно и то же условие используется несколько раз в разных функциях (выбор функции в таком случае осуществляется по другим параметрам) — соответственно автоматика с номерами строк не пройдет: придется условие повторить, но тогда оно придет в противоречие с ним же самим, использованным ранее.
Здравствуйте, jazzer, Вы писали:
J>Что ты там говорил, "f_arithmetic — между integral и не-integral" — не говоря уже об очевидном несовпадении имени и смысла, на которое указал zaufi, что тут делает is_same< T, double >?
f_arithmetic принимает arithmetic и выбирает подходящую специализацию или общее решение. Сначала это был выбор между integral и остальными aritmetic, то есть не-integral; в новом варианте появился дополнительно double, то есть теперь f_arithmetic принимает arithmetic и выбирает между integral, double и всеми остальными arithmetic.
J>И заодно скажи мне, в сколько функций тебе пришлось внести изменения по сравнению с твоим исходным вариантом с 4 условиями (мне самому лень считать).
В две, как и тебе.
J>Не говоря уже о засорении пространства имен — у меня одна единственная фукнция f, а у тебя целый зоопарк: f, f_arithmetic, f_integral...
А у тебя f и Conditions, последнее — мусор.
J>Причем три f, три f_arithmetic и почему-то всего две f_integral
Ну так в соответствии с иерархией типов в поставленной задаче. f выбирает между array — это раз, arithmetic — это два и общим решением — это три; f_arithmetic — между integral (1), double (2) и общим решением (3); f_integral — между char (1) и общим решением (2):
generic
array
arithmetic
integral
chardouble
А вот в твоем решении эта иерархия потеряна.
J>А если я enable_cond предложу в Boost.Utility и его включат, что ты будешь делать?
Вот оно где.
Re[3]: template pattern matching - в защиту отдельных услови
Здравствуйте, jazzer, Вы писали:
J>На самом деле, таблица принятия решений в одном месте — довольно удобная штука.
Фокус в том, что таблица — в одном месте, а функции — в другом хотя и рядышком.
Перекуём баги на фичи!
Re[4]: template pattern matching - в защиту отдельных услови
Здравствуйте, Кодт, Вы писали:
К>Здравствуйте, jazzer, Вы писали:
J>>На самом деле, таблица принятия решений в одном месте — довольно удобная штука. К>Фокус в том, что таблица — в одном месте, а функции — в другом хотя и рядышком.
Естественно, в другом, иначе как ты сможешь с ней работать как с единым целым
Но ты так односложно отвечаешь — это значит, что ни один из приведенных доводов тебя не убедил?
Или что ни один из доводов не перевешивает разделения?
Или что ответ по пунктам на подходе?
Здравствуйте, jazzer, Вы писали:
J>>>На самом деле, таблица принятия решений в одном месте — довольно удобная штука. К>>Фокус в том, что таблица — в одном месте, а функции — в другом хотя и рядышком. J>Естественно, в другом, иначе как ты сможешь с ней работать как с единым целым
J>Но ты так односложно отвечаешь — это значит, что ни один из приведенных доводов тебя не убедил? J>Или что ни один из доводов не перевешивает разделения? J>Или что ответ по пунктам на подходе?
Здравствуйте, jazzer, Вы писали:
J>Но ты так односложно отвечаешь — это значит, что ни один из приведенных доводов тебя не убедил? J>Или что ни один из доводов не перевешивает разделения? J>Или что ответ по пунктам на подходе?
Я не могу говорить, убедил или не убедил: была твоя ситуация, твоё решение, тебя удовлетворило, — при чём здесь моё мнение?
Ты опытным путём показал, что отдельный список условий удобен ("есть как минимум одна овца, чёрная как минимум с одного бока").
Почему же не нравится, очень даже нравится
R>Так вроде как всё локально — просто добавляешь нужную специализацию с нужным приоритетом. Или дополнительный диспатчер не нравится?
Нет, с дополнительным диспатчером проблем почти нет (за исключением той проблемы, что он абсолютно неприменим в моем проекте — у меня там кучерявые типы аргументов, которые компилятор выводит сам — с диспатчером все перестанет работать и будет одна сплошная ошибка компиляции из-за неразрешимой неоднозначности).
Там еще было две причины озвучено — 1) реюз условий для нескольких семейств функций и 2) автоматическая генерация условий из других условий или из чего еще угодно (метапрограммированием тут можно заняться в полный рост и очень разнообразно, например, привязать к условиям возвращаемые типы, если это можно сделать алгоритмически, из них же сгенерить соответствующие enable_cond-ы и в самих функциях уже использовать нечто совсем простое без копи-пейста).
Но для простых случаев вроде того, что я привел в качестве мотивации в своей статье, твое решение замечательно подходит, включая решение с номерами строк (главное, чтоб это не случилось ниже 500 строки — сразу получишь по рукам от компилятора, у моего ГЦЦ максимальная глубина рекурсивного инстанцирования — 500, хотя на это есть опция), и таких простых случаев, как учит нас практика, большинство
Но для сложных ситуаций, типа той, что у меня в проекте, придется использовать нечто более обобщенное/низкоуровневое (и поэтому менее удобное из-за разделения условий и самих функций).
R>
J>Почему же не нравится, очень даже нравится
R>>Так вроде как всё локально — просто добавляешь нужную специализацию с нужным приоритетом. Или дополнительный диспатчер не нравится? J>Нет, с дополнительным диспатчером проблем почти нет (за исключением той проблемы, что он абсолютно неприменим в моем проекте — у меня там кучерявые типы аргументов, которые компилятор выводит сам — с диспатчером все перестанет работать и будет одна сплошная ошибка компиляции из-за неразрешимой неоднозначности).
А это как? Вроде же передача через диспатчер не должна ничего менять в плане типов и их выводимости...
J>Там еще было две причины озвучено — 1) реюз условий для нескольких семейств функций и 2) автоматическая генерация условий из других условий или из чего еще угодно (метапрограммированием тут можно заняться в полный рост и очень разнообразно, например, привязать к условиям возвращаемые типы, если это можно сделать алгоритмически, из них же сгенерить соответствующие enable_cond-ы и в самих функциях уже использовать нечто совсем простое без копи-пейста).
J>Но для простых случаев вроде того, что я привел в качестве мотивации в своей статье, твое решение замечательно подходит, включая решение с номерами строк (главное, чтоб это не случилось ниже 500 строки — сразу получишь по рукам от компилятора, у моего ГЦЦ максимальная глубина рекурсивного инстанцирования — 500, хотя на это есть опция), и таких простых случаев, как учит нас практика, большинство
J>Но для сложных ситуаций, типа той, что у меня в проекте, придется использовать нечто более обобщенное/низкоуровневое (и поэтому менее удобное из-за разделения условий и самих функций).
Здравствуйте, remark, Вы писали:
R>>>Так вроде как всё локально — просто добавляешь нужную специализацию с нужным приоритетом. Или дополнительный диспатчер не нравится? J>>Нет, с дополнительным диспатчером проблем почти нет (за исключением той проблемы, что он абсолютно неприменим в моем проекте — у меня там кучерявые типы аргументов, которые компилятор выводит сам — с диспатчером все перестанет работать и будет одна сплошная ошибка компиляции из-за неразрешимой неоднозначности).
R>А это как? Вроде же передача через диспатчер не должна ничего менять в плане типов и их выводимости...
у меня же несколько аргументов, один свободный совсем, а вот другой в некоторых перегрузках зависит от первого и там все срабатывает.
Здравствуйте, Кодт, Вы писали:
К>Я не могу говорить, убедил или не убедил: была твоя ситуация, твоё решение, тебя удовлетворило, — при чём здесь моё мнение? К>Ты опытным путём показал, что отдельный список условий удобен ("есть как минимум одна овца, чёрная как минимум с одного бока"). К>Понятно, что "три" перспективнее. Но "раз" проще и нагляднее, если всё ограничивается одним разом.
Ну в общем-то да.
Просто моя цель была показать, что решение без отдельных условий не однозначно хуже, и у него есть своя вполне весомая область применения, и что отказ от отдельных условий небесплатен — мы теряем вполне конкретные рулезы (хотя приобретаем другие в виде простоты записи условий в простых случаях).
Т.е, как обычно в нашей профессии, мы имеем выбор из разных вариантов, и в зависимости от задачи у одного будет преимущество над другим.
Сорри, что я не уловил понимания этого в твоем изначальном ответе "Фокус в том, что таблица — в одном месте, а функции — в другом "
Иными словами, нужно пользоваться моим решением, если нужно:
1) переиспользование списков условий для нескольких наборов функций,
2) автоматическая генерация списков условий,
3) непосредственный доступ к сигнатурам каждой перегрузки,
4) использование одного и того же условия для нескольких перегрузок.
Решение Ремарка c __LINE__ подходит для всех остальных случаев, а решение с явным указанием приоритетов — для остальных случаев плюс пункт 4 выше.