как сделать свой оператор в с++
От: Sergeem Израиль  
Дата: 18.03.03 09:20
Оценка: 132 (10)
Эта идея была предложена Александреску в comp.lang.c++.moderated.
Гуру нос воротили, а мне понравилось

Короче идея состоит в том, чтобы дать возможность писать:

if (a <in> b) { ...

Я надеюсь, вы меня поняли
Всю примочку я замотал в namespace cust_op, чтобы не было конфликтов.
Вот пример использования:
// test.cpp

#include <list>
#include <set>
#include <iostream>
using namespace std;

#include "cust_op.h"
#include "cust_op_in.h"


int main(int argc, char *argv[])
{
    int array[4] = {1, 2, 3, 4};
    set<int> s;
    list<int> v;

    s.insert(1);
    s.insert(2);
    s.insert(3);

    v.insert(v.begin(), array, array + 4);

    if (1 <in> s)
        cout << "set: OK" << endl;
    else
        cout << "set: not found..." << endl;

    if (10 <not_in> v)
        cout << "list: OK" << endl;
    else
        cout << "list: not found..." << endl;

    if (2 <in> pair<int*,int*>(array, array + 4))
        cout << "array: OK" << endl;
    else
        cout << "array: not found..." << endl;

    return 0;
}


А вот и сорцы
Я сделал только in и not_in, однако юзер
вправе добавлять свои собственные. Только
надо их в namespace засовывать.

// custom_op.h

#ifndef _cust_op__
#define _cust_op__

namespace cust_op
{

template <class T, class Op>
struct operator_arg_t
{
    operator_arg_t(const T &l, const Op &o)
        : lhs(l), op(o) {}

    const T        &lhs;
    const Op    &op;
};

template <class T, class Op>
operator_arg_t<T, Op> operator <(const T &lhs, const Op &op)
{
    return operator_arg_t<T,Op>(lhs, op);
}

template <class T, class Op, class U>
bool operator >(const operator_arg_t<T, Op> &arg, const U &rhs)
{
    return arg.op(arg.lhs, rhs);
}

} // namespace

#endif



// custom_op_in.h

#ifndef _cust_op_in_h__
#define _cust_op_in_h__

#include <set>

namespace cust_op
{

namespace _private
{

    template <class T>
    struct find_helper
    {
        template <class C>
        static bool find(const C &c, const T &t)
        {
            return std::find(c.begin(), c.end(), t) != c.end(); 
        }

        template <>
        static bool find(const std::set<T> &c, const T &t)
        {
            return c.find(t) != c.end();
        }

        template <>
        static bool find(const std::pair<T*,T*> &c, const T &t)
        {
            return std::find(c.first, c.second, t) != c.second; 
        }
    };
}

struct in_t
{
    template <class T, class U>
    bool operator() (const T &lhs, const U &rhs) const
    {
        return _private::find_helper<T>::find(rhs, lhs);
    }
};

struct not_in_t
{
    template <class T, class U>
    bool operator() (const T &lhs, const U &rhs) const
    {
        return !_private::find_helper<T>::find(rhs, lhs);
    }
};

}

extern cust_op::in_t        in;
extern cust_op::not_in_t    not_in;

#endif



// cust_op_in.cpp

#include "cust_op_in.h"

cust_op::in_t        in;
cust_op::not_in_t    not_in;


Жду ваших мнений и критики! Заранее спасибо.
Serge.

Hасколько проще была бы жизнь, если бы она была в исходниках.
Re: как сделать свой оператор в с++
От: Кодт Россия  
Дата: 18.03.03 12:34
Оценка:
Здравствуйте, Sergeem, Вы писали:

S>Эта идея была предложена Александреску в comp.lang.c++.moderated.

S>Гуру нос воротили, а мне понравилось

Прикольно!

S>Короче идея состоит в том, чтобы дать возможность писать:


S>if (a <in> b) { ...


Единственное, что здесь неудобно — это ограничения по выводу типа.
Экземпляр оператора имеет конкретный тип; поэтому все, что можно варьировать — это сигнатуры его метода operator(). (Они могут быть шаблонными).

Правда, можно повыделываться с шаблонами шаблонов...
template < template<class,class> class Op >
struct MetaOp2
{
  template< class A, class B >
  bool operator() (A a, B b) { Op<A,B> op; return op(a,b); }
};

template < template<class> class Op >
struct MetaOp1A
{
  template< class A>
  bool operator() (A a, B b) { Op<A> op; return op(a,b); }
};

template < template<class,class> class Op >
struct MetaOp1B
{
  template< class A, class B >
  bool operator() (A a, B b) { Op<B> op; return op(a,b); }
};

(стоит ли говорить, что на такие подвиги VC не способен).

А чтобы не делать глобальные переменные — экземпляры операторов и метаоператоров, то нужно писать
if(123 <MetaOp1A<less>()> 456) ...
// в данном случае less - это симметричный (по типам) оператор, поэтому можно использовать как 1A, так и 1B.

// или завести макросы
#define LESS <MetaOp1A<less>()>
#define IN <MetaOp2<in_t>()>
Перекуём баги на фичи!
Re[2]: как сделать свой оператор в с++
От: Аноним  
Дата: 18.03.03 12:42
Оценка: :))
Здравствуйте, Кодт, Вы писали:

К>
К>// или завести макросы
К>#define LESS <MetaOp1A<less>()>
К>#define IN <MetaOp2<in_t>()>
К>


Кодт, я потерял к вам уважение.
Re[2]: как сделать свой оператор в с++
От: Sergeem Израиль  
Дата: 18.03.03 13:07
Оценка:
Здравствуйте, Кодт, Вы писали:

[skip]

К>А чтобы не делать глобальные переменные — экземпляры операторов и метаоператоров, то нужно писать

К>
К>if(123 <MetaOp1A<less>()> 456) ...
К>// в данном случае less - это симметричный (по типам) оператор, поэтому можно использовать как 1A, так и 1B.

К>// или завести макросы
К>#define LESS <MetaOp1A<less>()>
К>#define IN <MetaOp2<in_t>()>
К>


Нее, вот макросы я не люблю, тем более такие.
А чем плохи глобальные переменные?
Serge.

Hасколько проще была бы жизнь, если бы она была в исходниках.
Re[3]: как сделать свой оператор в с++
От: Кодт Россия  
Дата: 18.03.03 14:53
Оценка:
Здравствуйте, Аноним, Вы писали:

А>Кодт, я потерял к вам уважение.


На этот случай есть нолики

А что касается макросов — тут, конечно, дело хозяйское: что красивее писать?
///////

static MyOp1A<less> LESS;

... if(a <LESS> b)

///////

... if(a <MyOp1A<less>()> b)

///////

#define LESS (MyOp1A<less>())

... if(a <LESS> b)

Мне вообще не нравится разводить статические объекты. А макросами я пользуюсь в рамках одной единицы компиляции (внутри .cpp файла).



Кстати, вместо экземпляров класса можно использовать "экземпляры" функций. Ведь они тоже имеют оператор ()

bool in(int elem, std::list<int> const& the_list)
{
  return std::find(the_list.begin(), the_list.end(), elem) != the_list.end();
}

Соответственно, нужно причесать шаблон...

Но здесь типы будут прибиты гвоздями насмерть.
(Если у объекта-функтора могут быть несколько сигнатур оператора(), то у конкретной функции — ровно одна, своя родная, сигнатура).
Перекуём баги на фичи!
Re: как сделать свой оператор в с++
От: Кодт Россия  
Дата: 02.03.04 11:00
Оценка:
Здравствуйте, Sergeem, Вы писали:

S>Эта идея была предложена Александреску в comp.lang.c++.moderated.

S>Гуру нос воротили, а мне понравилось

S>Короче идея состоит в том, чтобы дать возможность писать:


