Re[6]: Возврат ошибок из недр вложенных вызовов
От: Евгений Музыченко Франция https://software.muzychenko.net/ru
Дата: 05.11.20 19:44
Оценка:
Здравствуйте, Muxa, Вы писали:

M>Чот не похоже что это будет вызываться сотни тысяч раз в секунду.


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

M>Брось исключение. Производительность в этом случае уже не важна.


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

Кстати, C++RT для виндового ядра, судя по всему, до сих пор не поддерживает плюсовые исключения. Но в ядерном коде я пока обхожусь обычными результатами.
Re[2]: Возврат ошибок из недр вложенных вызовов
От: Евгений Музыченко Франция https://software.muzychenko.net/ru
Дата: 05.11.20 19:48
Оценка: +1
Здравствуйте, reversecode, Вы писали:

R>в предвери наступающего 2021 года

R>Евгений все так же не желал осваивать С++ новее стандарта С++98 ...

Обижаете, C++03. Я давно хочу C++11, но последние студии, в компиляторах которых он нормально поддерживается, раздражают своими тормозами.

R>и не хотел инвестировать свое время в изучение чего то нового ...


А что нового изучать, когда я до сих пор делаю в основном ядерный код?
Re[7]: Возврат ошибок из недр вложенных вызовов
От: so5team https://stiffstream.com
Дата: 05.11.20 20:03
Оценка: -1
Здравствуйте, Евгений Музыченко, Вы писали:

EM>Как туда можно прикрутить move-семантику, я в принципе не представляю. Если знаете секрет — поделитесь, пожалуйста.


Может как-то так, если уж вы принципиально не включаете оптимизацию в компиляторе:
std::vector<std::string> child() {...}

std::vector<std::string> parent() {
  ...
  auto r = std::move(child()); // (1)
  ...
  return std::move(r); // (2)
}

Но для современных компиляторов этот явный move в точке (2) -- это как раз принудительная пессимизация. А move в точке (1) не имеет смысла.

S>>Так вы мало того, что рил-тайм код в отладчике умудряетесь пошагово отлаживать


ЕМ>Я уже объяснил, что не весь реалтайм, а только соответствующие части. Что именно Вас в этом удивляет?


Удивляет, что есть человек, который может пошагово отлаживать реалтаймовый код в отладчике.

S>>так еще и одним взглядом определяете во что современные шаблонные кружева транслируются?


ЕМ>"Современные шаблонные кружева" как-то влияют на алгоритмы кодогенерации, и есть примеры, когда для шаблонного класса создается более эффективный код, чем для эквивалентного нешаблонного?


Современные шаблонные кружева влияют на то, что видит человек в коде. Там может быть шаблонный класс, который наследуется от кучи других шаблонных классов, которые для какого-то случая имеют специализации и все это схлопывается во что-то благодаря empty base optimization.

Вы, походу, все это можете увидеть просто глазом. Впрочем, если вам под силу пошагово отлаживать реалтаймовый код, то вам и это по плечу.

S>>Реальное время -- оно же такое, не критичное.


ЕМ>Вы твердо уверены в том, что правильно и полностью понимаете смысл понятия "реальное время"?


До ваших откровений был уверен. Но теперь сильно сомневаюсь.
Отредактировано 05.11.2020 20:48 so5team . Предыдущая версия .
Re[3]: Возврат ошибок из недр вложенных вызовов
От: reversecode google
Дата: 05.11.20 20:11
Оценка: :)
студия это что, редактор для текстовых файлов ?
пишите как все нормальные программисты — far+wdk/wsdk+опционально clang
Re[3]: Возврат ошибок из недр вложенных вызовов
От: alexanderfedin США http://alexander-fedin.pixels.com/
Дата: 05.11.20 22:27
Оценка:
Здравствуйте, Евгений Музыченко, Вы писали:
ЕМ>Я ж специально пояснил, что не все отказы функций нижнего уровня являются фатальными для верхних. В ряде случаев нет другого способа что-то сделать, кроме как тупо перебрать несколько десятков-сотен наборов параметров, последовательно вызывая функции нижнего уровня с каждым из них. Если при каждом вызове функция будет выбрасывать исключение, то накладные расходы станут чрезмерными, а главное — слабопредсказуемыми. Некоторые функции вызываются в достаточно жестком (максимум сотни микросекунд на всю обработку) реалтайме, поэтому исключения там допустимы лишь в фатальных случаях.

