-
Notifications
You must be signed in to change notification settings - Fork 1
User Guide
- Create a
*.ecore
model - Right click on the ECore model, from the context menu select ECore Generator -> Generate C++ files
(This is an unfinished experimental feature with many bugs)
- Create a
*.vql
file - Import the ECore models and write your patterns
- To generate code, right click on the
*.vql
file, select theGenerate query code...
submenu and choose either runtime or iterator based codegeneration
For each pattern, the following files get generated:
-
*QuerySpecification
- contains the search plan and some meta information -
*Matcher
- this is the class capable of executing the query defined by the query specification -
*Match
- represents a simple match, containing the values of the parameters -
*MatchingFrame
- only generated for the runtime based query execution approach, this is an internal structure containing the momentary state of the query execution
For each *.vql
file a single *QueryGroup
file gets generated, which contains Metamodel related information.
The following code example shows a usage scenario of a generated query from the pattern named students
auto engine = ::Viatra::Query::QueryEngine<School>::empty();
auto matcher = engine.matcher<::Viatra::Query::School::StudentsQuerySpecification>();
auto match_set = matcher.matches();
The first line initializes a query engine without a model root. The template parameter of the QueryEngine is the type of the root element of the model. The QueryEngine#empty
method is used in case the model is a global in memory model without a specific root element (an type for root model element still has to be passed). In case of a model with a proper root, the QueryEngine::of
method can be used, which takes the model root as a parameter.
The second line creates a matcher based on a query specification. Here, the engine#matcher
method requires the QuerySpecification of the pattern for which a matcher is required. Make sure to import the Matcher (which will in turn import the QuerySpecification) instead of the QuerySpecification. Doing it the other way around will result in compile error.
The third line queries the matches from the matcher. This will result in an std::unordered_set
containing the SchoolMatch values.
Compiling the above code will result in the following compile error: Please specialize a model indexer for this type!
Examining the exact compile error will tell us that we need to implement ModelIndex
specializations for our model elements. This ModelIndex
is a facility used to iterate over specific type of elements of the model.
The indexer for this case:
namespace Viatra {
namespace Query {
template<>
struct ModelIndex<::school::Student, ::school::School> {
static const std::list<::school::Student*>& instances(const ::school::School* modelroot) {
return ::school::Student::_instances;
}
};
}
}
The ModelIndex specialization has to be in the ::Viatra::Query
namespace. Its first template parameter is the model element type, while the second is the model root type. It has a single method which returns a const list of the specified type. It receives the model root as a parameter (if the engine was specified as empty, this will be a nullptr). In this case the model itself contains the meta information required in a static _instances
field of the proper type.
With the ModelIndex specialization, the query can now be compiled and run.
Full code listing:
// main.cpp
#include "school_def.h"
#include "Viatra\Query\QueryEngine.h"
#include "Viatra\Query\Matcher\ModelIndex.h"
#include "Viatra\Query\School\StudentsMatcher.h"
namespace Viatra {
namespace Query {
template<>
struct ModelIndex<::school::Student, ::school::School> {
static const std::list<::school::Student*>& instances(const ::school::School* modelroot) {
return ::school::Student::_instances;
}
};
}
}
int main(int argc, char **argv) {
auto engine = ::Viatra::Query::QueryEngine<School>::empty();
auto matcher = engine.matcher<::Viatra::Query::School::StudentsQuerySpecification>();
auto match_set = matcher.matches();
}
As both the runtime and the generated code is header only, the only necessary additional compilation configuration is the inclusion of the root directory of both the runtime and the generated code.
There is support for query code generation with bound parameters. Parameter bindings have to be marked on the pattern with an annotation for the search plan to be generated. The bound parameters can be indicated with the @Bind
annotation as shown in the example below.
@Bind(parameters={name})
pattern teacherName(teacher, name) {
Teacher.name(teacher, name);
}
Multiple parameters can be bound at the same time with enumerating them in a comma separated list, i.e. @Bind(parameters={param1, param2})
. A single pattern can have multiple bindings defined.