Main Page   Namespace List   Class Hierarchy   Alphabetical List   Compound List   File List   Namespace Members   Compound Members   File Members   Related Pages  

C++ Tutorial

Please read UPF Concepts and Language-Independent Tutorial before!

Headers and libraries

First of all, you have to include UPF headers:

#include <upf/upf.h>

The app has to link against libupf (use pkg-config --libs upf under Unix). Note that this only applies to application's main executable, not DLL modules! (See Runtime-loaded Modules.)

Attention:
Never include other files in upf/ (e.g. upf/upf_cxx.h) directly!

Initialization

UPF-enabled application must initialize UPF at startup and shut it down before exiting:

#include <upf/upf.h>
...
int main()
{
    upf::init();
    doSomethingInteresting();
    upf::done();
}

(Note the word "application" -- this does not apply to DLL modules and must be done exactly once.)

Class definition

It's time to add some classes into the system now. Let's assume we have following IDL definition of our interface:

#include <upf/IObject.idl>
interface ICalc : upf::IObject
{
    long sum(in long a, in long b);
};

We generate C++ binding with upf-idl2cxx:

$ upf-idl2cxx -oICalc.h ICalc.idl

ICalc.h will contain lots of misterious code, but the import part for us is ICalc abstract class declaration:

class ICalc : virtual public ::upf::IObject
{
public:
    virtual long sum(long a, long b) = 0;
};

Let's implement it:

#include <upf/upf.h>
#include "ICalc.h"
class MyCalc : public ICalc
{
public:
    long sum(long a, long b)
    {
      return a+b;
    }
    
    UPF_DECLARE_CLASS(MyCalc)
};

UPF_IMPLEMENT_CLASS(MyCalc)
{
    UPF_INTERFACE(ICalc)
}

A bunch of macros is used here. UPF_DECLARE_CLASS is neccessary to declare some methods and data used internally by UPF. UPF_IMPLEMENT_CLASS adds some neccessary boilerplate code and, finally, UPF_INTERFACE tags \i inside UPF_IMPLEMENT_CLASS block let you declare which interfaces does the class implement.

Notice that you don't have to implement upf::IObject methods, UPF_DECLARE_CLASS and UPF_IMPLEMENT_CLASS take care of them.

Here's a more complicated example that involves two interfaces. Additional IDL:

// copyright.idl:
#include <upf/IObject.idl>
interface ICopyrightedClass : upf::IObject
{
    string getCopyright(in string app_name);
};

And the implementation:

// calc_impl.cpp:
class MyCalc : public ICalc, public ICopyrightedClass
{
public:
    long sum(long a, long b)
    {
      return a+b;
    }
    
    std::string getCopyright(const std::string& app_name)
    {
      return "(c) Me, ThisYear (used in " + app_name + ")";
    }
    
    UPF_DECLARE_CLASS(MyCalc)
};

UPF_IMPLEMENT_CLASS(MyCalc)
{
    UPF_INTERFACE(ICalc)
    UPF_INTERFACE(ICopyrightedClass)
    UPF_PROPERTY("Description", "A more complicated example")
}

There are two interesting things in the example above: first, notice that string type is translated into std::string. UPF uses C++ Standard Library for nontrivial types (IDL's sequence<T> type is mapped to std::vector<T> by UPF's generator).

Secondly, a new feature is used here, UPF's class properties. You can assign arbitrary number of key-value property pairs to the class with UPF_PROPERTY macro (both key and value are strings). Use upf::IObject::getClassID, upf::IManager::getClassInfo and upf::IClassInfo::getProperty and upf::IClassInfo::getProperties to query object instance for its property tags even if you don't know anything about it in advance.

The only thing missing in our example is how to let UPF runtime know about the class. As you have undoubtely guessed, there's another macro for it, UPF_REGISTER_CLASS:

int main()
{
    upf::init();
    UPF_REGISTER_CLASS(MyCalc);
    doSomethingInteresting();
    upf::done();
}

Creating instances and keeping them around

We would like to create an instance of our new MyCalc class and use it. It's simple:

upf::Ptr<ICalc> calc = upf::create("MyCalc"); //   <---
long result = calc->sum(40, 2);

The first line does in fact two things: upf::create creates MyCalc instance and returns pointer to upf::IObject as if MyCalc only implemented upf::IObject interface. If you want to use another interface (and you always do), you have to query the object for desired interface first.

Fortunately it all happens automatically in C++ thanks to upf::Ptr template class. Whenever you assing to variable of type upf::Ptr<Foo>, the object is first queried for the Foo interface and the variable will be assigned NULL if the object doesn't implement it. Another thing that upf::Ptr does is that it keeps track of reference counting and so you don't have to worry about deleting objects. Other than that, you can treat upf::Ptr<Foo> as if it were Foo*.

Querying for any interface is equally simple:

{
    upf::Ptr<ICalc> calc = upf::create("MyCalc");
    upf::Ptr<ICopyrightedClass> cr_class = calc;
} // calc and cr_class were destroyed now, object's ref cnt was descreased by 2 and
  // it was destroyed because there's no other reference pointing to it

Runtime-loaded Modules

UPF has the ability to load modules (plugins) from DLLs (or shared libraries on Unix). Creating such module is easy: you define implementation classes as in previous section, but don't register them. Then you add UPF_DLL_MODULE macro to one of source files that are used to build the DLL (make sure it is at global namespace). You have to declare all classes that will be visible from outside with UPF_EXPORTED_CLASS, too. Here's an example:

/* --------------- foo+bar.cpp --------------- 
#include <upf/upf.h>

namespace my {
...define Foo and Bar classes here...
}

UPF_DLL_MODULE()
{
    UPF_EXPORTED_CLASS(my::Foo)
    UPF_EXPORTED_CLASS(my::Bar)
}

/* --------------- myapp.cpp --------------- 
...
upf::Ptr<upf::IObject> upf::create("my.Foo");
  // Note that you must use fully-qualified name here, "my.Foo".
  // Writing only "Foo" would not be enough!
...

When compiling a module, you must define UPF_BUILDING_MODULE macro! You can use any of these approaches:

Once you created the DLL/shared library, copy it to a directory that your application uses for plugins and let UPF know about the directory by calling upf::IManager::addModulesPath (you can add as many directories you need). UPF will load all modules from the directory automatically.

Attention:
Do not call upf::init or upf::done from the module! Don't link the module against libupf, either! UPF modules are self-contained and don't need any symbols from libupf, by design.

Generated on Wed Jan 15 23:10:55 2003 for Universal Plugins Framework by doxygen1.2.18