3.3. Exported Objects of a Module

In this section we explain how modules are filled with functionality. We can define an arbitrary number of objects in a module as long as they do not conflict (e.g. have name clashes etc.)

3.3.1. Define Normalization Functions (Normalizers)

In this chapter we introduce how to declare a normalizer function in a module for defining your own DLL form data types. First we introduce the data structures you have to know to implement normalizer functions and then we will show the module building blocks to declare a normalizer function in a module.

Normalizer Interface

A normalize function is defined as interface in order to be able to define it as object with data. This is because normalizer functions can be parametrized. For example to express the normalize function domain. The following listing shows the interface definition:

namespace _Wolframe {
namespace types {

struct NormalizeFunction
{
    virtual ~NormalizeFunction(){}
    virtual const char* name() const=0;
    virtual Variant execute( const Variant& i) const=0;
};
}}

	

The object is created by a function type (here with the example function name CreateNormalizeFunction) with the following interface


_Wolframe::types::NormalizeFunction* CreateNormalizeFunction(
        _Wolframe::types::NormalizeResourceHandle* reshnd,
        const std::vector<types::Variant>& arg);


	

The resource handle parameter (reshnd) is the module singleton object instance that is declared as class in the module building blocks (see following section). The argument (arg) is a list of variant type arguments that parametrize the function. What the function gets as arguments are the comma separated list of parameters in '(' brackets ')' when the function is referenced in a .wnmp file (type normalization declaration file, see section "Data Types in DDLs" in the chapter "Forms" of the "Application Development Manual") or constructed with the provider.type method in a script.

Building Blocks

When you include "appdevel/normalizeModuleMacros.hpp" or simply "appDevel.hpp" you get the building blocks declared to build a normalizer function in a module. These building blocks will be exmplained in this section.

Declaring a resource singleton object

Some normalizer functions share resource object declared only once as a singleton in this module. Such a resource class is defined as a class derived from types::NormalizeResourceHandle with an empty constructor. When we have declared this resource signleton class we can include it in the module before any normalizer referencing it as

    WF_NORMALIZER_RESOURCE( ResourceClass )
	

with ResourceClass identifying the module singleton resource class and object.

Declaring a normalizer not using any resource

The following declaration shows a declaration of a simple normalizer function.

    WF_NORMALIZER_FUNCTION(name,constructor)
	

where name is the identifier string of the function in the system and constructor a function with the signature of the CreateNormalizeFunction shown in the section 'Normalize Interface' above.

Declaring a normalizer using a resource

The following declaration shows a declaration of a normalizer function using a resource module singleton object defined as class 'ResourceClass' and declared with the WF_NORMALIZER_RESOURCE macro (section 'Declaring a resource singleton object').

    WF_NORMALIZER_WITH_RESOURCE(name,constructor,ResourceClass)
	

The parameter name and constructor are defined as in the WF_NORMALIZER_FUNCTION macro.

Examples

Example without resources

As first example we show a module that implements 2 normalization functions Int and Float without a global resource class. Int converts a value to an 64 bit integer or throws an exception, if this is not possible. Float converts a value to a double presicion floating point number or throws an exception, if this is not possible.

#include "appDevel.hpp"

using namespace _Wolframe;

class NormalizeInt
    :public types::NormalizeFunction
{
public:
    NormalizeInt( const std::vector<types::Variant>&){}
    virtual ~NormalizeInt(){}
    virtual const char* name() const
            {return "int";}
    virtual types::Variant execute( const types::Variant& i) const
            {return types::Variant( i.toint());}
    virtual types::NormalizeFunction* copy() const
            {return new NormalizeInt(*this);}
};

class NormalizeFloat
    :public types::NormalizeFunction
{
public:
    NormalizeFloat( const std::vector<types::Variant>&){}
    virtual ~NormalizeFloat(){}
    virtual const char* name() const
            {return "float";}
    virtual types::Variant execute( const types::Variant& i) const
            {return types::Variant( i.todouble());}
    virtual types::NormalizeFunction* copy() const
            {return new NormalizeFloat(*this);}
};

WF_MODULE_BEGIN(
    "example1",
    "normalizer module without resources")

    WF_NORMALIZER( "int", NormalizeInt)
    WF_NORMALIZER( "float", NormalizeFloat)

WF_MODULE_END



	

Example with resources

The second example show one of the functions in the example above (Int) but declares to use resources. The resource object is not really used, but you see in the example how it gets bound to the function that uses it.

#include "appDevel.hpp"

using namespace _Wolframe;

class ConversionResources
    :public types::NormalizeResourceHandle
{
public:
    ConversionResources()
    {}
    virtual ~ConversionResources()
    {}
};

