LINUX.ORG.RU

Наилучшая стратегия обработки ошибок в Си

 


1

4

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

 MYERROR create_some_thing (SOME_THING **ptr/*other parameters*/);
глобальная переменная
 SOME_THING *create_some_thing (/*other parameters*/);
или указатель в списке параметров функций?
 SOME_THING *create_some_thing (/*other parameters*/, MYERROR* ptr);

Deleted

Последнее исправление: av0r (всего исправлений: 2)

Чем уродливее - тем правильнее

MYERROR create_some_thing (SOME_THING **ptr/*other parameters*/);
anonymous
()

глобальная переменная

Что угодно, только не вот это. Максимум если хочется выпендриться - thread local variable

vertexua ★★★★★
()

Первый вариант лучше. Простой для тебя, привычный и понятный из описания функции для клиента.

Второй сложный и для тебя и для клиента. И да, не глобальную переменную, а thread local(как errno в linux). У тебя там будет два пути. Или все равно возращать некий признак ошибки из функции(-1, как почти все *nix API) или возвращать что угодно, но заставлять клиента проверять твою переменную. Оба ужасны=)

Третий вариант не так плох, но заставит тебя проверять еще один указатель на NULL(причем не очень понятно, что делать, если он NULL, а ошибка критическая), а клиенту еще один передаваемый параметр, который только запутывает API.

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

Точно, спасибо, что-то на меня нашло :)

Deleted
()

Третий вариант

anonymous
()

3 вообще, да и вроде все нормальные либы именно так и делают. Куда уж привычней.

anonymous
()

глобальная переменная

SOME_THING *create_some_thing (/*other parameters*/);

Здесь не обязательно глобальная переменная. В ядре используют такой трюк - возвращают либо валидный указатель, либо целое число (код ошибки), преобразованное к указателю. Результат проверяется макросом IS_ERR, код ошибки извлекается PTR_ERR, преобразуется ERR_PTR.

Но, если есть выбор, Си лучше не использовать.

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

Но, если есть выбор, Си лучше не использовать.

Конечно есть, альтернативой видится — фортран. Но я не знаю что лучше использовать.

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

пофиксил

Лучшая стратегия обработки ошибок — предназначенные для этого ADT’s и GADT’s. В C её нет.

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

альтернативой видится — фортран.

А. Ну, если «видится» такая альтернатива, то спокойно используй Си++. Там есть и исключения, и возврат нормальных ошибок (ADT, не просто тупых целочисленных кодов ошибок, как в Си).

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

Эх, хотелось бы, да он сложный слишком, может со временем перейду, да и с массивами там не так удобно работать.

Deleted
()

1й и 3й варианты, в зависимости от ситуации.

ни в коем случае не 2й. даже thread-local плохая идея. не бери пример с легаси типа errno.

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

Ну, на сколько я понял — не совсем. На Си:

//-std=c99
double *arr[y][z] = malloc(sizeof(double) * x * y * z);
На Си++:
double ***arr = (double ***)malloc(sizeof(double **) * x);
for(int i = 0; i < x; ++i) {
    arr[i] = (double **)malloc(sizeof(double *) * y);
    for(int j = 0; j < y; ++j) {
        arr[i][j] = (double *)malloc(sizeof(double) * z);
    }
}
Надеюсь я не налажал, но не уверен. Хотя, если налажал, то это ещё лучше демонстрирует проблему. С освобождением такая же пляска.

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

Оба фрагмента кода одинаково сработают и в Си, и в Си++.

Надеюсь я не налажал, но не уверен.

Ты не то, чтобы налажал, ты просто не понимаешь. Пожалуй, тебе лучше использовать Фортран.

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

Оба фрагмента кода одинаково сработают и в Си, и в Си++.

Погоди-погоди. В первом — variable length-array. Они работают в C++?

Я даже не уверен, что первый вариант сработает в Си.

