Страниц: 1 [2]
  Печать  
Автор Тема: Нужна помощь. Ошибка QNX Momentix при компиляции  (Прочитано 7429 раз)
lastcross
Full Member
***
Offline Offline

Сообщений: 234


Просмотр профиля
« Ответ #15 : Мая 25, 2018, 11:08:15 am »

Цитировать
Так это стандартный подход. Undecided

Это не "стандартный" подход (в понимании стандарта - он не является ни требованием ни даже рекомендацией как таковой). Это один из подходов решения, при этом не самый лучший (со стороны гибкости), так как создает очень связанный код. Другими словами класс A никак не связана с классом B ни своим состоянием ни операциями над ними (как и класс B c А). Но вы принудительно вводите ДРУЖЕСТВЕННЫЕ функция которые должны знать о деталях реализации двух классов сразу. Появляется класс C и вы делаете все то же самое для каждого из классов A, B и С.
Рекомендованный подход - это чисто свободные функции (НЕ дружественные, а свободные). Да, придется выбирать - или создавать отдельную абстракцию через которую будет реализован доступ к данным (геттеры и сеттеры) или способ хранения данных набора классов (сторажи хранения) или делать публичные поля (что как бы для таких типов отражающих объекты/операции линейной алгебры как вектор, матрица и кватернионы - вполне общепринятое решение).
Кроме того, советую посмотреть на уже готовые реализации во многих библиотеках (если этот совет еще применим).
Некоторые из них отказались от реализации таких типов шаблонным параметром размера, а реализовали отдельно Vector2, Vector3, Vector4 (как и марицы и др). Так как не все из этих типов имеют обобщенные общепринятые операции (например GetX который есть у всех, и GetZ() - который у Vector4 (или более 4)).

Цитировать
У нас с вами разные вкусы. Smiley Мне не нравится, когда класс напичкан реализациями функций
Еще раз, речь идет только о шаблонных классах. Никакого явного выигрыша при отделение объявления методов от их реализации вы практически не получаете (тем более до С++11).
Шаблонные классы - это обобщенный код (для множества типов). Этот код должен быть максимально простым (и компактным) иначе читать его становится напорядок тяжелее чем  реализацию обычных (не шаблонных классов). В случае если вы разделяете описание и объявление методов, то кроме того, что вам приходится набирать больше текста (начиная от указания параметров шаблонного класса, до указания членом какого шаблонного класса с каким параметрами он является), вам нужно будет еще и постоянно удерживать контекст при просмотре (в голове) - что это за метод, к какому классу он относится (так как шаблонный класс может оказаться частично/или полностью специализирован и таких классов может быть уйма).
Близкий аналог такого "переключения" контекста для не шаблонного класса можно привести как - наследование. При просмотре тела метода без видения описания класса иногда возникает вопрос - а от кого этот класс наследуется, и если не помнишь то вынужден просмотреть описание класса.

Цитировать
Пользуясь случаем, также спрошу у вас один занятный вопрос: может вы знаете, зачем нужны замыкания функций? Я от современной функциональщины очень далёк (а от чтения руководства по Haskel остаётся недоумение, зачем и кому всё это нужно) и её просто не понимаю. Потому и не нахожу ни одной причины для использования замыкания и извращений с ним связанным в Си++.  Undecided Надуманные примеры я видел, но они именно что "а вот теперь мы можем и вот так". Ну, можем. А... зачем? Smiley

Если речь только про замыкания - то для того чтобы удерживая контекст вызвать обработку чего либо (любых переданных данных на обработку) с учетом того контекста который удерживаем.
То есть, вы принимаете решение что нужно сформировать/собрать "подходящий" контекст для обработки ожидаемых событий (событие может еще как не наступить или же уже наступило, что заставило вас формировать контекст). Дальше, по приходу события вы подготавливаете параметры либо же знаете кому можете делегировать формирование таких параметров. Ну и последний этап - отдаете на обработку "замыканию", которое знает как параметры обрабатывать с учетом удерживаемого ею контекста. Это так, упрощенно и своими словами.
Ближайший аналог - функторы (доступен с лохматых времен), объекты некого типа с перегруженной операцией operator().  Контекст ей может передаваться (такой функтор может как содержать внутри себя данные), а может не содержать (внутри функтора данные не содержатся).
Или же лямбды - более предпочтительнее чем функторы по ряду причин. Например потому что не требуют описания отдельного типа (что крайне многословно при использовании функторов). Умеют захватывать контекст автоматически, вдобавок поваляют выводить типы (auto и decltype) - что невероятно круто (относительно последнего), когда пишешь обработку обобщенную (а замыкание - это именно обработка и как правило используемая не однократно/обобщенно).
« Последнее редактирование: Мая 25, 2018, 12:25:32 pm от lastcross » Записан
da-nie
Full Member
***
Online Online

