LINUX.ORG.RU

Литература, пример проекта обработки ошибок на С

 


0

3

Я делаю для себя небольшой проект на С, но мой код превратился в месиво проверок на ошибки и это очень усложняет его чтение. подскажите пожалуйста какую литературу можно почитать или пример какого проекта можно посмотреть, чтобы поучиться делать это правильно.

Ответ на: комментарий от reprimand

Не думаешь почему сишка всё еще используется людьми

Смотря на ораторов выше - из-за синдрома утёнка.

Ну и статические анализаторы НЕ использовать - ссзб.

В нормальных языках для этого есть компилятор.

RazrFalcon ★★★★★
()
Ответ на: комментарий от anonymous

Но если заменить исключения чейном из bind'ов каких-нибудь монадок, вроде option или either, то будет побыстрее, но все равно не сишка.

anonymous
()
Ответ на: комментарий от anonymous

Ибо функция, которая записывает -1 в регистр, + операция сравнения всяко быстрее что адово медленных исключений, что передачи монадных оберток из функции в функцию. Так что не эквивалентно ни разу.

anonymous
()
Ответ на: комментарий от anonymous

при большом количество ошибок тормозить будет знатно

Большое количество ошибок означает, что i2c не работает. И тогда быстро работающий Си-код превращается в быстро неработающий Си-код.

P.S. склоняюсь к мысли, что исключения зло, но не по причинам их скорости.

tailgunner ★★★★★
()
Ответ на: комментарий от tailgunner

что исключения зло, но не по причинам их скорости.

А из-за того, что это неявный goto в неизвестном направлении?

В любом случае я просто указал, что код не эквивалентен совсем, а случаи ошибок не редки в целом.

anonymous
()
Ответ на: комментарий от anonymous

из-за того, что это неявный goto в неизвестном направлении?

Из-за того, что он неявный.

В любом случае я просто указал, что код не эквивалентен совсем

Не знаю насчет «совсем», но он короче и выполняет те же функции.

а случаи ошибок не редки в целом.

Случаи сбойного железа редки. И самая распространенная их обработка - возврат -EIO.

tailgunner ★★★★★
()
Ответ на: комментарий от anonymous

Если да, то код совсем не идентичен сишному варианту и при большом количество ошибок тормозить будет знатно.

Ошибки и исключения это разные вещи. Исключения - это исключительные ситуации, их не бывает «большое количество» по определению. Так, в моих проектах как правило исключение бывает ровно одно и оно приводит к индикации ошибки и halt'у. Для работы с кривой переферией где ошибка на I2C - ожидаемая ситуация я бы написал другое API, ненамного сложнее.

slovazap ★★★★★
()
Ответ на: комментарий от tailgunner

но он короче и выполняет те же функции.

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

Не, мне тоже, может быть, приятнее писать что-то вроде map().reduce(), но глупо отрицать, что вкусненькие вещи стоят чего-то. Иногда и на ассемблере приходится писать. А на си тоже писать приятно, если много времени, хоть и многословно)

Случаи сбойного железа редки.

Может они и редки, только вот возможно тебе за цикл нужно успеть сделать определенное количество дел, а тут у тебя тяжеленное исключение, которое сжирает кучу времени.

anonymous
()
Ответ на: комментарий от anonymous

но он короче и выполняет те же функции.

Но не с аналогичной скоростью

Пока ошибок нет, скорость Си++ примерно такая же или выше. При возникновении ошибки - да, Си отвалится быстрее, но всем пофиг.

возможно тебе за цикл нужно успеть сделать определенное количество дел, а тут у тебя тяжеленное исключение, которое сжирает кучу времени.

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

tailgunner ★★★★★
()
Ответ на: комментарий от anonymous

Ибо функция, которая записывает -1 в регистр, + операция сравнения всяко быстрее что адово медленных исключений

Во-первых, ещё раз: исключения это исключительные ситуации, это обычно объясняют в C++'ных яслях. При этом код на исключениях быстрее кода с возвращением значения потому что весь оверхед - одна проверка на самом низком уровне (кидать? не кидать). При возвращении же значений имеем проверку на каждом уровне.

Более того, никто не мешает изменить API так что в приведённом коде между методами будет передаваться не I2C&, а обёртка, хранящая I2C& и result. Таким образом код не изменится, но можно будет получить результат: I2C(pindata, pinclock).Send(header).Send(data).IsSuccess(). И да, кстати, на самом деле Send(header, data) без оверхеда, потому что variadic template.

