ContRap-Core
|
The core library of ContRap implements the two main components: the parser (crp::Parser) and the symbolic interpreter (crp::Interpreter) of the ContRap language. These two classes are combined in an aggregate engine class crp::Engine, which initializes and destroys a pair of an interpreter and an associated parser. At runtime, the interpreter may change the parser and vice versa. You should use the crp::Engine class, when you want to work with the ContRap language.
Additionally, the core library implements some platform independent tools for manipulating strings (stringtools.h), working with file systems (filetools.h), calling the operating system commands (systemtools.h), threading (crp::Thread), and so on. Each of these tools is used inside the engine. You can (and should) use the tools when you write ContRap plugins.
ContRap consequently works with smart pointers to C/C++ data types inside its core. The smart pointers are implemented by the crp::DPtr and the crp::SPtr classes.
The weak typed pointer crp::DPtr is used to implement type-polymorphic interfaces. Its identical (with respect to the memory layout) strong typed derivative crp::SPtr is used as a replacement for the regular pointers.
Since the constructors of crp::DPtr besides the copy constructor are protected, you can not create instances of crp::DPtr. Instead, you create an instance of crp::SPtr with the corresponding type given by the template argument and use the copy constructor of crp::DPtr to generate a weak typed pointer.
The pointers crp::DPtr and crp::SPtr are smart. They implement non-intrusive reference counting. Whenever the object referenced by a smart pointer has no referencing objects, it is deleted depending on the deletion mode.
To export C/C++ data types into ContRap the meta registration concept is used.
Each type you want to work with in the user-level mode must be registered by calling to one of the meta registration macros defined in the objectfactory.h file. The action of those macros is limited to the scope of the current library (and libraries which load the current library). In other words, ContRap knows about the types registered in the current library if this library is loaded. You can put several (even different) registration macros in different libraries. Then the library, which is loaded as last, replaces its own registered types.
The crp::ObjectFactory class automatically takes responsibility of un-/registering the type information whenever new libraries are loaded or unloaded.
Registered types can be thought of as instances of a global ''Object'' type. You can cast any registered type to any other registered type by using the replacements cast() and hard_cast() for the dynamic_cast()-method for the smart pointers defined in typecast.h. This type cast methods are the so-called ''light casts''. This means that only the RTTI (RunTime Type Information) is used to cast the objects. There is a ''heavy cast'' crp::Interpreter::type_cast() method implemented in the crp::Interpreter class (or crp::Engine), which performs a cast additionally using the plugins loaded by the current engine.
If you want to call the interpreter from your C++ code you have to know how to form ContRap commands.
Principally, ContRap can evaluate any object by calling crp::Interpreter::evaluate() method. The result depends besides the value of the input on the evaluation scope and on the currently loaded libraries.
ContRap commands are implemented inside a hierarchy starting with the base class crp::Command.
A simple, but inefficient, way to call ContRap is to use the parser to create the commands. You simply apply the crp::Parser::parse() method to a string containing the command, e.g. ''f(x,y)''. The resulting object can be evaluated by calling the crp::Interpreter::evaluate() function. This method involves parsing strings with the parser, which can result in extremely inefficient programs.
A better way to call the interpreter is to create a command using the interfaces of the subclasses of crp::Command. The essential class, which you will probably need at most is the crp::Call class. This class represents a call to a function. You create an instance of crp::Call by specifying the function to evaluate, e.g. by giving the identifier (crp::Identifier) name of the function, and the list of arguments with which the function is called.