Сообщений: 211



Просмотр профиля
« Ответ #16 : Мая 25, 2018, 12:35:31 pm »

Цитировать
Это не "стандартный" подход

Ну не знаю, я нигде не видел, чтобы от данного подхода отказывались в случае разнотипных классов в операторах.

Цитировать
Да, придется выбирать - или создавать отдельную абстракцию через которую будет реализован доступ к данным (геттеры и сеттеры) или способ хранения данных набора классов (сторажи хранения) или делать публичные поля (что как бы для таких типов отражающих объекты/операции линейной алгебры как вектор, матрица и кватернионы - вполне общепринятое решение).

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

Цитировать
Некоторые из них отказались от реализации таких типов шаблонным параметром размера,

 Cheesy Это очень весело, пока не увидишь какой-нибудь фильтр Калмана с матрицами произвольного размера (который ещё и будут менять, так как процессор не справляется за заданное время), кучей векторов состояний и математикой с десяток листов, написанных не пограмистами, а математиками в их чудесном стиле. Smiley

Цитировать
Еще раз, речь идет только о шаблонных классах. Никакого явного выигрыша отделение объявления методов и их реализации вы практически не получаете

Да читаемость я получаю, читаемость. Smiley Ну вот текст я привёл - что, не понятно, что он делает? Понятно, разумеется. А засунь я всё это внутрь класса, попробуйте сходу отделите одну функцию от другой. При работе с классом нафиг не нужно знать его реализацию, а вот интерфейс нужен кровь из носу. Ну не помню, я как в матрице задать элемент - смотришь в описание класса и всё сразу понятно.

Спасибо за ответ. Smiley

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

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

Цитировать
Ну и последний этап - отдаете на обработку "замыканию", которое знает как параметры обрабатывать с учетом удерживаемого ею контекста. Это так, упрощенно и своими словами.

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

Цитировать
а замыкание - это именно обработка и как правило используемая не однократно/обобщенно

А вот это вот не понял совсем.  Undecided Ведь любая функция - это обработка. Но не от любой требуется залазить во внешний, ей не передаваемый явно, контекст. Roll Eyes
Записан

И день и ночь в пути
lastcross
Full Member
***
Offline Offline

Сообщений: 234


Просмотр профиля
« Ответ #17 : Мая 25, 2018, 01:04:50 pm »

Цитировать
Да читаемость я получаю, читаемость. Smiley
Все это исключительно субъективная оценка (разумеется я и свою называю субъективной. Когда-то рассуждал именно как вы.. после пришел к другому подходу)

Цитировать
Вот только нафиг это нужно?
Ну самый ближайший пример - калбек события GUI который не позволяет вам через навязанный свой интерфейс передать произвольный объект. Но вы как пользователь считаете что корректная обработка может зависеть не только от параметров интерфейса калбека, но и от состояния отдельного объекта (связанного с окном, главным/родительским.. состоянием его контролов которые существуют независимо от контрола сгенерировавшего событие).

Код:
class MyFarme
{

bool CanApply(){}

void doApply(){}

///....

void Initialize()
{
  applyButton->Bind(BUTTON_PRESS_EVENT_ID, [this](EventButtonPress & event)
     {
       auto button = event->GetCtrl();
       if (this->CanApply())
       {
         doApply();
       }
     }
  );
}

};

Тут мы зарегистрировали событие обработки нажатия некой кнопки "Применить". Нажатие нашей системой GUI генерирует событие с калбеком
Код:
void OnButtonPress(EventButtonPress & event);
, где EventButtonPress - недоступный для изменения тип.