Может они и редки, только вот возможно тебе за цикл нужно успеть сделать определенное количество дел, а тут у тебя тяжеленное исключение, которое сжирает кучу времени.

Солнце моё, тыжпрограммист (или хотя бы хочешь им казаться) - думай головой и делай как тебе нужно. Ошибка - нормальная ситуация? Возвращай значение. Исключительная? Кидай исключение. Речь о том что в C++ это в любом случае одна строчка пользовательского кода, а в говноси - всегда страница, как бы ты не изгалялся с API.

slovazap ★★★★★
()
Последнее исправление: slovazap (всего исправлений: 2)
Ответ на: комментарий от slovazap

И да, кстати, на самом деле Send(header, data) без оверхеда, потому что variadic template.

Что, вот совсем без оверхеда? Покажи, как.

tailgunner ★★★★★
()
Ответ на: комментарий от tailgunner

Мне к жене пора, поэтому если покажу то потом. Сам попробуй - тривиальнейший шаблон же, и оверхеду там неоткуда взяться, он просто вызовет два Send() как и в изначальном коде.

slovazap ★★★★★
()
Ответ на: комментарий от tailgunner

оно говорит об ошибке

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

anonymous
()
Ответ на: комментарий от slovazap

Возвращай значение.

Так я и возвращаю. Это словозар тут предлагает заменить это на I2C(pindata, pinclock).Send(header).Send(data), которое, якобы, эквивалентно, о чем, собсна, и речь.

anonymous
()
Ответ на: комментарий от slovazap

Мне к жене пора, поэтому если покажу то потом. Сам попробуй - тривиальнейший шаблон же

Если он такой тривиальнейший, он тебя не задержит.

он просто вызовет два Send() как и в изначальном коде.

Два Send, как в изначальном коде, возможны только с исключениями.

tailgunner ★★★★★
()
Ответ на: комментарий от anonymous

Ну вот у тебя фреймцикл, ты опрашиваешь порты и что-то еще делаешь, ты что, на ошибке чтения из одного порта уронишь все?

Если мне необходимы значения из всех портов - я обязан буду уронить всё. Если значения опциональны и оверхед исключений слишком велик - понятно, что использовать исключения нельзя.

что за глупости-то несешь.

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

tailgunner ★★★★★
()
Ответ на: комментарий от tailgunner

Два Send, как в изначальном коде, возможны только с исключениями.

С mонадками тоже возможно. Правда, вся конструкция в итоге должна будет все равно вернуть какое-нибудь значение Error.

anonymous
()
Ответ на: комментарий от dzidzitop

zero-cost exception handling

На крестах? Это как, покажи, не слышал о таком. Чего все эти исключения выкидывают тогда?

anonymous
()
Ответ на: комментарий от dzidzitop

(в случае когда всё ок).

А, ложная тревога. Перечитай тогда мои посты что ли.

anonymous
()
Ответ на: комментарий от anonymous

Два Send, как в изначальном коде, возможны только с исключениями.

С mонадками тоже возможно

Точно такая же? Покажи, как.

tailgunner ★★★★★
()
Ответ на: комментарий от RazrFalcon

Смотря на ораторов выше - из-за синдрома утёнка.

ясно-понятно

В нормальных языках для этого есть компилятор.

ну давай, расскажи мне, какие языки ты можешь назвать «нормальными»

reprimand ★★★★★
()

Вы тут че, обсуждаете, что работает быстрее рядом с опросом периферии? :/

anonymous
()
Ответ на: комментарий от anonymous

Теоретически да, но интересны детали. Прокси и dummy наследуют один интерфейс? Или это sum type? И как выглядит монада в Си++?

tailgunner ★★★★★
()
Ответ на: комментарий от tailgunner

Что-то не пойму, то ли я торможу, то ли ты. Вроде ж должно быть элементарно. Если совсем уж в лоб:

class actual_device;
class device_proxy {
  friend class actual_device;
  actual_device * dev_;
  int last_result_;

  device_proxy(actual_device * dev, int last_result)
    : actual_device_{dev}, last_result_{last_result}
  {}
public :
  device_proxy send(const buffer & b) {
    device_proxy r(*this);
    if(!r.last_result_) r = dev_->send(b);
    return r;
  }
};
class actual_device {
public :
  device_proxy send(const buffer & b) {
    ... // some actual actions
    return {this, actual_result};
  }
};
Естественно, здесь нужно более тщательно подойти к порядку декларации классов и определения методов. Но я бы так делал.

