A counter examples for writing a share library
#ifndef UTILS_H
#define UTILS_H
#include <iostream>
#include <string>
void LOG(std::string s)
{
std::cout << s << std::endl;
}
#endif /* UTILS_H */
CXX=g++
CFLAGS=-Wall
EXEC=run
all: bye.o hello.o
$(CXX) $(CFLAGS) main.cpp bye.o hello.o -o $(EXEC)
bye.o: bye.cpp
$(CXX) -c $(CFLAGS) bye.cpp
hello.o: hello.cpp
$(CXX) -c $(CFLAGS) hello.cpp
clean:
rm $(EXEC) *.o
#include "bye.h"
#include "hello.h"
int main()
{
SayHello();
SayBye();
return 0;
}
#ifndef HELLO_H
#define HELLO_H
void SayHello();
#endif /* HELLO_H */
#include "hello.h"
#include "utils.h"
void SayHello()
{
LOG("Hello");
}
#ifndef BYE_H
#define BYE_H
void SayBye();
#endif /* BYE_H */
#include "bye.h"
#include "utils.h"
void SayBye()
{
LOG("Goodbye");
}
We will have duplicate symbol for architecture x86_64
when we compile the programs as follows:
$ g++ -c -Wall bye.cpp # Generate bye.o
$ g++ -c -Wall hello.cpp # Generate hello.o
$ g++ -Wall main.cpp bye.o hello.o -o run
duplicate symbol __Z3LOGNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEE in:
bye.o
hello.o
ld: 1 duplicate symbol for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
make: *** [all] Error 1
The reason is that we include the shared header(utils.h
)
into different files(bye.cpp
and hello.cpp
),
and compile those files into different libraries(bye.o
and hello.o
),
so the functions in the shared header(LOG
)
duplicate in those different libraries.
Thus, when we try using those different libraries(bye.o
and hello.o
)
at the same time, there are duplicated symbols for functions(LOG
)
included from the shared header(utils.h
).
The program has no idea about which one it should call
among those duplicated symbols.
The macro is only textual substitution that expanded by the preprocessor, so there is no symbol generated.
You can replace function LOG
by
#define LOG(s) (std::cout << s << std::endl)
Then the SayBye()
will be expanded into:
void SayBye()
{
(std::cout << "Goodbye" << std::endl);
}
You can run: $ g++ -E <file_name>.cpp
to watch
and confirm the preprocessor's output.
It works almost same as macro. Inline functions are actual functions whose copy of the function body are injected directly into each place the function is called.
inline void LOG(std::string s)
{
std::cout << s << std::endl;
}
The insertion occurs only if the compiler's cost/benefit analysis shows it to be profitable. Same as the macros, inline expansion eliminates the overhead associated with function calls.
Inline functions are parsed by the compiler, whereas macros are expanded by the preprocessor. The preprocessor macros are just substitution patterns in code before the compilation, so there is no type-checking at that time. While inline functions are actual functions, so compiler can keep an eye on type-checking issues to help debugging.
See here for more details.
Since their states are not sharable, they should not visible across each other. Thus, the generated symbols are also local in each file.
static void LOG(std::string s)
{
std::cout << s << std::endl;
}