This question is about gcc constructor, compile & link is right, but it NOT run.
There is a.c:
UTEST_BEGIN()
UID(a_test)
{
printf("a test");
return true;
}
UTEST_END(a)
b.c is simlar:
UTEST_BEGIN()
UID(b_test)
{
printf("b test");
return true;
}
UTEST_END(b)
The code object is using UID() link some test functions. My first version add UTEST_BEGIN() UTEST_END() to enclose UID(), at last I realize UTEST_BGIN() UTEST_END() isn't necessary, when I change them get unpredicated result.
when I change the definition of UTEST_BEGIN(), UID(), UTEST_END(), I got different result.
The basic idea come from can-i-auto-collect-a-list-of-function-by-c-macro!
Test 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; \
}
I got right result. I can call __uti_a_test() and __uti_b_test() through a link. In fact, the __uti_xxx() link is NOT realated with __m_uti_head, so I want to remove UTEST_BEGIN() & UTEST_END().
run gcc -E a.c, the macro extend as:
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;
}
Test 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)
The definition of UID() is same as Test 1. I keep UTEST_BEGIN() & UTEST_END() as blank. Compile & Link is right, But uti_construct_a_test() & uti_construct_b_test() NOT execute.
run gcc -E a.c, the macro extend as:
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;
}
The utest_item_list_add_global() is exist in other .c file, the function add a node into a link:
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;
}
The expanded macor is seem as right. I think the problem is in link stage, am I right?
I found gcc attribute((constructor)) have below fact:
cons.c is a file which contain constructor function.
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");
}
test 1:
main.c:
#include <stdio.h>
int main(void)
{
printf("this is main\n");
}
compile by:
gcc -c cons.c
ar cqs libcon.a cons.o
gcc main.c libcon.a
output is: this is main
test 2:
main.c:
#include <stdio.h>
extern void cons(void);
int main(void)
{
cons();
printf("this is main\n");
}
compile by:
gcc -c cons.c
ar cqs libcon.a cons.o
gcc main.c libcon.a
output:
this is a constructor
this is cons
this is main
test 3:
main.c
#include <stdio.h>
int main(void)
{
printf("this is main\n");
}
compile by:
gcc main.c cons.c
output:
this is a constructor
this is main
run "gcc -v", output:
Using built-in specs. COLLECT_GCC=gcc COLLECT_LTO_WRAPPER=/usr/libexec/gcc/i686-redhat-linux/4.7.2/lto-wrapper Target: i686-redhat-linux Configured with: ../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 Thread model: posix gcc version 4.7.2 20121109 (Red Hat 4.7.2-8) (GCC)
My question is:
Only constructor is exist in a .c file, compile it as static library, Why gcc ignore the construct? How to avoid it?
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With