VR-Link API Documentation for HLA 1.3
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
Test Object Example

Table of Contents

The testObj example is an HLA-specific example that shows how to add a new object class to a FOM.

You must extend the VR-Link top-level API by adding a new kind of state repository, publisher, reflected object, and reflected object list. You also need to create mappings between the new FOM class and the API extensions.

This example adds an object class called Test to the FED file VrlExtend.fed. It has two attributes:

How to Run the Example

  1. Launch two testObj applications from your VR-Link install's bin64 directory using the same HLA protocol.
  2. Once running, you can see the state data sent between the two applications printed in the console. The state data will include the two new parameters, Number and Vector.

How to Add a New Object Class

To add this class:

  1. Create a new subclass of DtStateRepository, called TestStateRepository, to hold the state of a new object of this kind. (For more information, please see testSR.h and testSR.cxx.)
  2. Create new subclasses of DtObjectPublisher, DtReflectedObject, and DtReflectedObjectList. Most of their functionality is in their base classes, but the derived classes provide an important element of type safety by localizing casts from base DtStateRepositories to our new TestStateRepository subclass. (For more information, please see testPub.h, testPub.cxx, refTest.h, refTest.cxx, refTestList.h and refTestList.cxx.)
  3. Create TestEncoder and TestDecoder classes, subclasses of DtHlaStateEncoder and DtHlaStateDecoder respectively. These classes contain encoding and decoding functions for the attributes of the Test object class. (For more information, please see testDec.h, testDec.cxx, testEnc.h and testEnc.cxx.)

While this may seem like a lot of classes to create, most of them are fairly trivial because they inherit most of their behavior from base classes, and you can use the files in ./examples/testObj as templates from which to create your own classes.

Creating a State Repository

We first create a new subclass of DtStateRepository called TestStateRepository, in testSR.h and testSR.cxx. This class is used to store the state of both local and reflected objects of our Test class. To this class, we add the two new parameters, Number and Vector, we want to add to the FOM.

The class definition is listed below. We provide inspector and mutator functions to access the state data, an override of the virtual printData() function that prints the current state data, and an override of the virtual clone() function that returned a new instance of this class. We represent state data in the same way as in the FOM, but this is not strictly necessary. We could perform a non-trivial conversion in FOM mapping code instead.

Creating Publishers, Reflected Objects, and Reflected Object List Classes

Next, we need to create subclasses of DtObjectPublisher, DtReflectedObject, and DtReflectedObjectList for our Test objects.

Technically, these classes are not strictly necessary. We can do just about everything we need to using the base classes, but then we would need to configure each instance of those base classes with the appropriate FOM classes, state repositories, encoders, and decoders every time we create one. By subclassing, we encapsulate the configuration of each base class for our new object type into one place. In addition, we hide the casting from base DtStateRepositories to the derived TestStateRepository, so that application code that uses our derived classes will be able to automatically obtain pointers to TestStateRepositories.

Defining Functions

Definitions of functions in these classes are very straightforward, and rely mostly on the base class versions. TestPublisher, ReflectedTest, and ReflectedTestList hard-code their own FOM mapping information, rather than relying on the default behavior, which is to obtain it from the FOM Mapper. This makes things simpler, by saving the step of configuring the FOM Mapper with mappings for the new class. But this choice means that it would be more difficult to change the way this kind of interaction is represented in the FOM.

Specifically, the TestPublisher and ReflectedTest constructors tell their base classes what kinds of encoders and decoders to use. The TestPublisher constructor chooses its own class to use to represent its objects, and the ReflectedTestList constructor chooses what FOM classes to manage.

The TestPublisher Class

We derive TestPublisher from DtObjectPublisher in testPub.h and testPub.cxx. As you can see, this class just configures its base class properly in its constructor, and provides type safety in accessing its state repository.

The ReflectedTest Class

ReflectedTest performs similar tasks for incoming objects. In addition to the testStateRep() and tsr() functions, the class provides type-specific versions of the list management functions next(), prev(), wrapNext(), and wrapPrev(), and of the post update callback registration functions.

This class is derived from DtReflectedObject in refTest.h and refTest.cxx. The implementations of these functions are trivial, in most cases calling down to base class versions, and performing a cast between a specific and a general type.

The ReflectedTestList Class

After we create a ReflectedTest class, we need to create a ReflectedTestList class that knows how to create and manage ReflectedTests. This class is derived from DtReflectedObjectList in refTestList.h and refTestList.cxx.

