LINUX.ORG.RU

Как вы обрабатываете ошибки с требующейся очисткой в Си?

 ,


1

6

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

Как живой пример:
Было: http://pastebin.com/BuUqYSRZ
Стало: http://pastebin.com/yHzMpv6C

Может вы используете какой-то другой метод?

Может вы используете какой-то другой метод?

С goto, но без макросов как у тебя.

p.s. Оформление кода какой-то хтонический п.

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

Ага, увидел примеры:

/*
 * Protects cgroup_idr and css_idr so that IDs can be released without
 * grabbing cgroup_mutex.
 */
static DEFINE_SPINLOCK(cgroup_idr_lock);

/*
 * Protects cgroup_file->kn for !self csses.  It synchronizes notifications
 * against file removal/re-creation across css hiding.
 */
static DEFINE_SPINLOCK(cgroup_file_kn_lock);

/*
 * Protects cgroup_subsys->release_agent_path.  Modifying it also requires
 * cgroup_mutex.  Reading requires either cgroup_mutex or this spinlock.
 */
static DEFINE_SPINLOCK(release_agent_path_lock);

<..>

#define __SPIN_LOCK_INITIALIZER(lockname) \
          { { .rlock = __RAW_SPIN_LOCK_INITIALIZER(lockname) } }
  
#define __SPIN_LOCK_UNLOCKED(lockname) \
          (spinlock_t ) __SPIN_LOCK_INITIALIZER(lockname)
  
#define DEFINE_SPINLOCK(x)      spinlock_t x = __SPIN_LOCK_UNLOCKED(x)

#define __RAW_SPIN_LOCK_INITIALIZER(lockname)   \
       {                                       \
       .raw_lock = __ARCH_SPIN_LOCK_UNLOCKED,  \
       SPIN_DEBUG_INIT(lockname)               \
        SPIN_DEP_MAP_INIT(lockname) }

Скипнуто еще полсотни макросов в этом дереве разверток

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

Ага, увидел примеры:
Скипнуто еще полсотни макросов в этом дереве разверток

А в чем проблема?

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

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

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

Шел 2016й год а люди писали на новых лагающих медленных и перегруженных языках.

фейспалм. Тебе только на таких языках и писать. На си ты пишешь говно.

tailgunner ★★★★★
()

goto в очиститель, как еще...

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

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

Так дело не в том, что ты сделал макрос. Дело в том, что макрос тут не нужен.

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

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

Я тебя расстрою, но во втором одна точка возврата

Я этому только рад. Но

#define CLEANING return; _cleaning:

...

CLEANING
    if (root ) CML_NodeFree(&root );

это фейспалм. И действие по очистке обычно называется cleanup, а не cleaning.

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