Объект MyFarme содержит внутри себя эту кнопку, и и имеет время жизни дольше чем сама кнопка. Обработка нажатой кнопки зависит от состояния MyFarme (результата CanApply). Как именно применять - также знает MyFarme (через doApply). Поэтому мы в качестве обработчика передаем лямбда функцию с замыканием (контекст this).
А теперь внимание вопрос - каким иным способом вы бы смогли точно также элегантно и немногословно реализовать такой обработчик?
(Напоминаю - система GUI-создана не вами и она навязывает способы обработки и интерфейсы событий. Изменения вы можете лишь вводить на уровне пользователя библиотеки, таких классов как MyFarme )
« Последнее редактирование: Мая 25, 2018, 01:07:10 pm от lastcross » Записан
da-nie
Full Member
***
Online Online

Сообщений: 211



Просмотр профиля
« Ответ #18 : Мая 25, 2018, 02:24:49 pm »

Цитировать
Когда-то рассуждал именно как вы.. после пришел к другому подходу

Что же изменило ваше мнение? Smiley

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

Вот тут сразу и возникает вопрос: я с GUI работал с WinAPI, с MFC, пару раз с Builder, с Photon в QNX. Что там происходит - GUI вызывает некую функцию, соответствующую событию. Скажем, нажали какую-то кнопку. О моей программе GUI не знает ничего, но это и нормально. Зачем же мне в эту функцию передавать какой-то ещё объект, если я могу этот объект сделать доступным в этой функции и так? Скажем, у меня будет некий глобальный класс-приложение (CMain), который является фасадом для всего остального. Я этот класс создам глобально и он в системе будет единственный глобальный элемент. В функции от GUI я просто сделаю extern CMain cMain и вызову cMain.PressButton(). А дальше CMain сделает всё, что нужно.

Цитировать
Поэтому мы в качестве обработчика передаем лямбда функцию с замыканием (контекст this).

Интересное решение. Cool Однако, непонятно, откуда этот самый this возьмётся. У GUI его точно нет. Значит, компилятор его прикладывает сам. Значит, для каждого объекта компилятор сам создал функцию обратного вызова, в которой сохранил this объекта, для которого она будет вызываться. Так?

Цитировать
А теперь внимание вопрос - каким иным способом вы бы смогли точно также элегантно и немногословно реализовать такой обработчик?

Но чем в таком случае не устроил вариант с перенаправлением события в объект MyFarme?
Записан

И день и ночь в пути
lastcross
Full Member
***
Offline Offline

Сообщений: 234


Просмотр профиля
« Ответ #19 : Мая 25, 2018, 07:56:34 pm »

Цитировать
Что же изменило ваше мнение? Smiley

Достаточно небольшого опыта работы с шаблонами, чтобы понять - что обобщенный код нужно стараться не усложнять. В обобщенном коде важнее не набор интерфейсов, а понимание что он делает и как, что именно он обобщает. Если класс обобщенный (не специализирован) - то любая его специализация (полная или частичная) может полностью не только изменить его "интерфейс" но и "суть" (хотя интерфейс останется таким же). Вот вам пример: std::vector<T> и его частичная специализация std::vector<bool> - это не пример того как надо делать, а скорее пример того что специализация может полностью "перевернуть" структуру шаблонного класса.
Это приводит к тому, что для шаблонных классов крайне желательно видеть на одной странице не только "интерфейс" но и его содержание (весьма вероятно что при просмотре вы углубитесь более чем требовалось на первый взгляд, разве что это не стандартная хорошо известная библиотека).

Цитировать
Вот тут сразу и возникает вопрос: я с GUI работал с WinAPI, с MFC, пару раз с Builder, с Photon в QNX. Что там происходит - GUI вызывает некую функцию, соответствующую событию. Скажем, нажали какую-то кнопку. О моей программе GUI не знает ничего, но это и нормально. Зачем же мне в эту функцию передавать какой-то ещё объект, если я могу этот объект сделать доступным в этой функции и так?
А у вас все объекты глобальны? Или существуют разные копии одного и того же типа объектов? У вас окно в единственном экземпляре состоящие из одной кнопки? Или бывают еще панели которые в один момент времени могут существовать несколько штук? Что в таком случае вы будете делать в обработчике? Ведь this захватывается не спроста. Это означает что я допускаю существование нескольких экземпляров фреймов и они могут делать сугубо свою обработку (в зависимости от данных которыми владеют эти экземпляры, а не какое-то глобальное окно).

