5

サードパーティのライブラリの問題を解決しようとしています。問題は、ライブラリがマクロに埋め込まれた GCC のネストされた関数を使用しており、Clang がネストされた関数をサポートしておらず、サポートする予定がないことです (参照: Clang Bug 6378 - error: illegal storage class on function )。

これが私と Clang の問題点であるマクロです。

#define RAII_VAR(vartype, varname, initval, dtor) \
    /* Prototype needed due to http://gcc.gnu.org/bugzilla/show_bug.cgi?id=36774 */ \
    auto void _dtor_ ## varname (vartype * v); \
    void _dtor_ ## varname (vartype * v) { dtor(*v); } \
    vartype varname __attribute__((cleanup(_dtor_ ## varname))) = (initval)

そして、これがどのように使用されるかです(コードコメントから):

 * void do_stuff(const char *name)
 * {
 *     RAII_VAR(struct mything *, thing, find_mything(name), ao2_cleanup);
 *     if (!thing) {
 *         return;
 *     }
 *     if (error) {
 *         return;
 *     }
 *     do_stuff_with_thing(thing);
 * }

Clang User Manualには、C++ とラムダ関数を使用してエミュレートすることが記載されています。それが最善の戦略であるかどうかはわかりません.CプロジェクトはC++パッチを受け入れない可能性があります.

(1) Clang により適応し、(2) 元の関数セマンティクスを保持するようにマクロを書き直す方法はありますか?

4

2 に答える 2

6

Clang は GCC のネストされた関数をサポートしていませんが、C モードであってもObjective C スタイルの「ブロック」をサポートしています。

void f(void * d) {
    void (^g)(void *) = ^(void * d){ };
    g(d);
}

clangではなくコマンドで呼び出す必要があり、 (?)コンパイラにgcc渡します。-fblocks -lBlocksRuntime

ブロックcleanupは関数名でなければならないため、値として直接使用することはできません。そのため (ここからアイデアを盗みます)、間接レイヤーを追加する必要があります。無効なブロックをクリーンアップする単一の関数を定義し、RAII の変数をスコープの最後で実行するブロックにします。

typedef void (^cleanup_block)(void);
static inline void do_cleanup(cleanup_block * b) { (*b)(); }

void do_stuff(const char *name) {
    cleanup_block __attribute__((cleanup(do_cleanup))) __b = ^{ };
}

ブロックはクロージャーを形成するため、変数に操作を配置して、そのブロック内で直接クリーンアップできます...

void do_stuff(const char *name) {
    struct mything * thing;
    cleanup_block __attribute__((cleanup(do_cleanup))) __b = ^{ ao2_cleanup(thing); };
}

...そして、ブロックのクリーンアップによって呼び出されて、以前と同じようにスコープの最後で実行する必要があります。マクロを再配置して追加し、__LINE__複数の宣言で機能するようにします。

#define CAT(A, B) CAT_(A, B)
#define CAT_(A, B) A##B

#define RAII_VAR(vartype, varname, initval, dtor) \
    vartype varname = (initval); \
    cleanup_block __attribute__((cleanup(do_cleanup))) CAT(__b_, __LINE__) = ^{ dtor(varname); };

void do_stuff(const char *name) {
    RAII_VAR(struct mything *, thing, NULL, ao2_cleanup);
    ...

とにかく、そのようなもの。

于 2014-07-25T16:45:07.193 に答える
1

私はあなたがclang固有のバージョンを使わずにこれを行うことができると信じています.私はこのようなことを試してみます.

struct __destructor_data {
    void (*func)(void *);
    void **data;
}

static inline __destructor(struct __destructor_data *data)
{
    data->func(*data->data);
}

#define RAII_VAR(vartype, varname, initval, dtor)  \
    vartype varname = initval;                     \
    __attribute((cleanup(__destructor)))           \
        struct __destructor_data __dd ## varname = \
             { dtor, &varname };

私たちのプロジェクトでは_auto_(dtor)、通常の変数宣言の前に gcc 固有のマクロがあります。

_auto_(free) char *str = strdup("hello");

この場合、私たちのマクロは変数宣言のに何も追加できず、変数の名前も知らないため、gcc 固有のネストされた関数の使用を避けるために、これが誰かに役立つ場合に備えて、次のハック バージョンを思いつきました。

static void *__autodestruct_value = NULL;
static void (*__autodestruct_dtor)(void *) = NULL;

static inline void __autodestruct_save_dtor(void **dtor)
{
       __autodestruct_dtor = *dtor;
       __autodestruct_dtor(__autodestruct_value);
}

static inline void __autodestruct_save_value(void *data)
{
       __autodestruct_value = *(void **) data;
}

#define __AUTODESTRUCT(var, func)                              \
       __attribute((cleanup(__autodestruct_save_dtor)))      \
               void *__dtor ## var = (void (*)(void *))(func); \
       __attribute((cleanup(__autodestruct_save_value)))
 
#define _AUTODESTRUCT(var, func)                       \
       __AUTODESTRUCT(var, func)

#define _auto_(func)                                    \
        _AUTODESTRUCT(__COUNTER__, func)

これは、宣言の順序の逆であるコンパイラによってデストラクタが呼び出される順序に依存するため、ハックです。gcc 固有のバージョンと比較して明らかな欠点がいくつかありますが、両方のコンパイラで動作します。

于 2021-05-17T22:01:36.363 に答える