i-rinat ★★★★★
()
Ответ на: комментарий от Deleted

На C++ нежелательно использовать malloc и тому подобное. Это громоздко и неудобно. Вместо этого нужно использовать ещё более громоздкие конструкции, которые зато потом достаточно просто использовать:

#include <iostream>
#include <vector>

template <typename ElementType> //
class array3d {
private:
  class subarray1d {
  public:
    subarray1d(array3d &a, size_t x, size_t y) : a_(a), x_(x), y_(y) {}
    ElementType &operator[](size_t z) {
      return a_.storage_[(x_ * a_.sy_ + y_) * a_.sz_ + z];
    }

  private:
    array3d &a_;
    size_t const x_;
    size_t const y_;
  };

  class subarray2d {
  public:
    subarray2d(array3d &a, size_t x) : a_(a), x_(x) {}

    subarray1d operator[](size_t k) { return subarray1d{a_, x_, k}; }

  private:
    array3d &a_;
    const size_t x_;
  };

public:
  array3d(size_t x, size_t y, size_t z) : sx_(x), sy_(y), sz_(z) {
    storage_.reserve(x * y * z);
  }

  subarray2d operator[](size_t idx) { return subarray2d{*this, idx}; }

  size_t sx() { return sx_; }
  size_t sy() { return sy_; }
  size_t sz() { return sz_; }

private:
  size_t sx_;
  size_t sy_;
  size_t sz_;
  std::vector<ElementType> storage_;
};

int main(void) {
  array3d<int> q{3, 3, 3};

  int cnt = 0;
  for (size_t k1 = 0; k1 < q.sx(); k1++) {
    for (size_t k2 = 0; k2 < q.sy(); k2++) {
      for (size_t k3 = 0; k3 < q.sz(); k3++) {
        q[k1][k2][k3] = ++cnt;
      }
    }
  }

  for (size_t k1 = 0; k1 < q.sx(); k1++) {
    std::cout << "plane " << k1 << ":\n";
    for (size_t k2 = 0; k2 < q.sy(); k2++) {
      std::cout << "  line " << k2 << ":";
      for (size_t k3 = 0; k3 < q.sz(); k3++) {
        std::cout << " " << q[k1][k2][k3];
      }
      std::cout << "\n";
    }
  }
}

То есть просто делаем сплошной массив, для доступа к элементам каждый раз вычисляем индекс, а чтобы это выглядело как array[1][2][3], создаём прокси-объекты. Компиляторы уже достаточно умные, чтобы в финальном результате от прокси-объектов ничего не осталось.

Ну или если синтаксис array[1][2][3] не обязателен, просто метод, принимающий три индекса.

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

В первом — variable length-array.

Я не вижу объявлений y и z. Если это VLA, то понятно, что в Си++ его нет.

Я даже не уверен, что первый вариант сработает в Си.

Ну, я стараюсь угадать, что имел в виду автор. И мне кажтся, что он имел в виду «нечто, к чему можно обращаться как arr[j][k]» и реализует многомерный массив как массив указателей на массивы. Это будет работать и в Си, и в Си++.

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

Хм, наверное слишком сократил пример. x, y, z — конечно, не константы. В случае Си это один большой кусок памяти, с которыми можно работать как с трехмерным массивом, в Си++ так не выйдет, надо делать массивы указателей на указатели итд.

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

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

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

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

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

В случае Си это один большой кусок памяти, с которыми можно работать как с трехмерным массивом, в Си++ так не выйдет,

Выйдет. Кроме того, в твоем примере на Си arr - это двухмерный массив указателей, а не «большой кусок памяти, с которым можно работать, как с трехмерным массивом». Правда, пиши лучше на Фортране.

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

в С++ при работе с простыми объектами абсолютно никаких отличий по работе с памятью нет. и точно так же можно выделять один кусок памяти (это даже более предпочтительно, потому что возня с выделением кучи отдельных указателей только будет жрать ресурсы). и для устойчивости софта статическое выделение памяти тоже предпочтительнее.

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

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