Цитировать
Интересное решение. Cool Однако, непонятно, откуда этот самый this возьмётся

Замыкание - это обобщенный термин. Я использовал лямбду, так как она короче и элегантнее в примере (это не единственные ее плюсы). Но замыкание можно реализовать через функциональный объект. В этом случае this - надо будет хранить явно. Но в любом случае - этот механизм позволяет инкапсулировать связи обработчика от кода вызывающий этот обработчик.
Да, для лямбды можете считать что компилятор сам создаст некий "функциональный объект" и осуществит захват тех параметров которые вы укажете. В случае с функциональным объектом - вам руками придется все это писать (но выбора у вас нет, если вы ниже С++11 - в этом случае только функциональные объекты или boost).

Цитировать
Но чем в таком случае не устроил вариант с перенаправлением события в объект MyFarme?

Тем что:
  • Использование глобального объекта внутри обработчика - не подходит. MyFarme - это дочерное окно, экземпляров которых может быть много. Единственный экземпляр окна не в состоянии обработать событие так как не знает от какого экземпляра окна MyFarme брать состояние.
  • Указание непосредственно в качестве обработчика экземпляра MyFarme со ссылкой на его метод обработки - не очень удобен и более многословен. Для начала, я бы попросил привести такой пример, чтобы точно точно прийти к пониманию что мы говорим про одно и тоже. Но даже без примера минусы (кроме многословности) - придется расширять сам класс, добавлять методы или менять интерфейс, вводить зависимости внутри хедера, между классами. Все это как минимум засоряет класс (помните свою жажду "читаемости" =) ), ну и кроме того - увеличивает время компиляции.

и др.

Можно взять еще како-либо пример который не связан с GUI
Код:

void DataStorage::doFilter(const Filter& filter)
{
  size_t count = 0;
  auto regectIt = std::remove_if(mData.begin(), mData.end(), [&filter, &count](const auto& element)

      return filter->Contains(count++, element);
   );
  mData.erase(regectIt , mData.end());
}


DataStorage - класс хранящий какие-то данные (контейнер). Метод doFilter фильтрует хранимые данные в соответствии с переданным фильтром filter типа Filter
Специфика интерфейса Filter::Contains  такова, что она принимает решения на основании передаваемых данных а также позиции хранимых данных (допустим некоторые фильтры исключительно фильтруют по позиции, а некоторые исключительно по содержимому, а третьи так вообще по тому и другому). Filter не знает как считать позиции это знает DataStorage. В свою очередь DataStorage не знает как фильтровать - это знает Filter (который был создан извне). алгоритм std::remove_if не умеет хранить счетчики и другие объекты, ему нужен функциональный объект или неxто чему он может применить вызов operator(). Такой оператор не содержится в Filter (так построен его интерфейс, и может он даже не доступен для модификаций). Но на примере удивительным образом все работает! Без особых усилий получилось связать те вещи которые не связывались и при этом не пришлось модифицировать (существенно) ни один из типов.
Мы сейчас не станем обсуждать, что можно было бы частично избежать всего этого путем банального явного цикла for по причине, что:
  • Явный цикл будет многословным и даже не эффективным, а при определенной реализации даже и опасным
  • Мы рассматриваем упрощенный пример. В реальных условиях предусловия захвата могут быть гораздо сложнее и одними циклами можно не отделатся

Записан
da-nie
Full Member
***
Online Online

Сообщений: 211



Просмотр профиля
« Ответ #20 : Мая 25, 2018, 08:41:24 pm »

Цитировать
В обобщенном коде важнее не набор интерфейсов, а понимание что он делает и как, что именно он обобщает.

Но когда я пишу list<long> мне глубоко фиолетово, как там внутри это сделано - я туда внутрь даже смотреть не буду. И как бы так и должно быть для любых шаблонных классов. Для vector<bool> в данном случае я также не планирую заглядывать в реализацию. Да зачем бы мне это? Я загляну в описание библиотеки и там прочту о особенностях, подобной этой. Просто так быть не должно - это авторы совершенно напрасно сделали, нарушив принцип "знание принципов компенсирует недостаток фактов". Как и ожидалось, потеря унификации ни к чему хорошему не привела.