У IBM в их библиотеке поддержки юникода было так:
void foo(int *error, char a, float b, short c)
{
   if (*error) return;
   // do some stuff
   if (/* condition */)
   {
      *error = ERROR_IO_FAIL;
      return;
   }
   // do some stuff
}

void bar(int *error, char a)
{
   if (*error) return;
   // do some stuff
   foo(error, a, 2.7f, 123); if (*error) return;
   if (/* condition */)
   {
      *error = ERROR_IO_FAIL;
      return;
   }
   foo(error, a, 3.14f, 456); if (*error) return;
   // do some stuff
}

void main()
{
   int error;
   // do some stuff
   bar(&error, 'X'); if (*error) return;
   bar(&error, 'Y'); if (*error) return;
   // do some stuff
}
Respectfully,
Alexander Fedin.
Re: Возврат ошибок из недр вложенных вызовов
От: bnk СССР http://unmanagedvisio.com/
Дата: 05.11.20 23:06
Оценка:
Здравствуйте, Евгений Музыченко, Вы писали:

ЕМ>Типичная ситуация: функция верхнего уровня вызывает функцию более низкого уровня, та — следующую и т.п., и где-то на N-м уровне очередной вызов возвращает ошибку. Если это что-то уникальное, то можно вернуть наверх (хоть через обычные return, хоть через исключения) уникальный код и/или описание. Но если ошибка общего характера (нехватка ресурсов, отказ в доступе, разрыв соединения и т.п.), то неплохо бы вернуть наверх более подробную информацию, которую можно показать пользователю, чтобы он имел более-менее адекватное представление о проблеме.


ЕМ>Например, в винде издавна принято возвращать отовсюду HRESULT. В результате, например, Windows Update при любой проблеме тупо выдает "failed" вместе с HRESULT, и даже по логам не сразу определишь, что и где обломалось. А программы на фреймворках типа .NET, наоборот, могут столь же тупо вывалить пользователю раскрутку стека, из которой он тоже мало что поймет, и которую таки лучше писать в лог. Хочется выдавать нечто промежуточное — не абстрактный "access denied", но и не историю вложенных вызовов в чистом виде.


ЕМ>Возникает соблазн завести какой-нибудь универсальный класс Result, и в процессе возврата изнутри наружу добавлять в объект уточнения, если необходимо. Но если возвращать его обычным образом, через return, то RVO/NRVO в отладочном режиме [практически] не используется, возвращаемые объекты (где будут минимум сотни байт) будут многократно копироваться, а функции нижнего уровня могут вызываться и сотни-тысячи раз в секунду. По той же причине не всегда годятся исключения — функции нижнего уровня не всегда возвращают ошибки, фатальные для верхних уровней.


ЕМ>Другой вариант — использовать TLS, как в винде для GetLastError. А как с этим в Linix/MacOS/Android/iOS?


ЕМ>Вообще, какие способы возврата уточненных результатов из многократно вложенных вызовов ныне считаются кошерными?


Если исключения слишком дороги, то вроде ничего кроме кода возврата не остается.
Как вариант видел много где, в том числе у Microsoft, паттерн "on error goto hell" для всякой низкоуровневой фигни, это код возврата с goto на очистку. пример

А так в нормальной практике это конечно исключения.
Вроде правила простые — если обработчик знает, что делать с исключением — обрабатывает, иначе пропускает наверх, возможно дописывая к нему какую-то полезную инфу (уточнение).
Отредактировано 05.11.2020 23:14 bnk . Предыдущая версия . Еще …
Отредактировано 05.11.2020 23:12 bnk . Предыдущая версия .
Re[3]: Возврат ошибок из недр вложенных вызовов
От: varenikAA  
Дата: 06.11.20 01:15
Оценка:
Здравствуйте, Евгений Музыченко, Вы писали:

ЕМ>Здравствуйте, varenikAA, Вы писали:


AA>>лиспы не пользуются популярностью в коммерческой разработке


ЕМ>В лиспах вопрос быстродействия и накладных расходов стоит на самых последних позициях.


