Как бы реализовать замыкания?
От: remark Россия http://www.1024cores.net/
Дата: 17.07.09 09:52
Оценка:
Первоначально предполагалось реализовать замыкания как:

template<typename T>
struct param_traits
{
  // определяет как передавать и хранить аргумент
};

template<typename T>
struct param_traits<T const&>
{
  //...
};

template<typename T>
struct param_traits<T&>
{
  //...
};

template<typename A1, typename A2>
void spawn (void(*f)(A1, A2), typename param_traits<A1>::param_type a1, typename param_traits<A2>::param_type a2)
{
  // запоминаем f, a1, a2 для последующего исполнения
}
// аналогичные функции spawn() для другого кол-ва аргументов


Важные преимущества:
— неправильные типы аргументов детектируются в месте вызова spawn
— автоматически определяется наиболее адекватный способ как хранить аргумент, т.е. не надо ref/cref как в boost
— отсутствует лишняя работа во время выполнения
— всего 1 шаблон spawn() для 2 аргументов

Теперь надо добавить различные хинты к аргументам, что-то типа такого:
X* x = new X (...);
spawn(foo, delete_after_execution(x));


или:
spawn(foo, pass_by_pointer(x)); // перекрываем способ передачи по-умолчанию



Не понимаю, как это сделать без потери указанных преимуществ.
Есть какие-нибудь идеи?



1024cores &mdash; all about multithreading, multicore, concurrency, parallelism, lock-free algorithms
Re: Как бы реализовать замыкания?
От: Аноним  
Дата: 17.07.09 10:11
Оценка:
Здравствуйте, remark, Вы писали:

R>Не понимаю, как это сделать без потери указанных преимуществ.

R>Есть какие-нибудь идеи?

А в чём проблемы у make-фнкции delete_after_execution, которая тоже использует эти же param_traits?

template<typename T>
struct delete_after_execution_t;

template<typename T>
struct param_traits<delete_after_execution_t<T> >
{
  // определяет как передавать и хранить аргумент
};


template<typename T>
struct delete_after_execution_t
{
  delete_after_execution(typename param_traits<T>::param_type);
  ...
}

template<typename T>
delete_after_execution_t delete_after_execution(typename param_traits<T>::param_type) 
{
  retrun delete_after_execution_t
}
Re[2]: Как бы реализовать замыкания?
От: remark Россия http://www.1024cores.net/
Дата: 17.07.09 10:27
Оценка:
Здравствуйте, Аноним, Вы писали:

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


R>>Не понимаю, как это сделать без потери указанных преимуществ.

R>>Есть какие-нибудь идеи?

А>А в чём проблемы у make-фнкции delete_after_execution, которая тоже использует эти же param_traits?



Что-то типа такого и хотелось бы получить. Но проблема в том, что param_traits никогда не будут параметризованы delete_after_execution_t, т.к. они параметризуются типом параметра функции, а не типом аргумента. Текущий spawn() полностью слеп к типам аргументов, это с одной стороны хорошо, т.к. все преобразования, варнининги и ошибки выдаются в строчке вызова spawn(); но с другой — не получается реализовать хинты.



1024cores &mdash; all about multithreading, multicore, concurrency, parallelism, lock-free algorithms
Re: Как бы реализовать замыкания?
От: remark Россия http://www.1024cores.net/
Дата: 17.07.09 10:40
Оценка:
У меня пока получилась только вот такая фигня.
Что в нём не нравится, так это то, что он без церемонно снимает const с аргументов, соотв. может скомпилировать код, который не должен компилироваться.
Плюс варнинги насчёт конвертации аргументов теперь выдаются внутри spawn, т.к. формально is_converible проверка для них проходит.
Возможно тут ещё что-то есть, например, для каких-то функций и аргументов будет вызываться не тот spawn.


#pragma warning (push)
#pragma warning (disable : 4244)

template<typename To, typename From>
struct is_converible
{
    static char test (To);
    static long test (...);
    static bool const result = (sizeof(test(*(From*)0)) == sizeof(char));
};

#pragma warning (pop)

template<bool>
struct enable_if
{
};

template<>
struct enable_if<true>
{
    typedef void type;
};

template<typename T>
struct identity
{
    typedef T type;
};

template<typename T>
struct param
{
    param(T const&) {}
};

template<typename A1, typename A2, typename R1, typename R2>
// этот вариант работет только если внутри мы сможем сконвертировать все аргументы
typename enable_if<is_converible<A1, R1>::result && is_converible<A2, R2>::result>::type
spawn (void (*f) (A1, A2), R1 const& a1, R2 const& a2)
{
    f(const_cast<R1&>(a1), const_cast<R2&>(a2));
}

// а этот вариант исключительно для выдачи ошибок конвертации
template<typename A1, typename A2>
void
spawn (void (*f) (A1, A2), typename identity<param<A1> >::type a1, typename identity<param<A2> >::type a2);




1024cores &mdash; all about multithreading, multicore, concurrency, parallelism, lock-free algorithms
Re: Как бы реализовать замыкания?
От: remark Россия http://www.1024cores.net/
Дата: 17.07.09 11:01
Оценка:
Второй вариант использует макросы:

#define spawn(f, ...) spawn_ (f, __VA_ARGS__, __VA_ARGS__)

template<typename A1, typename A2, typename R1, typename R2>
void spawn_ (void (*f) (A1, A2), typename param_traits<A1>::param_t a1, typename param_traits<A2>::param_t a2, R1 const&, R2 const&)
{
    // как дальше обрабатывать агрумент i определяется парой типов (Ai, Ri)
    f(a1, a2);
}