Цитировать
А у вас все объекты глобальны?

Нет, один. Он и является функцией main приложения по сути. Все остальные объекты живут внутри этого объекта.

Цитировать
У вас окно в единственном экземпляре состоящие из одной кнопки? Или бывают еще панели которые в один момент времени могут существовать несколько штук?

Внутри CMain могут существовать и классы панелей. Просто CMain знает, кому передать выполнение функции от интерфейса.

Цитировать
Это означает что я допускаю существование нескольких экземпляров фреймов и они могут делать сугубо свою обработку (в зависимости от данных которыми владеют эти экземпляры, а не какое-то глобальное окно).

Так даже и в MFC никто не мешает внутри главного класса окна создавать любые другие. При этом сообщения от интерфейса попадают в этот класс окна через карту сообщений. А дальше эти сообщения можно передать кому угодно.

Цитировать
Замыкание - это обобщенный термин.

Разумеется. Формально, даже вот этот глобальный cMain в функции обратного вызова является замыканием.

Цитировать
Но замыкание можно реализовать через функциональный объект. В этом случае this - надо будет хранить явно. Но в любом случае - этот механизм позволяет инкапсулировать связи обработчика от кода вызывающий этот обработчик.

Что-то мне подсказывает, что этот подход использован в каких-то новых фреймворках которых я не знаю (возможно, на C# - что-то много появилось программ на нём), и в тех, которые знаю я, он неприменим. Я не представляю, как к MFC или Qt, например, это вообще можно подключить. Smiley Вот в Win32API это может сработать - там функции обратного вызова задаются напрямую и очень было неудобно, что HWND присылается, а класс окна, который его и породил так просто не определить. Надо попробовать поиграться. Smiley

Цитировать
MyFarme - это дочерное окно, экземпляров которых может быть много. Единственный экземпляр окна не в состоянии обработать событие так как не знает от какого экземпляра окна MyFarme брать состояние.

Да, так, конечно, удобно. Smiley Это не требует сопоставления HWND и класса.

Цитировать
Указание непосредственно в качестве обработчика экземпляра MyFarme со ссылкой на его метод обработки - не очень удобен и более многословен. Для начала, я бы попросил привести такой пример, чтобы точно точно прийти к пониманию что мы говорим про одно и тоже.

Я имел в виду, вышеописанный метод с глобальным объектом MyFrame вместо CMain. В функции обратного вызова, разумеется, функцию класса записать нельзя.

Цитировать
В свою очередь DataStorage не знает как фильтровать - это знает Filter (который был создан извне).

То есть, всё это нужно только в том случае, когда хочется навесить новую функциональность на уже существующую, не меняя реализацию старой?
Записан

И день и ночь в пути
lastcross
Full Member
***
Offline Offline

Сообщений: 234


Просмотр профиля
« Ответ #21 : Мая 25, 2018, 11:39:27 pm »

Цитировать
Но когда я пишу list<long> мне глубоко фиолетово
По этому я и отметил - субъективно это. Мой опыт привел меня именно к своему выбору. У вас же свой опыт ).

Цитировать
Внутри CMain могут существовать и классы панелей. Просто CMain знает, кому передать выполнение функции от интерфейса.
В примере, он не может знать какому из объектов адресовать событие. Поэтому адресовать калбек к вашему объекту - некорректно.

Цитировать
Так даже и в MFC никто не мешает внутри главного класса окна создавать любые другие. При этом сообщения от интерфейса попадают в этот класс окна через карту сообщений. А дальше эти сообщения можно передать кому угодно.

Это потому что вы привязаны к такому механизму событий. Это было популярно в 90-е. На дворе 21 век. Замыкание позволяют избежать ситуации с двойной диспечерезацией (когда нужно передавать посреднику, который должен знать о всех обработчиках).
В Qt - есть свои механизмы сигнал-слот, построенный по схожему принципу. wxWidgets - по схожему принципу. Начиная с ++11 - большинство gui-библиотек переходят на схожий механизм, потому как это стало доступнее и практически дешево.

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


Цитировать
Я имел в виду, вышеописанный метод с глобальным объектом MyFrame вместо CMain. В функции обратного вызова, разумеется, функцию класса записать нельзя.
Почему же? Можно. Через std::bind в связке std::function (опять же в терминах С++11) или одноименными аналогами из boost::bind и boost::function(для до С++11)

