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: список условий объявляется в одном месте (и довольно прост для изменения), далее просто ссылаемся на условие по индексу...
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.