Ну, вот тут показано, что возможна Реализация Common Lisp Condition System на C#
Что если реализовать на плюсах? Сам я в них ни бум-бум.
☭ ✊ В мире нет ничего, кроме движущейся материи.
Re[2]: Возврат ошибок из недр вложенных вызовов
От: netch80 Украина http://netch80.dreamwidth.org/
Дата: 06.11.20 06:42
Оценка: +1
Здравствуйте, so5team, Вы писали:

S>Кого-то интересует скорость работы в отладочном режиме?


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

Во-вторых, в остальном умные вещи пишете, а тут — такую древнюю наркоманию. Все эти Debug/Release — концептуальные костыли для самых примитивных требований. В современном серьёзном продукте 95% кода должно компилироваться с максимумом жёсткости против ошибок разработки даже на том, что называлось Release, 1-2% критического пути — вылизано с максимумом хитрых оптимизаций даже при "Debug", и только оставшиеся несколько процентов могут хоть как-то зависеть от режима сборки. И режимы должны выставляться не для кода в целом, а гранулироваться до файла, функции или даже блока кода. Это на юзерленде, но к ядру может ещё больше относиться. Конечно, отрасль страшно консервативна и будет ещё 20 лет тормозить, но если вы где-то не видите возможности регулировать хотя бы оптимизации под конкретную функцию — самое время начинать бить ногами, чтобы авторы компилятора/среды проснулись.
The God is real, unless declared integer.
Re[3]: Возврат ошибок из недр вложенных вызовов
От: netch80 Украина http://netch80.dreamwidth.org/
Дата: 06.11.20 06:46
Оценка:
Здравствуйте, Евгений Музыченко, Вы писали:

N>>unique_ptr/shared_ptr не поможет?


ЕМ>Как оно может помочь само по себе?


Хранишь под таким указателем созданный объект ошибки. Набиваешь параметрами. Даже при проблемах с RVO максимум цены — это увеличение и уменьшение счётчика ссылок.

ЕМ>>>Другой вариант — использовать TLS, как в винде для GetLastError. А как с этим в Linix/MacOS/Android/iOS?

N>>Отлично в них с TLS.
ЕМ>Во всех?

В названных — да. Если у вас есть причины сомневаться — опубликуйте их.
Уточнение: я про юзерленд. В ядре TLS нет совсем, и не будет.
The God is real, unless declared integer.
Re[3]: Возврат ошибок из недр вложенных вызовов
От: so5team https://stiffstream.com
Дата: 06.11.20 06:58
Оценка: -1
Здравствуйте, netch80, Вы писали:

S>>Кого-то интересует скорость работы в отладочном режиме?


N>Во-первых, как уже сказано, и в отладочном режиме скорость может быть важна, и критически важна. И тогда может и отладчик включаться (но лучше — в неинтерактивном режиме, а, например, со скриптами на точки останова).


Остается позавидовать тем, у кого в Debug режиме производительность достаточная для решения их задач. И кто готов ходить по граблям "в Debug все работает, а в Release какие-то странные вещи происходят" просто потому, что без отладчика ни на что не годен.

N>Во-вторых, в остальном умные вещи пишете, а тут — такую древнюю наркоманию.


Я тут уже после первого упоминания об отладке реалтайм кода в отладчике перестал всерьез что-либо писать.
Re[3]: Возврат ошибок из недр вложенных вызовов
От: sergii.p  
Дата: 06.11.20 07:17
Оценка:
Здравствуйте, Евгений Музыченко, Вы писали:

ЕМ>Поглядел на реализации — выглядит чересчур монструозно. Для программ, не считающих ресурсов, сгодится, но мне нужно что-то более предсказуемое и экономичное.


std::expected можно заменить обычным
template<typename Res> 
using expected = std::variant<Res, std::exception_ptr>;


монадическая обработка ошибок тоже наворачивается легко

template<typename Functor>
struct action { Functor f; }

template<typename Functor>
auto map(Functor&& f)
{
    return action{std::forward<Functor>(f)};
}

template<typename Res> 
auto operator | (expected<Res> const& ex, action const& a)
{
    using Ret = decltype(a.f(std::declval<Res>()));
    return std::holds_alternative<Res>(ex)
        ? expected<Ret>{ a.f(std::get<0>(ex)) }
        : expected<Ret>{ std::get<1>(ex) };
}

// использование
const bool isEmptyLogin = getUserByHttp()
    | map(getLoginByUser)
    | map([](auto const& v) { return v.empty(); })