class NormalizeInt
        :public types::NormalizeFunction
{
public:
    explicit NormalizeInt( const types::NormalizeResourceHandle* res_,
			   const std::vector<types::Variant>&)
	:res(dynamic_cast<const ConversionResources*>(res_)){}
    virtual ~NormalizeInt()
        {}
    virtual const char* name() const
        {return "int";}
    virtual types::Variant execute( const types::Variant& i) const
        {return types::Variant( i.toint());}
    virtual types::NormalizeFunction* copy() const
        {return new NormalizeInt(*this);}
private:
    const ConversionResources* res;
};

WF_MODULE_BEGIN(
    "example2",
    "normalizer module with resources")

    WF_NORMALIZER_RESOURCE( ConversionResources)
    WF_NORMALIZER_WITH_RESOURCE(
	"Int", NormalizeInt, ConversionResources)
WF_MODULE_END




	

3.3.2. Define Custom Data Types

In this chapter we introduce how to declare a custom data type in a module. Custom data types can be used in scripting language bindings and as normalizers referenced in a .wnmp file (type normalization declaration file, see section "Data Types in DDLs" in the chapter "Forms" of the "Application Development Manual") First we introduce the data structures you have to know to implement a custom data type and then we will show the module building block to declare a custom data type in a module.

Custom Data Type Interface

A custom data type definition involves 3 classes: CustomDataType,CustomDataValue and CustomDataInitializer. The CustomDataInitializer class is optional and only needed when value construction has to be parametrized. If an initializer is involved then it is created and passed as argument to the method constructing the custom data type value (class CustomDataValue). The class CustomDataType defines the custom data type and all its methods defined. The class CustomDataValue defines a value instance of this type. The class CustomDataInitializer, if specified, defines an object describing the parametrization of the value construction. An example of an initializer could be the format of a date or the precision in a fixed point number. The following listings show these interfaces:

CustomDataType Structure

The class to build the custom data type definition structure composed of methods added with CustomDataType::define( .. ). From this class we do not derive. We incrementally add method by method by calling CustomDataType::define( .. ) in the type constructor function.


namespace _Wolframe {
namespace types {

// Custom Data Type Definition
class CustomDataType
{
public:
    typedef unsigned int ID;
    enum UnaryOperatorType {Increment,Decrement,Negation};
    enum BinaryOperatorType {Add,Subtract,Multiply,Divide,Power,Concat};
    enum ConversionOperatorType {ToString,ToInt,ToUInt,ToDouble,ToTimestamp};
    enum DimensionOperatorType {Length};

    typedef types::Variant (*ConversionOperator)(
        const CustomDataValue& operand);
    typedef types::Variant (*UnaryOperator)(
        const CustomDataValue& operand);
    typedef types::Variant (*BinaryOperator)(
        const CustomDataValue& operand, const Variant& arg);
    typedef std::size_t (*DimensionOperator)(
        const CustomDataValue& arg);
    typedef types::Variant (*CustomDataValueMethod)(
        const CustomDataValue& val,
        const std::vector<types::Variant>& arg);
    typedef CustomDataValue* (*CustomDataValueConstructor)(
        const CustomDataInitializer* initializer);
    typedef CustomDataInitializer* (*CreateCustomDataInitializer)(
        const std::vector<types::Variant>& arg);

public:
    CustomDataType()
        :m_id(0)
    {
        std::memset( &m_vmt, 0, sizeof( m_vmt));
    }

    CustomDataType( const std::string& name_,
            CustomDataValueConstructor constructor_,
            CreateCustomDataInitializer initializerconstructor_=0);

    void define( UnaryOperatorType type, UnaryOperator op);
    void define( BinaryOperatorType type, BinaryOperator op);
    void define( ConversionOperatorType type, ConversionOperator op);
    void define( DimensionOperatorType type, DimensionOperator op);
    void define( const char* methodname, CustomDataValueMethod method);
};

typedef CustomDataType* (*CreateCustomDataType)( const std::string& name);

}}//namespace


	
CustomDataInitializer Interface

The custom data inizializer definition. From this class we have to derive our own initializer definions.


namespace _Wolframe {
namespace types {

// Initializer for a custom data value
class CustomDataInitializer
{
public:
    CustomDataInitializer();
    virtual ~CustomDataInitializer();
};

}}//namespace


	
Class CustomDataValue

The custom data type value instance definition. From this class we have to derive our own custom value definions.


namespace _Wolframe {
namespace types {

// Custom data value interface
class CustomDataValue
{
public:
    CustomDataValue();
    CustomDataValue( const CustomDataValue& o);
    virtual ~CustomDataValue();

    const CustomDataType* type() const;
    const CustomDataInitializer* initializer() const;

    virtual int compare( const CustomDataValue& o) const=0;
    virtual std::string tostring() const=0;
    virtual void assign( const Variant& o)=0;
    virtual CustomDataValue* copy() const=0;

    // try to convert the value to one of the basic
    // variant types and return true on success:
    virtual bool getBaseTypeValue( Variant&) const;
};

}}//namespace


	

Building Blocks

When you include "appdevel/customDatatypeModuleMacros.hpp" or simply "appDevel.hpp" you get the building block declared to build a custom data type in a module.