Цитировать
То есть, всё это нужно только в том случае, когда хочется навесить новую функциональность на уже существующую, не меняя реализацию старой?
Это одно из ключевых объяснений. И при этом - немаловажное.

Еще один из примеров - некий метод должен рекурсивно что-то обойти и посчитать. Как известно, для рекурсивного обхода нужен метод входа, и метод рекурсивной операции перехода по итерациям (собственно обход). Таких методов может быть несколько (рекурсивно в зависимости от направления и критериев остановки/поиска). Заводить специальные методы не всегда нужно. Если учесть что лишь некоторые операции требуют такой рекурсии, то благодаря лямбдам можно не объявлять отдельные методы переходов (обходов), а реализовать их непосредственно внутри конкретных методов входа. Такие лямбды захватывают как условия выхода, счетчики, так и самих себя для возможности рекурсивно вызываться.

Надеюсь хотя бы малость мне удалось раскрыть "тайну использования" замыканий =)
« Последнее редактирование: Мая 26, 2018, 12:08:09 am от lastcross » Записан
da-nie
Full Member
***
Online Online

Сообщений: 211



Просмотр профиля
« Ответ #22 : Мая 26, 2018, 07:21:39 am »

Цитировать
В примере, он не может знать какому из объектов адресовать событие. Поэтому адресовать калбек к вашему объекту - некорректно.

Что-то я с таким не сталкивался. Если у меня есть форма, то я всегда знаю, от какого элемента и для кого будет это сообщение. Если же форма создаётся динамически, то элемент можно найти по его идентификатору/дескриптору.

Цитировать
На дворе 21 век.

Вот только на запрос "в чём написать графическое приложение для Windows для C++" ответ в поисковиках почему-то всё тот же MFC и Qt. Roll Eyes А запрос про применение замыканий приводит практически всегда к web-технологиям.

Цитировать
В Qt - есть свои механизмы сигнал-слот, построенный по схожему принципу.

А, ну до Qt5 я не дошёл. Smiley

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

Это несомненно. Smiley

Цитировать
Почему же? Можно. Через std::bind в связке std::function

Как я понимаю,  это такая же подмена истинной функции обратного вызова на нечто скрытое, создающееся компилятором с дополнительной информацией об объекте.
Кстати, по поводу functional.
То ли я что-то не понял, то ли VC2010 с functional работает странно. Вот я взял код из примера:
Код:
auto lambda_in=[](int x)
 {
  return([=](int y) {return(x+y);});
 };

 auto lambda_out=[](const function<int(int)>& f,int z)
 {
  return(f(z)*2);
 };
 auto answer=lambda_out(lambda_in(7),8);
Пример понятен (кроме const function<int(int)> - что за странная запись int(int)?).
Всё это студия компилирует вот так:

Цитировать
1>main.cpp(39): error C2664: 'int `anonymous-namespace'::<lambda2>::operator ()(const std::tr1::function<_Fty> &,int) const' : cannot convert parameter 1 from 'void' to 'const std::tr1::function<_Fty> &'
1>          with
1>          [
1>              _Fty=int (int)
1>          ]
1>          Expressions of type void cannot be converted to other types
1>main.cpp(39): fatal error C1903: unable to recover from previous error(s); stopping compilation
1>
1>Build FAILED.

Цитировать
Надеюсь хотя бы малость мне удалось раскрыть "тайну использования" замыканий =)

Да, спасибо за информацию. Smiley К сожалению, я не знаю абсолютно никого, кто бы знал для чего всё это нужно и как этим пользоваться на практике. Поиск же в инете приводит к вороху бесполезных статей (как и в случае с unit-тестами). Smiley
« Последнее редактирование: Мая 26, 2018, 07:31:18 am от da-nie » Записан

И день и ночь в пути
lastcross
Full Member
***
Offline Offline

Сообщений: 234


Просмотр профиля
« Ответ #23 : Мая 26, 2018, 01:34:57 pm »

Цитировать
Что-то я с таким не сталкивался. Если у меня есть форма, то я всегда знаю, от какого элемента и для кого будет это сообщение. Если же форма создаётся динамически, то элемент можно найти по его идентификатору/дескриптору.

