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

  1. On the path defined inside the environment variable LD_LIBRARY_PATH.
  2. 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 issuing ldconfig command after every modification of /etc/ld.so.conf file.
  3. 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

  1. 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;
}
  1. 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.

  1. 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

More reference

Footnotes:

Author: Shi Shougang

Created: 2015-10-18 Sun 19:28

Emacs 24.3.1 (Org mode 8.2.10)

Validate