MAK Data Logger API Documentation for DIS
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Groups Pages
6 - Logger API Coding Conventions

Table of Contents

This section describes some of the coding mechanisms used in the Logger API.

6.1 Namespaces

The Logger uses API namespaces where possible to limit symbol conflict with other APIs. The Logger API uses the MAKLogger namespace. When you use Logger API classes, you have the following options for specifying the required namespace:

6.1.1 Examples

// foo.h
namespace MAKLogger
{
class DtLogger;
}
class Foo
{
...
};
//foo.cxx
#include “foo.h”
// Import a single class
//or import them all
using namespace MAKLogger;
Foo() : myLogger( DtLogger::create( ) )
{
}

6.2 Templates

The Logger API uses C++ templates and template specializations. Therefore, to use the API, you should be familiar with their use.

6.3 Shared Pointers

The Logger uses shared pointers (also known as smart pointers). These are template classes that act like normal pointers, but are reference counted and only deallocated when the last reference is lost. All shared pointer specializations are appended with the characters SP. The exception to this rule is DtResponse which is an alias to DtCommandSP. Shared pointers are not explicitly deleted and should always be passed by value (not by reference). They are safe to pass across thread boundaries; however the objects themselves are not.

// test.cxx
#include <vLSharedPtr.h>
typedef DtBoost::shared_ptr<Foo> FooSP;
void test()
{
FooSP aFoo = FooSP(new Foo( ) );
aFoo->doSomething();
Foo* anUnsafeFoo = aFoo.get();
// No need to delete since there is only one reference to aFoo.
// aFoo will be deallocated on leaving the function
}

6.4 Virtual Constructors

The Logger implements a pattern for creating classes with virtual constructors. Virtual constructors allow objects to be replaced at runtime by enforcing a creating pattern via a static function pointer, which can be reassigned. Several macros are provided which make creating the necessary functions and members easier and smaller. Each type of virtual constructor has two macros, one for the declaration in the header, and one for the definition in the source.

Note
Argument lists must be surrounded by parentheses, including when there are no arguments.

The general format for a header file is:

DT_VIRTUAL_CTR( CLASS_NAME , ( TYPE_LIST ) )

The corresponding format for a source file is:

DT_VIRTUAL_CTR_DEFINE( CLASS_NAME , ( TYPE_NAME_LIST ) , (NAME_LIST) )

An example of a virtual constructor in a header file with no arguments (from logger.h):

DT_VIRTUAL_CTR(DtLogger,())

The corresponding virtual constructor in the source file:

DT_VIRTUAL_CTR_DEFINE(DtLogger, () , () )

An example of a virtual constructor in a header file, with arguments:

DT_VIRTUAL_CTR(DtLoggerFileWriter,(DtMessageIOInfo*, DtLoggerRecordFile*))

The corresponding virtual constructor in the source file:

DT_VIRTUAL_CTR_DEFINE(DtLoggerFileWriter ,( DtMessageIOInfo* info, DtLoggerRecordFile* file ) , ( info , file ) )

Classes that have a virtual constructor can only be created using a static create() function. This function, by default, calls a constructor with the supplied parameters. It can be overwritten via the static setCreator(functionPtr) function.

DtLogger* logger = DtLogger::create();
...
DtLogger* overrideConstructor() { return new DtOverrideLogger();}
DtLogger::setCreator(&overrideConstructor);
DtLogger* newLogger = DtLogger::create();

In addition to virtual constructors the API has pure virtual constructors, which must be defined before they can be called, DT_PURE_VIRTUAL_CTR. It also has virtual copy constructors and assignment operators using DT_VIRTUAL_COPY_ASSIGN. For documentation on all the macros and their implementations, please see virtualConstructor.h.

6.5 Runtime Values

The Logger uses a class of objects which wrap multiple C++ types in the same object hierarchy. This allows the Logger to provide a generic interface for commands and to convert strings to native types and vice-versa. The abstract base class is DtAnyValue, defined in runtimeValue.h. It has a templated sub-class, DtValue, which is templated upon a single value semantics type (for example, int, float DtString and so on). To construct a DtAnyValue for a specific type use the utility function DtAnyValue* DtWrapValue(const T& input), which constructs a new DtValue<T> object.

6.6 Thread Safety

You can ensure thread safety by using mutexes. VR-Link provides a mutex implementation via the DtMutex class. The Logger also provides a utility class call DtThreadSafe, which wraps a class's interface with the required Mutex calls to ensure the object is always thread safe.

DtThreadSafe<std::vector<int> > safe_vector;
safe_vector->push_back(4);
safe_vector->push_back(2);
//Create a local scope to lock the vector only during iteration.
{
DtScopedSequenceLock lock = safe_vector.sequenceLock();
std::vector<int>::iterator iter = safe_vector->begin();
std::vector<int>::iterator end = safe_vector->end();
for(;iter != end;++iter)
{
std::cout << *iter << _g,_h;
}
}

6.7 Interface Signal Slots

It is desirable to allow interfaces to call a function when a value changes. This is implemented by the Logger API as DtValueSignals. A DtValueSignal is templated on the type of value it signals. The signals are public objects that can be connected to functions and member functions. When a signal is emitted, all connected functions are called. The call signature of the function or member function must only take a constant reference to the type of the value signal. The signal slot implementation provides a capability similar to that of traditional callbacks.

class FooSig
{
int i;
DtValueSignal<int> i_signal;
};
class FooListen
{
void print(const int& i) { std::cout << i; }
};
int sum(const int& i)
{
static int sum
}
FooSig s;
FooListen l;
Connect(s,&l,FooListen::print);

6.8 Observer Pattern

The observer pattern is similar to signals and slots, but works on the basis of events and an observable object. The observer pattern allows events to trigger callbacks and pass a single object to be observed temporarily. The observer pattern implementation is primarily used through three templated classes, DtObserver (observe.h), DtObserveEvent (observeEvent.h), and DtObserveConnection (observeConnection.h). The DtObserver is an object that can be constructed using a function or a member function and acts as a functor when an event is fired. DtObserveEvent is an object that can be fired at any time by a host object. DtObserveConnection constructs the connection between the observer and an event of the same observed object type. The connection can track the lifetime of both the event and the observer and automatically breaks the connection if one object is deallocated. You can add multiple observers to the same event. For an example of how to use the observer pattern classes, please see the stateObserver example.

[<< The Logger Plug-in API] [Home] [Top of Page] [Logger Libraries >>]


Document ID: Generated on Thu Jul 14 15:45:33 EDT 2022 from SVN revision 244827
Copyright © 2021 MAK Technologies. All Rights Reserved (www.mak.com)