Pull to refresh
196
-8
Павел @ProgerXP

Вольный программист и дизайнер

Send message
будут ли в нем учтены файлы и вызовы функций по стеку, в которых нет макросов (они ведь макросы?) try-catch?

Не будут, так как новая запись во внутренний стек saneex добавляется только при входе в блок try. Вероятно, можно придумать способ добавлять в него вызовы при входе в любую функцию, но точно не стандартными средствами.


Оданко проблемы в этом нет, так как обычно при возникновении исключения вполне достаточно функций с try/catch внутри, а все промежуточные не интересуют.


Пример:


  1 #include "saneex.h"
  2
  3 int subsub(void) {
  4   throw(msgex("Test"));
  5 }
  6
  7 int sub(void) {
  8   subsub();
  9 }
 10
 11 int main(void) {
 12   try {
 13     sub();
 14   } endtry
 15 }

Вывод:


Uncaught exception (code 1) - terminating. 
Test
    ...at saneex-demo.c:4, code 0
rethrown by ENDTRY
    ...at saneex-demo.c:14, code 1

Как видите, в стеке только функции main (строка 14 с endtry) и subsub (4 с throw). Функции sub (строки 7-9) нет.

Единственное что смутило: int i{ vec.at(4) }; — это C++ головного мозга?

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

Попробуйте скомпилировать #define с # внутри, без -E — у меня ругается:


error: stray ‘#’ in program
#define TRY #include ...

Рассмотрим две проблемы из статьи:


блок обязан заканчиваться на endtry

Задача компилятора/среды программирования — максимально разгрузить программиста от контроля за мелочами (синтаксиса, платформы и прочего). В контексте моей библиотеки, может быть такой случай использования:


try {
  func();
} endtry   // <<<

Допустим, код выше (где try и endtry это макросы) разворачивается в такой:


if (xxx)    // "try"
{
  func();
}
if (!handled) ...   // "endtry'

Что будет, если программист забудет endtry?


if (xxx)    // "try"
{
  func();
}

Последней строчки нет. При этом в endtry происходит очистка стека и его пропуск является фатальной ошибкой, которую не поймать даже в рантайме (с точки зрения saneex блок try продолжает быть открытым до завершения процесса). А забыть endtry очень легко.


Поэтому в saneex (см. исходники):


#define try     {{{ if (_sxEnterTry2( setjmp(*_sxEnterTry()) ))
#define endtry  _sxLeaveTry(__FILE__, __LINE__); }}}

Обратите внимание на {{{ и }}}. Теперь, если пропустить endtry, код будет таким:


{{{ if (xxx)    // "try"
{
  func();
}

Это, очевидно, является синтаксической ошибкой, о чем компилятор сразу предупредит. Сравните с:


{{{ if (xxx)    // "try"
{
  func();
}
if (!handled) ... }}}  // "endtry"

Забыть endtry и не заметить этого теперь можно только если пропустить 3 закрывающие скобки помимо собственно endtry, а это сложно сделать.


нельзя делать return между try и endtry

Теперь такой пример:


try {
  return func();
} endtry

Это является точно такой же фатальной ошибкой, т.к. вызывает повреждение стека. Скобки нам уже не помогут, потому что endtry на месте. Можно было бы сделать что-то подобное:


#define try    #define return abort();

И тогда:


try {
  abort(); func();
} endtry

В этом случае в рантайме при попытке выполнить такой блок гарантированно получили бы падение. Еще можно было бы #define return $#@!, чтобы вызвать синтаксическую ошибку — однако макросы не раскрываются рекурсивно, так что это не сработает и за пропущенным return, в отличии от endtry, приходится следить программисту.


Вот это я и имел в виду.

Имеется в виду, что при наличии volatile не требуется других модификаторов/факторов — факт наличия только volatile является достаточной гарантией, что переменная не попадет в регистр и к ней не будут применены другие описанные оптимизации.

Имелось в виду использование стандартных переносимых конструкций, как, например, тройное { (как сделано для try и endtry) или #define return abort (но макросы не рекурсивны).

на фоне потока непрерывных новостей о коронавирусе она вселяет настоящий оптимизм.

А я вот смотрю на счетчик просмотров и понимаю, что теперь Хабр уже точно "не торт" :/ Верю и надеюсь, что это временно.

Насколько я понимаю, кэш в x86/x64 сделан так, чтобы программисту не нужно было о нем думать вообще — его как бы нет. За его корректность отвечает ЦП и переключение между ядрами для программы прозрачно. При использовании нескольких потоков это не отменяет необходимость синхронизации (atomic, критические секции и прочее), но сам кэш при этом всегда остается корректным (с точки зрения программы).


Беглый поиск выдал вот такой вопрос-ответ на SO:


x86 CPUs use a variation on the MESI protocol (MESIF for Intel, MOESI for AMD) to keep their caches coherent with each other (including the private L1 caches of different cores). A core that wants to write a cache line has to force other cores to invalidate their copy of it before it can change its own copy from Shared to Modified state.

You don't need any fence instructions (like MFENCE) to produce data in one thread and consume it in another on x86, because x86 loads/stores have acquire/release semantics built-in. You do need MFENCE (full barrier) to get sequential consistency.

То же самое относится к многопроцессорным системам.

Мне тоже предпросмотра ссылок часто не хватает — было бы неплохо, если бы deniskin взял это на вооружение. Хотя, возможно, это лучше решать на уровне плагина в браузере, а не отдельно взятого сайта.

Про более медленную работу fprinf по сравнению с cerr << в общем-то понятно — printf это комбайн на все случаи жизни и под многие типы переменных, ему приходится разбирать строку форматирования. Вывод в cerr, полагаю, пользуется какими-то узкотипизированными форматёрами.

Здесь интересно другое (см. таблицу, №3 и сноску): fprintf() в коде бенчмарка работает в 2-3 раза быстрее всегда, кроме одного случая в Visual Studio, когда в цикле выбрасывается исключение — тогда внезапно << начинает работать в 3 раза быстрее fprintf(). Причем это подтвердилось у другого читателя. Я не могу это никак объяснить. И это не повторяется на gcc.

Я уже привык к тому, что если кто-то выкладывает контекстную ссылку — там нечто очень желательное к ознакомлению.

Ну, 9600 бод и все-все-все вполне попадает в категорию "очень желательно" :)


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

построение вот этого самого стека, насколько я помню — это как раз то, что в Java сильно снижает производительность при использовании исключений.

Я с низким уровнем в JVM мало знаком, но, как пишут на SO, там используется подход zero-cost exceptions (см. в статье), то есть вся обработка делается в момент поимки исключения (если оно возникает). А Java — высокоуровневый язык с синхронизациями, объектами и прочим, поэтому такая обработка затратна.


В saneex и "голом" С все наоборот — throw это почти что один longjmp(), который, фактически, только сбрасывает указатель стека (ESP). Затраты на этот сброс околонулевые, что показывают мои замеры, по которым throw/longjmp() в C быстрее, чем throw в C++ (где, как и в JVM, дело не ограничивается только изменением ESP).


"Построение стека" происходит по мере вызовов try — там копируются параметры исключения (file, message и пр.) в статический массив, плюс вызывается setjmp(). Как раз последний является лимитирующим фактором, но от него избавиться нельзя никак, не уходя от C99. Но даже там счет идет на единицы-десятки мс при 100к повторений.

Я правильно понял, что само исключение у вас это структура, в которой есть сообщение, строка где его кинули, и указатель еще на что-то?

Да: https://github.com/ProgerXP/SaneC/blob/master/saneex.h#L187


struct SxTraceEntry {
  int   code;
  char  uncatchable;
  char  file[SX_MAX_TRACE_STRING];
  int   line;
  char  message[SX_MAX_TRACE_STRING];
  void  *extra;
};

можно ли будет посмотреть стектрейс, как скажем в Java, все строки, по которым проходил вызов до точки, где кинули исключение

Конечно, в этом и смысл. На КДПВ справа внизу — именно такой stack trace.


Uncaught exception (code 1) - terminating.
Feeling blue today...
    ...at saneex-demo.c:7, code 0
rethrown by ENDTRY
    ...at saneex-demo.c:11, code 1
Bye-bye my little pony
    ...at saneex-demo.c:18, code 0
rethrown by ENDTRY
    ...at saneex-demo.c:19, code 1

Структура доступна в рантайме, ее можно проитерировать через sxWalkTrace():
https://github.com/ProgerXP/SaneC/blob/master/saneex.c#L42


Причем без усилий вообще.

Да, "без усилий вообще" — это как раз "привычный по другим языкам механизм исключений" для меня. saneex это дает из коробки.

Есть ещё Notepad 2e с ещё большим количеством плюшек типа подсветки слов.
Ага. Сделали. Только они не совсем рандомные, не всегда включаются и вообще — почти не работают. Пруф.
А если честнее – потому что не видят альтернативы. А альтернатива есть, просто о ней забыли.

Яростно плюсую, именно то, о чём я постоянно говорю коллегам. А альтернатива есть — "Backbone без фантиков", когда на единственном принципе строится вся библиотека. Как его повернёшь — то и получишь: наследование, привязки, наблюдателе, всё прочее… Просто, компактно.

Полезная статья, буду отсылать на неё несогласных.
А как мелкое, но полезное (для кого-то) дополнение к более наглядным подсказкам — пусть будет!

Согласен. Вопрос исчерпан, спасибо за обсуждение :)
Какой холиварный сабж оказался :)

Согласен, что сейчас четкой грани нет, мой предыдущий коммент скорее пытался пояснить исторические корни этой разницы.

Так ведь формы были ещё тогда, когда у элементов не было даже :hover. Точно во времена CSS1. ИМХО, тут создатели смотрели в первую очередь на десктопные интерфейсы, где, действительно, только особых случаях использовалась «рука» (как сейчас помню — в ссылках Acrobat Reader'а).

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

Я тоже заметил, но тем сильнее мой аргумент про то, что раз никто (даже Яндекс с его экстазом по поводу интерфейсов) не делает 100% правильно и последовательно, то нет смысла к этому стремиться. Только путает.

И у меня до сих пор осталось твердое убеждение, что нужны и ссылки на объекты («существительные»), и кнопки для действий («глаголы»), которые никогда не смогут заменить друг друга и визуально им тоже лучше быть разными.

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

Визуальные подсказки — как раз ответ, ИМХО. А курсор — уже так, дань традиции. Суровый верстальщик будет делать, как глаголет спецификация, выражая этим своё отношение к сабжу наподобие того, как некоторые технические переводчики пытаются найти английским словам русский эквивалент, а какие-то просто делают транслит (browser = браузер = обозреватель). Тоже холивар ещё тот.

У меня опасение, что такая «забота о пользователе» через переупрощение

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

А форма нет? <button type=«submit»>? А <button type=«reset»>, которая не уводит, но уничтожает — это тогда как? Отдельный курсор надо?

еще ссылку можно открыть мидл-кликом или комманд-кликом, перетянуть в другое окно, послать другу и т.д.

Как аргумент — годится, но на практике не вытягивает. Возьмите тот же Яндекс: metrika.yandex.ru — они активно используют как обычные ссылки (которые <a href>), так и скриптовые ссылки (которые что-то там открывают в рамках этой же страницы). Первые выделяются сплошным подчёркиванием, вторые — точечные. Вроде всё сходится.

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

На этом фоне имеем то, что кнопки, которые, как бы, меняют состояние страницы, уводят с неё и т.п. — не выделяются курсором, потому как это не в рамках спецификации, зато также имеем ссылки, которые нормальные, и ссылки, которые как раз не меняют состояния страницы (как тот язык) и при этом и те, и те выделяются одинаково или почти одинаково (Яндекс — редкое исключение), но зато у обоих точно всегда будет курсор-рука. При таком положении дел надо затратить пару секунд только на определение того, что собственно перед тобой («рука» есть? адрес кончается на "#"? адрес вообще показывается (может это span)?) и проще сделать это методом тыка.

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

вопрос, кому придется потерпеть, остается открытым..:)

По мне так однотипный курсор для всего нажимаемого — это в первую очередь интуитивно понятно в первую очередь самому пользователю. То, что там какие-то аспекты нажимаемости могут отличаться, и поэтому надо делать разные курсоры — это уже мысль пришла позже, да и то не ко всем (см. пример с Яндексом). У меня вот, как у опытного программёра (веб- и не только) никогда не возникало мысли, что «рука» обозначает возможность вызвать контекстное меню. Уверен, что у 95% обычных пользователей — и подавно.
Уже и не чаял получить ответ.

Очень интересно, спасибо. Это проливает свет на вопрос с указателем. «Affordance» звучит убедительнее других аргументов (например, что ссылка меняет состояние страницы — так ведь и кнопка тоже), но тем не менее всё равно субъективно — ИМХО, выделенная цветом и иногда подчёркиванием ссылка вполне себе отличима от текста. Но даже если и нет как курсор поможет её найти? Ведь для этого придётся наводить мышь на это место, но если ты его (место) и так нашёл — зачем ещё какие-то индикаторы?

Тем более с новыми дизайнами вроде iOS 7+ и Metro кнопки нефигово потеряли в «affordance» и почти стали ссылками. Впору и трактовать всё кликабельное универсально.

В общем, спасибо за ссылки.

Information

Rating
Does not participate
Location
Россия
Registered
Activity