А можно device_proxy через шаблоны сделать более универсальным.

eao197 ★★★★★
()
Ответ на: комментарий от eao197

Да, Капитан, обертку можно реализовать вручную. Но 1) я хочу видеть _тривиальнейший_ variadic-шаблон, который сделает ее автоматически 2) продолжать чейнить методы, когда устройство уже вернуло ошибку - идея плохая.

tailgunner ★★★★★
()
Ответ на: комментарий от tailgunner

Я так понимаю, что через variadic предлагалось делать не цельный прокси, а именно что вариант send-а с произвольным количеством аргументов. Тут уж, действительно, лишнего чейнинга не будет, если записывать не d.send(b1).send(b2), а d.send(b1, b2)

eao197 ★★★★★
()
Ответ на: комментарий от tailgunner

А для случая именно d.send(b1).send(b2) нужно куда-то в сторону expression templates копать.

eao197 ★★★★★
()
Ответ на: комментарий от eao197

Я так понимаю, что через variadic предлагалось делать не цельный прокси, а именно что вариант send-а с произвольным количеством аргументов.

А еще на вариадиках можно замутить что-то вроде apply(device, send(b1), send(b2), send(b3)). Без лишнего чейнинга внутри. Если уж слишком захотелось упороться шаблонами :)

eao197 ★★★★★
()
Ответ на: комментарий от eao197

Интересно именно на тривиальнейшем, без упоротости. Может, я чего-то не знаю.

tailgunner ★★★★★
()
Ответ на: комментарий от tailgunner

Если он такой тривиальнейший, он тебя не задержит.

Подождёшь :)

Например, так:

class I2C {
public:
    I2C() {
    }

    I2C& Send(int i) {
        std::cerr << "send " << i << "\n";

        return *this;
    }

    template <class T>
    I2C Send2(T val) {
        return Send(val);
    }

    template <class T, class ... More>
    I2C Send2(T val, More ... more) {
        Send2(val);
        return Send2(more...);
    }
};

int main() {
#ifndef VARIADIC
    I2C().Send(1).Send(2);
#else
    I2C().Send2(1, 2);
#endif
}
% c++ -v
FreeBSD clang version 3.4.1 (tags/RELEASE_34/dot1-final 208032) 20140512
Target: x86_64-unknown-freebsd10.3
Thread model: posix
Selected GCC installation: 
% c++ -std=c++11 -O -o 1 1.cc; c++ -std=c++11 -O -DVARIADIC -o2 1.cc
marakasov@marakasov:/tmp% md5 ?
MD5 (1) = 6750dcb15601fe06e40e5643fa2aec2c
MD5 (2) = 6750dcb15601fe06e40e5643fa2aec2c

Два Send, как в изначальном коде, возможны только с исключениями.

Не понял к чему это.

slovazap ★★★★★
()
Ответ на: комментарий от anonymous

Так я и возвращаю. Это словозар тут предлагает заменить это на I2C(pindata, pinclock).Send(header).Send(data), которое, якобы, эквивалентно, о чем, собсна, и речь.

Внимательнее читай кому отвечаешь. Я ещё раз повторяю - приведённый C и C++ код эквиваленты в любом случае, потому что на обработку ошибки никаких требований обозначено не было - всё что они делают, это отправляют два куска данных по i2c без утечек.

slovazap ★★★★★
()
Ответ на: комментарий от tailgunner

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

А никто и не предлагает. В случае исключения следующие методы вызываться не будут. В случае прокси объекта следующие методы вызываться не будут при добавлении условия.

slovazap ★★★★★
()
Ответ на: комментарий от slovazap

Я ещё раз повторяю - приведённый C и C++ код эквиваленты в любом случае, потому что на обработку ошибки никаких требований обозначено не было

Тред про обработку ошибок

— Вы неправильно обрабатываете ошибки в си — надо вот так

— Но твоя обработка ошибок в 20 раз медленней

— Код эквиваленты в любом случае, потому что на обработку ошибки никаких требований обозначено не было - всё что они делают, это отправляют два куска данных по i2c без утечек.

Ну офигеть теперь. Ну че там, сразу бы на хаскиле написал или на смоллтоке/жабе, там бы еще безопаснее было бы, если для тебя куски кода, семантически одинаковые, эквивалентны.

anonymous
()
Ответ на: комментарий от anonymous

