25

作業中の C コードがいくつかあり、コードの実行中にエラーが見つかりましたが、(C# または C++ のように) 適切な try/catch を実行する方法についてほとんど情報がありません。

たとえば、C++ では次のようにします。

try{
//some stuff
}
catch(...)
{
//handle error
}

しかし、ANSI C では少し迷っています。オンライン検索をいくつか試しましたが、それを実現する方法について十分な情報がありません/誰かが私を正しい方向に向けることができる場合に備えて、ここで尋ねると思いました.

これが私が取り組んでいるコードです(かなり単純な再帰的な方法)。try/catch(または同等のエラー処理構造)でラップしたいと考えています。

ただし、私の主な質問は、ANSI C で try / catch を行う方法です...実装/例は再帰的である必要はありません。

void getInfo( int offset, myfile::MyItem * item )
{
    ll::String myOtherInfo = item->getOtherInfo();
    if( myOtherInfo.isNull() )
        myOtherInfo = "";
    ll::String getOne = "";
    myfile::Abc * abc = item->getOrig();
    if( abc != NULL )
    {
        getOne = abc->getOne();
    }
    for( int i = 0 ; i < offset ; i++ )
    {
             printf("found: %d", i);
    }
    if( abc != NULL )
        abc->release();
    int childCount = item->getChildCount();
    offset++;
    for( int i = 0 ; i < childCount ; i++ )
        getInfo( offset, item->getChild(i) );
    item->release();
}
4

8 に答える 8

26

一般的に、あなたはしません。

C にはデストラクタやスタックの巻き戻しのようなものはありませんが、try/catch にかなり似たものを使用setjmpおよびlongjmp構築することは可能であるため、RAII は問題外です。いわゆる「クリーンアップ スタック」(たとえば Symbian/C++ を参照) を使用して RAII を概算することもできますが、これは非常に厳密な概算ではなく、多くの作業が必要です。

C でエラーまたは失敗を示す通常の方法は、成功ステータスを示す値を返すことです。呼び出し元は戻り値を調べ、それに応じて動作します。たとえば、関数を指定する方法については、標準 C 関数: printfread、を参照してください。open

C コードと C++ コードを混在させる場合、C++ 例外が C コードに到達しないようにする必要があります。C から呼び出される C++ 関数を記述するときは、すべてをキャッチします。

于 2010-09-21T17:03:23.167 に答える
17

C は例外処理をサポートしていません。

この問題への 1 つのアプローチに関する情報がここにあります。これは単純なsetjmp/longjmpアプローチを示していますが、詳細に説明されているより洗練された代替手段も提供しています。

于 2010-09-21T17:04:36.703 に答える
4

私が好んで使用する便利なコーディング スタイルの 1 つを次に示します。特定の名前を持っているかどうかはわかりませんが、アセンブリ コードを同等の C コードにリバース エンジニアリングしていたときに見つけました。インデント レベルは下がりますが、私にとってはそれほど大したことではありません。無限ループを指摘するレビュアーに気をつけろ!:)

int SomeFunction() {
    int err = SUCCESS;

    do {
        err = DoSomethingThatMayFail();
        if (err != SUCCESS) {
            printf("DoSomethingThatMayFail() failed with %d", err);
            break;
        }

        err = DoSomethingElse();
        if (err != SUCCESS) {
            printf("DoSomethingElse() failed with %d", err);
            break;
        }

        // ... call as many functions as needed.

        // If execution gets there everything succeeded!
        return SUCCESS;
    while (false);

    // Something went wrong!
    // Close handles or free memory that may have been allocated successfully.

    return err;
}
于 2010-09-21T19:26:57.690 に答える
4

古典的な巻き戻しgotoのパターンがあります。

FILE *if = fopen(...);
FILE *of = NULL;
if (if == NULL) return; 

of = fopen(...);
if (of == NULL) goto close_if;

/* ...code... */
if (something_is_wrong) goto close_of;

/* ... other code... */

close_of:
  fclose(of);
close_if:
  fclose(if);

return state;

または、別の関数で「try」コードを分離することにより、限られた方法でそれを偽造することができます

int try_code(type *var_we_must_write, othertype var_we_only_read /*, ... */){
  /* ...code... */
  if (!some_condition) return 1;
  /* ...code... */
  if (!another_condition) return 2;
  /* ...code... */
  if (last_way_to_fail) return 4;
  return 0;
}

void calling_routine(){
  /* ... */
  if (try_code(&x,y/*, other state */) ) {
     /* do your finally here */
  }
 /* ... */
}

しかし、どちらのアプローチも完全に同等ではありません。すべてのリソースを自分で管理する必要があり、ハンドラーが見つかるまで自動ロールバックを取得できないなど...

于 2010-09-21T18:51:22.423 に答える
4

これは、C での例外処理システムの私の実装です: exceptions4c

setjmpとの上に構築されたマクロを搭載し、longjmp100% 移植可能な ANSI C です。

そこには、私が知っているすべてのさまざまな実装のリストもあります。

于 2011-04-28T07:05:50.273 に答える
3

この本でlongjmpの可能な実装を見つけることができます:Cインターフェースと実装:再利用可能なソフトウェアを作成するためのテクニック-David Hanson

そして、あなたは本で使われているスタイルの例としてここここにコードを見つけることができます:

を除いて.h

/* $Id$ */
#ifndef EXCEPT_INCLUDED
#define EXCEPT_INCLUDED
#include <setjmp.h>
#define T Except_T
typedef struct T {
    const char *reason;
} T;
typedef struct Except_Frame Except_Frame;
struct Except_Frame {
    Except_Frame *prev;
    jmp_buf env;
    const char *file;
    int line;
    const T *exception;
};
enum { Except_entered=0, Except_raised,
       Except_handled,   Except_finalized };
extern Except_Frame *Except_stack;
extern const Except_T Assert_Failed;
void Except_raise(const T *e, const char *file,int line);
#ifdef WIN32
#include <windows.h>

extern int Except_index;
extern void Except_init(void);
extern void Except_push(Except_Frame *fp);
extern void Except_pop(void);
#endif
#ifdef WIN32
/* $Id$ */
#define RAISE(e) Except_raise(&(e), __FILE__, __LINE__)
#define RERAISE Except_raise(Except_frame.exception, \
    Except_frame.file, Except_frame.line)
