Click here to Skip to main content
12,816,490 members (33,910 online)
Click here to Skip to main content
Add your own
alternative version


22 bookmarked
Posted 14 Sep 2007

Introduction to PDL

, 14 Sep 2007 BSD
Rate this:
Please Sign up or sign in to vote.
Portable Dynamic Loader

What is PDL?

PDL (Portable Dynamic Loader) is a light, simple and portable library designed especially for creating and using dynamically loaded class objects.

Why do we Need to Load Classes Dynamically?

The main purpose of dynamically loadable class technology is creating plug-ins, which extend the functionality of the main program. The main problem with dynamically loaded modules on many platforms is that they support procedure programming paradigm only. When you try to load classes, many problems appear. The PDL library solves most of these problems (but not all of them, sadly).

On Win32 platform, PDL is a very simple alternative to COM technology, without reference counting, global class registration and many other features. There are several similar libraries for Unix/Linux platforms, for example, C++ Dynamic Class Loader. The dynamic class loading support feature also presents in a large cross-platform library called WxWidgets.

The main goal of PDL development was to create a cross-platform library, which could support a single dynamic class loading mechanism for both Win32 and Unix/Linux platforms (unlike COM and C++ Dynamic Class Loader). This library should also be lightweight and independent (unlike the huge WxWidgets one).

Creating the Dynamically Loadable Class

Let's consider creation of the dynamically loadable class using the PDL library in detail. First of all, we need to declare an interface, which will be used to work with the instance of a loadable class instance.
An indispensable condition is that this interface must be inherited from PDL::DynamicClass. Let's look at the DynamicClass declaration:

class DynamicClass
     * @brief Get class name
     * return class name
    virtual const char * GetClassName() const throw() = 0;   

     * @brief Destroy class instance
    void Destroy() throw() { delete this; } 

     * @brief Destructor
    virtual ~DynamicClass() throw() { ;; }

The pure virtual method GetClassName() returns the name of the class. You don't have to worry about its definition, and later I'll explain why.

The non-virtual method Destroy() destroys the class instance. The class destructor is declared as protected, to prevent its direct call. I'll describe later, why we need such a trick.

We have to inherit our interface from PDL::DynamicClass and define protected virtual destructor (according to the PDL ideology).

We also need to insert the DECLARE_DYNAMIC_CLASS macro inside of a class declaration with the class name as a parameter. If we look at the definition of this macro, we'll see that it simply defines the virtual method GetClassName():

#define DECLARE_DYNAMIC_CLASS( className ) \
public: \ 
    virtual const char * GetClassName() const throw() 
        return #className; 

Finally, let's add to our interface pure virtual methods, which implement useful functionality of dynamically loadable class. Let it be DoSomething() method for example.

As a result, we have the following interface:

#include <span class="code-keyword"><DynamicClass.hpp></span>

class MyTestInterface : public PDL::DynamicClass
     * @brief Test method
    virtual void DoSomething() throw() = 0;

     * @brief Declare this class dynamically loadable
    DECLARE_DYNAMIC_CLASS( MyTestInterface )

We should put this declaration to a separate header file, in our case – MyTestInterface.hpp. For compatibility purposes, this file must be included both for building the dynamically loadable class and its direct usage.

Then, we should declare a class inherited from our abstract interface, MyTestInterface, and define methods, which implement its useful functionality. We also need to export this class using the EXPORT_DYNAMIC_CLASS macro. Please note that this macro should be placed outside of a class declaration:

#include <span class="code-keyword"><MyTestInterface.hpp></span>
#include <span class="code-keyword"><stdio.h></span>

class MyTestClass1 : public MyTestInterface
     * @brief Test method
    void DoSomething() throw()
        fprintf( stderr, "MyTestClass1::DoSomething()\n" );

Let's look at the definition of EXPORT_DYNAMIC_CLASS macro (DynamicClass.hpp file):

