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.)
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.
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.
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.
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.
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.
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.
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
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
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.
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:
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
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
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
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.
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);
In this chapter we introduce how to declare a filter type in a module. Filters are used to deserialize input and to serialize output.
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
Identifier | Description |
---|---|
OpenTag | Open a substructure (element value is the name of the structure) as the current scope. |
CloseTag | Close the current substructure scope or marks the end of the document if there is no substructure scope open left (top level close). |
Attribute | Declare an attribute (element value is the name of the attribute) |
Value | Declare 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 values are chunks of the input and are interpreted depending on the filter element type.
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.
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
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
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
When you include "appdevel/filterModuleMacros.hpp" or simply "appDevel.hpp" you get the building block declared to build a filter in a module.
Copyright © 2014 - Project Wolframe - All Rights Reserved