#define RETURN switch (Except_pop(),0) default: return
#define TRY do { \
    volatile int Except_flag; \
    Except_Frame Except_frame; \
    if (Except_index == -1) \
        Except_init(); \
    Except_push(&Except_frame);  \
    Except_flag = setjmp(Except_frame.env); \
    if (Except_flag == Except_entered) {
#define EXCEPT(e) \
        if (Except_flag == Except_entered) Except_pop(); \
    } else if (Except_frame.exception == &(e)) { \
        Except_flag = Except_handled;
#define ELSE \
        if (Except_flag == Except_entered) Except_pop(); \
    } else { \
        Except_flag = Except_handled;
#define FINALLY \
        if (Except_flag == Except_entered) Except_pop(); \
    } { \
        if (Except_flag == Except_entered) \
            Except_flag = Except_finalized;
#define END_TRY \
        if (Except_flag == Except_entered) Except_pop(); \
        } if (Except_flag == Except_raised) RERAISE; \
} while (0)
#else
#define RAISE(e) Except_raise(&(e), __FILE__, __LINE__)
#define RERAISE Except_raise(Except_frame.exception, \
    Except_frame.file, Except_frame.line)
#define RETURN switch (Except_stack = Except_stack->prev,0) default: return
#define TRY do { \
    volatile int Except_flag; \
    Except_Frame Except_frame; \
    Except_frame.prev = Except_stack; \
    Except_stack = &Except_frame;  \
    Except_flag = setjmp(Except_frame.env); \
    if (Except_flag == Except_entered) {
#define EXCEPT(e) \
        if (Except_flag == Except_entered) Except_stack = Except_stack->prev; \
    } else if (Except_frame.exception == &(e)) { \
        Except_flag = Except_handled;
#define ELSE \
        if (Except_flag == Except_entered) Except_stack = Except_stack->prev; \
    } else { \
        Except_flag = Except_handled;
#define FINALLY \
        if (Except_flag == Except_entered) Except_stack = Except_stack->prev; \
    } { \
        if (Except_flag == Except_entered) \
            Except_flag = Except_finalized;
#define END_TRY \
        if (Except_flag == Except_entered) Except_stack = Except_stack->prev; \
        } if (Except_flag == Except_raised) RERAISE; \
} while (0)
#endif
#undef T
#endif

を除く.c

static char rcsid[] = "$Id$" "\n$Id$";
#include <stdlib.h>
#include <stdio.h>
#include "assert.h"
#include "except.h"
#define T Except_T
Except_Frame *Except_stack = NULL;
void Except_raise(const T *e, const char *file,
    int line) {
#ifdef WIN32
    Except_Frame *p;

    if (Except_index == -1)
        Except_init();
    p = TlsGetValue(Except_index);
#else
    Except_Frame *p = Except_stack;
#endif
    assert(e);
    if (p == NULL) {
        fprintf(stderr, "Uncaught exception");
        if (e->reason)
            fprintf(stderr, " %s", e->reason);
        else
            fprintf(stderr, " at 0x%p", e);
        if (file && line > 0)
            fprintf(stderr, " raised at %s:%d\n", file, line);
        fprintf(stderr, "aborting...\n");
        fflush(stderr);
        abort();
    }
    p->exception = e;
    p->file = file;
    p->line = line;
#ifdef WIN32
    Except_pop();
#else
    Except_stack = Except_stack->prev;
#endif
    longjmp(p->env, Except_raised);
}
#ifdef WIN32
_CRTIMP void __cdecl _assert(void *, void *, unsigned);
#undef assert
#define assert(e) ((e) || (_assert(#e, __FILE__, __LINE__), 0))

int Except_index = -1;
void Except_init(void) {
    BOOL cond;

    Except_index = TlsAlloc();
    assert(Except_index != TLS_OUT_OF_INDEXES);
    cond = TlsSetValue(Except_index, NULL);
    assert(cond == TRUE);
}

void Except_push(Except_Frame *fp) {
    BOOL cond;

    fp->prev = TlsGetValue(Except_index);
    cond = TlsSetValue(Except_index, fp);
    assert(cond == TRUE);
}

void Except_pop(void) {
    BOOL cond;
    Except_Frame *tos = TlsGetValue(Except_index);

    cond = TlsSetValue(Except_index, tos->prev);
    assert(cond == TRUE);
}
#endif
于 2010-09-21T19:00:29.887 に答える
1

複数レベルのジャンプをしたい場合は、検索しsetjmp()longjmp()ください。これらは、プリミティブ例外スローとして使用できます。このsetjmp()関数は、戻る場所を設定し、ステータス値を返します。longjmp()関数は戻り先の場所に移動し、ステータス値を提供します。ステータス値に応じてcatch後で呼び出された関数を作成できます。setjmp()

何らかの理由で、C++ で使用しないでください。スタックの巻き戻しやデストラクタの呼び出しは行いません。

于 2010-09-21T17:03:53.277 に答える
0

C ++は元々Cプリプロセッサとして実装されており、Try / Catchが含まれていたため、Bjarne Stroustrupの作業をやり直して、それを行うためのプリプロセッサを作成できました。

于 2010-09-21T17:16:31.423 に答える