template pattern matching
От: jazzer Россия Skype: enerjazzer
Дата: 02.03.10 04:34
Оценка: 384 (30) +2
Ну, может, не совсем pattern matching, ниже будет понятно, что я имею в виду.

Представьте, что Вы написали шаблонную функцию
  template < class T >
  std::string
  f(T x)
  {
    return "generic";
  }

Она замечательно работает, то Вам захотелось добавить специализацию для арифметических типов, т.е. чтобы для типов char, int, double вызывалась специальная функция
  template < class T >
  std::string
  f(T x)
  {
    return "arithmetic";
  }

а для всех остальных — самая общая функция.
Вопрос — как этого добиться?
В Boost есть замечательная штука — enable_if, которая возволяет "включить" определенную перегрузку функции, если удовлетворено некоторое условие.
В нашем случае это будет выглядеть так:
  template < class T >
  typename boost::enable_if< boost::is_arithmetic< T >, std::string >::type
  f(T x)
  {
    return "arithmetic";
  }

теперь эта специализация не будет рассматриваться для разрешения перегрузки, если тип аргумента не арифметический.
Однако если он арифметический, то сработают обе функции и мы получим неоднозначность в точке вызова.
Чтобы избавиться от неоднозначности, мы теперь должны "выключить" нашу самую обобщенную функцию при помощи аналогичного disable_if:
  template < class T >
  typename boost::disable_if< boost::is_arithmetic< T >, std::string >::type
  f(T x)
  {
    return "generic";
  }

Ура, теперь все работает!

До тех пор, пока мы не обнаружим, что у нас есть замечательный алгоритм для целых чисел, и нам срочно нужно его заюзать!
Мы радостно пишем
  template < class T >
  typename boost::enable_if< boost::is_integral< T >, std::string >::type
  f(T x)
  {
    return "integral";
  }

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

В чем же корень проблемы? В том, что матчинг у нас неупорядочен, в отличие от pattern matching в, скажем, Haskell — там все сверху вниз.
Стандартный ПМ через enable_if отлично работает в случае, когда условия не пересекаются, например:
if (i<100) ...;
/*else*/ if (i>100) ...;
/*else*/ if (i==100) ...;

тут мы можем эти ифы расставить в любом порядке, и нам не нужно писать else — результат будет точно такой же, что и без них.
Однако в этом случае:
if (i<0) ...;
else if (i<10) ...;
else if (i<100) ...;
else /*if (true)*/ ...;

мы уже не можем ни опустить else, ни переставить условия местами — результат изменится и очень сильно: во втором случае сработает не то условие, а в первом сработают сразу несколько, что в нашем случае выражается в неоднозначности. В случае перегрузки функций в С++ у нас нормального if-else нету, поэтому приходится переписывать эти условия на набор независимых условий:
if (i<0) ...;
if (i>=0 && i<10) ...;
if (i>=10 && i<100) ...;
if (i>=100) ...;

Внимание, вопрос — можно ли добиться, чтобы у нас было Haskell-gjдобное упорядочение перегрузок, без необходимости повторять одно и то же?
Т.е. если у нас есть некая последовательность условий
  // char -> integral -> arithmetic -> all
  template < class T >
  struct Conditions
    : boost::mpl::vector< boost::is_same< T, char >
                        , boost::is_integral< T >
                        , boost::is_arithmetic< T >
                        , boost::mpl::identity< boost::mpl::true_ >
                        >
  {};

можно ли написать перегрузки так, чтоб они разрешались именно в этом порядке?

Оказывается, можно, и притом с помощью очень простого, похожего на enable_if, шаблонного заклинания:
  // if T is char, then use this overload:
  template < class T >
  typename enable_cond_с< Conditions< T >, 0, const char* >::type
  f(T x)
  {
    return "char";
  }

  // else if T is integral, then use this overload:
  template < class T >
  typename enable_cond_с< Conditions< T >, 1, std::string >::type
  f(T x)
  {
    return "integral";
  }

  // else if T is arithmetic, then use this overload:
  template < class T >
  typename enable_cond_с< Conditions< T >, 2, std::string >::type
  f(T x)
  {
    return "arithmetic";
  }

  // else use this overload:
  template < class T >
  typename enable_cond_с< Conditions< T >, 3, std::string >::type
  f(T x)
  {
    return "generic";
  }

Естественно, возвращаемый тип может быть разным, я для разнообразия сделал const char* для первой перегрузки.
Количество шаблонных аргументов также не ограничено одним, что дает возможность реализовывать разные веселые схемы перегрузки (у меня в проекте как раз была такая, поэтому я и начал писать нечто обобщенное).
enable_cond_с следует стандартной схеме именования, т.е. есть enable_cond, enable_cond_c, lazy_enable_cond и lazy_enable_cond_с.
Реализация ниже:
#include <boost/mpl/at.hpp>
#include <boost/mpl/size.hpp>
#include <boost/mpl/find_if.hpp>
#include <boost/mpl/iterator_range.hpp>

// Helper meta-function to take N first elements from Seq
template< class Seq, class N >
struct take
{
  BOOST_MPL_ASSERT_MSG( boost::mpl::size< Seq >::type::value >= N::type::value
                      , SEQUENCE_IS_SHORTER_THAN
                      , (Seq, N) );
  typedef typename boost::mpl::begin< Seq >::type first;
  typedef typename boost::mpl::advance< first, N >::type last;
  typedef typename boost::mpl::iterator_range< first, last > type;
};

// ordered matching metafunction
template< class CondSeq, class Index >
struct chain_cond_match
{
  typedef typename take< CondSeq, Index >::type prev;
  typedef typename
    boost::mpl::and_< typename boost::mpl::at< CondSeq, Index >::type
                    , boost::is_same< typename boost::mpl::find_if< prev, boost::mpl::_ >::type
                                    , typename boost::mpl::end< prev >::type
                    > >::type type;
};

// enable_cond family
template< class CondSeq, class Index, class Ret >
struct enable_cond
  : boost::enable_if< typename chain_cond_match< CondSeq, Index >::type, Ret >
{};

template< class CondSeq, int i, class Ret >
struct enable_cond_c
  : enable_cond< CondSeq, boost::mpl::int_< i >, Ret >
{};

template< class CondSeq, class Index, class Ret >
struct lazy_enable_cond
  : boost::lazy_enable_if< typename chain_cond_match< CondSeq, Index >::type, Ret >
{};

template< class CondSeq, int i, class Ret >
struct lazy_enable_cond_c
  : lazy_enable_cond< CondSeq, boost::mpl::int_< i >, Ret >
{};

