tags:

views:

271

answers:

2

I have a problem. I wrote example code and I want to build it without the error:

main.cpp(.text+0x5): undefined reference to `test()'

Library


test1.c

#include <stdlib.h>
void test()
{
 puts("Działa");
}

test1.h

#ifndef TEST1_H
#define TEST1_H

extern void test();

#endif

makefile

all:
 gcc -c ./src/test1.c -o ./lib/test1.o
 ar rcs ./lib/libtest1.a ./lib/test1.o

Program


main.cpp

#include <test1.h>

int main()
{
 test();
 return 0;
}

makefile

all:
 g++ -static -I../test1/include -L../test1/lib ./src/main.cpp -o ./build/MyApp -ltest1

What am I doing wrong?

+10  A: 

You are compiling a C code function, but you are expecting to link a C++ function.

Because of 'type safe linkage', the function you provide is not the function that the C++ code calls.

Either in test1.h use:

#ifdef __cplusplus
extern "C" {
#endif

extern void test1(void);

#ifdef __cplusplus
}
#endif

Or:

  • Compile the function with the C++ compiler.

The C++ compiler will mangle the symbol names to provide type-safe linkage (a term which you should be able to search for via your preferred search engine).

The 'compiler' - actually the linker - is looking for a function with a C++ mangled name representing the C++ function with the signature 'void test1(void);'.

For example (but remember - different compilers deliberately mangle things differently), G++ 4.2.1 on MacOS X 10.6.2 generates a symbol '__Z5test1v' for the function; GCC generates a symbol '_test1'. Clearly, when the linker is looking for '__Z5test1v', the symbol '_test1' is not going to be used - it is not spelled the same. This is a good thing.

You can use 'nm -g' on the object file for the main program to see what it is looking for, and on the object file in the library to see what it is providing. And, given that the spellings are different, that is why the loader does not pick up the library function - it is looking for something with a different name.

Jonathan Leffler
@Jonathan: This is a valid point, but it doesn't explain the compiler error.
quamrana
@quamrana: Yes it does; the 'compiler' - actually the linker - is looking for a function with a C++ mangled name representing the C++ function with the signature 'void test1(void);'. For example, G++ 4.2.1 on MacOS X 10.6.2 generates a symbol '__Z5test1v' for the function; GCC generates a symbol '_test1'. Clearly, when the linker is looking for '__Z5test1v', the symbol '_test1' is not going to be used - it is not spelled the same.
Jonathan Leffler
@Jonathan: Oh, I see - I keep forgetting that gcc can link as well as compile. +1 for an improved answer.
quamrana
@Jonathan: Is that how gcc can give you a line number for a linker error? What happens if many lines were referencing an undefined symbol?
quamrana
@quamrana: in the example, the compiler is not giving you a line number; it is giving you an offset into the text (code) where the reference appears. If you have debug symbols (compiled with '`-g`'), then there is information in the object file about the line numbers etc, and you might get better information.
Jonathan Leffler
+3  A: 

You are calling a C function from a C++ function. The naming between the two is different (C++ mangles names to include parameter information).

Change the header file to look like this:

#ifdef __cplusplus
extern "C" {
#endif

extern void test();

#ifdef __cplusplus
}
#endif

This will tell the compiler that the function follows the C naming/calling convention.

Gordon
Is the semi-colon after the close brace required, optional, or 'must be absent'? I looked at some of my code and the semi-colon is absent, so that's how I wrote it in my answer. You have it present...do you know who's more nearly correct?
Jonathan Leffler
@Jonathan It shouldn't nbe there - g++ compiled with -Wall and -pedantic will warn about it. It's purpose is to terminate the list of names that can come after a class, struct etc. but there can't be such a list after an extern block.
anon
Agreed. Semi-colon should not be there. Just a habit from class declarations...
Gordon