Тут основной недостаток, что аргументы могут вычисляться 2 раза, что может быть проблемой, если пользователь напишет:
spawn(foo, some_heavy_function());


Мысли, как улучшить этот или предыдущий вариант, что бы устранить указанные недостатки?


1024cores &mdash; all about multithreading, multicore, concurrency, parallelism, lock-free algorithms
Re: Как бы реализовать замыкания?
От: remark Россия http://www.1024cores.net/
Дата: 18.08.09 15:09
Оценка:
up

1024cores &mdash; all about multithreading, multicore, concurrency, parallelism, lock-free algorithms
Re: Как бы реализовать замыкания?
От: IROV..  
Дата: 18.08.09 17:07
Оценка: 14 (1)
Здравствуйте, remark, Вы писали:

нужно убегать, но вот чтото в голову придумалось )

template<typename T>
struct param_traits
{
    // определяет как передавать и хранить аргумент
    typedef T param_type;
    typedef T hold_type;
};

template<typename T>
struct param_traits<T const&>
{
    //...
    typedef T param_type;
    typedef T hold_type;
};

template<typename T>
struct param_traits<T&>
{
    //...
    typedef T param_type;
    typedef T hold_type;
};

template<class T>
struct some_policity_t
{
    some_policity_t( typename param_traits<T>::param_type _value )
        : value( _value )
    {
    }

    typedef T param_type;

    typename param_traits<T>::hold_type value;
};

template<class T>
some_policity_t<T> some_policity( T _value )
{
    return some_policity_t<T>( _value );
}

struct type_true{};

struct type_false{};

template<class A, class B>
struct type_check
{
    typedef type_true result;
};

template<>
struct type_check<std::string, float>
{
    typedef type_false result;
};

template<typename A1, typename A2, typename P1, typename P2>
void spawn ( void(*f)(A1, A2), P1 p1, P2 p2, typename type_check<A1, typename P1::param_type>::result & _r1 = type_true(), typename type_check<A2, typename P2::param_type>::result & _r2 = type_true() )
{
    // запоминаем f, a1, a2 для последующего исполнения
}

void foo( int a1, std::string a2 )
{

}

int main()
{
    spawn( &foo, some_policity(1), some_policity(1.f) );
}


Попробуй развить эту идею может что выйдет

R>

я не волшебник, я только учусь!
Re: Как бы реализовать замыкания?
От: Кодт Россия  
Дата: 19.08.09 09:36
Оценка: 14 (1)
Здравствуйте, remark, Вы писали:

<>

Мотивировочную часть пропустим, перейдём к фактической.
Дано:
— с одной стороны, формальный тип аргумента (у тебя — выведенный из сигнатуры функции)
— с другой стороны, фактический тип, который может быть в обёртке
Нужно:
— аккуратно протащить информацию об обёртке.

Исходный нерабочий прототип
template<class Formal> // этот параметр - явный, а не выводится из фактического аргумента
void foo(argument_traits<Formal>::type arg)
{
    argument_traits<Formal>::type storage(arg);
    .....
}


Лобовое решение
template<class Formal, class Real> // Formal задаётся явно, Real выводится
void foo(Real const& arg) // здесь мы на всякий случай передаём по ссылке, чтобы случайно не сделать копирование
{
    storage_traits<Formal,Real>::type storage(arg); // а уже в недрах - делаем что должно
    .....
}

struct wrapper_base {}; // от него должны унаследоваться все обёртки
struct is_wrapper<T> : is_base_and_derived< wrapper_base, T > {};

// конкретные обёртки (make-функции для них подразумеваются)
template<class T> struct byref_t          : wrapper_base { ..... };
template<class T> struct byvalue_t        : wrapper_base { ..... };
template<class T> struct finally_delete_t : wrapper_base { ..... };


// а вот, собственно, возможная реализация storage_traits
template<class Formal, class Real>
struct storage_traits :
    if_<
        is_wrapper<Real>,
        Real, // если обёртка чего бы то ни было - используем копию обёртки
        argument_traits<Formal>::type // иначе используем оптимизированную передачу формального аргумента
    >
{};


Вот что-то в таком роде.

Но я, после того, как намаялся с Loki, очень и очень зауважал подход boost. Там, по крайней мере, никаких сюрпризов с владением связанных аргументов.
... << RSDN@Home 1.2.0 alpha 4 rev. 1237>>
Перекуём баги на фичи!
Re: Как бы реализовать замыкания?
От: remark Россия http://www.1024cores.net/
Дата: 25.08.09 08:40
Оценка:
IROV.., Кодт, спасибо за варианты.

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

По поводу варианта IROV... Во-первых, правильно ли я понял, что type_check есть is_convertible?
Не нравится, что будут делаться копии копии аргументов, т.к. параметры передаются по значению.
Насколько я понимаю, что так не получится передавать ссылки, т.к. в целевую функцию пойдёт ссылка на неправильный объект.
Вырнинги при конвертации будут где-то в дебрях реализации.

ПО поводу варианта Кодт.
Не нравится, что можем скомпилировать некорректный код. Например, целевая функция принимает string&, пользователь передаёт string const&, мы по-тихому снимаем константность и успешно компилируем это. По-скольку мы принимаем T const& мы не знаем, что пользователь нам передал — то ли T const&, то ли T&.
Не нравится, что ошибки конвертации и варнинги будут внутри библиотечного кода.



1024cores &mdash; all about multithreading, multicore, concurrency, parallelism, lock-free algorithms
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.