Re[4]: Возврат ошибок из недр вложенных вызовов
От: so5team https://stiffstream.com
Дата: 06.11.20 07:28
Оценка:
Здравствуйте, sergii.p, Вы писали:

ЕМ>>Поглядел на реализации — выглядит чересчур монструозно. Для программ, не считающих ресурсов, сгодится, но мне нужно что-то более предсказуемое и экономичное.


SP>std::expected можно заменить обычным

SP>
SP>template<typename Res> 
SP>using expected = std::variant<Res, std::exception_ptr>;
SP>


К сожалению, expected не выражается через variant, если и нормальный результат, и код ошибки представляется одним и тем же типом. Например, int-ом:
expected<int, int> do_something() {...}

Ну и плюс удобный (в отсутствии паттерн-матчинга в C++) интерфейс expected (operator bool, operator*, error()), плюс полезная свободная функция make_unexpected.
Re: Возврат ошибок из недр вложенных вызовов
От: Коваленко Дмитрий Россия http://www.ibprovider.com
Дата: 06.11.20 07:45
Оценка: +2
Здравствуйте, Евгений Музыченко, Вы писали:

ЕМ>Типичная ситуация: функция верхнего уровня вызывает функцию более низкого уровня, та — следующую и т.п., и где-то на N-м уровне очередной вызов возвращает ошибку. Если это что-то уникальное, то можно вернуть наверх (хоть через обычные return, хоть через исключения) уникальный код и/или описание. Но если ошибка общего характера (нехватка ресурсов, отказ в доступе, разрыв соединения и т.п.), то неплохо бы вернуть наверх более подробную информацию, которую можно показать пользователю, чтобы он имел более-менее адекватное представление о проблеме.


