C++ library programming
Table of Contents
static libraries
filter.h
#ifndef FILTER_H_ #define FILTER_H_ class Filter { public: virtual void process(int v) = 0; }; #endif // FILTER_H_
low_pass_filter.h
and low_pass_filter.cc
#ifndef LOW_PASS_FILTER_H_ #define LOW_PASS_FILTER_H_ #include "filter.h" class LowPassFilter : public Filter { public: virtual void process(int v); }; #endif // LOW_PASS_FILTER_H_ #include <iostream> using namespace std; #include "low_pass_filter.h" void LowPassFilter::process(int v) { cout << "Low Pass Filter value = " << v << endl; }
program.cc
#include "filter.h" #include "low_pass_filter.h" using namespace std; int main() { Filter* filter = new LowPassFilter(); filter->process(10); return 0; }
g++ -Wall -c low_pass_filter.cc ar rcs libfilter.a ./low_pass_filter.o g++ -static program.cc -L. -lfilter -o program
dynamic libraries
ldd
List dynamic dependencies ldd
command is to list all necessary
libraries for given executable file.
Linux dynamic loader order
- On the path defined inside the environment variable
LD_LIBRARY_PATH
. - On the path defined inside the
/etc/ld.so.cache
file made from the/etc/ld.so.conf
file where user can list his own library paths. The act of creating/etc/ld.so.cache
is initiated by issuingldconfig
command after every modification of/etc/ld.so.conf
file. - On the
/lib/
and/usr/lib
paths.
soname
A soname is a field of data in a shared object file. The soname is a string, which is used as a "logical name" describing the functionality of the object.
The soname is often used to provide version backwards-compatibility information. For instance, if versions 1.0 through 1.9 of the shared library libx provide identical interface, they would all have the same soname, e.g. libx.so.1. If the system only includes version 1.3 of that shared object, with filename libx.so.1.3, the soname field of the shared object tells the system that it can be used to fill the dependency for a binary which was originally compiled using version 1.2.1
make and execute
Set the LD_LIBRARY_PATH
to the current directory because Linux dynamic
loader uses this variable to search for libraries.
g++ -Wall -fPIC -c low_pass_filter.cc g++ -shared -Wl,-soname,libfilter.so.1 -o libfilter.so.1.0 low_pass_filter.o ln -sf libfilter.so.1.0 libfilter.so ln -sf libfilter.so.1.0 libfilter.so.1 g++ -Wall -L. program.cc -lfilter -o program export LD_LIBRARY_PATH=. ./program
dynamic loaded(DL) libraries
name mangling
In any executable or shared library, all non-static functions are
represented by a symbol, which is usually the same as the function
name, and represents the start address of the function in memory. In
C, the symbol is the same as the function name — e.g., the symbol for
the init
function will be init
, since no two functions can have the
same name.
However, in C++, because of overloading and polymorphism in classes, it is possible to have the same name for two functions in a program. Hence, it’s not possible to use the function name as the symbol. To solve this problem of having two functions with the same name, C++ compilers use name-mangling techniques, in which they change the symbol names. Name mangling makes it difficult for programmers to access a specific symbol in the compiled shared library file, even if they know the original function name.
The solution to this issue is to use the special keyword extern “C”
before the function implementation (i.e., extern "C" void
function_name()
). This tells the C++ compiler to compile the
function in C style — to keep the symbol name the same as the function
name.
example
- change the original
low_pass_filter.cc
to:
#include <iostream> using namespace std; #include "low_pass_filter.h" extern "C" { void* loadFilter(void) { return reinterpret_cast<void *>(new LowPassFilter()); } void deleteFilter(void* obj) { delete reinterpret_cast<LowPassFilter*>(obj); } } void LowPassFilter::process(int v) { cout << "Low Pass Filter value = " << v << endl; }
- change
program.cc
to dynamic load this library.
#include <cstdlib> #include <cstdio> #include <dlfcn.h> #include "filter.h" using namespace std; int main() { char *error; void* library = dlopen("./libfilter.so", RTLD_LAZY); if (!library) { fputs (dlerror(), stderr); exit(1); } typedef void* CreateT(); CreateT* loadFilter = (CreateT*)dlsym(library, "loadFilter"); if ((error = dlerror()) != NULL) { fputs(error, stderr); exit(1); } void* filter_vp = (*loadFilter)(); Filter* filter = reinterpret_cast<Filter*>(filter_vp); filter->process(10); typedef void* DestoryT(void*); DestoryT* deleteFilter = (DestoryT*)dlsym(library, "deleteFilter"); (*deleteFilter)(filter_vp); return 0; }
used dlopen
to load the library, then retrieved references to the
symbols for our two access functions with dlsym
, and then via these,
first invoked the create function to get an instance of the shared
object, and then destroyed the object.
- compile program without link with filter library.
g++ -Wall -fPIC -c low_pass_filter.cc g++ -shared -Wl,-soname,libfilter.so.1 -o libfilter.so.1.0 low_pass_filter.o ln -sf libfilter.so.1.0 libfilter.so ln -sf libfilter.so.1.0 libfilter.so.1 g++ -Wall program.cc -o program -ldl