Обратите внимание на BOOST_MPL_ASSERT_MSG — если кто не знал о существовании этого замечательного макроса: я всех призываю его использовать, он дает очень читабельные сообщения об ошибках, типа (правда, это после моего собственного постпроцессора ошибок):
test.h:32: error: ************::SEQUENCE_IS_SHORTER_THAN::************(Conditions<char>, mpl_::int_<7>)


Бонус
  Скрытый текст
С небольшой помощью макросов можно добиться такого синтаксиса, но он уже будет только для одного шаблонного аргумента:
OVERLOAD_START(F, 4)
    
  OVERLOAD(F) const char*
    CONDITION: boost::is_same< T, char >
  FUNCTION(T x)
  {
    return "char";
  }

  OVERLOAD(F) std::string
    CONDITION: boost::is_integral< T >
  FUNCTION(T x)
  {
    return "integral";
  }

  OVERLOAD(F) std::string
    CONDITION: boost::is_arithmetic< T >
  FUNCTION(T x)
  {
    return "arithmetic";
  }

  OVERLOAD(F) std::string
    CONDITION: boost::mpl::true_
  FUNCTION(T x)
  {
    return "generic";
  }

OVERLOAD_END()

Оставляю написание этого макроса вам в качестве развлечения

jazzer (Skype: enerjazzer) Ночная тема для RSDN
Автор: jazzer
Дата: 26.11.09

You will always get what you always got
  If you always do  what you always did
boost mpl pattern matching overloading перегрузка сопоставление с образцом шаблоны
Re: template pattern matching
От: zaufi Земля  
Дата: 02.03.10 07:55
Оценка: 62 (1)
чтобы уменьшить синтаксический оверхед я бы еще замутил пару фичек:

1) дабы не писать в обязательом порядке последним элементом сиквенса условий boost::mpl::identity, может луче его push_back'ать в chain_cond_match перед тем как начать основные действия... все равно он па любому должен быть последним... и более того буит оч неприятно разбирать километры спама от компилятора если его случайно забыть

template< class CondSeq, class Index >
struct chain_cond_match
{
  typedef typename boost::mpl::push_back<CondSeq, boost::mpl::identity< boost::mpl::true_> >::type seq_with_default;
  typedef typename take< seq_with_default, Index >::type prev;
  typedef typename
    boost::mpl::and_< typename boost::mpl::at< seq_with_default, Index >::type
                    , boost::is_same< typename boost::mpl::find_if< prev, boost::mpl::_ >::type
                                    , typename boost::mpl::end< prev >::type
                    > >::type type;
};


2) не оч сложно избавиться от необходимости создавать свой класс каждый раз когда хочется завести новую пачку условий... lambda нам поможет!
typedef boost::mpl::vector<
    boost::is_same<boost::mpl::_, char>
  , boost::is_integral<boost::mpl::_>
  , boost::is_arithmetic<boost::mpl::_>
  > conditions;


imho выглядит несколько покороче чем создание собственного класса... ну да, слегка изменится синтаксис вызова enable_cond'a... както так:

template < class T >
typename enable_cond_c< conditions, T, 0, const char* >::type
f(T x)
{
    return "char";
}

ну и код для проверки условия чуть усложнится на один boost::mpl::apply -- но веть это внутренности реализации )

3) раз уж мы впихиваем identity сами, от чего бы не сделать специализацию enable_cond'a для деволтного значения таким обраазом чтоб не приходилось писать индекс условия!?... ну типа дефольный параметр равный сиквенс сайзу (+1, ибо мы веть теперь сами добавляем дефольный кондишин %)

ну вот както так
Re[2]: template pattern matching
От: jazzer Россия Skype: enerjazzer
Дата: 02.03.10 09:12
Оценка:
Здравствуйте, zaufi, Вы писали:

Z>чтобы уменьшить синтаксический оверхед я бы еще замутил пару фичек:


Z>1) дабы не писать в обязательом порядке последним элементом сиквенса условий boost::mpl::identity, может луче его push_back'ать в chain_cond_match перед тем как начать основные действия... все равно он па любому должен быть последним... и более того буит оч неприятно разбирать километры спама от компилятора если его случайно забыть


Я так и написал вначале, а потом в своих задачах обнаружил, что он нужен далеко не всегда (ты ведь не всегда пишешь просто else, написав if).
Например, если у тебя в принципе нет функции, которая принимает любые параметры, а есть только для арифметических + специализации для интегральных.
Так что я решил, что лучше пусть он будет явным.

Z>2) не оч сложно избавиться от необходимости создавать свой класс каждый раз когда хочется завести новую пачку условий... lambda нам поможет!

Z>imho выглядит несколько покороче чем создание собственного класса... ну да, слегка изменится синтаксис вызова enable_cond'a... както так:

Z>
Z>typename enable_cond_c< conditions, T, 0, const char* >::type

Опять же, я так и сделал вначале (оно даже лучше выглядело, потому что я в запихал в макросы), но мне в моих задачах были нужны условия на нескольких типах. В случае с лямбдой придется городить pack/unpack либо делать кучу специализаций enable_cond для разного количества аргументов...

Ну и разницы между классом и тайпдефом я не вижу, если честно — и там, и там ты вводишь новое имя.

Z>3) раз уж мы впихиваем identity сами, от чего бы не сделать специализацию enable_cond'a для деволтного значения таким обраазом чтоб не приходилось писать индекс условия!?... ну типа дефольный параметр равный сиквенс сайзу (+1, ибо мы веть теперь сами добавляем дефольный кондишин %)


Можешь показать, что именно ты имеешь в виду?
Я не смог избавиться от индексов так, чтоб не было явной ссылки на предыдущие условия.
Если тебе удалось, покажи!
jazzer (Skype: enerjazzer) Ночная тема для RSDN
Автор: jazzer
Дата: 26.11.09

You will always get what you always got
  If you always do  what you always did
Re: template pattern matching
От: remark Россия http://www.1024cores.net/
Дата: 02.03.10 10:39
Оценка:
Здравствуйте, jazzer, Вы писали:

J>Ну, может, не совсем pattern matching, ниже будет понятно, что я имею в виду.


jazzer жжот


1024cores &mdash; all about multithreading, multicore, concurrency, parallelism, lock-free algorithms
Re[2]: template pattern matching
От: remark Россия http://www.1024cores.net/
Дата: 02.03.10 10:47
Оценка: :))
Здравствуйте, remark, Вы писали:

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


J>>Ну, может, не совсем pattern matching, ниже будет понятно, что я имею в виду.


R>jazzer жжот


Ладно, мы ещё померяемся, у кого шаблон длиннее