Declaring a custom data type

The following declaration shows a declaration of a simple custom data type.

    WF_CUSTOM_DATATYPE(name,constructor)
	

where name is the identifier string of the function in the system and constructor a function with the following signature:


typedef CustomDataType* (*CreateCustomDataType)( const std::string& name);


	

3.3.3. Define Filters

In this chapter we introduce how to declare a filter type in a module. Filters are used to deserialize input and to serialize output.

Filter element types

Filters provide a uniform interface to content as sequence of elements. The elements have one of the following types.

Table 3.2. Filter element types

IdentifierDescription
OpenTagOpen a substructure (element value is the name of the structure) as the current scope.
CloseTagClose the current substructure scope or marks the end of the document if there is no substructure scope open left (top level close).
AttributeDeclare an attribute (element value is the name of the attribute)
ValueDeclare a value. If the previous element was an attribute then the value specifies the content value of the attribute. Otherwise the value specifies the content value (only one allowed) of the current substructure scope.

Filter element values

Filter values are chunks of the input and are interpreted depending on the filter element type.

Filter Interface

A filter definition is a structure with 2 substructure references: An input filter (InputFilter) and an output filter (OutputFilter). You have to include "filter/filter.hpp" to declare a filter.

Input Filter Structure

From this interface you have to derive to get an input filter class.


namespace _Wolframe {
namespace langbind {

// Input filter interface
class InputFilter
{
public:
    // State of the input filter
    enum State
    {
        Open,         // normal input processing
        EndOfMessage, // end of message reached (yield)
        Error         // an error occurred
    };

    // Default constructor
    explicit InputFilter( const char* name_);

    // Copy constructor
    ///\param[in] o input filter to copy
    InputFilter( const InputFilter& o);

    // Destructor
    virtual ~InputFilter();

    // Get a self copy
    virtual InputFilter* copy() const=0;

    // Declare the next input chunk to the filter
    virtual void putInput(
        const void* ptr,
        std::size_t size, bool end)=0;

    // Get the rest of the input chunk left
    // unparsed yet (defaults to nothing left)
    virtual void getRest(
        const void*& ptr,
        std::size_t& size, bool& end);

    // Get a named member value of the filter
    virtual bool getValue(
        const char* id, std::string& val) const;

    // Get next element
    virtual bool getNext(
        ElementType& type,
        const void*& element, std::size_t& elementsize)=0;

    // Get the document meta data
    virtual const types::DocMetaData* getMetaData();

    // Get the current state
    State state() const;

    // Set input filter state with error message
    void setState( State s, const char* msg=0);
};

// Shared input filter reference
typedef boost::shared_ptr<InputFilter> InputFilterR;


}}//namespace
#endif



	
Output Filter Structure

From this interface you have to derive to get an output filter class.


namespace _Wolframe {
namespace langbind {

// Output filter
class OutputFilter
    :public FilterBase
{
public:
    // State of the input filter
    enum State
    {
        Open,           //< normal input processing
        EndOfBuffer,    //< end of buffer reached
        Error           //< have to stop with an error
    };

    // Default constructor
    OutputFilter(
        const char* name_,
        const ContentFilterAttributes* attr_=0);

    // Copy constructor
    OutputFilter( const OutputFilter& o);

    // Destructor
    virtual ~OutputFilter(){}

    // Get a self copy
    virtual OutputFilter* copy() const=0;

    // Print the follow element to the buffer
    virtual bool print(
        ElementType type,
        const void* element,
        std::size_t elementsize)=0;

    // Set the document meta data.
    void setMetaData( const types::DocMetaData& md);

    // Get a reference to the document meta data.
    const types::DocMetaData* getMetaData() const;

    // Get the current state
    State state() const;

    // Set output filter state with error message
    void setState( State s, const char* msg=0);

protected:
    std::size_t write( const void* dt, std::size_t dtsize);
};

///\typedef OutputFilterR
// Shared output filter reference
typedef types::SharedReference<OutputFilter> OutputFilterR;

}}//namespace
#endif



	
Filter Structure

The structure 'filter' you have to create and instantiate with an input filter and an output filter reference. There is a filter type defined with a virtual constructor to instantiate the filter. From this class you have to derive.


namespace _Wolframe {
namespace langbind {

typedef std::pair<std::string,std::string> FilterArgument;

class FilterType
{
public:
    virtual ~FilterType(){}
    virtual Filter* create( const std::vector<FilterArgument>& arg) const=0;
};

}}//namespace



	

Building Blocks

When you include "appdevel/filterModuleMacros.hpp" or simply "appDevel.hpp" you get the building block declared to build a filter in a module.

Declaring a filter

The following declaration shows a declaration of a simple custom data type.

    WF_FILTER_TYPE(name,constructor)
	

where name is the identifier string of the function in the system and constructor a function with the following signature:


typedef FilterType* (*CreateFilterType)();