а сишный код можно выполнять хоть на микроконтроллерах

И сишные компиляторы не «предлагают свои расширения, которые вообще в стандарт не входят», правда ведь?

на плюсах аналогичный код просто станет жирнее и немного медленнее

Это ты о чём?

Deleted
()
Последнее исправление: ecko (всего исправлений: 4)
Ответ на: комментарий от Deleted

И сишные компиляторы не «предлагают свои расширения, которые вообще в стандарт не входят», правда ведь?

обычно только сокращения. особенно в микроконтроллерах.

Это ты о чём?

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

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

а вдруг стек испортится?

А вдруг метеорит на серверную упадёт.

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

это тут растаманы всякие шаратся, попсой своей народ баламутят.

можешь аргументированно объяснить, чем тебе так раст не угодил?

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

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

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

обычно только сокращения. особенно в микроконтроллерах.

Особенно с -std=gnu*, даааа.

о том, что в плюсы натащили много ненужно за последние десять лет.

Это уже совсем другая мысль. Я же хотел подтверждения мысли

на плюсах аналогичный код просто станет жирнее и немного медленнее

Пример можно?

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

Пример можно?

да напиши два варианта любой программы: на чистом С и на плюсах - и сравнивай.

подсказка для ленивых: бенчмарков в сети навалом.

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

Ну, вот, например, вариант простого использования vector.

#include <dirent.h>
#include <stdio.h>

#include <string>
#include <vector>


int main(int argc, char *argv[])
{
    if (argc != 2) {
        fprintf(stderr, "Usage: %s DIR\n", argv[0]);
        return 1;
    }

    DIR *dir = opendir(argv[1]);
    if (dir == nullptr) {
        perror("opendir");
        return 1;
    }

    std::vector<std::string> entries;
    for (dirent *ent = readdir(dir); ent != nullptr; ent = readdir(dir))
        entries.push_back(ent->d_name);
    closedir(dir);

    for (const auto& e : entries)
        printf("%s\n", e.c_str());
    return 0;
}

Напиши мне не такой жирный вариант такой программы на Си. Обязательно с хранением. Вывод на экран — просто частный случай.

Учитывай, что ты не знаешь, сколько у тебя entries в директории.

Deleted
()
Последнее исправление: ecko (всего исправлений: 1)

или указатель в списке параметров функций?

this.

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

Ну да, я был невнимателен. Хотел написать:

int x, y, z;
x = atoi(argv[1]);
y = atoi(argv[2]);
z = atoi(argv[3]);

double (*arr)[y][z] = malloc(sizeof(double) * x * y * z);
Для такого кода, если я правильно понимаю, нужен VLA, а в стандарте Си++ этого нет.

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

Для такого кода, если я правильно понимаю, нужен VLA

Да, для такого - нужен.

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

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

Да, что-то было. Я писал, что хорошая штука и в предыдущем сообщении показал пример, где это бывает полезным.

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

Ну, эксперта я из себя никогда не строил, но я стремлюсь расширять своё понимание, этот топик создан именно для этого. Если вы видите у меня определённые пробелы, я буду рад рекомендации как самостоятельно восполнить пробел.

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

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

Deleted
()
Ответ на: комментарий от i-rinat

Очень интересный код, спасибо.

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

Чувак, у тебя крыша поехала. Ты сам с собой беседу ведёшь и сам себе вопросы задаёшь.

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

Напиши мне не такой жирный вариант такой программы на Си. Обязательно с хранением. Вывод на экран — просто частный случай.

А в чём проблема? Думаешь, push_back делает какую-то магию? Его можно 1-в-1 переписать на Си. А дальше std::vector<std::string> превращается в char**. Исчезает копирование из char* в std::string при push_back (так как можно складывать указатели). И исчезает потребность вызывать c_str().

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