Russian Qt Forum

Программирование => С/C++ => Тема начата: kambala от Июль 02, 2014, 20:45



Название: сократить написание типа лямбды
Отправлено: kambala от Июль 02, 2014, 20:45
Здравствуйте. Есть такой код (сокращенный, не относящиеся к делу части убраны):
Код
C++ (Qt)
   auto traverseGrid = [](const NumberGrid &numberGrid, std::function<const NumberCell &(const NumberGrid &numberGrid, uchar m, uchar a)> getCell)
   {
       getCell(numberGrid, 0, 1);
   };
 
   traverseGrid(_leftNumberGrid, [](const NumberGrid &numberGrid, uchar m, uchar a) { return numberGrid.cellAt(m, a); });
   traverseGrid(_topNumberGrid,  [](const NumberGrid &numberGrid, uchar m, uchar a) { return numberGrid.cellAt(a, m); });
Можно ли как-то покороче описать лямбды (не писать их огромный тип)? traverseGrid может быть и методом, не суть важно.


Название: Re: сократить написание типа лямбды
Отправлено: Bepec от Июль 02, 2014, 21:17
define не подойдёт?
Или ещё что то надо?


Название: Re: сократить написание типа лямбды
Отправлено: kambala от Июль 02, 2014, 21:30
ну хотелось бы typedef например, дефайны мне не надо


Название: Re: сократить написание типа лямбды
Отправлено: Igors от Июль 03, 2014, 08:37
Хотелось бы узнать чем вызван такой синтаксис, что он дает, какую общность Вы хотели получить. По коду это не очень ясно, подробности бы не помешали

Спасибо


Название: Re: сократить написание типа лямбды
Отправлено: navrocky от Июль 03, 2014, 09:02
Хотелось бы узнать чем вызван такой синтаксис, что он дает, какую общность Вы хотели получить. По коду это не очень ясно, подробности бы не помешали

Спасибо

Ну я думаю, имелось ввиду сократить запись лямбды, поиск способа. Для примера лямбда на C#:

Код:
traverseGrid(_leftNumberGrid, (numberGrid, m, a) => { return numberGrid.cellAt(m, a); });

и еще раз оригинал:

Код:
traverseGrid(_leftNumberGrid, [](const NumberGrid &numberGrid, uchar m, uchar a) { return numberGrid.cellAt(m, a); });

К сожалению, тут даже typedef-ить нечего, типы параметров однословные. Разве что typedef const NumberGrid& NumberGridConstRef;. Но это не помогает сокращатить код в данном случае.


Название: Re: сократить написание типа лямбды
Отправлено: m_ax от Июль 03, 2014, 10:06
Здравствуйте. Есть такой код (сокращенный, не относящиеся к делу части убраны):
Код
C++ (Qt)
   auto traverseGrid = [](const NumberGrid &numberGrid, std::function<const NumberCell &(const NumberGrid &numberGrid, uchar m, uchar a)> getCell)
   {
       getCell(numberGrid, 0, 1);
   };
 
   traverseGrid(_leftNumberGrid, [](const NumberGrid &numberGrid, uchar m, uchar a) { return numberGrid.cellAt(m, a); });
   traverseGrid(_topNumberGrid,  [](const NumberGrid &numberGrid, uchar m, uchar a) { return numberGrid.cellAt(a, m); });
Можно ли как-то покороче описать лямбды (не писать их огромный тип)? traverseGrid может быть и методом, не суть важно.

А чем мотивировано то, что traverseGrid должна быть именно лямбдой?

Можно например проще:

Код
C++ (Qt)
template <class R, class Arg, class F>
R traverseGrid(const Arg & arg, F function)
{
   return function(arg, 0, 1);
}
 
traverseGrid(_leftNumberGrid, [](const NumberGrid &numberGrid, uchar m, uchar a) { return numberGrid.cellAt(m, a); });
traverseGrid(_topNumberGrid,  [](const NumberGrid &numberGrid, uchar m, uchar a) { return numberGrid.cellAt(a, m); });
 


Название: Re: сократить написание типа лямбды
Отправлено: kambala от Июль 03, 2014, 12:39
Хотелось бы узнать чем вызван такой синтаксис, что он дает, какую общность Вы хотели получить. По коду это не очень ясно, подробности бы не помешали

Спасибо
в теле traverseGrid выполняется двойной цикл (обход сетки :) ), тело цикла которого для двух объектов отличается лишь способом получения ячейки по индексам. сейчас подумалось, что может стоит перегрузить cellAt, чтобы не заниматься подобными вещами.
Цитировать
А чем мотивировано то, что traverseGrid должна быть именно лямбдой?
пока что написал как лямбду чтобы метод на «один чих» не создавать. в будущем скорее всего появятся еще объекты, для которых надо будет выполнять traverseGrid в подклассе.

вообще вопрос был не о traverseGrid, а о длинном типе [](const NumberGrid &numberGrid, uchar m, uchar a) :) Пусть, например, есть 10 объектов, для которых надо написать подобную лямбду, неужели надо каждый раз этот длинный тип пропихивать (без дефайнов)? По сути он же повторяет тип из std::function в traverseGrid, может можно это как-то унифицировать?


Название: Re: сократить написание типа лямбды
Отправлено: m_ax от Июль 03, 2014, 13:54
Если все лямбды подобны (т.е. вызывается один и тот же метод cellAt) то почему не унифицировать так:

Код
C++ (Qt)
std::function<uchar (const NumberGrid&, uchar, uchar)> traverseGrid = &NumberGrid::cellAt;
 
traverseGrid(_leftNumberGrid, m, a);
traverseGrid(_topNumberGrid, a, m);
 
 