Представим такую иерархию владения: MainWnd->UseForms->SelectorForm->Fields->SomeButton
Те что слева содержат тех что справа, при это типы окон (контролов) справа могут существовать не в единственно экземпляре (вообразите такой сложный продукт уровня Photoshop или IDE). Дальше возможна ситуация, когда конкретную кнопку SomeButton нужно обработать в соответствии с состоянием контрола который ею владеет (непсредственого владельца). Я так понимаю что вы предлагаете связывать обработчик с единственным экземпляром MainWnd. Допустим GUI система в связке со своей системой событий предоставляет идентификатор окна от которого пришло событие (хотя это не обязательно должно быть в общем случае - мы ведь не только рассматриваем GUI, а вообще концепцию передачи сообщений некой системе и ее обработки). Что будет делать MainWnd обработчик? Как он сопоставит по Handle-окна конкретный экземпляр Fields среди нескольких? Как вы приведете вообще это окно к типу Fields, чтобы начать делегирование обработки события? Вы набросайте это конструкцию и по-моему сразу станет все очевидно, где краткость и ясность - а где лишние телодвижения, лес в котором можно заблудится и нагородить ошибок.
Вдобавок, я не отметил тот факт - что замыкания так же используются для передачи не только ссылок на объект хранимый состояния, но и копии состояний. Это значит что на момент регистрации обработчика мы знаем с каким контекстом надо обрабатывать событие. То есть контекст обработки важен на момент регистрации, а не в момент создания события(связь окон с потоками, где окно может умереть раньше чем начать обрабатываться сам поток). Все это благодаря захвату с копированием. Даже на примере синхронного вызова - создаем множество кнопок, по нажатию которых выполнятся операции с выбранным файлом. Основываясь на патернах Command, для каждого обработчика нажатия кнопки захватываем по копии созданый объект команд. Все что нужно в обработчике - просто исполнить команду. Никакой явной диспечерезации в коде и размазанной логики (проверки что за кнопка - как соотнести ее с командой, где хранить/откуда получить дескриптор выбранного файла и т.д.)

Цитировать
Как я понимаю,  это такая же подмена истинной функции обратного вызова на нечто скрытое, создающееся компилятором с дополнительной информацией об объекте.
Почему скрытое? Это полноценный функциональный объект, который ведет себя как функция но способен хранить дополнительные параметры требуемые функции. Любда - вот это "мистика" создаваемая компилятором, так как некоторые механизмы скрываются на этапе компиляции (тип лямбды, ее расположение в коде и подобное). Функциональный объект он же как все объекты.

Цитировать
Кстати, по поводу functional.
То ли я что-то не понял, то ли VC2010 с functional работает странно. Вот я взял код из примера:
....
Всё это студия компилирует вот так:
...

А попробуйте вот так
Основная причина - ограничение стандарта ++11. В отличии от ++14 (вот пример), лямбда в ++11 не умеет автоматически выводить тип для своих аргументов (ну или умеет с ограничениями). Это означает, что недостаточно просто передать в место функтора лямбду. Лямбду надо преобразовать явно. То есть построить функтор на основе лямбды и передать

Цитировать
Пример понятен (кроме const function<int(int)> - что за странная запись int(int)?).
Что расположено до круглых скобочек - тип возвращаемого значения. Что внутри типы параметров. Про это можно почитать в документации  - ссылку я давал выше
« Последнее редактирование: Мая 26, 2018, 04:24:44 pm от lastcross » Записан
da-nie
Full Member
***
Online Online

Сообщений: 211



Просмотр профиля
« Ответ #24 : Мая 26, 2018, 05:09:51 pm »

Цитировать
справа могут существовать не в единственно экземпляре (вообразите такой сложный продукт уровня Photoshop или IDE).

Так это всё решается механизмом диспетчеризации. Да, он будет сложным.

Цитировать
Дальше возможна ситуация, когда конкретную кнопку SomeButton нужно обработать в соответствии с состоянием контрола который ею владеет (непсредственого владельца).

Да я не спорю, что так гораздо удобнее. Проблема только в том, что непосредственное подключение функций обратного вызова, вообще говоря, достаточно низкоуровневая часть, которую обычно скрывают. В том же photon я могу назначить обработчик, но это будет далеко не лямбда. В результате возникает вопрос - что нужно взять (кроме Qt5), чтобы это  применять на практике. Вот на чём сейчас десктопные приложения пишут (ну, кроме net)? Я не знаю. Вот запускаю я DrWeb - на чём написан его интерфейс? Без понятия.