Creating Encoders

The TestEncoder class (in testEnc.h and testEnc.cxx) is implemented similarly to the decoder class, but it also contains checking functions in addition to encoding functions.

Creating Decoders

TestDecoder is defined in testDec.h and testDec.cxx. Macros help out in the declaration and definition of decoding functions, which are implemented as static member functions of TestDecoder. The TestDecoder constructor self-registers its decoding functions.

Making an Application

Once all of these classes are set up, they can be used in applications just like VR-Link's normal publishers, reflected object lists, and reflected objects, as in testObj.cxx.

After you build the testObj example, run two copies of it. You should see each print out state data that it receives from the other.


TestObj Example Code

Main Application

/****************************************************************************
* Copyright (c) 1992-2025 MAK Technologies, Inc
* All rights reserved.
****************************************************************************/
#include "testPub.h"
#include "refTestList.h"
int main( int argc, char* argv[] )
{
// Used for error handling
DtINIT_MINIDUMPER( "ObjTest" );
DtExerciseConn conn("example-vrlExtend", "TestObj", new DtEmptyFomMapper());
DtClock* clock = conn.clock();
// Create a reflected test list
ReflectedTestList rtl(&conn);
// Create a test publisher
TestPublisher pub(&conn);
// Grab a pointer to its state repository
TestStateRepository* tsr = pub.tsr();
// Set some data
tsr->setVector(DtVector(1,2,3));
// Run the main loop for 20 seconds
DtTime beginTime = clock->absRealTime();
while (clock->absRealTime() < beginTime + 20.0)
{
clock->setSimTime(clock->absRealTime());
conn.drainInput();
// Print the state of the first object in the list:
if (rtl.first())
{
rtl.first()->tsr()->printData();
}
// Change the value of "Number" once a second
tsr->setNumber((int) clock->simTime());
// Tick the publisher, causing data to be sent when necessary.
pub.tick();
// Sleep till next iteration
DtSleep(0.2);
}
return 0;
}

TestObj State Repository

State Repository Header

/*********************************************************************
** Copyright (c) 1992-2025 MAK Technologies, Inc
** All rights reserved.
*********************************************************************/
#pragma once
#if DtHLA
{
public:
virtual DtStateRepository* clone(bool copy) const;
virtual void printData() const;
virtual void printDataToStream(std::ostream& stream) const;
virtual void setNumber(int num);
virtual int number() const;
virtual void setVector(const DtVector& vec);
virtual const DtVector& vector() const;
public:
protected:
int myNum;
};
#endif

State Repository Source

/*********************************************************************
** Copyright (c) 1992-2025 MAK Technologies, Inc
** All rights reserved.
*********************************************************************/
#if DtHLA
#include "testSR.h"
#include <vlutil/vlPrint.h>
#include <iostream>
myNum(0),
myVec()
{
}
{
}
myNum(orig.myNum),
myVec(orig.myVec)
{
}
const TestStateRepository& orig)
{
// Guard against assignment to yourself.
if (this == &orig)
{
return *this;
}
myNum = orig.myNum;
myVec = orig.myVec;
return *this;
}
{
if (copy)
{
return new TestStateRepository(*this);
}
else
{
return new TestStateRepository();
}
}
{
}
void TestStateRepository::printDataToStream(std::ostream& stream) const
{
DtPrintAttribute(stream,"Number: ", number());
DtPrintAttribute(stream,"Vector: ", vector().string());
}
// Accessor functions are typically pretty straightforward.
{
myNum = num;
}
{
return myNum;
}
{
myVec = vec;
}
{
return myVec;
}
{
return new TestStateRepository();
}
#endif

TestObj Publisher

TestObj Publisher Header

/*********************************************************************
** Copyright (c) 1992-2025 MAK Technologies, Inc
** All rights reserved.
*********************************************************************/
#pragma once
#if DtHLA
#include "testSR.h"
{
public:
TestPublisher(DtExerciseConn* conn, const char* objName = NULL);
virtual ~TestPublisher();
};
#endif

TestObj Publisher Source

/*********************************************************************
** Copyright (c) 1992-2025 MAK Technologies, Inc
** All rights reserved.
*********************************************************************/
#if DtHLA
#include "testPub.h"
#include "testEnc.h"
#include "testDec.h"
#include <vl/fom.h>
DtExerciseConn* conn, const char* objName) :
{
// setHlaObject is defined in the base DtObjectPublisher. Pass the FOM
// class name, and an encoder and decoder. These objects will be deleted
// by the base class on destruction.
const char* className = "Test";
DtObjClassDesc* desc = conn->fom()->objClassByName(className);
setHlaObject(className, new TestEncoder(conn, desc),
new TestDecoder(conn, desc), objName);
}
{
}
{
}
{
}
#endif

TestObj Reflected Object

TestObj Reflected Object Header

/*********************************************************************
** Copyright (c) 1992-2025 MAK Technologies, Inc
** All rights reserved.
*********************************************************************/
#pragma once
#if DtHLA
#include "testSR.h"
typedef void (*TestCallbackFunction)(ReflectedTest* obj, void* userData);
{
public:
virtual ~ReflectedTest();
virtual TestStateRepository* tsr() const;
virtual ReflectedTest* next() const;
virtual ReflectedTest* prev() const;
virtual ReflectedTest* wrapNext() const;
virtual ReflectedTest* wrapPrev() const;
virtual void addPostUpdateCallback(
TestCallbackFunction cb, void* userData);
TestCallbackFunction cb, void* userData);
};
#endif

TestObj Reflected Object Source

/*********************************************************************
** Copyright (c) 1992-2025 MAK Technologies, Inc
** All rights reserved.
*********************************************************************/
#if DtHLA
#include "refTest.h"
#include "testDec.h"
{
setHlaObject(obj, new TestDecoder(conn, &obj->classDesc()));
}
{
}
{
}
{
}
{
return (ReflectedTest*) nextObj();
}
{
return (ReflectedTest*) prevObj();
}
{
return (ReflectedTest*) wrapNextObj();
}
{
return (ReflectedTest*) wrapPrevObj();
}
TestCallbackFunction cb, void* userData)
{
}
TestCallbackFunction cb, void* userData)
{
}
#endif

TestObj Reflected List

TestObj Reflected List Header

/*********************************************************************
** Copyright (c) 1992-2025 MAK Technologies, Inc
** All rights reserved.
*********************************************************************/
#pragma once
#if DtHLA
#include "refTest.h"
{
public:
virtual ~ReflectedTestList();
#if DtHLA_1516
virtual ReflectedTest* lookup(RTI::ObjectInstanceHandle id);
#else
virtual ReflectedTest* lookup(RTI::ObjectHandle id);
#endif
virtual ReflectedTest* lookup(const DtGlobalObjectDesignator& objName);
virtual ReflectedTest* first() const;
virtual ReflectedTest* last() const;
TestCallbackFunction cb, void* userData);
TestCallbackFunction cb, void* userData);
virtual void addTestRemovalCallback(
TestCallbackFunction cb, void* userData);
TestCallbackFunction cb, void* userData);
protected:
};
#endif

TestObj Reflected List Source

/*********************************************************************
** Copyright (c) 1992-2025 MAK Technologies, Inc
** All rights reserved.
*********************************************************************/
#if DtHLA
#include "refTestList.h"
DtExerciseConn* exConn) :
{
// Tell the base class that we will be managing the FOM class "Test".
addObjectClassByName("Test");
}
{
}
#if DtHLA_1516
ReflectedTest* ReflectedTestList::lookup(RTI::ObjectInstanceHandle id)
#else
#endif
{
return (ReflectedTest*) lookupObj(id);
}
const DtGlobalObjectDesignator& objName)
{
return (ReflectedTest*) lookupObj(objName);
}
{
return (ReflectedTest*) firstObj();
}
{
return (ReflectedTest*) lastObj();
}
TestCallbackFunction cb, void* userData)
{
}
TestCallbackFunction cb, void* userData)
{
}
TestCallbackFunction cb, void* userData)
{
}
TestCallbackFunction cb, void* userData)
{
}
DtHlaObject* obj) const
{
return new ReflectedTest(obj, myExConn);
}
#endif

TestObj Encoder

TestObj Encoder Header