Я не понимаю твоих претензий и при чём тут вообще smalltalk. Я уже написал несколько раз: что с исключениями, что с возвращенем значения (что конкретно использовать решать разработчику в конкретном случае) на C++ для этого примера всегда получается код из одной строки, без возможности утечек и ошибок. В C всегда получается страница дерьма с кучей возможностей ошибиться. Вот и весь поинт. С чем ты споришь?

slovazap ★★★★★
()
Ответ на: комментарий от RazrFalcon

В нормальных языках для этого есть компилятор.

Т.е. это было для красного словца сказано? Очевидно, подразумевалось «в единственном любимом мной языке, на котором я не написал еще ничего серьезного, для этого есть компилятор».

eao197 ★★★★★
()
Ответ на: комментарий от RazrFalcon

И как, по-вашему мнению, может выглядеть пруф отсутствия чего-либо?

Вы бы могли сделать пруф наличия у вас серьезных разработок на Rust-е. Но из общения с вами я что-то таких пруфов не помню.

eao197 ★★★★★
()
Ответ на: комментарий от RazrFalcon

Ну OK:

http://cloc.sourceforge.net v 1.64  T=0.33 s (236.8 files/s, 54160.8 lines/s)
-------------------------------------------------------------------------------
Language                     files          blank        comment           code
-------------------------------------------------------------------------------
Rust                            72           1980           2732          12766
HTML                             3             28              0            130
C++                              1             28             32             95
C/C++ Header                     1              7             22             14
Qt Project                       1              2              0              5
-------------------------------------------------------------------------------
SUM:                            78           2045           2786          13010
-------------------------------------------------------------------------------
Свои слова по поводу «не написал еще ничего серьезного», пожалуй, заберу.

А с какого языка вы с нуля все переписывали? С плюсов?

eao197 ★★★★★
()
Ответ на: комментарий от eao197

Да, с плюсов. Только на гитхабе лежала старая версия, еще с Qt.

Потом я попытался переписать ее на чистые плюсы, без stdc++, так как из контейнеров нужен был только вектор и мап (и вообще я ее не перевариваю). Но когда дело дошло до тестирования, документации и низкоуровневых оптимизаций - началась ломка. Ибо в расте ссылки проверяются компилятором, а в с C++ - нет. Что сделало всю низкоуровневую часть абсолютно не безопасной. Пришлось ваять на умных указателях, а это доп. расходы. Ну а документация и тесты на плюсах - вообще боль, по сравнению с растом, который может запускать тесты прямо из документации, что очень удобно.

Из печали - недописанная версия на плюсах всё еще в 3-е раза быстрее версии на расте, пусть и не безопасная. По большей части из-за отсутствия некоторых SSE2 оптимизаций. И еще rust тратит ~3M инструкций на инициализацию своего рантайма и линковку, а C++, слинкованный только с libc - ~200K.

В целом, для этого конкретного проекта раст подходит лучше. Но некоторые вещи я бы всё равно писал бы на плюсах.

RazrFalcon ★★★★★
()
Ответ на: комментарий от slovazap

Подождёшь :)

Лишь бы было чего ждать.

#ifndef VARIADIC
I2C().Send(1).Send(2);
#else
I2C().Send2(1, 2);
#endif

Т.е. либо чейнинг, либо тривиальнейший вариадик.

Send2(val);
return Send2(more...);

Обработка ошибок опущена (для краткости, разумеется).

В общем, ждать было нечего.

Два Send, как в изначальном коде, возможны только с исключениями.

Не понял к чему это.

Вот к этому:

I2C().Send(1).Send(2)

tailgunner ★★★★★
()
Последнее исправление: tailgunner (всего исправлений: 1)
Ответ на: комментарий от slovazap

В C++ как минимум любой массив и указатель можно обернуть в класс, проверяющий границы и инициализированность, и использовать его прозрачно, типобезопасно и без оверхеда помимо самой проверки, можно использовать обёртки с проверкой диапазона в compile time без оверхеда вообще, можно (и нужно) писать без работы с указателями вовсе.

Расскажи это разработчикам Webkit, а то они похоже не в курсе https://web.nvd.nist.gov/view/vuln/detail?vulnId=CVE-2016-4657

Работа с массивом (или там std::vector) с проверкой границ на каждый чих - она попросту неэффективна. Пилите тогда уже какой-нибудь верификатор, который бы доказывал невозможность вылезти за пределы массива. В той же Java например сделали Array Bounds Check Elimination http://www.ssw.uni-linz.ac.at/Research/Papers/Wuerthinger07/Wuerthinger07.pdf есть ли такое же в плюсах?

SZT ★★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.