3

この質問はgccコンストラクターに関するもので、コンパイルとリンクは正しいですが、実行されません。

交流があります:

UTEST_BEGIN()
UID(a_test)
{
    printf("a test");
    return true;
}
UTEST_END(a)

bc も同様です。

UTEST_BEGIN()
UID(b_test)
{
    printf("b test");
    return true;
}
UTEST_END(b)

コード オブジェクトは、いくつかのテスト関数にリンクする UID() を使用しています。私の最初のバージョンでは、UID() を囲むために UTEST_BEGIN() UTEST_END() を追加しました。最後に、UTEST_BGIN() UTEST_END() が必要ではないことに気付きました。変更すると、予期しない結果が得られます。

UTEST_BEGIN()、UID()、UTES_END() の定義を変更すると、異なる結果が得られました。

基本的なアイデアはcan-i-auto-collect-a-list-of-function-by-c-macroから来ています!

テスト 1:

#define UTEST_BEGIN()                                   \
static const bool __m_en = true;                        \
static struct __uti *__m_uti_head = NULL;



bool utest_item_list_add_global(struct __uti *uti);
#define UID(f)                                                          \
static bool __uti_##f(void);                                            \
__attribute__((constructor))                                            \
static void uti_construct_##f(void)                                     \
{                                                                       \
    printf("%s\n", #f); \
    static struct __uti __m_uti_##f = {NULL, this_file_id, __uti_##f, #f };       \
    utest_item_list_add_global(&__m_uti_##f);                           \
}                                                                       \
static bool __uti_##f(void)


bool unit_test_item_pump_do(int file_id, bool (*f)(void), const char *f_name);
#define UTEST_END(file_name)                                            \
bool unit_test_##file_name(void)                                        \
{                                                                       \
    if (!__m_en)                                                        \
            return true;                                                \
    struct __uti *cur;                                                  \
    for(cur = __m_uti_head; cur; cur = cur->next) {                     \
            unit_test_set_run_last_line(__LINE__);                      \
            if (!unit_test_item_pump_do(this_file_id, cur->f, cur->f_name)) \
                    return false;                                       \
    }                                                                   \
    return true;                                                        \
}

正しい結果が得られました。リンクから __uti_a_test() と __uti_b_test() を呼び出すことができます。実際、__uti_xxx() リンクは __m_uti_head と関連付けられていないため、UTEST_BEGIN() と UTEST_END() を削除したいと考えています。

gcc -E ac を実行すると、マクロは次のように拡張されます。

static const bool __m_en = 1; 
static struct __uti *__m_uti_head = ((void *)0);

static bool __uti_a_test(void); 
__attribute__((constructor)) 
static void uti_construct_a_test(void) 
{ 
    static struct __uti __m_uti_a_test = {((void *)0), file_id_a, __uti_a_test, "a_test" }; 
    utest_item_list_add_global(&__m_uti_a_test); 
} 
static bool __uti_a_test(void)
{
    printf("a test");
    return 1;
}


bool unit_test_a(void) 
{ 
    if (!__m_en) 
        return 1; 
    struct __uti *cur; 
    for(cur = __m_uti_head; cur; cur = cur->next) { 
        unit_test_set_run_last_line(19); 
        if (!unit_test_item_pump_do(file_id_a, cur->f, cur->f_name)) 
            return 0; 
    } 
    return 1; 
}

テスト 2:

#define UTEST_BEGIN()



bool utest_item_list_add_global(struct __uti *uti);
#define UID(f)                                                          \
static bool __uti_##f(void);                                            \
__attribute__((constructor))                                            \
static void uti_construct_##f(void)                                     \
{                                                                       \
    printf("%s\n", #f);                                                 \
    static struct __uti __m_uti_##f = {NULL, this_file_id, __uti_##f, #f };       \
    utest_item_list_add_global(&__m_uti_##f);                           \
}                                                                       \
static bool __uti_##f(void)


#define UTEST_END(file_name)

UID() の定義はテスト 1 と同じです。 UTEST_BEGIN() と UTEST_END() は空白のままにします。コンパイルとリンクは正しいが、uti_construct_a_test() と uti_construct_b_test() は実行されない。

gcc -E ac を実行すると、マクロは次のように拡張されます。

static bool __uti_a_test(void); 
__attribute__((constructor)) 
static void uti_construct_a_test(void) 
{ 
    static struct __uti __m_uti_a_test = {((void *)0), file_id_a, __uti_a_test, "a_test" }; 
    utest_item_list_add_global(&__m_uti_a_test); 
} 
static bool __uti_a_test(void)
{
    printf("a test");
    return 1;
}

utest_item_list_add_global() は他の .c ファイルに存在し、関数はノードをリンクに追加します。

static struct __uti *m_uti_head = NULL;
bool utest_item_list_add_global(struct __uti *uti)
{
        if (NULL == m_uti_head) {
                m_uti_head = uti;
                return true;
        }

        struct __uti *tail = m_uti_head;
        while (NULL != tail->next)
                tail = tail->next;
        tail->next = uti;
        return true;
}

展開されたマクロは正しいようです。問題はリンク段階にあると思いますが、そうですか?

4

1 に答える 1

4

gcc属性((コンストラクタ))には以下の事実があることがわかりました:

cons.c は、コンストラクター関数を含むファイルです。

  1. cons.c ファイルにコンストラクターしか存在しない場合は、それをスタティック ライブラリとしてコンパイルし、main() でリンクすると、コンストラクターは無視されます。
  2. main.c で呼び出される関数が cons.c に存在する場合、cons.c をスタティック ライブラリとしてコンパイルし、main() でリンクすると、コンストラクタは main の前に呼び出されます。
  3. "gcc main.c cons.c" を使用すると、メインの前にコンストラクターが呼び出されます。

cons.c:

#include <stdio.h>
static void __attribute__((constructor)) construct_fun(void)
{
        printf("this is a constructor\n");
}

void cons(void)
{
        printf("this is cons\n");
}

テスト 1:

main.c:

#include <stdio.h>
int main(void)
{
        printf("this is main\n");
}

コンパイル:

gcc -c cons.c
ar cqs libcon.a cons.o
gcc main.c libcon.a

出力は次のとおりです。これはメインです

テスト 2:

main.c:

#include <stdio.h>
extern void cons(void);
int main(void)
{
        cons();
        printf("this is main\n");
}

コンパイル:

gcc -c cons.c
ar cqs libcon.a cons.o
gcc main.c libcon.a

出力:

this is a constructor
this is cons
this is main

テスト 3:

main.c

#include <stdio.h>
int main(void)
{
        printf("this is main\n");
}

コンパイル:

gcc main.c cons.c

出力:

this is a constructor
this is main

「gcc -v」を実行すると、次のように出力されます。

組み込みの仕様を使用します。COLLECT_GCC=gcc COLLECT_LTO_WRAPPER=/usr/libexec/gcc/i686-redhat-linux/4.7.2/lto-wrapper ターゲット: i686-redhat-linux 構成: ../configure --prefix=/usr --mandir=/ usr/share/man --infodir=/usr/share/info --with-bugurl= http://bugzilla.redhat.com/bugzilla--enable-bootstrap --enable-shared --enable-threads=posix --enable-checking=release --disable-build-with-cxx --disable-build-poststage1-with-cxx --with-system- zlib --enable-__cxa_atexit --disable-libunwind-exceptions --enable-gnu-unique-object --enable-linker-build-id --with-linker-hash-style=gnu --enable-languages=c, c++,objc,obj-c++,java,fortran,ada,go,lto --enable-plugin --enable-initfini-array --enable-java-awt=gtk --disable-dssi --with-java-home =/usr/lib/jvm/java-1.5.0-gcj-1.5.0.0/jre --enable-libgcj-multifile --enable-java-maintainer-mode --with-ecj-jar=/usr/share/ java/eclipse-ecj.jar --disable-libjava-multilib --with-ppl --with-cloog --with-tune=generic --with-arch=i686 --build=i686-redhat-linux スレッド モデル: posix gcc バージョン 4.7.2 20121109 (Red Hat 4.7.2-8) (GCC)

私の質問は:

.c ファイルにはコンストラクターのみが存在し、静的ライブラリとしてコンパイルします。なぜ gcc はコンストラクトを無視するのですか? それを避ける方法は?

于 2013-04-23T05:56:41.277 に答える