HowTo create a module for PACLib

(written by Oliver Granert)

This HowTo describes the steps to write a PACLib module.

1. step (which interfaces)

You must decide which interfaces your module should have. In this HowTo we want to create as an example a "ppm image file reader" module. Ok, which of the interfaces (look in directory PACBase/PACInterfaces) do we need?

A IFile interface. The IFile interface can be used to determine which file extension is supported by our new module.
An IImageSource interface. The ImageSource interface can be used to get the image (when someone wants to start a read process).
An IImageTarget interface. This interface can be used to set an image (when someone wants to start a write process).
An IOptions interface. This interface can be used to set the filename.

2. step (write module.config)

To create the module skeleton (template makefiles and  c++ source code) for our new PACLib module we can use the PACCreateModuleFiles program (in directory PACTools/PACCreateModuleFiles). This program needs a module config file to get informations about the interfaces of the new module. For our new module we write the following module.config file:

module.config file

line 1

PACImageFileIO_PPM

line 2

PACImageFilePPM

line 3

File

line 4

ImageSource

line 5

ImageTarget

line 6

Options


What does this mean?
In line 1 we write the name of the new PACLib module.
In line 2 we write the name of the first class in our module.
In the following lines we list the interfaces of the first class.
That's all for our example (we only need one class in our module).
If you want to write a module with more than one class you can insert a blank line and list more classes with other interfaces (a new line separates classes).

3. step (create module skeleton)

Now we can create the module files with the following command:
   
    PACCreateModuleFiles module.config

Now there should be a subdirectory with the module name (in our example this directory is called PACImageFileIO_PPM).


The skeleton (for our example) has the following files:

configure and makefiles:


SETUP
BUILD
configure.in
makefile.am
PACImageFileIO_PPM.vcproj
PACMainPACImageFileIO_PPM.def


sourcecode (in subdir source):


PACMainPACImageFileIO_PPM.cpp

? (here only Christan knows the real truth)

PACClsRepPACImageFileIO_PPM.cpp
PACClsRepPACImageFileIO_PPM.h

here all classes of our new module are listed (declared and registered)

PACIfcsPACImageFileIO_PPM.cpp
PACIfcsPACImageFileIO_PPM.h

here all used interfaces of our new module are listed (included)

PACImageFilePPM.cpp
PACImageFilePPM.h

our own class (which we want to implement)



4. step (implement the class)


Now we take a look into out class skeleton (PACImageFilePPM.h and PACImageFilePPM.cpp in our example).
We can find the following section in the header (PACImageFilePPM.h) file:

...
PAC_DECLARE_DYNCLASS(CPACImageFilePPM),
    // List interfaces implemented by this class as follows:
    public IFile,
    public IImageSource,
    public IImageTarget,
    public IOptions
{

...


This section declares our class CPACImageFilePPM and derives is from the interface classes (which are pure virtual).
It's translated in the C-code:

class CPACImageFilePPM : public IPACDynamicObject, public IFile, public IImageSource, public IImageTarget, public IOptions
{...}


Now we can remove the example functions in the generated code. You can find them under the following section:

public:
    // Implement interface functions here.


and implement our own member function. It's normal C++ programming. Remember that all member functions in the interfaces are pure virtual. You have to implement them!

In our example there must be the following member functions:

IFile

  • virtual bool SetFilename(const string& sFilename, int iFileIndex = 0, void* pData = NULL);

  • virtual bool GetFilename(string& sFilename, int iFileIndex = 0, void* pData = NULL);

IImageSource

  • virtual void GetImage(CImageBase &Image, bool bCopyImage = false) throw (CPACException);

IImageTarget

  • virtual void SetImage(CImageBase &rImage, bool bCopyImage = false) throw (CPACException);

  • virtual void ImageChanged();

IOptions

  • virtual int  GetOptionCount() throw (CPACException);

  • virtual std::string GetOptionName(const int OptionNumber) throw (IOptions::UnknownOption,CPACException);

  • virtual int GetOptionNumber(const std::string &OptionName) throw (IOptions::UnknownOption,CPACException);

  • virtual void GetOptionValue(const int OptionNumber,CPACDataWrapper &value) throw (IOptions::UnknownOption,CPACException);

  • virtual void GetOptionValue(const std::string &OptionName,CPACDataWrapper &value) throw (IOptions::UnknownOption,CPACException);

  • virtual void SetOptionValue(const int OptionNumber,const CPACDataWrapper &value,TPAC_SETOPTIONINFO *info = 0) throw (IOptions::UnknownOption,InvalidType,InvalidValue,CPACException);

  • virtual void SetOptionValue(const std::string &OptionName,const CPACDataWrapper &value,TPAC_SETOPTIONINFO *info = 0) throw (IOptions::UnknownOption,InvalidType,InvalidValue,CPACException);

  • virtual void GetOptionInfo(const int OptionNumber,COptionInfo &info) throw (IOptions::UnknownOption,CPACException); 

  • virtual void GetOptionInfo(const std::string &OptionName,COptionInfo &info) throw (IOptions::UnknownOption,CPACException);


Ok. We have a lot of work now. Specially for the IOptions interface. But we can use base class to simplify the implementation.
This is shown in the next step.

5. step (simplify the implementation)

In our example we want so simplify the implementation of the IOption interface. We can use the CSimpleOptions or the CPACStdOptions class (in PACBase/PACMain directory). Here we will take the CSimpleOptions class.
We have to include the header file SimpleOptions.h in PACImageFilePPM.h:

...
#include "SimpleOptions.h"
...


and replace the IOptions class with the CSimpleOptions class:

...
PAC_DECLARE_DYNCLASS(CPACImageFilePPM),
    // List interfaces implemented by this class as follows:
    public IFile,
    public IImageSource,
    public IImageTarget,
    public CSimpleOptions
{
...


The CSimpleOptions class is derived from the IOptions class. So our own class has also an IOptions interface. We have not removed the IOption interface. The CSimpleOption class implements all member function from the IOption interface.

6. step (implement the other member functions)

Now we implement the missing member functions:

The header file looks like this:

...
PAC_DECLARE_DYNCLASS(CPACImageFilePPM),
    // List interfaces implemented by this class as follows:
    public IFile,
    public IImageSource,
    public IImageTarget,
    public CSimpleOptions
{
    std::string m_sFilename; // to store the filename of the ppm file
public:
    // Implement interface functions here.
/** member function for the IFile interface */
    virtual bool SetFilename(const string& sFilename, int iFileIndex = 0, void* pData = NULL);
    virtual bool GetFilename(string& sFilename, int iFileIndex = 0, void* pData = NULL);
/** member function for the IImageSource interface */
    virtual void GetImage(CImageBase &Image, bool bCopyImage = false) throw (CPACException);
/** member function for the IImageTarget interface */
    virtual void SetImage(CImageBase &rImage, bool bCopyImage = false) throw (CPACException);
    virtual void ImageChanged();


protected:
    PAC_DECLARE_STD_IINFO(IFile);
    PAC_DECLARE_STD_IINFO(IImageSource);
    PAC_DECLARE_STD_IINFO(IImageTarget);
    PAC_DECLARE_STD_IINFO(IOptions);
...


The cpp file looks like this:




7. step (add non standard interface info)

This step is only usefull for a non standard interface info (in our example the IFile interface).
Add library PACInterfaceInfos in file source/Makefile.am -lPACInterfaceInfos.  The makefile.am looks like:

...
PACMOD_PACImageFileIO_PPM_la_LDFLAGS = -module -L@PAC_HOME@/lib -lPACUtil \
-lPACdms -lPACImage -lPACInterfaceInfos -lPACMain -lpthread @LIBRT@
...


In header (.h) file:

...
protected:
// remove  PAC_DECLARE_STD_IINFO(IFile);
// and add
    PAC_DECLARE_IINFO(CFileInfo, IFile);

    PAC_DECLARE_STD_IINFO(IImageSource);
    PAC_DECLARE_STD_IINFO(IImageTarget);
    PAC_DECLARE_STD_IINFO(IOptions);
...


In implementation (.cpp) file:

// remove PAC_IMPLEMENT_STD_IINFO(CPACImageFilePPM, IFile);
// and add
CFileInfo CPACImageFilePPM::sm_IFile;
PAC_IMPLEMENT_STD_IINFO(CPACImageFilePPM, IImageSource);
PAC_IMPLEMENT_STD_IINFO(CPACImageFilePPM, IImageTarget);
PAC_IMPLEMENT_STD_IINFO(CPACImageFilePPM, IOptions);

...

// remove    PAC_INIT_STD_IINFO(IFile,
//            "File Interface",
//            "This interface has no description.",
//            0, 1, 0);
// and add

sm_IFile.SetIInfoFile(UID_IFile,"CPACImageFilePPM","read/write bmp images","ppm");
sm_vecIInfoList.push_back(&sm_IFile);

...




8. step (compile and install the module)

Simply call SETUP and BUILD.