Или суть в том, что сигнатура лямбды одинакова, но реализация может сильно отличаться от случая к случаю?


Название: Re: сократить написание типа лямбды
Отправлено: kambala от Июль 03, 2014, 14:17
Или суть в том, что сигнатура лямбды одинакова, но реализация может сильно отличаться от случая к случаю?
изначально был именно такой вопрос. но в данном конкретном случае будет достаточно и ссылки на cellAt. спасибо за подсказку!
Цитировать
сейчас подумалось, что может стоит перегрузить cellAt, чтобы не заниматься подобными вещами.
так не получится, в другом месте код станет нелогичным


Название: Re: сократить написание типа лямбды
Отправлено: kambala от Июль 03, 2014, 14:48
что-то не хочет компилиться:
Код
C++ (Qt)
std::function<const NumberCell &(const NumberGrid &numberGrid, uchar m, uchar a)> x = &NumberGrid::cellAt;
Цитировать
/Users/kambala/Documents/Projects/iOS/relaks/Relaks/classes/model/puzzles/Nonogram.cpp:70:87: error: no viable conversion from '<overloaded function type>' to 'std::function<const NumberCell &(const NumberGrid &, uchar, uchar)>'
    std::function<const NumberCell &(const NumberGrid &numberGrid, uchar m, uchar a)> x = &NumberGrid::cellAt;
                                                                                      ^   ~~~~~~~~~~~~~~~~~~~
In file included from /Users/kambala/Documents/Projects/iOS/relaks/Relaks/classes/model/puzzles/Nonogram.cpp:9:
In file included from /Users/kambala/Documents/Projects/iOS/relaks/Relaks/classes/model/puzzles/Nonogram.h:12:
In file included from /Users/kambala/Documents/Projects/iOS/relaks/Relaks/classes/model/puzzles/Puzzle.hpp:15:
In file included from /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../lib/c++/v1/fstream:169:
In file included from /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../lib/c++/v1/ostream:131:
In file included from /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../lib/c++/v1/ios:216:
In file included from /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../lib/c++/v1/__locale:18:
In file included from /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../lib/c++/v1/mutex:177:
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../lib/c++/v1/functional:1448:5: note: candidate constructor not viable: no overload of 'cellAt' matching 'nullptr_t' for 1st argument
    function(nullptr_t) _NOEXCEPT : __f_(0) {}
    ^
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../lib/c++/v1/functional:1449:5: note: candidate constructor not viable: no overload of 'cellAt' matching 'const std::__1::function<const NumberCell &(const NumberGrid &, unsigned char, unsigned char)> &' for 1st argument
    function(const function&);
    ^
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../lib/c++/v1/functional:1450:5: note: candidate constructor not viable: no overload of 'cellAt' matching 'std::__1::function<const NumberCell &(const NumberGrid &, unsigned char, unsigned char)> &&' for 1st argument
    function(function&&) _NOEXCEPT;
    ^
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../lib/c++/v1/functional:1452:7: note: candidate template ignored: couldn't infer template argument '_Fp'
      function(_Fp, typename enable_if
      ^
1 error generated.

Методы объявлены так:
Код
C++ (Qt)
template <class Cell>
class Grid
{
         Cell &cellAt(uchar row, uchar column);
   const Cell &cellAt(uchar row, uchar column) const;
};
 
class NumberGrid : public Grid<NumberCell> {}
Цитировать
$ clang -v
Apple LLVM version 5.1 (clang-503.0.40) (based on LLVM 3.4svn)
Target: x86_64-apple-darwin13.3.0
Thread model: posix


Название: Re: сократить написание типа лямбды
Отправлено: m_ax от Июль 03, 2014, 16:18
Правильно будет так:
Код
C++ (Qt)
std::function<const NumberCell &(const NumberGrid &, uchar, uchar)> x = &NumberGrid::cellAt;
 

Но проблема ещё и в том, что метод cellAt - перегруженный..
Не знаю как в такой ситуации std::function заставить работать..


Название: Re: сократить написание типа лямбды
Отправлено: kambala от Июль 03, 2014, 17:25
без имен параметров тоже пробовал, ошибка была та же. пробовал даже
Код
C++ (Qt)
auto x = &NumberGrid::cellAt;
все равно ошибка, только другая.

придется создать еще один метод, который будет сам вызывать cellAt с нужным порядком параметров (объекты-сетки на самом деле типов, производных от NumberGrid).

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


Название: Re: сократить написание типа лямбды
Отправлено: m_ax от Июль 03, 2014, 17:51
значит, ответом на исходный вопрос будет «невозможно, сигнатуру лямбды надо всегда писать»?

Нет, "невозможно" здесь слишком громкое слово..
Например, если посмотреть в boost::phoenix то там вообще вместо лямбды можно писать сами выражения (без всяких сигнатур функций)
например можно как то так (отрывок из одного проекта):
Код
C++ (Qt)
using boost::phoenix::arg_names::arg1;
auto it = std::find_if(container.begin(), container.end(), arg1 == key);
 

Думаю, здесь  можно и как-нить более изящнее решить проблему..


Название: Re: сократить написание типа лямбды
Отправлено: m_ax от Июль 03, 2014, 22:06
Вобщем у меня заработало так:

Код
C++ (Qt)
typedef const NumberCell & (NumberGrid::*const_mem_fun_type)(uchar, uchar) const;
typedef NumberCell & (NumberGrid::*mem_fun_type)(uchar, uchar);
 
std::function<const NumberCell &(const NumberGrid &, uchar, uchar)> x = static_cast<const_mem_fun_type>(&NumberGrid::cellAt);
 
 


Название: Re: сократить написание типа лямбды
Отправлено: kambala от Июль 03, 2014, 22:51
от старых добрых указателей никуда не деться :)

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