Страниц: 1 ... 8 9 [10]
 91 
 : Мая 25, 2018, 11:39:27 pm 
Автор DavASko - Последний ответ от lastcross
Цитировать
Но когда я пишу list<long> мне глубоко фиолетово
По этому я и отметил - субъективно это. Мой опыт привел меня именно к своему выбору. У вас же свой опыт ).

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

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

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

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


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

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

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

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

 92 
 : Мая 25, 2018, 10:36:01 pm 
Автор qnx_user - Последний ответ от qnx_user
1.
Код:
-T level Set privity level of load file (0 .. 3 with default 3)

2. Система QNX4, тема создана в соответствующей ветке не с проста Wink

3. Отладчиком посмотреть могу, но он консольный (псевдографика) и не юзал его никогда, т.е. надо разбираться, asm код никогда не генерировал, нужно посмотреть в ключах компиляции. Хотя могу в IDA Pro посмотреть Smiley Только вызовы стандартные не думаю что это что-то даст если честно. Тут надо знать что происходит внутрях функции _getvideoconfig(), потому что если его убрать то все без проблем работает.

 93 
 : Мая 25, 2018, 10:10:13 pm 
Автор qnx_user - Последний ответ от da-nie
Тогда другие вопросы:

Цитировать
P.S. программу собирал c ключом -T1.

Что он означает?  Roll Eyes
Что у вас за система? Вы всё это в QNX4 делаете (я её никогда не видел, кроме демонстрационной дискеты, потому и не в курсе, похож ваш код на код для этой ОС или нет -а так, на MS-DOS похож)? Есть ли возможность посмотреть отладчиком, что за код создаётся и какие функции вызываются?

 94 
 : Мая 25, 2018, 09:52:38 pm 
Автор qnx_user - Последний ответ от qnx_user
Возможно, у вас другие части программы (это ведь не вся программа, верно? int10h вы же где-то вызывали.) что-то повреждают в процессе работы и к моменту вызова выполняется вовсе не указанный код. Попробуйте прогнать программу статическим анализатором, например, cppcheck.
Эта конкретная программа так работает. Специально написал чистый тест. int10 запускался из консоли. Можете сами попробовать. Roll Eyes

 95 
 : Мая 25, 2018, 09:38:42 pm 
Автор qnx_user - Последний ответ от da-nie
Возможно, у вас другие части программы (это ведь не вся программа, верно? int10h вы же где-то вызывали.) что-то повреждают в процессе работы и к моменту вызова выполняется вовсе не указанный код. Попробуйте прогнать программу статическим анализатором, например, cppcheck.

 96 
 : Мая 25, 2018, 09:33:31 pm 
Автор qnx_user - Последний ответ от qnx_user
Здравствуйте!

Столкнулся со странной проблемой, есть тестовый код:

Код:
#include <stdio.h>
#include <graph.h>
#include <malloc.h>

static struct videoconfig vc;

int main()
{
void *p;

p = malloc( 4 );
if ( NULL == p ) {
printf( "Error allocating memory\n" );
}
free( p );

_getvideoconfig( &vc );

p = malloc( 512 );
if ( NULL == p ) {
printf( "Error allocating memory\n" );
}
free( p );

return 0;
}

после вызова _getvideoconfig() функция malloc() отказывается выделять блок памяти. Если поиграться с размерами выделяемого блока в вызовах malloc(), то можно заметить что при различных комбинациях 2-й вызов функции malloc() отрабатывает по разному. Подскажите с чем связано такое поведение?

P.S. программу собирал c ключом -T1. Предварительно запустил int10 &.

Спасибо!

 97 
 : Мая 25, 2018, 08:41:24 pm 
Автор DavASko - Последний ответ от da-nie
Цитировать
В обобщенном коде важнее не набор интерфейсов, а понимание что он делает и как, что именно он обобщает.

Но когда я пишу 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 (который был создан извне).

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

 98 
 : Мая 25, 2018, 07:56:34 pm 
Автор DavASko - Последний ответ от lastcross
Цитировать
Что же изменило ваше мнение? 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 по причине, что:
  • Явный цикл будет многословным и даже не эффективным, а при определенной реализации даже и опасным
  • Мы рассматриваем упрощенный пример. В реальных условиях предусловия захвата могут быть гораздо сложнее и одними циклами можно не отделатся


 99 
 : Мая 25, 2018, 02:24:49 pm 
Автор DavASko - Последний ответ от da-nie
Цитировать
Когда-то рассуждал именно как вы.. после пришел к другому подходу

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

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

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

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

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

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

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

 100 
 : Мая 25, 2018, 01:04:50 pm 
Автор DavASko - Последний ответ от lastcross
Цитировать
Да читаемость я получаю, читаемость. 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 )

Страниц: 1 ... 8 9 [10]