/*********************************************************************
** Copyright (c) 1992-2025 MAK Technologies, Inc
** All rights reserved.
*********************************************************************/
#pragma once
#if DtHLA
#define DtDECLARE_TEST_ATTR_CHECKER(attrName) \
DtDECLARE_ATTR_CHECKER(TestStateRepository, attrName)
#define DtDECLARE_TEST_ATTR_ENCODER(attrName) \
DtDECLARE_ATTR_ENCODER(TestStateRepository, attrName)
#define DtDEFINE_SIMPLE_TEST_ATTR_ENCODER( \
attrName, netType, inspector) \
DtDEFINE_SIMPLE_ATTR_ENCODER(TestEncoder, \
TestStateRepository, attrName, netType, inspector)
#define DtDEFINE_SIMPLE_TEST_ATTR_CHECKER( \
attrName, inspector) \
DtDEFINE_SIMPLE_ATTR_CHECKER(TestEncoder, \
TestStateRepository, \
attrName, inspector)
{
public:
DtExerciseConn* exConn,
DtObjClassDesc* classDesc);
virtual ~TestEncoder();
protected:
};
#endif

TestObj Encoder Source

/*********************************************************************
** Copyright (c) 1992-2025 MAK Technologies, Inc
** All rights reserved.
*********************************************************************/
#if DtHLA
#include "testEnc.h"
#include "testSR.h"
DtExerciseConn* exConn,
DtObjClassDesc* classDesc) :
DtHlaStateEncoder(exConn, classDesc)
{
// Register the individual encoding and checking functions for Test's
// attributes with the object. The encoding functions have names like
// encodeNumber, while checking functions have names like needNumber. The
// macros expands to look like this:
// addEncoder("Number", (DtAttributeEncoder) encodeNumber);
// addChecker("Number", (DtAttributeChecker) needNumber);
}
{
}
// Encoding functions
Number, DtNetU32, number);
Vector, DtNet64Vector, vector);
// Checking functions
Number, number);
Vector, vector);
#endif

TestObj Decoder

TestObj Decoder Header

/*********************************************************************
** Copyright (c) 1992-2025 MAK Technologies, Inc
** All rights reserved.
*********************************************************************/
#if DtHLA
#pragma once
#define DtDECLARE_TEST_ATTR_DECODER(attrName) \
DtDECLARE_ATTR_DECODER(TestStateRepository, attrName)
#define DtDEFINE_SIMPLE_TEST_ATTR_DECODER( \
attrName, netType, inspector) \
DtDEFINE_SIMPLE_TEST_ATTR_DECODER_WITH_CAST( \
attrName, netType, inspector, (netType))
#define DtDEFINE_SIMPLE_TEST_ATTR_DECODER_WITH_CAST( \
attrName, netType, mutator, castExpr) \
DtDEFINE_SIMPLE_ATTR_DECODER_WITH_CAST( \
TestDecoder, TestStateRepository, \
attrName, netType, mutator, castExpr)
{
public:
DtExerciseConn* exConn,
DtObjClassDesc* classDesc);
virtual ~TestDecoder();
protected:
};
#endif

TestObj Decoder Source

/*********************************************************************
** Copyright (c) 1992-2025 MAK Technologies, Inc
** All rights reserved.
*********************************************************************/
#if DtHLA
#include "testDec.h"
#include "testSR.h"
#include <vlutil/vlPrint.h>
// Default constructor
DtExerciseConn* exConn,
DtObjClassDesc* classDesc) :
DtHlaStateDecoder(exConn, classDesc)
{
// Register the individual decoding functions for Test's attributes with
// the object. The decoding functions have names like decodeNumber and
// decodeVector. The macro expands to look like this:
// addDecoder("Number", (DtAttributeDecoder) decodeNumber);
}
{
}
// Decoding functions
// The DtDEFINE_SIMPLE_ATTR_DECODER macro can be used to define a "simple"
// decoding function. Expanded, a decoding function looks like this:
//
// void TestDecoder::decodeNumber(TestStateRepository* stateRep,
// const RTI::AttributeHandleValuePairSet& attrs,
// int index)
// {
// RTI::ULong length = 0;
// // Get value from RTI representation into netVal.
// DtNetInt32* netVal = (DtNetInt32*) attrs.getValuePointer(index, length);
// // Check for size mismatch
// if (length > sizeof(DtNetInt32))
// {
// DtWarn("Size of value decoded for attribute %s (%d)\n",
// "Number", length);
// DtWarn(" is larger than size of %s (%d)\n",
// "DtNetInt32", sizeof(DtNetInt32));
// }
// // Pass the value to the mutator function, assuming the "net" type can
// // be implicitly cast to the native type expected by the mutator.
// stateRep->setNumber(*netVal);
// }
#endif

Document ID: Generated on Thu Oct 16 00:12:25 EDT 2025 from SVN revision 280738
Copyright © 1992-2025 MAK Technologies. All Rights Reserved (www.mak.com)