1024cores &mdash; all about multithreading, multicore, concurrency, parallelism, lock-free algorithms
Re: template pattern matching
От: Кодт Россия  
Дата: 02.03.10 11:13
Оценка:
Здравствуйте, jazzer, Вы писали:

J>Ну, может, не совсем pattern matching, ниже будет понятно, что я имею в виду.


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

Вот что-нибудь такое хотелось
void foo(int x, priority<10> _);
void foo(short x, priority<20> _);
// возможно, потребуется диспетчер
template<class T> void foo(T x) { ????? }

foo('z'); // priority<20> => short


Может быть, кстати, сделать вот этак
template<class T, int P> struct check_foo : define_conditions< 100 > {}; // вводим семейство и определяем лимит

template<class T> struct check_foo<10> : define_condition< is_same<T,int> > {};
template<class T> typename check_condition< check_foo, T, 10, void >::type foo(T x);

template<class T> struct check_foo<20> : define_condition< is_same<T,char> > {};
template<class T> typename check_condition< check_foo, T, 20, void >::type foo(T x);

template<class T> struct check_foo<30> : define_condition< is_same<T,short> > {};
template<class T> typename check_condition< check_foo, T, 30, void >::type foo(T x);

(это эскиз!!! я не уверен даже в окончательном виде)
Перекуём баги на фичи!
Re[2]: template pattern matching
От: remark Россия http://www.1024cores.net/
Дата: 02.03.10 11:16
Оценка:
Здравствуйте, Кодт, Вы писали:

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


J>>Ну, может, не совсем pattern matching, ниже будет понятно, что я имею в виду.


К>А интересно, можно ли вообще раздавать статические приоритеты сигнатурам, не вытаскивая таблицу принятия решений в отдельное место?

К>В хаскелле-то не россыпь функций вводится, а просто синтаксический сахар для централизованного паттерн-матчинга.

http://www.rsdn.ru/forum/cpp/3722457.1.aspx
Автор: remark
Дата: 02.03.10



1024cores &mdash; all about multithreading, multicore, concurrency, parallelism, lock-free algorithms
Re: template pattern matching
От: Vain Россия google.ru
Дата: 02.03.10 11:23
Оценка:
Здравствуйте, jazzer, Вы писали:

J>Ну, может, не совсем pattern matching, ниже будет понятно, что я имею в виду.

Чёто не вижу слона. is_arithmetic и is_integral — пересекаемые условия и в случае пересечения у вас будет двойное объявление одного и того же шаблона. Почему во втором случае не будет неоднозначности?
[In theory there is no difference between theory and practice. In
practice there is.]
[Даю очевидные ответы на риторические вопросы]
Re[3]: template pattern matching
От: remark Россия http://www.1024cores.net/
Дата: 02.03.10 11:29
Оценка:
Здравствуйте, remark, Вы писали:

J>>>Ну, может, не совсем pattern matching, ниже будет понятно, что я имею в виду.


R>>jazzer жжот


R>Ладно, мы ещё померяемся, у кого шаблон длиннее


Мой шаблон здесь
Автор: remark
Дата: 02.03.10



1024cores &mdash; all about multithreading, multicore, concurrency, parallelism, lock-free algorithms
Re[2]: template pattern matching
От: remark Россия http://www.1024cores.net/
Дата: 02.03.10 11:50
Оценка:
Здравствуйте, Vain, Вы писали:

J>>Ну, может, не совсем pattern matching, ниже будет понятно, что я имею в виду.

V>Чёто не вижу слона. is_arithmetic и is_integral — пересекаемые условия и в случае пересечения у вас будет двойное объявление одного и того же шаблона. Почему во втором случае не будет неоднозначности?

Его решение как раз и предназначено для обхода этой неоднозначности.


1024cores &mdash; all about multithreading, multicore, concurrency, parallelism, lock-free algorithms
Re[3]: template pattern matching
От: Vain Россия google.ru
Дата: 02.03.10 12:54
Оценка:
Здравствуйте, remark, Вы писали:

J>>>Ну, может, не совсем pattern matching, ниже будет понятно, что я имею в виду.

V>>Чёто не вижу слона. is_arithmetic и is_integral — пересекаемые условия и в случае пересечения у вас будет двойное объявление одного и того же шаблона. Почему во втором случае не будет неоднозначности?
R>Его решение как раз и предназначено для обхода этой неоднозначности.
Я и прошу разъяснить — каким образом?

R>
[In theory there is no difference between theory and practice. In
practice there is.]
[Даю очевидные ответы на риторические вопросы]
Re[3]: template pattern matching
От: zaufi Земля  
Дата: 02.03.10 13:56
Оценка:
Здравствуйте, jazzer, Вы писали:

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


Z>>чтобы уменьшить синтаксический оверхед я бы еще замутил пару фичек:


Z>>1) дабы не писать в обязательом порядке последним элементом сиквенса условий boost::mpl::identity, может луче его push_back'ать в chain_cond_match перед тем как начать основные действия... все равно он па любому должен быть последним... и более того буит оч неприятно разбирать километры спама от компилятора если его случайно забыть


J>Я так и написал вначале, а потом в своих задачах обнаружил, что он нужен далеко не всегда (ты ведь не всегда пишешь просто else, написав if).

J>Например, если у тебя в принципе нет функции, которая принимает любые параметры, а есть только для арифметических + специализации для интегральных.
J>Так что я решил, что лучше пусть он будет явным.

