CAPU stands for Collections and Platform Utils. It is a library providing both an implementation of a modified C++ STL interface and a platform abstraction framework. This library has been designed for projects running on multiple hardware platforms and/or operation systems.
The CAPU build scripts are based on CMake, but we avoided writing plain CMake build scripts for each module of the project. Instead we use a centralized set of CMake macros for project configuration, which are called ACME2 ("Another CMake Extension 2"). Having centralized scripts greatly improves maintainability of our build scripts and guarantees the same behaviour for each module.
Concerning build environment setup, ACME2 is fully transparent, therefore CAPU can be treated as standard CMake project.
There's one special behaviour, which is uncommon for CMake based projects: CAPU requires the use of a toolchain file to configure itself for the given platform. These example toolchain files are located in the following directory: cmake/toolchain
Example to generate a Makefile build environment on Linux 64 bit host:
$ mkdir <build-dir>
$ cd <build-dir>
$ cmake -DCMAKE_TOOLCHAIN_FILE=<capu-root>/cmake/toolchain/Linux_X86_64.toolchain <capu-root>
$ make
If you use the cmake-gui you need to select the option "Specify toolchain file for cross-compiling" during the configure step.
The CAPU unit tests are based on the Google C++ Testing Framework (https://github.com/google/googletest). This dependency is already contained in the Git repository as a Git submodule. In order to checkout the repository, please execute the following Git command:
$ cd <capu-root>
$ git submodule update --init --recursive
In order to enable the CAPU unit tests, you need to activate the CMake option Capu_BUILD_TESTS. Command line:
$ cmake -DCapu_BUILD_TESTS:BOOL=ON -DCMAKE_TOOLCHAIN_FILE=<capu-root>/cmake/toolchain/Linux_X86_64.toolchain <capu-root>
On multiplatform projects normally you don’t want to use the STL implementation shipped with the target’s compiler in order to avoid getting in trouble with different behaviors, error handling mechanisms or performance issues.
Why we don’t use existing implementations?
- In the embedded world you often have the requirement that C++ features as RTTI and exceptions are not supported by the target platform. Therefore we implemented our STL containers with an STL-like interface which works without those features but enables error management by error codes.
- Furthermore we are trying to enable both static and dynamic containers in respect to memory management by using the same interface. So in the prototyping phase you are fine to use dynamically allocated memory blocks, when it goes to product you can become stricter without changing your code.
At the moment we support only a subset of the known STL constructs:
- Array (static + dynamic)
- Queue, Blocking Queue
- Hash table, Hash set
- List
- Ring buffer
- Stack
- String, ConstString
- Vector
The platform abstraction itself has been designed in a way that it is easy to maintain and extend without any IFDEF blocks in the code itself. Furthermore if the platform abstraction is the same on multiple platforms (e.g. ARM and x86, Linux and QNX) no code duplication is needed.
At the moment we have an abstraction for the following platforms/OS:
- Linux (X86 32/64 bit, ARMv7)
- QNX (X86 32 bit)
- GreenHills Integrity (ARM)
- Windows (32/64 bit)
- Mac OSX (64 bit)
- Android (ARMv7)
The following features are available on those platforms:
- Atomic Operations
- Condition Variables
- Console abstraction
- Debug (Assertions)
- Dynamic Library
- Environment variables
- File, Filesystem iterator
- Math
- Memory
- Mutex
- Numeric Limits
- Random numbers
- Semaphores
- StringUtils
- TcpSocket, UdpSocket
- Thread
- Time
- BinaryInputStream, BinaryOutputStream: Classes for serializing and reading/writing primitive data type from/to binary files or TCP sockets
- Logger, Console appender Logger implementation with expandable appender interface
- ScopedLock, ReadWriteLock, CountDownLatch, easy synchronization Classes for easier synchronization in multi-thread environments
- File utils, File system traverser File / file system utils often needed when dealing with files
- SmartPointer, ScopedPointer Automatic memory management by using reference counting
- Memory allocators (static, dynamic, hybrid) Memory allocation abstraction, e.g. the any STL container data type could use static or dynamic memory pool inside. A hybrid allocator uses static memory and tries to get more memory dynamically, if needed.
- String tokenizer Utils working on strings
- Traits Usefuls traits when dealing with templates
- Threadpool
All mentioned components are heavily tested by using the GoogleMock/GoogleTest framework. Furthermore it is possible to execute the test executable on the all of the mentioned platforms.