Цитировать
Почему скрытое?

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

Цитировать
А попробуйте вот так

Нет, так тоже не хочет. Ни основной, ни альтернативный. "1>main.cpp(46): error C2440: 'static_cast' : cannot convert from 'void' to 'InFunction_t'".
Но на самом деле, я даже не знаю, установлен у меня патч для Си++ 11 или нет. Я его сам точно не ставил. Я даже не помню, откуда у меня данный дистрибутив студии. Просто попробовал некоторые операторы и они заработали. Но они могут быть реализованы и до появления стандарта.

Цитировать
Что расположено до круглых скобочек - тип возвращаемого значения. Что внутри типы параметров

А, понятно. Smiley Это мне в голову не пришло.
Хотя непонятно, как такой шаблон может существовать. Ведь это же параметр. Разве можно сделать так: vector<int(int)>? Нельзя - непонятно, что это. А function<int(int)> создать можно. Каким образом так получается-то?
Записан

И день и ночь в пути
lastcross
Full Member
***
Offline Offline

Сообщений: 234


Просмотр профиля
« Ответ #25 : Мая 26, 2018, 05:27:16 pm »

Цитировать
Так это всё решается механизмом диспетчеризации. Да, он будет сложным.
О чем и речь! И не просто сложным, а через чур сложным - с построением иерархии наследования и обилием кастования.

Цитировать
Да я не спорю, что так гораздо удобнее. Проблема только в том, что непосредственное подключение функций обратного вызова, вообще говоря, достаточно низкоуровневая часть, которую обычно скрывают.

Берите связку boost::function и boost::bind пишите свои функторы и без ++11 уже можете передать в куда угодно свой кастомный объект с захватом вместо привычного всеми калбек методов.

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

Это магия называется operator() и она не такая уж магия (компилятор за вас ее не строит). А вот с лямбдой дело стоит иначе - компилятор за вас формирует истинный тип лямбды - вы даже его получить не можете в явном виде.

Цитировать
Нет, так тоже не хочет. Ни основной, ни альтернативный. "1>main.cpp(46): error C2440: 'static_cast' : cannot convert from 'void' to 'InFunction_t'".
Но на самом деле, я даже не знаю, установлен у меня патч для Си++ 11 или нет. Я его сам точно не ставил. Я даже не помню, откуда у меня данный дистрибутив студии. Просто попробовал некоторые операторы и они заработали.
Скорее всего у вас не поддерживается ++11

Цитировать
Каким образом так получается-то?

Ну а тут магия использование средств ++11 и макросов.
Записан
da-nie
Full Member
***
Online Online

Сообщений: 211



Просмотр профиля
« Ответ #26 : Мая 26, 2018, 07:26:05 pm »

Цитировать
О чем и речь! И не просто сложным, а через чур сложным - с построением иерархии наследования и обилием кастования.

Да нет, просто каждый класс наделяется списком того, чем он владеет. А дальше обычная диспетчеризация по маршруту графа с опросом "это твоё?" и остановкой, когда найден владелец. На это можно пойти в GUI, так как в 99.9999999999% случаев скорость реакции это не снизит никак. В конце концов, как же писалось всё то ПО до С++ 11?

Цитировать
Берите связку boost::function и boost::bind пишите свои функторы и без ++11 уже можете передать в куда угодно свой кастомный объект с захватом вместо привычного всеми калбек методов.

С boost я не знаком вовсе (и насколько я слышал, там что-то очень монструозное). Smiley Но я о чём речь-то веду? Привязка функции обратного вызова в GUI библиотеках является низким уровнем, до которого пользователь, обычно, не допускается. В той же MFC всё это скрыто. Я не могу переопределить в MFC функцию окна оставаясь в MFC - мне для этого придётся регистрировать свой класс окна со всеми вытекающими.

Цитировать
А вот с лямбдой дело стоит иначе

Так мы о лямбда-функциях и говорим. У вас в примере они и есть.
Записан

И день и ночь в пути
Страниц: 1 [2]
  Печать  
 
Перейти в: