Module Class

Each native module is implemented as a class inheriting from abstract class NativeModule, defined in NativeModule.h. It must implement the following abstract virtual functions: In addition, it can implement the following virtual functions: The only variable visible to subclasses is:

World Server Example

We start our "world" module by including the necessary files and defining the skeleton of our class. Our module will maintain a connection to the world server, i.e. an instance of the Symbian class RWorldServer, defined in t32wld.h. Note that all class members can be kept private, except for the implementation of the virtual functions defined in NativeModule:

#include "NativeModule.h"
#include <t32wld.h>

class WorldModule : public NativeModule {
private:
  // @todo: some declarations

  // a connection to the Symbian world server, and the current city id
  RWorldServer server;
  TWorldId id;

  // destroying the module should c;

We will now fill the @todo sections step by step.

Module Versioning

Since neither C++ nor Symbian offer a proper interface or DLL versioning mechanism interface, we must somehow ensure that a DLL we load really is an m module. This is achieved by implementing the following function defined in NativeModule:

TInt ExpectedRuntimeVersion() { return Runtime::VERSION; }

Runtime::VERSION is guaranteed to change if the interface defined by the Runtime class changes. Modules relying on another version of the runtime system can therefore be identified and prevented from initializing before harming the m application.

Furthermore, there is a simple module versioning mechanism built into m: Each module has its ..version constant. For a native module, this version is the string returned by ModuleVersion(). If you manage your versions via CVS, you can automatically have CVS insert the current revision, for instance

const char *ModuleVersion() { return "$Revision: 869 $"; }

The m runtime system will trim non-numeric characters and just assign 869 to ..version.

Defining Module Members

As outlined above, each function is identified by its index (a unique integer). The simplest way to define those indices is to define an (anonymous) enumeration. The functions and any global module variables and constants are then added to m's internal tables in the 2nd phase constructor of the class, in its ConstructL() function, using the following functions from class Runtime:

World Server Example, Continued

For the functions and the calls variable of our "world" module, defining module members looks as follows:

// the functions this module supports
// the enumeration values correspond to function indices
enum {
  CountFunction, FindFunction, FirstFunction, HomeFunction, NextFunction
};

Since we use them more than once, we also declare the module and variable name descriptor constants for the world.calls variable:

// the names to access the calls variable
_LIT(WORLD, "world");
_LIT(CALLS, "calls");

void ConstructL() {
  // the five functions this module supports
  runtime->AddNativeFunctionL(_L("count"), 0, 0, CountFunction);
  runtime->AddNativeFunctionL(_L("find"), 1, 2, FindFunction);
  runtime->AddNativeFunctionL(_L("first"), 0

None of our functions take arguments except world.find, which can have one or two.

The value reference returned by runtime->GetVariableL() is set to (the number) zero (the C++ type of an m variables is Runtime::Value, which will be explained in detail in section * ()).

Implementing Functions

Each native module has only a single entry point for all the functions it declares: the virtual function ExecuteL(). The actual function called is passed as a parameter, and corresponds to the index set when adding the function via runtime->AddNativeFunctionL().

Besides the function index, ExecuteL() gets the following parameters passed by the m runtime system:

ExecuteL() returns the Runtime::Value which is the result of the m function.

World Server Example, Continued

The skeleton for the functions of our module world might therefore look as follows:

Runtime::Value ExecuteL(TInt index, Runtime::Value *params,
                        TInt paramCount, TRequestStatus &status)
{
  // if the world server is not connected, connect it
  if (server.Handle() == 0) User::LeaveIfError(server.Connect());
  // a variable which will contain the function result
  Runtime::Value result;
 &

Note that the server connection is not established until the first call of ExecuteL(), i.e. until a function of the module is called. The reasoning behind this is explained in section * ().

The next chapter will replace the @todo comments by actual code.


© 2004-2010 airbit AG, CH-8008 Zürich
Document AB-M-NMI-869