1. [Firebird]: I/O error during "WriteFile" operation for file "D:\DATABASE\RAM\IBP_TEST_FB30_D3_2.GDB"
Error while trying to write to file
Неверный дескриптор.
2. [LCPI.IBProvider.5]: Ошибка подтверждения транзакции. Транзакция будет отменена.
3. [Firebird]: internal Firebird consistency check (can't continue after bugcheck)
4. [LCPI.IBProvider.5]: Ошибка отката транзакции.

По неизвестной причине зафиксировать транзакцию не удалось. Данная транзакция была отменена.
COM Error Code: XACT_E_COMMITFAILED


Исключение (на базе std::exception) возит коллекцию описаний проблемы.

Каждый уровень (в catch-секциях) может добавить свое описание к произошедшей проблеме.

... уже более 18-ти лет как
-- Пользователи не приняли программу. Всех пришлось уничтожить. --
Re[5]: Возврат ошибок из недр вложенных вызовов
От: sergii.p  
Дата: 06.11.20 08:19
Оценка:
Здравствуйте, so5team, Вы писали:

S>К сожалению, expected не выражается через variant, если и нормальный результат, и код ошибки представляется одним и тем же типом. Например, int-ом:

S>
S>expected<int, int> do_something() {...}
S>


вариант 1: можно инициализировать через in_place_index
вариант 2: использовать для ошибки свой враппер

template<typename T>
struct error
{
    T val;
}

template<typename Res, typename Err = std::exception_ptr> expected = std::variant<Res, error<Err>>;



S>Ну и плюс удобный (в отсутствии паттерн-матчинга в C++) интерфейс expected (operator bool, operator*, error()), плюс полезная свободная функция make_unexpected.


всё это реализуется свободными функциями по мере надобности. Сам же Страуструп из core guidelines вещает, что предпочитайте свободные функции
Вообще, конечно, вы отчасти правы.
Если хочется, можно пойти дальше. Использовать union.
template<typename Val, typename Err>
struct expected_helper
{
    union
    {
    Val v;
    Err e;
    }
    bool isError;
}

и уже писать свой класс expected со всякими прочими операторами.
Re[6]: Возврат ошибок из недр вложенных вызовов
От: so5team https://stiffstream.com
Дата: 06.11.20 08:39
Оценка:
Здравствуйте, sergii.p, Вы писали:

SP>Если хочется, можно пойти дальше. Использовать union.

SP>
SP>template<typename Val, typename Err>
SP>struct expected_helper
SP>{
SP>    union
SP>    {
SP>    Val v;
SP>    Err e;
SP>    }
SP>    bool isError;
SP>}
SP>


И решать вопросы с типами Val/Err, у которых нетривиальные деструкторы и/или конструкторы/операторы копирования/перемещения.

SP>и уже писать свой класс expected со всякими прочими операторами.


В библиотеках вроде expected_lite это все уже сделано. И вряд ли ТС сможет сделать это лучше.
Re[2]: Возврат ошибок из недр вложенных вызовов
От: Евгений Музыченко Франция https://software.muzychenko.net/ru
Дата: 06.11.20 08:42
Оценка:
Здравствуйте, bnk, Вы писали:

bnk>Как вариант видел много где, в том числе у Microsoft, паттерн "on error goto hell" для всякой низкоуровневой фигни, это код возврата с goto на очистку.


Это работает в рамках отдельной функции — я для этого использую do { } while (false) с break, и давно зол на MS за то, что их __try/__finally работает только через SEH.
Re[3]: Возврат ошибок из недр вложенных вызовов
От: Qulac Россия  
Дата: 06.11.20 09:23
Оценка:
Здравствуйте, Евгений Музыченко, Вы писали:

ЕМ>Здравствуйте, kaa.python, Вы писали:


KP>>Не очень понял, чем исключения для такой цели плохи.


ЕМ>Я ж специально пояснил, что не все отказы функций нижнего уровня являются фатальными для верхних. В ряде случаев нет другого способа что-то сделать, кроме как тупо перебрать несколько десятков-сотен наборов параметров, последовательно вызывая функции нижнего уровня с каждым из них. Если при каждом вызове функция будет выбрасывать исключение, то накладные расходы станут чрезмерными, а главное — слабопредсказуемыми. Некоторые функции вызываются в достаточно жестком (максимум сотни микросекунд на всю обработку) реалтайме, поэтому исключения там допустимы лишь в фатальных случаях.


А тут нет "серебренной пули", каждый случай нужно рассматривать отдельно, часто лучше всего это подойти к проблеме чисто формально, т.е. если у нас система может реагировать на ошибки двумя способами, то делаем два исключения, что бы выбор действия происходил в блоке try — catch
Программа – это мысли спрессованные в код
Re[3]: Возврат ошибок из недр вложенных вызовов
От: reversecode google
Дата: 06.11.20 10:23
Оценка:
ЕМ>А что нового изучать, когда я до сих пор делаю в основном ядерный код?

http://www.zer0mem.sk/?p=517
Re[3]: Возврат ошибок из недр вложенных вызовов
От: Евгений Музыченко Франция https://software.muzychenko.net/ru
Дата: 06.11.20 10:30
Оценка:
Здравствуйте, netch80, Вы писали:

N>И режимы должны выставляться не для кода в целом, а гранулироваться до файла, функции или даже блока кода. Это на юзерленде, но к ядру может ещё больше относиться. Конечно, отрасль страшно консервативна и будет ещё 20 лет тормозить


Самое странное, когда тормозят те части отрасли, которые работают прежде всего сами на себя. Вот GNU делает gcc, которым собирается, в том числе, и линуксовое ядро — и наделали оптимизаций на любой вкус и цвет. А в MS VC++ периодически натыкаюсь на очевидные ляпы вроде этого
Автор: Евгений Музыченко
Дата: 25.12.19
. Но тут у них, безусловно, нет обратной связи — само ядро до сих пор на чистом C, и только отдельные модули на C++, они пока не роялят.

N>если вы где-то не видите возможности регулировать хотя бы оптимизации под конкретную функцию — самое время начинать бить ногами, чтобы авторы компилятора/среды проснулись.


Ну вот пнул еще раз в соответствующей теме — подождем еще лет десять...
Re[2]: Возврат ошибок из недр вложенных вызовов
От: Евгений Музыченко Франция https://software.muzychenko.net/ru
Дата: 06.11.20 10:39
Оценка:
Здравствуйте, kov_serg, Вы писали:

_>Логи.


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

Сделаю, наверное, подобную штуку в универсальном виде, чтобы можно было использовать и для других целей. Например, некоторые мои драйверы работают через системные обертки, которые получают запрос от клиента, затем вызывают отдельные функции драйвера. Периодически приходится корректировать работу этих оберток, перехватывая запрос на входе и запоминая его параметры в глобальной структуре, чтобы достать потом внутри своей функции. Это обеспечивает передачу снаружи внутрь, а если сделать в общем виде, то и в любую сторону.
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.