Согласен, чаще всего это где-то в legacy коде, и приходиться как-то поддерживать ... :(

joy4eg ★★★★★
()

goto же. Расставляешь cleanup-метки в обратном порядке выделяемым ресурсам. Можно ещё макрос вспомогательный написать:

#define ASSERT_CLEANUP(EXPR, CLEANUP_LABEL)     \
  do { \
    if (!(EXPR)) { \
      goto CLEANUP_LABEL; \
    } \
  } while(0)

void foo() {
  foo *resource1 = alloc_resource1();
  ASSERT_CLEANUP(resource1 != NULL, ret);

  bar *resource2 = alloc_resource2(resource1);
  ASSERT_CLEANUP(resource2 != NULL, cleanup_resource1);

  bool success = do_something_with(resource2);
  ASSERT_CLEANUP(!success, cleanup_resource2);

  /* snip */

 cleanup_resource2:
  free_resource2(resource2);

 cleanup_resource1:
  free_resource1(resource1);

 ret:
  return;
}
theNamelessOne ★★★★★
()

Если plain C, то только goto, безо всяких обёрток. Посмотри внимательно, всё что ты обёртываешь в макрос - свой отвратительный стиль кода с ненужными скобками и строками впустую потраченными на открывающую скобку. Ещё макрос опасен тем что скрывает проверку, а разные функции могут требовать разных проверок. Всего делов-то:

if (function_returning_0_on_error() == 0)
    goto cleanup;

if (function_returning_negative_on_error() < 0)
    goto cleanup;

if (function_returning_42_on_error() == 42)
    goto cleanup;

cleanup:
    ...

А так, конечно, закопать уродский C и использовать C++ с RAII.

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

щё макрос опасен тем что скрывает проверку, а разные функции могут требовать разных проверок

У нас в проекте все функции имеют сигнатуру CML_Error <name><args>

someoneelsenotme
() автор топика

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

xaizek ★★★★★
()

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

CLEANING и прочее - слабо читаемо.

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

А ещё есть функции не из вашего проекта.

anonymous
()

goto, sjlj Возможно функция Shutdown, которая проводит очистку

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

Только вот в Си нет перегруженных функций

А там в примере они и не используются. А вместо них: функции с переменным кол-вом параметров. Такие есть в Си: включить stdarg.h.

gag ★★★★★
()
Ответ на: комментарий от Deleted
int function(int x)
	{//??????
switch (action) {//?????

        switch (suffix) {
	case 'G'://??????
	case 'g':
	if (x is true) {
		we do y//?????
	}

Л - логика. У меня к тебе вопрос, как к адепту - объясни мне это. Ладно, кейсы ещё можно объяснить метками для гото, т.к. код в них должен быть на уровне функции, ибо код функции. Хотя так же спорно, ибо тогда они(метки) становятся на уровне функции, что нелогично.

registrant27492
()

*ударил челом*

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

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

registrant27492
()

«Стало» гораздо хуже.

goto из дефайна наружу это эпическое зло. Как правильно готовить goto уже ссылку давали, могу только заметить что нестрашно делать «сильно-вложенные» частные функции - всё равно компилер их свернёт, а читается проще. Вообще придерживайтесь стиля чтобы код читался как песня и сила прибудет с вами :-)

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

goto из дефайна наружу это эпическое зло

Почему?

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

А так можно было бы вообще сделать красиво, как описано тут:

Там предлагают вариант реализации на плюсах как его себе представляет сишник, но так неправильно и всё равно неудобно ибо можно неусмотреть все места где нужно позвать cleanup(). Правильно использовать RAII, например, через std::unique_ptr с кастомным Deleter-ом или вообще сделать нормальные плюсовые обёртки.

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

А если обозвать макрос CLEAN_AND_RETURN()?

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

Макросы типа CHK(), которые выполняют goto fail, ещё можно оправдать в определённых ситуациях. Или инструкции логирования, которые выглядят как функции, а на самом деле их параметры могут и не вычисляться.

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

В данном случае макрос выглядит как функция, но на самом деле - ничего общего

Ну ок. А я думал макросы без скобок только для значений (#define BUF_LEN 1024), а для остального принято ставить скобки

можно вставить return и метку прямо в тело функции, и от этого читаемость кода только увеличится.

А если разные значения возвращаются?


enum FB function f(int n){
define CLEAN_N_RETURN(result) \{
  clean(); \
  return result; \
}

  alloc_something();

  if(n == 9) CLEAN_N_RETURN(FIZZ);
  else if(n == 20) CLEAN_N_RETURN(BUZZ);
}


// VS

enum FB function f(int n){
  alloc_something();

  if(9){
    result = FIZZ;
    goto end;
  }
  else if(20) {
    result = BUZZ;
    goto end;
  }

  end:
    clean()
    return result;
}
  
makoven ★★★★★
()
Последнее исправление: makoven (всего исправлений: 2)
Ответ на: комментарий от makoven

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

#define CHK(cond, rc, rc_) \
    do { \
        if (!(cond)) { \
            rc = (rc_); \
            goto fail; \
        } \
    } while (0)

enum FB function f(int n){
    enum FB rc;

    alloc_something();

    CHK(n != 9, rc, FIZZ);
    CHK(n != 20, rc, BUZZ);
    ...
  fail:
    clean();
    return rc;
}

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

С goto, но без макросов как у тебя.

++

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