Using Loreline with C++
Loreline provides a C++ library with pre-built binaries for macOS, Linux, and Windows. The API uses C-style functions with a single header file, requiring only C++11. This guide shows how to set up a project, load a .lor script, and handle dialogue, choices, and script completion.
Installing the library
Download loreline-cpp.zip from the GitHub releases page. The archive contains:
loreline/include/Loreline.h: the public headerloreline/mac/libLoreline.dylib: pre-built macOS libraryloreline/linux-arm64/libLoreline.soandloreline/linux-x86_64/libLoreline.so: pre-built Linux librariesloreline/windows/Loreline.dllandloreline/windows/Loreline.lib: pre-built Windows libraryCMakeLists.txt: ready-to-use CMake projectmain.cpp: complete sample applicationstory/: sample.lorfiles
The archive can be used as a starting point for your own project, or as a reference for integrating Loreline into an existing one.
Project setup with CMake
To add Loreline to an existing CMake project, point to the include and lib directories:
target_include_directories(your_app PRIVATE /path/to/loreline/include)
target_link_directories(your_app PRIVATE /path/to/loreline/mac) # or linux-x86_64, windows
target_link_libraries(your_app PRIVATE Loreline)
Make sure the shared library (libLoreline.dylib, libLoreline.so, or Loreline.dll) is next to your executable at runtime. On macOS and Linux, set the rpath:
# macOS
set_target_properties(your_app PROPERTIES BUILD_RPATH "@executable_path")
# Linux
set_target_properties(your_app PROPERTIES BUILD_RPATH "$ORIGIN")
On Windows, copy the DLL next to your executable as a post-build step.
Initialization and cleanup
Before using any Loreline function, call Loreline_init(). When you are done, call Loreline_dispose():
#include "Loreline.h"
int main() {
Loreline_init();
// ... use Loreline ...
Loreline_dispose();
return 0;
}
Loading a script
Use Loreline_parse() to parse a .lor string. The third argument is a file handler for resolving import statements:
#include <fstream>
#include <sstream>
#include <string>
std::string readFile(const std::string& path) {
std::ifstream f(path, std::ios::binary);
if (!f.is_open()) return std::string();
std::ostringstream ss;
ss << f.rdbuf();
return ss.str();
}
void onFileRequest(
Loreline_String path,
void (*provide)(Loreline_String content),
void* userData
) {
std::string content = readFile(path.c_str());
provide(content.empty() ? Loreline_String() : Loreline_String(content.c_str()));
}
std::string content = readFile("story/CoffeeShop.lor");
Loreline_Script* script = Loreline_parse(
content.c_str(), "story/CoffeeShop.lor", onFileRequest, NULL
);
If your script has no import statements, you can pass NULL for the file handler:
Loreline_Script* script = Loreline_parse(content.c_str(), "CoffeeShop.lor", NULL, NULL);
Handling dialogue
The dialogue handler is a C function pointer. It receives the interpreter, a character identifier (use isNull() to check for narrative text), the dialogue text, tags, and an advance function to call when ready to continue:
void onDialogue(
Loreline_Interpreter* interp,
Loreline_String character,
Loreline_String text,
const Loreline_TextTag* tags,
int tagCount,
void (*advance)(void),
void* userData
) {
if (!character.isNull()) {
// Resolve display name from character definition
Loreline_Value nameVal = Loreline_getCharacterField(interp, character, "name");
const char* displayName = (nameVal.type == Loreline_StringValue && nameVal.stringValue)
? nameVal.stringValue.c_str()
: character.c_str();
printf("%s: %s\n", displayName, text.c_str());
} else {
// Narrative text
printf("%s\n", text.c_str());
}
advance();
}
Loreline_String is a ref-counted string type, so you never need to free it manually. Use .c_str() to get a null-terminated C string, and .isNull() to check if it represents a null value.
Handling choices
The choice handler receives an array of Loreline_ChoiceOption. Each option has text (a Loreline_String) and enabled (a bool). Call select(index) with the 0-based index of the chosen option:
void onChoice(
Loreline_Interpreter* interp,
const Loreline_ChoiceOption* options,
int optionCount,
void (*select)(int index),
void* userData
) {
for (int i = 0; i < optionCount; i++) {
if (options[i].enabled) {
printf(" [%d] %s\n", i + 1, options[i].text.c_str());
}
}
// Read player input
printf("> ");
fflush(stdout);
char buf[64];
if (fgets(buf, sizeof(buf), stdin)) {
int choice = atoi(buf);
if (choice >= 1 && choice <= optionCount) {
select(choice - 1);
}
}
}
Handling script completion
The finish handler is called when the script reaches its end:
void onFinish(Loreline_Interpreter* interp, void* userData) {
printf("--- The End ---\n");
}
Complete example
Here is a complete console application that loads and plays a Loreline script:
#include "Loreline.h"
#include <cstdio>
#include <cstdlib>
#include <fstream>
#include <sstream>
#include <string>
static std::string readFile(const std::string& path) {
std::ifstream f(path, std::ios::binary);
if (!f.is_open()) return std::string();
std::ostringstream ss;
ss << f.rdbuf();
return ss.str();
}
static void onFileRequest(
Loreline_String path, void (*provide)(Loreline_String), void*
) {
std::string content = readFile(path.c_str());
provide(content.empty() ? Loreline_String() : Loreline_String(content.c_str()));
}
static void onDialogue(
Loreline_Interpreter* interp, Loreline_String character,
Loreline_String text, const Loreline_TextTag*, int,
void (*advance)(void), void*
) {
if (!character.isNull()) {
Loreline_Value nameVal = Loreline_getCharacterField(interp, character, "name");
const char* name = (nameVal.type == Loreline_StringValue && nameVal.stringValue)
? nameVal.stringValue.c_str() : character.c_str();
printf("%s: %s\n\n", name, text.c_str());
} else {
printf("%s\n\n", text.c_str());
}
advance();
}
static void onChoice(
Loreline_Interpreter*, const Loreline_ChoiceOption* options,
int optionCount, void (*select)(int), void*
) {
for (int i = 0; i < optionCount; i++) {
if (options[i].enabled)
printf(" [%d] %s\n", i + 1, options[i].text.c_str());
}
printf("> ");
fflush(stdout);
char buf[64];
if (fgets(buf, sizeof(buf), stdin)) {
int choice = atoi(buf);
if (choice >= 1 && choice <= optionCount)
select(choice - 1);
}
}
static void onFinish(Loreline_Interpreter*, void*) {
printf("--- The End ---\n");
}
int main() {
std::string content = readFile("story/CoffeeShop.lor");
if (content.empty()) {
fprintf(stderr, "Error: cannot read story file\n");
return 1;
}
Loreline_init();
Loreline_Script* script = Loreline_parse(
content.c_str(), "story/CoffeeShop.lor", onFileRequest, NULL
);
if (!script) {
fprintf(stderr, "Error: failed to parse script\n");
Loreline_dispose();
return 1;
}
Loreline_Interpreter* interp = Loreline_play(
script, onDialogue, onChoice, onFinish, Loreline_String(), NULL, NULL
);
if (interp) Loreline_releaseInterpreter(interp);
Loreline_releaseScript(script);
Loreline_dispose();
return 0;
}
Going further
The loreline-cpp.zip download includes platform-specific build scripts (build-mac.sh, build-linux.sh, build-windows.bat) and a complete working sample with a story that uses character definitions and imports.