ну это не страшно совершенно... если не хочется писать дженериковую функцию -- не пиши! -- получишь просто `no matching function call' если попытаешься ее вызвать...

Z>>2) не оч сложно избавиться от необходимости создавать свой класс каждый раз когда хочется завести новую пачку условий... lambda нам поможет!

Z>>imho выглядит несколько покороче чем создание собственного класса... ну да, слегка изменится синтаксис вызова enable_cond'a... както так:

Z>>
Z>>typename enable_cond_c< conditions, T, 0, const char* >::type
J>

J>Опять же, я так и сделал вначале (оно даже лучше выглядело, потому что я в запихал в макросы), но мне в моих задачах были нужны условия на нескольких типах. В случае с лямбдой придется городить pack/unpack либо делать кучу специализаций enable_cond для разного количества аргументов...
да лана прям таки городить... вот я тут накидал "на коленке":
#include <boost/mpl/and.hpp>
#include <boost/mpl/apply.hpp>
#include <boost/mpl/at.hpp>
#include <boost/mpl/deref.hpp>
#include <boost/mpl/eval_if.hpp>
#include <boost/mpl/find_if.hpp>
#include <boost/mpl/identity.hpp>
#include <boost/mpl/is_sequence.hpp>
#include <boost/mpl/iterator_range.hpp>
#include <boost/mpl/or.hpp>
#include <boost/mpl/push_back.hpp>
#include <boost/mpl/size.hpp>
#include <boost/mpl/unpack_args.hpp>
#include <boost/mpl/vector.hpp>
#include <boost/type_traits/is_arithmetic.hpp>
#include <boost/type_traits/is_integral.hpp>
#include <boost/type_traits/is_same.hpp>
#include <boost/utility/enable_if.hpp>
#include <cassert>
#include <cstdlib>
#include <iostream>
#include <string>
#include <typeinfo>
#include <vector>
#include <cxxabi.h>

template <typename T>
inline std::string type_name(const T&)
{
    const char* const name = typeid(T).name();
    // Try to demangle name using gcc API (of couse if current compiler is gcc)
    int status;
    char* demangled_name = abi::__cxa_demangle(name, 0, 0, &status);
    if (!status)
    {
        std::string name = demangled_name;
        free(demangled_name);
        return name;
    }
    assert(!"How is that possible? Fail to demangle name produced by itself??");
    return name;
}

struct always_true_precondition
{
    template <typename T>
    struct apply
    {
        typedef boost::mpl::true_ type;
    };
};

template <typename P, typename T>
struct check_predicate
{
    typedef typename boost::mpl::apply<
        typename boost::mpl::eval_if<
            boost::mpl::is_sequence<T>
          , boost::mpl::identity<boost::mpl::unpack_args<P> >
          , boost::mpl::identity<P>
          >::type
      , T
      >::type type;
};

// Helper meta-function to take N first elements from Seq
template <typename Seq, typename N>
struct take
{
    BOOST_MPL_ASSERT_MSG(
        boost::mpl::size<Seq>::type::value >= N::type::value
      , SEQUENCE_IS_SHORTER_THAN
      , (Seq, N)
      );
    typedef typename boost::mpl::begin<Seq>::type first;
    typedef typename boost::mpl::advance<first, N>::type last;
    typedef typename boost::mpl::iterator_range<first, last> type;
};

// ordered matching metafunction
template <typename CondSeq, typename T, typename Index>
struct chain_cond_match
{
    typedef typename boost::mpl::push_back<
        CondSeq
      , always_true_precondition
      >::type seq_with_default;

    typedef typename take<seq_with_default, Index>::type prev;

    typedef typename boost::mpl::find_if<
        prev
      , check_predicate<boost::mpl::_, T>
      >::type iter;

    typedef typename boost::mpl::apply<
        typename boost::mpl::eval_if<
            boost::mpl::is_sequence<T>
          , boost::mpl::identity<
                boost::mpl::unpack_args<
                    typename boost::mpl::at<seq_with_default, Index>::type
                  >
              >
          , boost::mpl::at<seq_with_default, Index>
          >::type
      , T
      >::type predicate_result;

    typedef typename boost::is_same<
        iter
      , typename boost::mpl::end<prev>::type
      >::type iter_comp_result;

    typedef typename boost::mpl::and_<predicate_result, iter_comp_result>::type type;
};

// enable_cond family
template <typename CondSeq, typename T, typename Index, typename Ret>
struct enable_cond : boost::enable_if<typename chain_cond_match<CondSeq, T, Index>::type, Ret>
{};

template <typename CondSeq, typename T, int i, typename Ret>
struct enable_cond_c
  : enable_cond<CondSeq, T, boost::mpl::int_<i>, Ret>
{};

typedef boost::mpl::vector<
    boost::is_same<boost::mpl::_, char>
  , boost::is_integral<boost::mpl::_>
  , boost::is_arithmetic<boost::mpl::_>
  > conditions;

// if T is char, then use this overload:
template <typename T>
typename enable_cond_c<conditions, T, 0, const char*>::type
f(T x)
{
    return "char";
}

// else if T is integral, then use this overload:
template <typename T>
typename enable_cond_c<conditions, T, 1, std::string>::type
f(T x)
{
    return "integral";
}

// else if T is arithmetic, then use this overload:
template <typename T>
typename enable_cond_c<conditions,  T, 2, std::string>::type
f(T x)
{
    return "arithmetic";
}

// else use this overload: TRY TO COMMENT IT OUT...
template <typename T>
typename enable_cond_c<conditions, T, 3, std::string>::type
f(T x)
{
    return "generic";
}

struct test {};

// (*)

typedef boost::mpl::vector<
    boost::mpl::or_<
        boost::is_same<boost::mpl::_1, char>
      , boost::is_same<boost::mpl::_2, char>
      >
  , boost::mpl::and_<
        boost::is_integral<boost::mpl::_1>
      , boost::is_same<
            boost::mpl::_2
          , std::vector<boost::mpl::_1, std::allocator<boost::mpl::_1> >
          >
      >
  , boost::mpl::and_<
        boost::is_arithmetic<boost::mpl::_2>
      , boost::is_integral<boost::mpl::_3>
      , boost::is_same<boost::mpl::_1, test>
      >
  > binary_conditions;


template <typename T, typename U>
typename enable_cond_c<binary_conditions, boost::mpl::vector<T, U>, 0, std::string >::type
b(T x, U z)
{
    return "one of args is char";
}

template <typename T, typename U>
typename enable_cond_c<binary_conditions, boost::mpl::vector<T, U>, 1, std::string >::type
b(T x, U z)
{
    return "integral and vector";
}

template <typename T, typename U, typename V>
typename enable_cond_c<binary_conditions, boost::mpl::vector<T, U, V>, 2, const char* const>::type
b(U x, V z)
{
    return "test + integral + arithmetic";
}

int main()
{
    using namespace std;

    cout << f(int(0)) << endl;
    cout << f(double(0)) << endl;
    cout << f(float(0)) << endl;
    cout << f('f') << endl;

    cout << f(std::string("fuck")) << endl;
    cout << f(test()) << endl;

    cout << b('a', 'b') << endl;
    cout << b('a', 123) << endl;
    cout << b(123, 'b') << endl;
    cout << b(123, std::vector<int>()) << endl;
    cout << b<test>(3.14, 345) << endl;
}


пример функций с несколькими параметрами начинается от // (*)
если когото пугает что несколько параметров приходится упаковывать в mplный сиквенс, пусть напишет (сгенерит) на BOOST_PREPROCESSOR несколько версий enable_cond... но я чесс гря бы не парился -- нам ли боятся MPL! сиквенсы выглядят вполне себе понятно imho...

J>Ну и разницы между классом и тайпдефом я не вижу, если честно — и там, и там ты вводишь новое имя.


Z>>3) раз уж мы впихиваем identity сами, от чего бы не сделать специализацию enable_cond'a для деволтного значения таким обраазом чтоб не приходилось писать индекс условия!?... ну типа дефольный параметр равный сиквенс сайзу (+1, ибо мы веть теперь сами добавляем дефольный кондишин %)


J>Можешь показать, что именно ты имеешь в виду?

J>Я не смог избавиться от индексов так, чтоб не было явной ссылки на предыдущие условия.
J>Если тебе удалось, покажи!

да не я просто имел ввиду дефолтный параметр для индекса взять равным индексу последнего (нами же внутри добавляемого always_true_precondition'a) -- убрать индексы совсем нифига не просто (и хотя я глубоко не копал тему, не думаю что возможно)
Re[4]: template pattern matching
От: zaufi Земля  
Дата: 02.03.10 13:59
Оценка:
забыл добавить:
проверялось на
zaufi@zaufi /work/tests $ g++ --version
g++ (Gentoo 4.4.3 p1.0) 4.4.3
Copyright (C) 2010 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

zaufi@zaufi /work/tests $ uname -a
Linux zaufi 2.6.32-gentoo-r5 #1 SMP PREEMPT Thu Feb 18 02:49:09 MSK 2010 x86_64 Intel(R) Core(TM)2 Duo CPU T7300 @ 2.00GHz GenuineIntel GNU/Linux


а вот что будет если нет дженериковой функции:
zaufi@zaufi /work/tests $ g++ -o enable_cond enable_cond.cc
enable_cond.cc: In function 'int main()':
enable_cond.cc:218: error: no matching function for call to 'f(std::string)'
enable_cond.cc:219: error: no matching function for call to 'f(test)'
Re[4]: template pattern matching
От: jazzer Россия Skype: enerjazzer
Дата: 02.03.10 14:27
Оценка:
Здравствуйте, Vain, Вы писали:

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


J>>>>Ну, может, не совсем pattern matching, ниже будет понятно, что я имею в виду.

V>>>Чёто не вижу слона. is_arithmetic и is_integral — пересекаемые условия и в случае пересечения у вас будет двойное объявление одного и того же шаблона. Почему во втором случае не будет неоднозначности?
R>>Его решение как раз и предназначено для обхода этой неоднозначности.
V>Я и прошу разъяснить — каким образом?

Я уберу boost::mpl, ::type и typename, чтоб было читабельнее:
template< class CondSeq, class Index >
struct chain_cond_match
{
  typedef take< CondSeq, Index > prev;             // CondSeq[0:Index]
  typedef 
    and_< at< CondSeq, Index >                     // CondSeq[Index]
        , is_same< find_if< prev, _ >, end< prev > // find_if(prev, _1) == prev.end()
        > > type;
};

MPL представляет собой STL, только на типах
Т.е. find_if вернет end, если ничего не найдет.
prev содержит список условий, которые НЕ должны выполниться.
Я ищу в prev условие, которое выполнится (туда надо передавать предикат, но в нашем случае само условие и есть предикат, поэтому я просто отдаю _), и сравниваю результат с end — если получился end, значит, ни одно условие из prev не выполнилось.
И этот результат мы объединяем через and с нашим текущим условием.
jazzer (Skype: enerjazzer) Ночная тема для RSDN
Автор: jazzer
Дата: 26.11.09

You will always get what you always got
  If you always do  what you always did
Re[5]: template pattern matching
От: Vain Россия google.ru
Дата: 02.03.10 15:02
Оценка:
Здравствуйте, jazzer, Вы писали:

J>>>>>Ну, может, не совсем pattern matching, ниже будет понятно, что я имею в виду.

V>>>>Чёто не вижу слона. is_arithmetic и is_integral — пересекаемые условия и в случае пересечения у вас будет двойное объявление одного и того же шаблона. Почему во втором случае не будет неоднозначности?
R>>>Его решение как раз и предназначено для обхода этой неоднозначности.
V>>Я и прошу разъяснить — каким образом?
J>Я уберу boost::mpl, ::type и typename, чтоб было читабельнее:
J>И этот результат мы объединяем через and с нашим текущим условием.
Т.е. получается некоторая перегрузка возвращаемого типа во время вызова? Вот здесь и есть проблема, непонятно почему компилятор разрешает такое...
[In theory there is no difference between theory and practice. In
practice there is.]
[Даю очевидные ответы на риторические вопросы]
Re[6]: template pattern matching
От: remark Россия http://www.1024cores.net/
Дата: 02.03.10 15:06
Оценка:
Здравствуйте, Vain, Вы писали:

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


J>>>>>>Ну, может, не совсем pattern matching, ниже будет понятно, что я имею в виду.

V>>>>>Чёто не вижу слона. is_arithmetic и is_integral — пересекаемые условия и в случае пересечения у вас будет двойное объявление одного и того же шаблона. Почему во втором случае не будет неоднозначности?
R>>>>Его решение как раз и предназначено для обхода этой неоднозначности.
V>>>Я и прошу разъяснить — каким образом?
J>>Я уберу boost::mpl, ::type и typename, чтоб было читабельнее:
J>>И этот результат мы объединяем через and с нашим текущим условием.

V>Т.е. получается некоторая перегрузка возвращаемого типа во время вызова? Вот здесь и есть проблема, непонятно почему компилятор разрешает такое...


Поточу что ему явно сказали, что вот эта функция для типа Т, который is_integral; а вот эта для типа Т, который is_arithmetic, но не is_integral. Тут нет никакой неоднозначности и двойного объявления.


1024cores &mdash; all about multithreading, multicore, concurrency, parallelism, lock-free algorithms
Re[7]: template pattern matching
От: Vain Россия google.ru
Дата: 02.03.10 16:50
Оценка:
Здравствуйте, remark, Вы писали:

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


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


J>>>>>>>Ну, может, не совсем pattern matching, ниже будет понятно, что я имею в виду.

V>>>>>>Чёто не вижу слона. is_arithmetic и is_integral — пересекаемые условия и в случае пересечения у вас будет двойное объявление одного и того же шаблона. Почему во втором случае не будет неоднозначности?
R>>>>>Его решение как раз и предназначено для обхода этой неоднозначности.
V>>>>Я и прошу разъяснить — каким образом?
J>>>Я уберу boost::mpl, ::type и typename, чтоб было читабельнее:
J>>>И этот результат мы объединяем через and с нашим текущим условием.
V>>Т.е. получается некоторая перегрузка возвращаемого типа во время вызова? Вот здесь и есть проблема, непонятно почему компилятор разрешает такое...
R>Поточу что ему явно сказали, что вот эта функция для типа Т, который is_integral; а вот эта для типа Т, который is_arithmetic, но не is_integral. Тут нет никакой неоднозначности и двойного объявления.
Я правильно понял, что все функции на момент вызова, кроме одной задизейблены?

R>
[In theory there is no difference between theory and practice. In
practice there is.]
[Даю очевидные ответы на риторические вопросы]
Re[8]: template pattern matching
От: jazzer Россия Skype: enerjazzer
Дата: 02.03.10 22:49
Оценка:
Здравствуйте, Vain, Вы писали:

V>Я правильно понял, что все функции на момент вызова, кроме одной, задизейблены?

Правильно.
jazzer (Skype: enerjazzer) Ночная тема для RSDN
Автор: jazzer
Дата: 26.11.09

You will always get what you always got
  If you always do  what you always did
Re: Только Boost!
От: igna Россия  
Дата: 03.03.10 07:26
Оценка:
Здравствуйте, jazzer, Вы писали:

J>  // char -> integral -> arithmetic -> all
J>  template < class T >
J>  struct Conditions
J>    : boost::mpl::vector< boost::is_same< T, char >
J>                        , boost::is_integral< T >
J>                        , boost::is_arithmetic< T >
J>                        , boost::mpl::identity< boost::mpl::true_ >
                          >
J>  {};

J>  // if T is char, then use this overload:
J>  template < class T >
J>  typename enable_cond_с< Conditions< T >, 0, const char* >::type
J>  f(T x)
J>  {
J>    return "char";
J>  }

J>  // else if T is integral, then use this overload:
J>  template < class T >
J>  typename enable_cond_с< Conditions< T >, 1, std::string >::type
J>  f(T x)
J>  {
J>    return "integral";
J>  }

J>  // else if T is arithmetic, then use this overload:
J>  template < class T >
J>  typename enable_cond_с< Conditions< T >, 2, std::string >::type
J>  f(T x)
J>  {
J>    return "arithmetic";
J>  }

J>  // else use this overload:
J>  template < class T >
J>  typename enable_cond_с< Conditions< T >, 3, std::string >::type
J>  f(T x)
J>  {
J>    return "generic";
J>  }


То же, но обходясь средствами, которые предоставляет Boost:

#include <iostream>
#include <string>

#include <boost/type_traits.hpp>
#include <boost/utility/enable_if.hpp>

template < class T >
typename boost::disable_if< boost::is_same< T, char >, std::string >::type
f_integral(T x)
{
    return "integral";
}

template < class T >
typename boost::enable_if< boost::is_same< T, char >, const char* >::type
f_integral(T x)
{
    return "char";
}

template < class T >
typename boost::disable_if< boost::is_integral< T >, std::string >::type
f_arithmetic(T x)
{
    return "arithmetic";
}

template < class T >
typename boost::enable_if< boost::is_integral< T >, std::string >::type
f_arithmetic(T x)
{
    return f_integral(x);
}

template < class T >
typename boost::disable_if< boost::is_arithmetic< T >, std::string >::type
f(T x)
{
    return "generic";
}

template < class T >
typename boost::enable_if< boost::is_arithmetic< T >, std::string >::type
f(T x)
{
    return f_arithmetic(x);
}

int main()
{
    std::cout
        << f('a') << '\n'
        << f(1) << '\n'
        << f(1.) << '\n'
        << f("") << '\n'
        ;
}


На две строки длиннее, зато комментарии не нужны, поскольку нет никаких ничего не говорящих индексов 0, 1, 2, 3. И можно не только последовательность, но и дерево условий использовать. Ну и Бритва Оккама к тому же.
Re[2]: Только Boost!
От: zaufi Земля  
Дата: 03.03.10 08:36
Оценка:
I>То же, но обходясь средствами, которые предоставляет Boost:
да никто и не говорил что это не возможно...
но согласись в твоем коде логика disable/enable не столь очевидна...
я посмотрев по диагонале понял что для того чтоб разобраться в этом надо садиться с ручкой+бумажкой и вырисовывать все кейсы...
кроме того я бы не хотел быть тем чуваком которому понадобится добавить пару-тройку "перегрузок" сюда...

I>На две строки длиннее, зато комментарии не нужны, поскольку нет никаких ничего не говорящих индексов 0, 1, 2, 3. И можно не только последовательность, но и дерево условий использовать. Ну и Бритва Оккама к тому же.

ну они такие же ничего не говорящие как и индексы в tuple... (и даже в отличии от tuple используются примерно один раз, и все таки несмотря на это ничего не говорящие индексы по прежнему могут быть наменены на enum, или typedef в случае enable_cond)

enable_cond чемта похож на tuple: список условий объявляется в одном месте (и довольно прост для изменения), далее просто ссылаемся на условие по индексу...
Re[2]: Только Boost!
От: jazzer Россия Skype: enerjazzer
Дата: 03.03.10 10:08
Оценка:
Здравствуйте, igna, Вы писали:

У меня вообще-то тоже только буст

I>На две строки длиннее, зато комментарии не нужны, поскольку нет никаких ничего не говорящих индексов 0, 1, 2, 3.


Т.е. вместо 4 простых понятных функций мы имеем 6 делающих не очень понятно что
По крайней мере, я не понял, что каждая делает. Но вполне допускаю, что разобраться можно, если аккуратно нарисовать на листочке граф зависимостей.
Разбираться с enable/disable_if — это как раз то, от чего хотелось уйти, а ты предлагаешь к этому вернуться.

Если ты по-прежнему считаешь, что твое решение проще, добавь пару перегрузок, скажем, для is_floating (или лучше для double через is_same, а то у тебя is_arithmetic никогда ни сматчится) и для is_array. У меня добавятся две очевидные функции.

I>И можно не только последовательность, но и дерево условий использовать. Ну и Бритва Оккама к тому же.

Не очень понял, к чему бритва Оккама конкретно здесь и в программировании вообще.
Любая функция, кроме main, противоречит этому принципу.
jazzer (Skype: enerjazzer) Ночная тема для RSDN
Автор: jazzer
Дата: 26.11.09

You will always get what you always got
  If you always do  what you always did
Re[3]: Только Boost!
От: igna Россия  
Дата: 03.03.10 21:55
Оценка:
Здравствуйте, 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 либо относится к используемым "библиотечным" классам, и тогда у тебя не только буст; либо является частью решения, тогда оно в два раза больше моего.
Re[4]: Только Boost!
От: zaufi Земля  
Дата: 03.03.10 23:05
Оценка:
Здравствуйте, igna, Вы писали:

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


J>>Т.е. вместо 4 простых понятных функций мы имеем 6 делающих не очень понятно что


I>Это не просто 6 функций (точнее шаблона функций), а три раза по две (два), у каждой пары свое имя. f выбирает между arithmetic и не-arithmetic, f_arithmetic — между integral и не-integral, а f_integral — между char и не-char.


выделенное звучит очень логично
а связь genericовой функции с массивами и арифметическими типами вообще самоочевидна
Re[4]: Только Boost!
От: jazzer Россия Skype: enerjazzer
Дата: 03.03.10 23:56
Оценка:
Здравствуйте, 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 (Skype: enerjazzer) Ночная тема для RSDN
Автор: jazzer
Дата: 26.11.09

You will always get what you always got
  If you always do  what you always did
Re[5]: Только Boost!
От: zaufi Земля  
Дата: 04.03.10 00:10
Оценка:
Здравствуйте, jazzer, Вы писали:

J>Боюсь, что ты погорячился насчет простейших, очевидных и прочих эпитетов.

складывается ощущение что автор сам слегка запутался в накопипастеных функциях демонстрируя простоту своего решения
Re[2]: template pattern matching - в защиту отдельных услови
От: jazzer Россия Skype: enerjazzer
Дата: 04.03.10 05:29
Оценка:
Здравствуйте, Кодт, Вы писали:

К>А интересно, можно ли вообще раздавать статические приоритеты сигнатурам, не вытаскивая таблицу принятия решений в отдельное место?


На самом деле, таблица принятия решений в одном месте — довольно удобная штука.
Например, в моем проекте без нее не обойтись было никак, и решение с номерами строк и упоминанием условий непосредственно перед функциями не сработало бы по трем причинам:

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 (Skype: enerjazzer) Ночная тема для RSDN
Автор: jazzer
Дата: 26.11.09

You will always get what you always got
  If you always do  what you always did
Re[5]: Только Boost!
От: igna Россия  
Дата: 04.03.10 06:37
Оценка:
Здравствуйте, 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
      char
    double


А вот в твоем решении эта иерархия потеряна.

J>А если я enable_cond предложу в Boost.Utility и его включат, что ты будешь делать?


Вот оно где.
Re[3]: template pattern matching - в защиту отдельных услови
От: Кодт Россия  
Дата: 04.03.10 09:01
Оценка:
Здравствуйте, jazzer, Вы писали:

J>На самом деле, таблица принятия решений в одном месте — довольно удобная штука.

Фокус в том, что таблица — в одном месте, а функции — в другом хотя и рядышком.
Перекуём баги на фичи!
Re[4]: template pattern matching - в защиту отдельных услови
От: jazzer Россия Skype: enerjazzer
Дата: 04.03.10 09:15
Оценка:
Здравствуйте, Кодт, Вы писали:

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


J>>На самом деле, таблица принятия решений в одном месте — довольно удобная штука.

К>Фокус в том, что таблица — в одном месте, а функции — в другом хотя и рядышком.
Естественно, в другом, иначе как ты сможешь с ней работать как с единым целым

Но ты так односложно отвечаешь — это значит, что ни один из приведенных доводов тебя не убедил?
Или что ни один из доводов не перевешивает разделения?
Или что ответ по пунктам на подходе?
jazzer (Skype: enerjazzer) Ночная тема для RSDN
Автор: jazzer
Дата: 26.11.09

You will always get what you always got
  If you always do  what you always did
Re[5]: template pattern matching - в защиту отдельных услови
От: remark Россия http://www.1024cores.net/
Дата: 04.03.10 11:18
Оценка:
Здравствуйте, jazzer, Вы писали:

J>>>На самом деле, таблица принятия решений в одном месте — довольно удобная штука.

К>>Фокус в том, что таблица — в одном месте, а функции — в другом хотя и рядышком.
J>Естественно, в другом, иначе как ты сможешь с ней работать как с единым целым

J>Но ты так односложно отвечаешь — это значит, что ни один из приведенных доводов тебя не убедил?

J>Или что ни один из доводов не перевешивает разделения?
J>Или что ответ по пунктам на подходе?

А чем не нравится "KISS template pattern matching":
http://www.rsdn.ru/forum/cpp/3722508.1.aspx
Автор: remark
Дата: 02.03.10


Так вроде как всё локально — просто добавляешь нужную специализацию с нужным приоритетом. Или дополнительный диспатчер не нравится?


1024cores &mdash; all about multithreading, multicore, concurrency, parallelism, lock-free algorithms
Re[5]: template pattern matching - в защиту отдельных услови
От: Кодт Россия  
Дата: 04.03.10 11:23
Оценка:
Здравствуйте, jazzer, Вы писали:

J>Но ты так односложно отвечаешь — это значит, что ни один из приведенных доводов тебя не убедил?

J>Или что ни один из доводов не перевешивает разделения?
J>Или что ответ по пунктам на подходе?

Я не могу говорить, убедил или не убедил: была твоя ситуация, твоё решение, тебя удовлетворило, — при чём здесь моё мнение?
Ты опытным путём показал, что отдельный список условий удобен ("есть как минимум одна овца, чёрная как минимум с одного бока").

Лучше я приведу аналогию
// раз
int step(int x)
{
  if(x <= 0)
    return 0;
  else if(x == 1)
    return 1;
  else if(x % 2)
    return x*3+1;
  else
    return x/2;
}

// два
int step0(int) { return 0; }
int step1(int) { return 1; }
int stepO(int x) { return x*3+1; }
int stepE(int x) { return x/2; }

int step(int x)
{
  if(x <= 0)
    return step0(x);
  else if(x == 1)
    return step1(x);
  if(x % 2)
    return stepO(x);
  else
    return stepE(x);
}

// три
bool test0(int x) { return x<=0; }
bool test1(int x) { return x==1; }
bool testO(int x) { return x%2!=0; }
bool testE(int x) { return true; }

bool (*const tests [4])(int) = { test0, test1, testO, testE };

int  (*const steps [4])(int) = { step0, step1, stepO, stepE };

int ztepO(int x) { return x*5+1; }
int  (*const zteps [4])(int) = { step0, step1, ztepO, stepE };

int test(int x)
{
  for(int i=0; i!=4; ++i)
    if(tests[i](x)) return i;
  assert(false); throw wtf;
}
int step(int x) { return steps[test(x)](x); }
int ztep(int x) { return zteps[test(x)](x); }

Понятно, что "три" перспективнее. Но "раз" проще и нагляднее, если всё ограничивается одним разом.
Перекуём баги на фичи!
Re[6]: template pattern matching - в защиту отдельных услови
От: jazzer Россия Skype: enerjazzer
Дата: 04.03.10 12:58
Оценка:
Здравствуйте, remark, Вы писали:

R>А чем не нравится "KISS template pattern matching":

R>http://www.rsdn.ru/forum/cpp/3722508.1.aspx
Автор: remark
Дата: 02.03.10

Почему же не нравится, очень даже нравится

R>Так вроде как всё локально — просто добавляешь нужную специализацию с нужным приоритетом. Или дополнительный диспатчер не нравится?

Нет, с дополнительным диспатчером проблем почти нет (за исключением той проблемы, что он абсолютно неприменим в моем проекте — у меня там кучерявые типы аргументов, которые компилятор выводит сам — с диспатчером все перестанет работать и будет одна сплошная ошибка компиляции из-за неразрешимой неоднозначности).

Там еще было две причины озвучено — 1) реюз условий для нескольких семейств функций и 2) автоматическая генерация условий из других условий или из чего еще угодно (метапрограммированием тут можно заняться в полный рост и очень разнообразно, например, привязать к условиям возвращаемые типы, если это можно сделать алгоритмически, из них же сгенерить соответствующие enable_cond-ы и в самих функциях уже использовать нечто совсем простое без копи-пейста).

Но для простых случаев вроде того, что я привел в качестве мотивации в своей статье, твое решение замечательно подходит, включая решение с номерами строк (главное, чтоб это не случилось ниже 500 строки — сразу получишь по рукам от компилятора, у моего ГЦЦ максимальная глубина рекурсивного инстанцирования — 500, хотя на это есть опция), и таких простых случаев, как учит нас практика, большинство


Но для сложных ситуаций, типа той, что у меня в проекте, придется использовать нечто более обобщенное/низкоуровневое (и поэтому менее удобное из-за разделения условий и самих функций).

R>

jazzer (Skype: enerjazzer) Ночная тема для RSDN
Автор: jazzer
Дата: 26.11.09

You will always get what you always got
  If you always do  what you always did
Re[7]: template pattern matching - в защиту отдельных услови
От: remark Россия http://www.1024cores.net/
Дата: 04.03.10 13:16
Оценка:
Здравствуйте, jazzer, Вы писали:

R>>А чем не нравится "KISS template pattern matching":

R>>http://www.rsdn.ru/forum/cpp/3722508.1.aspx
Автор: remark
Дата: 02.03.10

J>Почему же не нравится, очень даже нравится

R>>Так вроде как всё локально — просто добавляешь нужную специализацию с нужным приоритетом. Или дополнительный диспатчер не нравится?

J>Нет, с дополнительным диспатчером проблем почти нет (за исключением той проблемы, что он абсолютно неприменим в моем проекте — у меня там кучерявые типы аргументов, которые компилятор выводит сам — с диспатчером все перестанет работать и будет одна сплошная ошибка компиляции из-за неразрешимой неоднозначности).

А это как? Вроде же передача через диспатчер не должна ничего менять в плане типов и их выводимости...


J>Там еще было две причины озвучено — 1) реюз условий для нескольких семейств функций и 2) автоматическая генерация условий из других условий или из чего еще угодно (метапрограммированием тут можно заняться в полный рост и очень разнообразно, например, привязать к условиям возвращаемые типы, если это можно сделать алгоритмически, из них же сгенерить соответствующие enable_cond-ы и в самих функциях уже использовать нечто совсем простое без копи-пейста).


J>Но для простых случаев вроде того, что я привел в качестве мотивации в своей статье, твое решение замечательно подходит, включая решение с номерами строк (главное, чтоб это не случилось ниже 500 строки — сразу получишь по рукам от компилятора, у моего ГЦЦ максимальная глубина рекурсивного инстанцирования — 500, хотя на это есть опция), и таких простых случаев, как учит нас практика, большинство


J>Но для сложных ситуаций, типа той, что у меня в проекте, придется использовать нечто более обобщенное/низкоуровневое (и поэтому менее удобное из-за разделения условий и самих функций).


Понятно

R>>

J>

1024cores &mdash; all about multithreading, multicore, concurrency, parallelism, lock-free algorithms
Re[8]: template pattern matching - в защиту отдельных услови
От: jazzer Россия Skype: enerjazzer
Дата: 04.03.10 13:53
Оценка:
Здравствуйте, remark, Вы писали:

R>>>Так вроде как всё локально — просто добавляешь нужную специализацию с нужным приоритетом. Или дополнительный диспатчер не нравится?

J>>Нет, с дополнительным диспатчером проблем почти нет (за исключением той проблемы, что он абсолютно неприменим в моем проекте — у меня там кучерявые типы аргументов, которые компилятор выводит сам — с диспатчером все перестанет работать и будет одна сплошная ошибка компиляции из-за неразрешимой неоднозначности).

R>А это как? Вроде же передача через диспатчер не должна ничего менять в плане типов и их выводимости...

у меня же несколько аргументов, один свободный совсем, а вот другой в некоторых перегрузках зависит от первого и там все срабатывает.
jazzer (Skype: enerjazzer) Ночная тема для RSDN
Автор: jazzer
Дата: 26.11.09

You will always get what you always got
  If you always do  what you always did
Re[6]: template pattern matching - в защиту отдельных услови
От: jazzer Россия Skype: enerjazzer
Дата: 05.03.10 06:58
Оценка:
Здравствуйте, Кодт, Вы писали:

К>Я не могу говорить, убедил или не убедил: была твоя ситуация, твоё решение, тебя удовлетворило, — при чём здесь моё мнение?

К>Ты опытным путём показал, что отдельный список условий удобен ("есть как минимум одна овца, чёрная как минимум с одного бока").
К>Понятно, что "три" перспективнее. Но "раз" проще и нагляднее, если всё ограничивается одним разом.

Ну в общем-то да.
Просто моя цель была показать, что решение без отдельных условий не однозначно хуже, и у него есть своя вполне весомая область применения, и что отказ от отдельных условий небесплатен — мы теряем вполне конкретные рулезы (хотя приобретаем другие в виде простоты записи условий в простых случаях).

Т.е, как обычно в нашей профессии, мы имеем выбор из разных вариантов, и в зависимости от задачи у одного будет преимущество над другим.
Сорри, что я не уловил понимания этого в твоем изначальном ответе "Фокус в том, что таблица — в одном месте, а функции — в другом "

Иными словами, нужно пользоваться моим решением, если нужно:
1) переиспользование списков условий для нескольких наборов функций,
2) автоматическая генерация списков условий,
3) непосредственный доступ к сигнатурам каждой перегрузки,
4) использование одного и того же условия для нескольких перегрузок.

Решение Ремарка c __LINE__ подходит для всех остальных случаев, а решение с явным указанием приоритетов — для остальных случаев плюс пункт 4 выше.
jazzer (Skype: enerjazzer) Ночная тема для RSDN
Автор: jazzer
Дата: 26.11.09

You will always get what you always got
  If you always do  what you always did
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.