Name Last Update
docs Loading commit data...
include Loading commit data...
m4 Loading commit data...
src Loading commit data...
tests Loading commit data...
.gitignore Loading commit data...
.gitlab-ci.yml Loading commit data...
AUTHORS Loading commit data...
COPYING Loading commit data...
ChangeLog Loading commit data...
Makefile.am Loading commit data...
NEWS Loading commit data...
README Loading commit data...
README.html Loading commit data...
README.md Loading commit data...
TODO Loading commit data...
bootstrap Loading commit data...
configure.ac Loading commit data...

LIBTRBASE

As the very basic building block of taskrambler this library provides a system to create basic classes and interfaces in ANSI C, some common macro definitions (always useful) and an optimized memory management system.

The class system

The class system is not what you know from "real" object oriented languages. It only provides these few OOP features:

  • code and data encapsulation
  • late binding through interfaces
  • inheritance

I created it with the idea that at least for me the most useful part of object orientation is the ability to create interfaces and bind them to a specific data structure. This binding in turn enables injection which for me is by far the most important type of object combination.

The second important thing, mostly to reduce code duplication, is inheritance.

The class system combines C structures with one or more interfaces. Each of these interfaces consists of one ore more pointers to functions which must be set during the class creation. These functions are then called via selector functions which have to be defined for the specific interface.

The interface of the class system consists of Preprocessor definitions which provide an easy way to create the neccessary data structures and definitions. There are also definitions for calling the correct interface implementation for a given class instance. If no implementation can be found in the class of the current object it will be looked for in the parent class if there is one and so forth.

The ideas for this are partly derived from the Book "Object Oriented Programming in ANSI C" from Axel-Tobias Schreiner.

In the examples I will show how libtrbase supports you in creating these structures.

The optimized memory management

Allocating and freeing memory on the heap is an expensive action. And because of fragmentation effects it might become even more expensive if the process runs really long.

To overcome this issue I implemented a memory management which never really free's a given memory region, instead it stores the address in a ballanced btree indexed by the size of the memory segment. Multiple adresses for memory of the same size are simply listed. The next allocation first looks in this btree if there is a fitting segment (same size or larger as the requested segment) and returns it if available.

For the ballanced btree I use red-black trees. Thanks to the authors of the Wikipedia article. This implementation is more or less taken from there, with only some small changes here and there.

The idea is based on an idea found on this page of the The Joseph M. Newcomer Co. The concrete idea was first described by Charles B. Weinstock in his Ph.D. dissertation on storage allocation.

INSTALLATION

This can be installed via the usual configure, make, make install cycle. For gentoo users am ebuild is added under docs.

API DOC

To generate the api doc a patched version of doxygen is neccessary. A patch is included under docs.

make docs creates the api doc.

TEST COVERAGE REPORT

gcov and lcov are needed to build these.

The source has to be configured with configure --enable-gcov. make coverage-html creates the converage reports then.

USAGE

API

The public API consists of several preprocessor macros and some functions. When you look through the code you will find other symbols (functions or macros) which might seem useful. Here I try to focus on the neccessaties for using the library.

function-like macros - class creation

  • TR_CLASS(name): Declare a new class.
  • TR_CREATE_CLASS(name, parent, ...): Create a new class.
  • TR_EXTENDS(parent): Extend another class

function-like macros - class information

  • TR_GET_CLASS(object): Get the class of the given object.
  • TR_HAS_PARENT(class): Check if the class extends another class.
  • TR_IS_OBJECT(obj): Check that the given pointer is really an instance of a class.
  • TR_INSTANCE_OF(class, obj): Check that the given obj is an instance of class.

function-like macros - interface selector helper

  • TR_CALL(object, iface, method, ...): Call the interface implementation of the class or one of the parent classes of object.
  • TR_RETCALL(object, iface, method, ret, ...): Same as TR_CALL but with return value.
  • TR_PARENTCALL(object, iface, method, ...): Directly call the implementation within the parent class.

function-like macros - interface creation

  • TR_INTERFACE(name): Declare a new inerface.
  • TR_CREATE_INTERFACE(name, nfunc): Create the new interface.
  • TR_INIT_IFACE(name, ...): Create an interface implementation and assign functions to it.
  • TR_IF(name): Convenience for getting an interface implementation by name. Used when assigning an interface to a class.

function-like macros for the class interface

The valious constructor and destructors are also assigned to an interface. The is the only interface every class MUST have so that instances can be created and destroyed. At least a constructor and a destructor must be assigned to this interface. The following selectors then utilize the interface to create and destroy instances.

  • TR_new(class, ...): Create a new instance of a class with a variable argument list for the constructor interface.
  • TR_newv(class, args): Same as TR_new but accepts a va_list* for the constructor arguments.
  • TR_delete(object): Destroy an instance.
  • TR_clone(object): Call an constructor that accepts another instance to create a clone from this instance.

selector functions subject/observer interface

These are in fact two interfaces that can be used to implement the subject/observer design pattern.

  • TR_subjectAttach(Subject, Observer): Add an observer to a subject. The concrete implementation has to take care of memory management etc.
  • TR_subjectDetach(Subject, Observer): Remove an observer from a subject.
  • `TR_notify(Subject):* Notify all registered observer of an event.
  • TR_observerUpdate(Observer, Subject): This must be called in TR_subjectNotify to inform a registered observer of an event.

selector functions indexable interface

  • TR_getIndex(Indexable): Get a unique index of an instance. How this is created is subject of the concrete implementation.

selector functions

  • TR_serializedSize(Serializable): Get the size of the serialized instance.
  • TR_serialize(Serializable, unsigned char * serialized): Serialize the instance into the serialized buffer. The buffer has to be large enough to hold the serialized instance.
  • TR_unserialize(Serializable, const unsigned char * serialized, size_t ssize): Initialize the instance with the serialized data stored in serialized.

memory management - functions

  • void * TR_malloc(size_t):
  • void * TR_calloc(size_t, size_t):
  • void * TR_reference(void *):
  • size_t TR_getSize(void *):
  • void TR_cleanup():

memory management - macros

  • TR_MEM_FREE(seg):

EXAMPLE

optimized memory management

create a new class

create a new interface

implement an interface in a class

class extension

TESTING

This comes with the start of a unit test suite. You can use make test to build and run the existent tests.

REQUIREMENTS

Currently, you need valgrind to build the tests, because some memory checking is done by the way.

CONTRIBUTION

I would really like to see some people possibly interested in this stuff. I think it contains some really interesting ideas.

If you like to contribute anyway, make a fork, do your changes and generate a pull request. Or simply contact me on georg@steffers.org.