Страниц: 1 ... 8 9 [10]
 91 
 : Мая 25, 2018, 10:10:13 pm 
Автор qnx_user - Последний ответ от da-nie
Тогда другие вопросы:

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

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

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

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

 94 
 : Мая 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 &.

Спасибо!

 95 
 : Мая 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 (который был создан извне).

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

 96 
 : Мая 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 по причине, что:
  • Явный цикл будет многословным и даже не эффективным, а при определенной реализации даже и опасным
  • Мы рассматриваем упрощенный пример. В реальных условиях предусловия захвата могут быть гораздо сложнее и одними циклами можно не отделатся


 97 
 : Мая 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?

 98 
 : Мая 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 )

 99 
 : Мая 25, 2018, 12:35:31 pm 
Автор DavASko - Последний ответ от da-nie
Цитировать
Это не "стандартный" подход

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

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

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

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

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

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

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

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

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

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

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

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

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

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

 100 
 : Мая 25, 2018, 11:08:15 am 
Автор DavASko - Последний ответ от lastcross
Цитировать
Так это стандартный подход. Undecided

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

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

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

Если речь только про замыкания - то для того чтобы удерживая контекст вызвать обработку чего либо (любых переданных данных на обработку) с учетом того контекста который удерживаем.
То есть, вы принимаете решение что нужно сформировать/собрать "подходящий" контекст для обработки ожидаемых событий (событие может еще как не наступить или же уже наступило, что заставило вас формировать контекст). Дальше, по приходу события вы подготавливаете параметры либо же знаете кому можете делегировать формирование таких параметров. Ну и последний этап - отдаете на обработку "замыканию", которое знает как параметры обрабатывать с учетом удерживаемого ею контекста. Это так, упрощенно и своими словами.
Ближайший аналог - функторы (доступен с лохматых времен), объекты некого типа с перегруженной операцией operator().  Контекст ей может передаваться (такой функтор может как содержать внутри себя данные), а может не содержать (внутри функтора данные не содержатся).
Или же лямбды - более предпочтительнее чем функторы по ряду причин. Например потому что не требуют описания отдельного типа (что крайне многословно при использовании функторов). Умеют захватывать контекст автоматически, вдобавок поваляют выводить типы (auto и decltype) - что невероятно круто (относительно последнего), когда пишешь обработку обобщенную (а замыкание - это именно обработка и как правило используемая не однократно/обобщенно).

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