C++11, передача Lambda-выражения как параметра
От: vintch  
Дата: 12.01.12 16:06
Оценка:
Доброго времени суток.

Занимаюсь проектом, сейчас начал использовать некоторые прелести C++0x (компилятор, AVR GCC, пока что не поддерживает полностью C++11).
Вроде бы всё вполне понятно, но не могу решить одну проблему.
Суть:

typedef void (*func_type)();

void SomeFunction(func_type function)
{
   function();
}

int main()
{
   int someVar = 42;

   SomeFunction([] () -> void { /* Do Nothing */ }); // Случай 1

   SomeFunction([&] () -> void { someVar++; }); // Случай 2
}


Согласно стандарту C++11 в первом случае я имею полное право вот так вот писать лямбда-выражение.
Компилятор неявно приведёт его к указателю на функцию и всё замечательно.

Но вот во втором случае всё не так просто. Случай 2 использует лямбда-функцию с замыканием, подтягивая локальную переменную.
И в таком случае естественно компилятор не приводит её к указателю на функцию, отказывается компилировать.

Мне нужно реализовать код, подобный второму случаю.

STL под AVR GCC я не нашел, да если бы и нашёл — подозреваю, он был бы чертовски тяжелым. Поэтому, пожалуйста, std::function не предлагать.
Но если кто знает где взять STL под AVR GCC (я пишу AVR GCC, хотя в сущности GCC он и в Африке GCC...) — буду рад ссылке.
Вариант, вида:

template <class T>
void SomeFunction(T function)
{
    function();
}


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

Таким образом, по-сути остаётся вариант — написать некий упрощённый аналог std::function. Но я в упор не могу представить, как соорудить
такой шаблон.

Лучшее что на сегодня вышло, это вот (следующий код — только набросок, без какого-либо внятного стиля):

class Func2 {
    public:
    
    class Invoker {
        public:
        virtual int Invoke()=0;
    };    
    
    template <class T>
    class intFunc : public Invoker {
        public:
        T f;
        intFunc(T t)
        : f(t) {}
        virtual int Invoke()
        {
            return f(1);    
        }
    };            
    
    Invoker* invoker;
    
    template <class T>
    Func2(T f)
    {
        invoker = new intFunc<T>(f);
    }
    
    int Invoke()
    {
        return invoker->Invoke();
    }    
};        

void func(Func2 f)
{
    a = f.Invoke();
}

int main(void)
{            
    int a = 0;
    func([&a](int)->int { a++; return a; });
}


В общем — работает, но "работает" — далеко не показатель успеха, увы.
Во-первых очень не хочется использовать память кучи, которую потом ещё и освобождать как-то нужно.
Во-вторых, с таким подходом я не представляю как предопределять тип возвращаемого значения и тип параметров.


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

p.s. Полезная информация по теме есть вот здесь:
http://stackoverflow.com/questions/3534812/how-does-template-parameter-of-stdfunction-work-implementation

p.p.s. Находил описание концепции лямбда-выражений в С++, описывающей, что семантически они эквивалентны вложенному классу-функтору. Если кому-то нужно, найду где я это видел, поделюсь.

Если есть мысли по этому поводу, прошу, пожалуйста, поделитесь.
C++11 C++0x lambda
Re: C++11, передача Lambda-выражения как параметра
От: uzhas Ниоткуда  
Дата: 12.01.12 16:10
Оценка:
Здравствуйте, vintch, Вы писали:

V>Если есть мысли по этому поводу, прошу, пожалуйста, поделитесь.

могу посоветовать погуглить по словам "fast delegates"
возможно, найдете что-то полезное
Re: C++11, передача Lambda-выражения как параметра
От: k06a http://k06a.blogspot.com/
Дата: 12.01.12 16:13
Оценка:
Попробуйте по ссылке лямбду в функции получать.
Re[2]: C++11, передача Lambda-выражения как параметра
От: vintch  
Дата: 12.01.12 16:49
Оценка:
Здравствуйте, k06a, Вы писали:

K>Попробуйте по ссылке лямбду в функции получать.


Идея вполне хороша, вот только — какого типа должна быть ссылка?

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

Единственный способ (из мне известных) получить одновременно собственно лямбда-функцию и её тип, это:

int someVar = 0;
auto lambdaFunction = [&] () -> void { someVar++ };


Здесь:
lambdaFunction — идентификатор функции
decltype(lambdaFunction) — её тип

Но всё это локально, как его передать нужной функции то...
Re[2]: C++11, передача Lambda-выражения как параметра
От: vintch  
Дата: 12.01.12 16:51
Оценка:
Здравствуйте, uzhas, Вы писали:

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


V>>Если есть мысли по этому поводу, прошу, пожалуйста, поделитесь.

U>могу посоветовать погуглить по словам "fast delegates"
U>возможно, найдете что-то полезное

Искал. Не нашёл. Вернее нашёл, но то, что я нашёл — никаким боком не коррелирует с лямбдами((
Re[3]: C++11, передача Lambda-выражения как параметра
От: k06a http://k06a.blogspot.com/
Дата: 12.01.12 17:03
Оценка:
Здравствуйте, vintch, Вы писали:

V>Идея вполне хороша, вот только — какого типа должна быть ссылка?


Я имел ввиду написать так:

typedef void (*func_type)();

void SomeFunction(func_type & function)
{
   function();
}


И вообще не проще ли писать шаблоном вместо тайпдефа функции:

template<typename T>
void SomeFunction(T & function)
{
   function();
}
Re[3]: C++11, передача Lambda-выражения как параметра
От: k06a http://k06a.blogspot.com/
Дата: 12.01.12 17:07
Оценка:
Недавно ковырялся с лямбдами... Насколько я понял...
Лямбды с замыканиями — нельзя копировать.
Re[4]: C++11, передача Lambda-выражения как параметра
От: vintch  
Дата: 12.01.12 17:18
Оценка:
Здравствуйте, k06a, Вы писали:

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


V>>Идея вполне хороша, вот только — какого типа должна быть ссылка?


K>Я имел ввиду написать так:


K>
K>typedef void (*func_type)();

K>void SomeFunction(func_type & function)
K>{
K>   function();
K>}
K>


Такая функция не сможет принять аргументом замкнутую лямбда-функцию по той простой причине, что её тип не будет соответствовать void ().

K>И вообще не проще ли писать шаблоном вместо тайпдефа функции:


K>
K>template<typename T>
K>void SomeFunction(T & function)
K>{
K>   function();
K>}
K>


м.. Вы читали мой первый пост? Мне нужно любую замкнутую лямбда-функцию (каждая из которых будет иметь одинаковые параметры и тип возвращаемого значения),
которая по-определению, каждая, будет иметь свой тип, привести к некоему универсальному типу, позволяющему её вызвать.
Ну то есть в сущности мне нужно её делегировать. При решении этой задачи через функцию-шаблон, я просто получу N инстанированных шаблонов для N лямбда-функций и соответственно N типов функций. И как мне их сложить в массив, например?
Re[4]: C++11, передача Lambda-выражения как параметра
От: vintch  
Дата: 12.01.12 17:21
Оценка:
Здравствуйте, k06a, Вы писали:

K>Недавно ковырялся с лямбдами... Насколько я понял...

K>Лямбды с замыканиями — нельзя копировать.

Можно. Не уверен насчёт, буквально, копирования; но делегировать тем или иным образом — можно.
Просмотрите мой пример в конце первого поста. Он работает. Но он очень громоздкий и неуклюжий, не удобен в использовании.
Re: C++11, передача Lambda-выражения как параметра
От: Banned by IT  
Дата: 12.01.12 17:24
Оценка:
Здравствуйте, vintch, Вы писали:

V>Согласно стандарту C++11 в первом случае я имею полное право вот так вот писать лямбда-выражение.

V>Компилятор неявно приведёт его к указателю на функцию и всё замечательно.
Можно ссылку на пункт стандарта?

ICC на обе строки говорит:
error: no suitable conversion function from "lambda []()->void" to "func_type" exists
      SomeFunction([] () -> void { /* Do Nothing */ }); // Случай 1
                   ^

error: no suitable conversion function from "lambda []()->void" to "func_type" exists
      SomeFunction([&] () -> void { someVar++; }); // Случай 2
                   ^


И что то мне кажется что он прав.
... << RSDN@Home 1.1.4 stable SR1 rev. 568>>
Re[2]: C++11, передача Lambda-выражения как параметра
От: vintch  
Дата: 12.01.12 17:50
Оценка: 2 (1)
Здравствуйте, Banned by IT, Вы писали:

BBI>Можно ссылку на пункт стандарта?


BBI>И что то мне кажется что он прав.


Увы, не прав

Источник:
https://connect.microsoft.com/VisualStudio/feedback/details/572138

From the C++0x FCD (N3092 5.1.2/6):

"The closure type for a lambda-expression with no lambda-capture has a public non-virtual non-explicit const conversion function to pointer to function having the same parameter and return types as the closure type’s function call operator. The value returned by this conversion function shall be the address of a function that, when invoked, has the same effect as invoking the closure type’s function call operator."

this language was not present in N3035; it was added very recently, almost right before the launch of Visual Studio 2010,
by proposal N3052: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2010/n3052.html


Пруф:
http://s018.radikal.ru/i522/1201/ce/9733e7299c16.png
Re: C++11, передача Lambda-выражения как параметра
От: enji  
Дата: 13.01.12 09:17
Оценка:
Здравствуйте, vintch, Вы писали:

V>Доброго времени суток.


V>Занимаюсь проектом, сейчас начал использовать некоторые прелести C++0x (компилятор, AVR GCC, пока что не поддерживает полностью C++11).


Кучу саму по себе не боитесь использовать для AVR? Или у вас внешнее ОЗУ прицеплено?

А по теме — вот тут подробно разжевано http://habrahabr.ru/blogs/cpp/78299/
Re: C++11, передача Lambda-выражения как параметра
От: Кодт Россия  
Дата: 13.01.12 10:53
Оценка:
Здравствуйте, vintch, Вы писали:

V>
V>typedef void (*func_type)();

V>void SomeFunction(func_type function)
V>{
V>   function();
V>}
V>


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

В последнем случае можно и нужно сделать колбек с аргументом (либо интерфейс с методом, что по сути является колбеком с аргументом).
А уж как протащить в него произвольное замыкание — тут много подходов.
Например http://rsdn.ru/forum/cpp/4545231.1.aspx
Автор: Кодт
Дата: 16.12.11
и плюс-минус по теме.

А если всё-таки приколочена — придётся или генерировать машинные коды (сомневаюсь, что на микроконтроллере такое позволено), или завести глобальный пул функций и их аргументов, и далее как с колбеком-с-параметром.
http://files.rsdn.org/4783/catsmiley.gif Перекуём баги на фичи!
Re[2]: C++11, передача Lambda-выражения как параметра
От: vintch  
Дата: 13.01.12 16:02
Оценка:
Здравствуйте, enji, Вы писали:

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


V>>Доброго времени суток.


V>>Занимаюсь проектом, сейчас начал использовать некоторые прелести C++0x (компилятор, AVR GCC, пока что не поддерживает полностью C++11).


E>Кучу саму по себе не боитесь использовать для AVR? Или у вас внешнее ОЗУ прицеплено?


Честно — страшно) Но я перечитал документацию, просмотрел исходники malloc, потестил всё это дело и пришел к выводу, что, да, можно использовать.
Но осторожно и ни в коем случае не применять для размещения объектов, чей размер диктуется внешними условиями (появляется риск фрагментации памяти).

E>А по теме — вот тут подробно разжевано http://habrahabr.ru/blogs/cpp/78299/

Спасибо, но не нашёл там ни слова о лямбда-выражениях.
Re: C++11, передача Lambda-выражения как параметра
От: vintch  
Дата: 13.01.12 16:14
Оценка:
Спасибо всем за участие, проблему разрулили.
Если кому интересно — смотрите пример в моём первом сообщении топика, в самом конце.
Там основа. Я её просто доработал и приукрасил. А чтобы избежать кучи
(что не возможно сделать полностью) — можно применять поле класса, а если его недостаточно — обращаться к куче.
К слову, подобным образом реализован std::function из STL. Отдельное спасибо tchr, который помог разобраться с кодом STL.
Когда оформлю всё, покажу реализацию.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.