Skip to: site menu | section menu | main content

 

Hank FAQ - Object Creation


This document attempts to answer frequently asked questions pertaining to object creation in Hank.
Last Update:


  1. I want to add a new dynamic object (e.g. a vehicle, a pedestrian, etc...) to Hank. How do I go about doing this?
  2. This appears to be too complicated. Why can't I just use new MyObject( p1, p2, p3, ..., pN ) when I want to create it?
  3. How do I pass parameters to my object?
  4. This parameter stuff is complicated too. How do I set the parameters if I want to create the object from inside Hank and not through the SDL?
  5. Ok, so I know how to pass parameters, but how does my object interpret them?

I want to add a new dynamic object (e.g. a vehicle, a pedestrian, etc...) to Hank. How do I go about doing this?

In brief, you need to create a new object class similar to those found in the src/libhankobj directory. After that, you will need to add your new class information to the ObjectFactory.

There are several things to understand about the dynamic objects in Hank. Visual objects are generally derived from the PerceivableObject class and non-visual objects (things that cannot be seen in the simulation) are derived from the HankObject class. This, however, is only the first step and prepares the object to be used by the various components within the simulation framework.

Step 1

First, you will need to create your object class files. Linked to this file are two files that you can use as your initial template in creating a new object for use in Hank. The header file should be placed in include/Hank/obj and the source file should be placed in src/libhankobj.

After you rename the template and add your code to Hank, you will need to edit the Makefile.am file in the src/libhankobj directory by simply adding your source file (.C) to the list of files that form the libhankobj.a library.

At this point, your object should compile into the libhankobj.a library.

Step 2

Next, you will need to add a string identifying your object as well as a pointer to your object's static creation function to the ObjectFactory part list. You will also have to increment the number of objects on the part list. As of this writing the ObjectFactory's partlist structure looks like the following:
static int partsize = 3;
static PartStruct partlist[] = {{ "SimpleEntity", SimpleEntity::create },
   { "Vehicle", Vehicle::create },
   { "TrafficLight", TrafficLight::create }};
Do you notice the pattern here? Basically, this is just a simple array that is used to initialize the ObjectFactory when Hank is started. The ObjectFactory is used by the SDL and other parts of Hank to create objects by referring to their string identifier. There will be more discussion of why this is the case later.

So, for example, say you just created a MechanicalBull object for Hank and you want to use it in Hank. You would then augment the file src/libhankobj/ObjectFactory.C and the partlist structure as follows:

static int partsize = 4; // increment this number
static PartStruct partlist[] = {{ "SimpleEntity", SimpleEntity::create },
   { "Vehicle", Vehicle::create },
   { "TrafficLight", TrafficLight::create },
   { "MechanicalBull", MechanicalBull::create }};

At this point, your object can be used in Hank. It can be created from the SDL (Scenario Description Language), or it can be created by directly calling the ObjectFactory::create(...) functions. For an example of how internal Hank code creates objects (and what the SDL eventually calls), refer to the function edf_create_object in the file src/libhankdb/edf_interface.C.

Return To Top


This appears to be too complicated. Why can't I just use new MyObject( p1, p2, p3, ..., pN ) when I want to create it?

There are several reasons for the structure of the object creation code in Hank, but the primary reason is that the new operator will instance the object immediately, and in turn, the classes the object was derived from will also be created. In the case of an object derived from PerceivableObject, the object will be placed on the PerceivableObject list affecting any visual processes or database occupancy processes that use the PerceivableObject list. If this insertion happens in between critical computations, there may be no side effect. However, if the object is inserted into the list in the middle of a critical occupancy computation, the resulting simulation may turn out differently than if the object was inserted at some other time. If we ran the simulation a second time, would we see the same results? Who knows? The key to remember is that we want to build a fairly deterministic simulation system and we want to minimize any order of execution dependencies.

There are other reasons for the inherent complexity in object creation in Hank. For instance, why do we use the ObjectFactory and why is it that the object constructors cannot just use whatever parameters we may deem necessary for the object. I'll try to explain these reasons here.

The SDL (Scenario Description Language) Interpreter is the other reason why we have the ObjectFactory and why we use the strange Hank::ParameterBase* structures to pass parameters. Since the SDL is an interpreter it must match strings to the correct objects in the simulation. Hence, the existance of the ObjectFactory hash table. The ObjectFactory binds a string to a specific creation function that can be called when it is time to instance the object. Without this mapping, the SDL would have no means to instance objects in the simulation unless we hard-coded the mapping into the interpreter itself. This seems unreasonable since we eventually expect many object to be added to Hank. Ideally, when we create a new object type, we shouldn't have to recompile the libhankobj.a library, but rather, we should be able to dynamically load and link the new object at runtime. The ObjectFactory was designed with this in mind and could eventually be used to support dynamic loading of objects created outside of Hank.

The SDL is also the reason why we use the list of Hank::ParameterBase*. Just as the SDL requires a mapping from an object name to a means to instance it, the SDL also needs to understand how parameters should be passed to the object constructors. Because there is no protocol dictating a specific set of parameters that all objects must take, the SDL has no way to understand how parameters get translated to the objects themselves. For instance, if every Hank object took four doubles as their constructor parameters, the implementation in the SDL would be simple. However, this is not the case. For example, a Vehicle may need it's initial position, an aggressiveness parameter, and an initial velocity whereas a Pedestrian may need more or less information. The problem lies in how the SDL should interpret this information.

I made the decision that the SDL should not be responsible for intrepreting how various parameters get passed to different objects. Instead, it is up to the object itself to interpret the parameters it is passed. This information is conveyed through a property name/value pair which provides a string identifier for the property (i.e.aggressiveness) and a templated value (i.e.4.321). This is implemented in the following classes:

Hank::ParameterBase
template <class T> Hank::Parameter : public Hank::ParameterBase

The class Hank::ParameterBase is the base class and allows us to refer to these parameters in a very abstract and polymorphic manner. The class Hank::Parameter is the templated class that lets you set whatever value you want your property to have, whether the parameter should be a list, your own structure, or a double.

The SDL understands this parameter system and packages your SDL parameters into the correct structures.

Return To Top


How do I pass parameters to my object?

There are two ways you might pass parameters to your object. If you create your object through the SDL, as in
create MechanicalBull( aggressiveness=3.23, meanness="high" );
then the SDL will package the correct parameters for you. You will have one parameter with the name/value pair ("aggressiveness", 3.23) and the other parameter will be ("meanness", "high").

If you create your object within the Hank code itself (e.g.from another Hcsm), the you will have to package the parameters yourself and pass the ParameterBase* list to the ObjectFactory creation functions.

Return To Top


This parameter stuff is complicated too. How do I set the parameters if I want to create the object from inside Hank and not through the SDL?

Here's a code example that shows you how you could pass the name/value pairs ("aggressiveness", 3.23) and ("meanness", "high") to your object from within the Hank source.
#include <list>
#include "Hank/db/edf_interface.H"

std::list<Hank::ParameterBase*> mechbull_params;

// set the first parameter, aggressiveness
mechbull_params.push_back( new Hank::Parameter<double>( "aggressiveness", 3.23 ) );

// set the second parameter, meanness
mechbull_params.push_back( new Hank::Parameter<std::string>( "meanness", "high" ) );

// have the system create the bull
edf_create_object( "MechanicalBull", mechbull_params );

That is the easiest way to currently create an object from within the Hank source itself.

Return To Top


Ok, so I know how to pass parameters, but how does my object interpret them?

This is currently a little more complicated, but I suspect that as more people use the system, we'll develop some code to nicely sugar-coat this interface.

I suggest that you interpret parameters passed to your object in the object's initialize member function. The reason for this is that the parameters may not be stored in the object when the constructor is called. The initialization function wfill be called before anything else happens to the object.

For this example, I'll parse the parameters passed to the MechanicalBull in one of the previous questions.

void MechanicalBull::initialize( void )
{
   // ****************************************************
   // Examine the property list and find the properties needed for
   // initialization of this object. Refer to the FAQ on object
   // creation for a detailed explanation of parameters as used in
   // object initialization.
   // ****************************************************

   const Hank::ParameterBase* ptr;

   //
   // Obtain the "aggressiveness" property
   //
   double aggr;
   ptr = getProperty( "aggressiveness" );

   if (ptr) {
      // Re-cast the abstract hank parameter into the specific type
      // that holds the aggressiveness value which is a double.
      const Hank::Parameter<double> *aggr_param = dynamic_cast<const Hank::Parameter<double>* >(ptr);

      // Obtain the aggressiveness in double form.
      aggr = aggr_param->value();
   }
   else
      aggr = 1.0;

   //
   // Obtain the "meanness" property
   //
   std::string mean;
   ptr = getProperty( "meanness" );

   if (ptr) {
      // Re-cast the abstract hank parameter into the specific type
      // that holds the meanness value which is a string.
      const Hank::Parameter<std::string> *mean_param = dynamic_cast<const Hank::Parameter<std::string>* >(ptr);

      // Obtain the meanness in string form.
      mean = mean_param->value();
   }
   else
      mean = "normal";
}

For a more complicated example in which the location list parameter is interpreted, see the Vehicle.C source code.

Return To Top


Author: Pete Willemsen Last Modified:
Please send comments or questions to The Hank Group
Copyright - The University of Iowa : Department of Computer Science