#define EXPORT_DYNAMIC_CLASS( className ) \
extern "C" PDL_DECL_EXPORT PDL::DynamicClass * Create##className() \
{ \
    try { return new className(); } \
    catch( ... ) { ;; } \
    return NULL; \

This macro defines and exports the function with name Create<class_name> (a builder function), which creates an instance of the class, given as a parameter. The extern "C" modifier is required to prevent function name mangling. The PDL_DECL_EXPORT macro declares this function as exportable. It's definition in platform.h is specific for different platforms.

There are several important issues. The builder function (Create<class_name>) catches all the exceptions, thrown by the class constructor, returning NULL in case of an exception. That solves all problems with handling of exceptions thrown by the plug-in in the main program. In case of instance creation, our builder function returns a pointer to PDL::DynamicClass, so, if you forgot to inherit your interface from this class, the compiler will remind you by producing a casting error.

Now we can build our plug-in. A single plug-in can contain several different classes, but their names should be unique at module level. Each class must be exported using a EXPORT_DYNAMIC_CLASS macro.

Using Dynamically Loadable Classes

At this moment, we have a plug-in which contains a dynamically loadable class. Let's try to use it.

First, we need to get an instance of dynamic class loader – PDL::DynamicLoader. This class is a singleton. To get a reference to the instance, we should use the static method DynamicLoader::Instance():

PDL::DynamicLoader & dynamicLoader = PDL::DynamicLoader::Instance();

Then, we need to load the class instance and get a pointer to it:

MyTestInterface * instance =
    dynamicLoader.GetClassInstance< MyTestInterface >
                ( myLibName, "MyTestClass1" );

Here myLibName is a filename of the plug-in library, for example "MyTestClass1.dll" or "". Don't forget to include the header file with interface declaration – in our case it is MyTestInterface.hpp, as described before.

Finally, we invoke a useful method of the loaded class:

instance -> DoSomething();

As the dynamic class loader throws PDL::LoaderException in case of failure, it will be correct to catch it. Here is the full source of our example:

#include <span class="code-keyword"><MyTestInterface.hpp></span>
#include <span class="code-keyword"><stdio.h> </span>

    PDL::DynamicLoader & dynamicLoader = PDL::DynamicLoader::Instance();
    MyTestInterface * instance =
        dynamicLoader.GetClassInstance< MyTestInterface >
                        ( myLibName, "MyTestClass1" );
    instance -> DoSomething();
catch( PDL::LoaderException & ex )
    fprintf( stderr, "Loader exception: %s\n", ex.what() );

There is an important feature: all the dynamically loaded classes are singletons. This means that repeated calls of DynamicLoader::GetInstance() with the same both library name and class name will return a pointer to the same class instance. This simplifies the control over loaded instances and prevents memory leaks. If you need to create many class instances, you may implement dynamically loadable class factory.

Let's examine how loaded class instances are being destroyed. This is the responsibility of the method DynamicClass::Destroy(). You don't need to invoke it directly – the DynamicLoader will do it in its destructor or in DynamicLoader::Reset(). Why shouldn't we use a generic destructor? This is because of the memory allocation/freeing mechanism issues, which are slightly different in different compilers. Let's imagine: we build a plug-in with compiler A, and a main program with compiler B. When we load a class by dynamic loader, its instance is created by a code produced by A compiler. But if we call a destructor, we invoke a code produced by compiler B. This may lead to unexpected problems.

To prevent such problems, the destructor ~DynamicClass() is declared as protected, and you need to call DynamicClass::Destroy() method instead. This method guarantees that the code of destructor is compiled by the same compiler as the code of constructor.

Fly in the Ointment

There is an issue with library names. If the filename of the library changes, PDL considers that it is a different library. However, different names may point to a single library, for example: C:\MyProg\libs\mylib.dll and MyLIB.DLL.

Pay attention that PDL doesn't solve problems with name mangling, used by different compilers. This problem is not set at the current moment.

The PDL library was tested on the following platforms:

  • FreeBSD 6.2
  • Debian 4.0 Linux 2.6.18-4
  • openSUSE 10.2
  • Windows XP

I will be grateful for any information about usage of PDL on other platforms.


Special thanks to Vladimir and Asya Storozhevykh, Alexander Ledenev and Valery Artukhin, who helped me to make this article better (I hope).



This article, along with any associated source code and files, is licensed under The BSD License


About the Author

Russian Federation Russian Federation
No Biography provided

You may also be interested in...

Comments and Discussions

QuestionDLL back trace Pin
Willian.BR3-Apr-08 8:25
memberWillian.BR3-Apr-08 8:25 
GeneralRe: DLL back trace Pin
isemenov8-Apr-08 5:23
memberisemenov8-Apr-08 5:23 
GeneralPDL compared to DynObj Pin
John Willcox2-Oct-07 6:07
memberJohn Willcox2-Oct-07 6:07 
GeneralRe: PDL compared to DynObj Pin
isemenov3-Oct-07 5:05
memberisemenov3-Oct-07 5:05 
GeneralRe: PDL compared to DynObj Pin
Arne Steinarson5-Nov-08 4:36
memberArne Steinarson5-Nov-08 4:36 
Being the author of DynObj library, I can add some here. I only saw this article today, but better late than never.

DynObj has gone through some evolution since my article at CodeProject. Here's an updated page for the library:[^]

Some facts:

- DynObj does not rely on a single 'root interface'. Once can use any class with virtual functions across host/plugin boundaries. So, one can apply it on existing class hierarchies without modifying sources.

- DynObj uses a compiler neutral type information (generated by the plugin compiler) to accomplish casts (interface queries).

- DynObj plugins can be used from other languages than C++ (the one requirement is that the language has means use VTables).

DynObj provides quite a bit of template helpers make use of plugin objects and type queries quite transparent.

// ATS.
Generalimplement dynamically loadable class factory [modified] Pin
John Willcox2-Oct-07 5:44
memberJohn Willcox2-Oct-07 5:44 
GeneralRe: implement dynamically loadable class factory Pin
isemenov2-Oct-07 23:36
memberisemenov2-Oct-07 23:36 
GeneralVisual Studio 2005 compile/link error Pin
John Willcox24-Sep-07 9:46
memberJohn Willcox24-Sep-07 9:46 
GeneralRe: Visual Studio 2005 compile/link error Pin
isemenov24-Sep-07 9:55
memberisemenov24-Sep-07 9:55 
GeneralRe: Visual Studio 2005 compile/link error Pin
John Willcox25-Sep-07 4:16
memberJohn Willcox25-Sep-07 4:16 
GeneralRe: Visual Studio 2005 compile/link error Pin
isemenov25-Sep-07 21:42
memberisemenov25-Sep-07 21:42 
GeneralRe: Visual Studio 2005 compile/link error Pin
John Willcox26-Sep-07 6:34
memberJohn Willcox26-Sep-07 6:34 
GeneralRe: Visual Studio 2005 compile/link error Pin
isemenov26-Sep-07 6:38
memberisemenov26-Sep-07 6:38 
GeneralRe: Visual Studio 2005 compile/link error Pin
John Willcox26-Sep-07 11:27
memberJohn Willcox26-Sep-07 11:27 
GeneralRe: Visual Studio 2005 compile/link error Pin
isemenov26-Sep-07 21:36
memberisemenov26-Sep-07 21:36 
GeneralTrying without cmake Pin
stefkeB20-Sep-07 0:07
memberstefkeB20-Sep-07 0:07 
GeneralRe: Trying without cmake Pin
isemenov20-Sep-07 0:32
memberisemenov20-Sep-07 0:32 
AnswerRe: Trying without cmake Pin
stefkeB20-Sep-07 3:03
memberstefkeB20-Sep-07 3:03 
GeneralRe: Trying without cmake Pin
isemenov20-Sep-07 3:15
memberisemenov20-Sep-07 3:15 
GeneralRe: Trying without cmake Pin
stefkeB20-Sep-07 4:55
memberstefkeB20-Sep-07 4:55 
GeneralMemory management Pin
Jim Crafton14-Sep-07 10:26
memberJim Crafton14-Sep-07 10:26 
GeneralRe: Memory management Pin
isemenov14-Sep-07 11:16
memberisemenov14-Sep-07 11:16 
GeneralDifferent compilers Pin
Jim Crafton14-Sep-07 10:22
memberJim Crafton14-Sep-07 10:22 
GeneralRe: Different compilers Pin
isemenov14-Sep-07 11:08
memberisemenov14-Sep-07 11:08 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

Permalink | Advertise | Privacy | Terms of Use | Mobile
Web02 | 2.8.170308.1 | Last Updated 14 Sep 2007
Article Copyright 2007 by isemenov
Everything else Copyright © CodeProject, 1999-2017
Layout: fixed | fluid