Why do we need C++ interface?
Applications may want to integrate Soufflé programs as subroutines rather than running them stand-alone. For this purpose, there is a C++ interface that can be used to instantiate, manipulate and run Soufflé programs.
By calling souffle -g <name>.cpp program.dl
a C++ file called <name>.cpp
is generated for the Soufflé program. This C++ file
can be directly embedded in a C++ application. When linked into a C++ application the Soufflé C++ interface can be used to
create any number of program instances of the Soufflé program. An instance provides interfaces to populate input relations,
to run the program, and to retrieve data from the output relations.
The generated C++ file includes a main()
function by default. The C++ application must be compiled with the
flag __EMBEDDED_SOUFFLE__
if you want to avoid emitting this function.
Detailed Usage
1. Including the Soufflé interface
souffle/SouffleInterface.h
The C++ interface is included by including souffle/SouffleInterface.h
as shown in the example. For any information not covered by this documentation, please refer to the comments in this header file.
Example
#include <souffle/SouffleInterface.h>
2. Loading a program
souffle::SouffleProgram* souffle::ProgramFactory::newInstance(std::string)
Creates an instance of a program by name. Returns nullptr
if the program could not be found.
Example
if (souffle::SouffleProgram *prog = souffle::ProgramFactory::newInstance("<name>")) {
// Run the program...
// Clean up
delete prog;
} else {
std::cerr << "Failed to create instance\n";
exit(1);
}
3. Populating input relations
void souffle::SouffleProgram::loadAll(std::string)
Loads all input facts from CSV files stored in the given directory.
Example
prog->loadAll("<dir>"); // load facts from CSV files stored in <dir>
souffle::Relation* souffle::SouffleProgram::getRelation(std::string)
Queries a program for a relation by name. The relation can then be populated programmatically. Returns nullptr
if the relation could not be found.
Example
if (souffle::Relation *rel = prog->getRelation("<in-rel>")) {
souffle::tuple myTuple(rel); // Create an empty tuple
myTuple << "Hello" << 10; // Write symbols and integers to tuple
// (Arity and data-types must match those of <in-rel>)
rel->insert(myTuple); // Add the new tuple to the relation
} else {
std::cerr << "Failed to get input relation\n";
exit(1);
}
4. Running the program
void souffle::SouffleProgram::run()
Executes the program, without any loads or stores.
Example
prog->run();
5. Reading output relations
void souffle::SouffleProgram::printAll()
Writes all output relations to their defined destinations as CSV.
Example
prog->printAll();
souffle::Relation* souffle::SouffleProgram::getRelation(std::string)
As before, queries a program for a relation by name. The relation can then be read programmatically. Returns nullptr
if the relation could not be found.
Example
if (souffle::Relation *rel = prog->getRelation("<out-rel>")) {
int myInt;
std::string mySymbol;
for (auto &output : *rel) { // Iterate through the tuples in the output relation
output >> mySymbol >> myString; // Read symbols and integers from each tuple
// (Data-types must match those of <out-rel>)
}
} else {
std::cerr << "Failed to get output relation\n";
exit(1);
}
bool souffle::Relation::contains(const souffle::tuple&)
Returns true
if the relation contains a tuple equal to a given tuple and false
otherwise.
Example
souffle::tuple myTuple(rel);
myTuple << "A" << 123;
if (rel->contains(myTuple)) {
// ...
}
std::string souffle::Relation::getSignature()
Gets the signature of a relation. The signature is in the form:
<<primitive type 1>:<type name 1>,<primitive type 2>:<type name 2>...>
for all of the attributes in the relation.
Example
rel->getSignature();
std::size_t souffle::Relation::size()
Returns the number of tuples in a relation.
Example
rel->size();
Complete Example
#include <souffle/SouffleInterface.h>
int main() {
if (souffle::SouffleProgram *prog = souffle::ProgramFactory::newInstance("<name>")) {
prog->loadAll("<dir>");
if (souffle::Relation *rel = prog->getRelation("<in-rel>")) {
souffle::tuple myTuple(rel);
myTuple << "Hello" << souffle::RamSigned(10);
rel->insert(myTuple);
} else {
std::cerr << "Failed to get input relation\n";
return 1;
}
prog->run();
if (souffle::Relation *rel = prog->getRelation("<out-rel>")) {
souffle::RamSigned myInt;
std::string mySymbol;
for (auto &output : *rel) {
output >> mySymbol >> myInt;
}
souffle::tuple myTuple(rel);
myTuple << "A" << souffle::RamSigned(123);
if (rel->contains(myTuple)) {
}
} else {
std::cerr << "Failed to get output relation\n";
return 1;
}
prog->printAll();
delete prog;
} else {
std::cerr << "Failed to create instance\n";
return 1;
}
return 0;
}