Ten steps to create a component

Component Development Kit for THRSim11

© Alex van Rooijen, Bart Grootendorst, and Harry Broeders.


Previous page Next page Index

Exor componentIn the following explanation an example of an exor component is used. The source code of this exor component is included with the CDK:

The code for the component must be placed in a dynamic link library (dll). We presume that you know how to create a dll with the compiler you use. Here is a quick reminder.

You have to follow ten steps to create a component, these steps are:

  1. Derive from ImpComponent
  2. Adding connectionpins
  3. Expose the connectionpins
  4. Defining component behaviour
  5. Give the component "eyes"
  6. Give the component a name
  7. Generate a GUID
  8. Register the classfactory
  9. Compiling
  10. Adding the component to the registry

For those who are not familiar with the exor: the exor (exclusive or) is a well known component in digital technology. An exor has two boolean inputs and one boolean output. It has the following symbol and truthtable:

in1 in2 out
0 0 0
0 1 1
1 0 1
1 1 0

1. Derive from ImpComponent.

Every component you want to make must be derived from the ImpComponent class. This will give the following class declaration for our exor example.

class Exor: public ImpComponent {
public:
	Exor();
	...
private:
	...
};

2. Adding connection pins.

You need to add pins to your component so it can interact with the THRSim11. There are four types of connection pin classes available. Each class can be used to create an input or an output pin:

CONNECTION TYPE Win32 TYPE CDK TYPE
BoolConnection 1 bit connection BOOL BoolConnection::BaseType
ByteConnection 8 bit connection BYTE ByteConnection::BaseType
WordConnection 16 bits connection WORD WordConnection::BaseType
LongConnection 32 bits connection LONG LongConnection::BaseType

These classes have two public memberfunctions to set and determine the state of the pin:

The Win32 types BOOL, BYTE, WORD and LONG are defined in the "standard" Win32 include file windef.h. For your convenience we defined the typename BaseType in each of the four connection classes. E.g. the return type for BoolConnection::get() is BoolConnection::BaseType (which is a typedef for the Win32 type BOOL).

You must add the connection pins as (private) data members to the class derived from ImpComponent. In our exor example we need three BoolConnection pins named in1, in2, and out.

class Exor: public ImpComponent {
public:
	Exor();
	...
private:
	BoolConnection in1, in2, out; 
	...
};

3. Expose the connection pins.

After you have declared the component connection pins, they must be made available for the THRSim11. This is done with the Expose function. This function takes two parameters, the pin to expose and the user friendly name of that pin. The user friendly name is used in the connection dialog. The constructor of your component has to call the Expose function for each connection pin as shown below for our exor example.

Exor::Exor() : ... {
... Expose(in1, "Input 1"); Expose(in2, "Input 2"); Expose(out, "Output"); }

With a third parameter (in string format) it's possible to define a pin within the THRSim11 to which the specified component pin prefers to connect. The THRSim11 will try to connect these pins together.

Here you see the connect dialog box which is opened when you create an exor component in THRSim11:

Connecting the exor component...

If you provide prefered conncetions e.g.:

Exor::Exor() : ... {
	...
	Expose(in1, "Input 1", "PC0"); 
	Expose(in2, "Input 2", "PC1"); 
	Expose(out, "Output", "PB0"); 
}

The connect dialog is not opened when you create a exor component in THRSim11 but the defined connections are mode.

4. Defining component behaviour.

Now you have a component with pins, but how can you give the component a behaviour? Well you just have to add a (private) memberfunction to the class derived from ImpComponent. The name of this memberfunction is yours to choose.

Let's see how this is done with the exor. The behaviour of an exor is easy to define. We just have to read the states of both input pins and set the output pin based on these states. This is done in the inputChanged function as shown below.

class Exor: public ImpComponent {
public:
	Exor();
	void inputChanged(); 
private:
	BoolConnection in1, in2, out;
	...
};
void Exor::inputChanged() {
	out.set(in1.get()&&!in2.get() || !in1.get()&&in2.get());
}

You're free to use multiple functions to describe the behaviour of a component.

5. Give the component "eyes".

The next step is to notify the component when one of the inputs is changed so the output pin(s) can be set accordingly using the previously written function(s). There are four CallOnxxxxx templates that do this:

For each pin you want to use as an input you need to include at least one CallOnxxxxx template (where xxxxx is the desired notification) as a private member of the class derived from ImpComponent. The use of multiple (different) CallOnxxxxx templates on one pin is allowed. It's also allowed that different CallOnxxxxx templates call different component behaviour functions.

You also need to initialize these templates in the constructor of the class derived from ImpComponent. All constructors of CallOnxxxxx classes need the same three parameters:

All memberfunctions of the connection pin monitored by a CallOnxxxxx template, are also available in the CallOnxxxxx that monitors it. So if you want to call the get function of a connection pin you can also make this call via the CallOnxxxxx object that monitors it. The CallOnxxxxx object behaves like a pointer to the pin which is monitored. So if a CallOnxxxxx object is named coc1 then the value of the pin can be obtained by:

value = coc1->get();

Our exor example uses two input pins, both want to call the same component behaviour function and only when the state of the pins changes. We first have to add two CallOnChange objects as members of the class derived from ImpComponent, as shown below:

class Exor: public ImpComponent {
public:
	Exor();
	void inputChanged();
private:
	BoolConnection in1, in2, out;
	CallOnChange<BoolConnection::BaseType, Exor>  coc1, coc2; 
};

As said before it is also necessary to initialize the CallOnChange objects:

Exor::Exor(): coc1(in1, this, &Exor::inputChanged), coc2(in2, this, &Exor::inputChanged) { 
	...
	Expose(in1, "Input 1");
	Expose(in2, "Input 2");
	Expose(out, "Output");
}

6. Give the component a name.

To give your component a name you have to call the void SetComponentName(const char* name) function in the constructor of the class derived from ImpComponent. This name is used in the pulldown Connect menu of the THRSim11 and will also be the default window title. If you want to sort components in submenus within the Connect menu you can also provide a groupname. This is a new feature in CDK version 3.0 and you need THRSim11 version 3.30 or higher to use it. To place your component in a submenu called groupname you have to call the void SetGroupname(const char* groupname) function in the constructor of your component. For our exor example this will give the following constructor:

Exor::Exor(): coc1(in1, this, &Exor::inputChanged), coc2(in2, this, &Exor::inputChanged) {
	SetComponentName("Exor"); 
	SetGroupName("CDK Examples"); 
	Expose(in1, "Input 1");
	Expose(in2, "Input 2");
	Expose(out, "Output");
}

7. Generate a GUID.

At this point the source of your component is almost ready, there are only two COM related things left to do. The first is to generate a GUID and put this in the sourcefile. A GUID (Global Unique Identifier) is a unique number that identifies a COM component. A GUID generator can be downloaded here: guidgen.zip and is also included with any of the supported compilers:

You need to include this GUID in the following format in your source file:

DEFINE_GUID(guid identifier, guid);

And in the exor example:

DEFINE_GUID(CLSID_EXOR,0x681299e0,0x8bb4,0x11d3,0xad,0xaa,0x00,0x60,0x67,0x49,0x62,0x45); 

CLSID_EXOR is the identifier for the GUID.

8. Register the classfactory.

The second COM related thing is to register a classfactory. A classfactory makes it possible to create more then one object of your component. To register the classfactory you need to insert a global function called RegisterClassFactories() with the following source code:

void RegisterClassFactories() {
	ClassFactory* new_classfactory(new ComponentFactory<component>(guid));
	class_factories.push_back(new_classfactory);
}

You only need to replace the two bold italic words. component needs to be replaced by the component classname (the one derived from ImpComponent) and guid needs to be replaced by the identifier of the GUID. For the exor example this gives:

void RegisterClassFactories() {
	ClassFactory* new_classfactory(new ComponentFactory<Exor>(CLSID_EXOR)); // Does not work for Visual C++
	class_factories.push_back(new_classfactory);
}

This standard C++ code doesn't compile with Visual C++. The initialization of the pointer using brackets is misunderstood by VC++6.0. You have to use the older initialization using the assignment operator to initialize the pointer:

void RegisterClassFactories() {
	ClassFactory* new_classfactory=new ComponentFactory<Exor>(CLSID_EXOR); // Old-fashioned but works fine for Visual C++
	class_factories.push_back(new_classfactory);
}

9. Compiling.

Your component is ready to be compiled but don't forget the following things:

10. Adding the component to the registry.

Before your component is ready for use, you need to do one last thing in order to let the THRSim11 detect your component. You have to add some lines to the registry which provides the THRSim11 with some information about your component. The most easy way to add these lines to your registry is to make your own .REG file. You need to insert the following lines in this file:

REGEDIT
HKEY_CLASSES_ROOT\CLSID\{componentguid} = componentname
HKEY_CLASSES_ROOT\CLSID\{componentguid}\InprocServer32 = componentpath
HKEY_CLASSES_ROOT\CLSID\{componentguid}\Interface\{6b93056f-799d-11d3-adaa-006067496245} = IComponent
HKEY_CLASSES_ROOT\Interface\{6b93056f-799d-11d3-adaa-006067496245}\ProxyStubCLSID32\{componentguid} = componentname

The following bold words have to be replaced:

The other words should be left unchanged. For the exor example for Borland C++ 5.02 this will give the following .REG file (exor.reg):

REGEDIT
HKEY_CLASSES_ROOT\CLSID\{681299e0-8bb4-11d3-adaa-006067496245} = EXOR
HKEY_CLASSES_ROOT\CLSID\{681299e0-8bb4-11d3-adaa-006067496245}\InprocServer32 = C:\Program Files\THRSim11\CDK\examples\BorlandC++5.02\exor\exor.dll
HKEY_CLASSES_ROOT\CLSID\{681299e0-8bb4-11d3-adaa-006067496245}\Interface\{6b93056f-799d-11d3-adaa-006067496245} = IComponent
HKEY_CLASSES_ROOT\Interface\{6b93056f-799d-11d3-adaa-006067496245}\ProxyStubCLSID32\{681299e0-8bb4-11d3-adaa-006067496245} = EXOR

Double click on the saved .REG file and it will be merged with your registry. When you copy the file exor.dll to the directory you have registered you can use your component with THRSim11.

NOTE: If you want to distribute your component you need to include the .DLL and the .REG file in the distribution package.

You can find the complete C++ sources here:


For comments and/or suggestions: cdk@hc11.demon.nl