S>if (a <in> b) { ...


А что, если вообще избавиться от хелперов?
Почему бы (помечтаем...) не сделать такое:
class in : public custom_operator<in> // некая обвеска - в недрах
{
public:
  template<class L, class R> SomeResult operator()(L l, R r) const { ..... }
};

Всё шаманство должен взять на себя базовый класс custom_operator<Op>.

Попробуем...
template<class Op>
struct custom_operator
{
  typedef Op operator_type;

  const operator_type& self() const { return *static_cast<const operator_type*>(this); }
};

template<class L, class Op>
struct custom_binder
{
  const L& lhs;
  const Op& op;

  custom_binder(const L& l, const Op& o) : lhs(l), op(o) {}
};

template<class L, class Op>
custom_binder<L,Op> operator< (const L& lhs, const custom_operator& op)
{ return custom_binder(l, op.self()); }

template<class L, class Op, class R>
SomeResult operator> (const custom_binder<L,Op>& binder, const R& rhs)
{ return binder.op(binder.lhs, rhs); }


Тонким местом является вывод типа SomeResult.
В STL с этим столкнулись и пошли по пути наименьшего сопротивления: функтор должен быть моделью (а то и потомком) binary_function, где результирующий тип прибит гвоздями.
struct in : custom_operator<in>, binary_function<bool, int, set<int> >
{
  bool operator()(int x, const set<int>& s) const { return s.find(x)!=s.end(); }
};

template<class L, class Op, class R>
Op::result_type operator> (const custom_binder<L,Op>& binder, const R& rhs)
{ return binder.op(binder.lhs, rhs); }


В действительности, это излишне жёсткое ограничение, которое несложно обойти с помощью type traits.
Нам нужен traits такого вида
template<class Op, class L, class R>
struct binary_traits
{
  typedef ????? result_type;
};

template<class L, class Op, class R>
binary_traits<Op,L,R>::result_type operator> (.....) {.....}

// или такого...

template<class Op>
struct binary_traits
{
  template<class L, class R>
  struct result_traits
  {
    typedef ????? result_type;
  };
};

template<class L, class Op, class R>
binary_traits<Op>::result_traits<L,R>::result_type operator> (.....) {.....}

Причём придётся рассмотреть следующие частные случаи:
Вопрос с архитектурой binary_traits пока что оставлю открытым.
Очевидное решение —
* сделать обобщённую версию с расчётом на наличие Op::result_traits<L,R>
* и две специализации, дискриминирующие потомков binary_function<Res,L,R> и generic_binary_function<Res>
— плохо масштабируется.
Перекуём баги на фичи!
Re: как сделать свой оператор в с++
От: McSeem2 США http://www.antigrain.com
Дата: 02.03.04 15:04
Оценка: +1 -1
S>Эта идея была предложена Александреску в comp.lang.c++.moderated.
S>Гуру нос воротили, а мне понравилось

"Не плоди сущностей без необходимости" — принцип бритвы Оккама. Теоретически это интересно, и в каком-то приложении, где подобных операций очень много, может даже пригодиться.
Но просто так, ради выпендрежа я бы использовать не стал — лишние зависимости, лишняя путаница, etc.
McSeem
Я жертва цепи несчастных случайностей. Как и все мы.
Re[2]: как сделать свой оператор в с++
От: c-smile Канада http://terrainformatica.com
Дата: 03.03.04 00:22
Оценка:
Здравствуйте, McSeem2, Вы писали:

MS>"Не плоди сущностей без необходимости" — принцип бритвы Оккама....


Макс, бритва это несерьезно.
Предлагаю вооружиться саблей Оккама. Или косой. Хм... коса Оккама... хочу!

Имхо: нотация templates в том виде что она есть сейчас ... м-м-м... не совсем подходит для meta вычислений так модных в наши дни.

Смотрю я на эти семиэтажно неуклюжие попытки превратить С++ в Perl и задаюсь вопросом это уже процессуализм или еще нет?

эстет от С++.

Зы: Каску одел.
Re[2]: как сделать свой оператор в с++
От: Sergeem Израиль  
Дата: 03.03.04 16:33
Оценка:
Здравствуйте, Кодт, Вы писали:

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


S>>Эта идея была предложена Александреску в comp.lang.c++.moderated.

S>>Гуру нос воротили, а мне понравилось

S>>Короче идея состоит в том, чтобы дать возможность писать:


S>>if (a <in> b) { ...


К>А что, если вообще избавиться от хелперов?


Уф... Что-то мудрено вышло!
На самом деле хелперы я вставил только для совместимости с Visual'ом, потому как у него проблемы с частичной специализацией. На самом деле, чтобы сделать новый оператор, не нужно даже наследоваться! Просто расширяем namespace cust_op.

namespace cust_op
{

  struct my_operator_t
  {
    template <class T, class U>
    bool operator() (const T &lhs, const U &rhs) const
    {
        return ....;
    }
  };
}

extern cust_op::my_operator_t my_operator;

...

if (x <my_operator> y)
{
   ...
Serge.

Hасколько проще была бы жизнь, если бы она была в исходниках.
Re[2]: как сделать свой оператор в с++
От: Sergeem Израиль  
Дата: 03.03.04 16:37
Оценка:
Здравствуйте, McSeem2, Вы писали:

S>>Эта идея была предложена Александреску в comp.lang.c++.moderated.

S>>Гуру нос воротили, а мне понравилось

MS>"Не плоди сущностей без необходимости" — принцип бритвы Оккама. Теоретически это интересно, и в каком-то приложении, где подобных операций очень много, может даже пригодиться.

MS>Но просто так, ради выпендрежа я бы использовать не стал — лишние зависимости, лишняя путаница, etc.

Во-во, зачем люди вообще STL придумали, шаблоны, паттерны всякие опять же...
Вот жили 20 лет без шаблонов, и хорошо!
Непонятно!
Serge.

Hасколько проще была бы жизнь, если бы она была в исходниках.
Re[3]: как сделать свой оператор в с++
От: Кодт Россия  
Дата: 03.03.04 16:47
Оценка:
Здравствуйте, Sergeem, Вы писали:

S>Уф... Что-то мудрено вышло!

S>На самом деле хелперы я вставил только для совместимости с Visual'ом, потому как у него проблемы с частичной специализацией. На самом деле, чтобы сделать новый оператор, не нужно даже наследоваться! Просто расширяем namespace cust_op.

Во-первых, ты перекрыл глобальный оператор < для ваще-всех типов (естественно, если есть специализации, то в силу вступят они).
Приключения начнутся там, где этот оператор выступает в своей исходной роли.
Например, ты делаешь std::set<YourClass>. И забыл определить для него оператор сравнения... Наготове — дефолтный оператор, который возвращает — сюрприз! — cust_op::operator_arg_t<YourClass,YourClass>
Что скажет кальтенбруннер, то есть, пардон, компилятор в недрах STL?

Во-вторых, ты ограничиваешься предикатами (функторы возвращающие bool). Хорошо, пока это <in>, <not_in>.
А если я делаю, к примеру, векторную алгебру: <crossproduct> ?
Перекуём баги на фичи!
Re[3]: как сделать свой оператор в с++
От: McSeem2 США http://www.antigrain.com
Дата: 03.03.04 19:21
Оценка: +1
Здравствуйте, Sergeem, Вы писали:

S>Во-во, зачем люди вообще STL придумали, шаблоны, паттерны всякие опять же...

S>Вот жили 20 лет без шаблонов, и хорошо!
S>Непонятно!

Все должно быть у месту. Не надо бросаться в крайности. Если я считаю STL неуместной в какой-то задаче, я ее и не использую. Уместность подобных операторов весьма сомнительна вообще.
McSeem
Я жертва цепи несчастных случайностей. Как и все мы.
Re[4]: как сделать свой оператор в с++
От: Sergeem Израиль  
Дата: 04.03.04 10:16
Оценка:
Здравствуйте, Кодт, Вы писали:

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


S>>Уф... Что-то мудрено вышло!

S>>На самом деле хелперы я вставил только для совместимости с Visual'ом, потому как у него проблемы с частичной специализацией. На самом деле, чтобы сделать новый оператор, не нужно даже наследоваться! Просто расширяем namespace cust_op.

К>Во-первых, ты перекрыл глобальный оператор < для ваще-всех типов (естественно, если есть специализации, то в силу вступят они).


Что за наезд??!

К>Приключения начнутся там, где этот оператор выступает в своей исходной роли.


Приключения закончатся, если ты внимательно посмотришь исходники.
Serge.

Hасколько проще была бы жизнь, если бы она была в исходниках.
Re[5]: как сделать свой оператор в с++
От: Кодт Россия  
Дата: 04.03.04 10:49
Оценка:
Здравствуйте, Sergeem, Вы писали:

К>>Во-первых, ты перекрыл глобальный оператор < для ваще-всех типов (естественно, если есть специализации, то в силу вступят они).


S>Что за наезд??!


К>>Приключения начнутся там, где этот оператор выступает в своей исходной роли.


S>Приключения закончатся, если ты внимательно посмотришь исходники.


Аааа. Почему-то показалось, что оператор< вне пространства cust_op.
Перекуём баги на фичи!
Re: как сделать свой оператор в с++
От: Plutonia Experiment Беларусь http://blogs.rsdn.org/ikemefula
Дата: 09.03.04 11:20
Оценка:
Здравствуйте, Sergeem, Вы писали:

S>if (a <in> b) { ...


Самое главное, чтобы код был предельно ясным, легко читался и изменялся.

Выгибоны интересные, но ...
Re[4]: как сделать свой оператор в с++
От: PK Sly http://www.vocord.ru/
Дата: 09.03.04 13:23
Оценка:
Здравствуйте, McSeem2, Вы писали:

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


S>>Во-во, зачем люди вообще STL придумали, шаблоны, паттерны всякие опять же...

S>>Вот жили 20 лет без шаблонов, и хорошо!
S>>Непонятно!

MS>Все должно быть у месту. Не надо бросаться в крайности. Если я считаю STL неуместной в какой-то задаче, я ее и не использую. Уместность подобных операторов весьма сомнительна вообще.


может быть и goto запретить?
VAX/VMS rulez!
Re[5]: как сделать свой оператор в с++
От: McSeem2 США http://www.antigrain.com
Дата: 09.03.04 14:52
Оценка:
PS>может быть и goto запретить?

Нет! Ни в коем случае! Он мне дорог как память
McSeem
Я жертва цепи несчастных случайностей. Как и все мы.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.