ObjectBox C and C++
ObjectBox.ioSync DocsFollow us
  • ObjectBox C / C++ Database
  • Installation
  • How to get started
  • Entity Annotations
  • Generator
  • Store
  • Queries
  • Relations
  • Transactions
  • Schema Changes
  • Time Series Data
  • Dev Tools and Debugging
  • FAQ
  • GitHub
  • ObjectBox Generator
  • C API docs
  • Golang Database
  • Swift Database
  • Java Database
Powered by GitBook
On this page
  • Generating binding code
  • Working with Object Boxes
  • Examples

Was this helpful?

How to get started

ObjectBox Generator produces binding code for ObjectBox C & C++ APIs. This greatly simplifies the model declaration and FlatBuffers serialization, allowing you to concentrate on the application logic.

PreviousInstallationNextEntity Annotations

Last updated 9 months ago

Was this helpful?

Generating binding code

ObjectBox Generator is a tool, which must be . For details, please check the page.

ObjectBox Generator uses FlatBuffer schema file (.fbs) as its primary input. The Generator also maintains some metadata around the data model in a JSON file (objectbox-model.json). Based on these two files, it generates code for the selected language (C or C++).

Let’s have a look at a sample schema and how Generator helps us.

tasklist.fbs
table Task {
    id: ulong;
    text: string;
    date_created: ulong;
    date_finished: ulong;
}

Use CMake or launch the following command to generate the binding code from the FlatBuffers schema file:

find_package(ObjectBoxGenerator 4.0.0 REQUIRED)
add_obx_schema(TARGET myapp SCHEMA_FILES tasklist.fbs INSOURCE)

The following files will be generated:

  • objectbox-model.h

  • objectbox-model.json

  • tasklist.obx.hpp

  • tasklist.obx.cpp

See for details on CMake integration.

# to generate for a single file
objectbox-generator -cpp tasklist.fbs

# to generate recursively for the current directory
objectbox-generator -cpp ./...

The following files will be generated:

  • objectbox-model.h

  • objectbox-model.json

  • tasklist.obx.hpp

  • tasklist.obx.cpp

# to generate for a single file
objectbox-generator -c tasklist.fbs

# to generate recursively for the current directory
objectbox-generator -c ./...

The following files will be generated:

  • objectbox-model.h

  • objectbox-model.json

  • tasklist.obx.h

You should add all these generated files to your source control (e.g. git), most importantly objectbox-model.json which ensures compatibility with previous versions of your database after you make changes to the schema.

Working with Object Boxes

Bet you wondered where our name comes from :)

From an ObjectBox Store, you get Box instances to manage your entities. While you can have multiple Box instances of the same type (for the same Entity) "open" at once, it's usually preferable to just use one instance and pass it around your code.

Now, you can include the generated headers in your application and start working with your database. Consider the following main file:

main.cpp
#define OBX_CPP_FILE
#include "objectbox.hpp"
#include "objectbox-model.h"
#include "tasklist.obx.hpp"

int main(int argc, char* args[]) {
    // create_obx_model() provided by objectbox-model.h
    // obx interface contents provided by objectbox.hpp
    obx::Store store(create_obx_model());
    obx::Box<Task> box(store);

    obx_id id = box.put({.text = "Buy milk"});  // Create
    std::unique_ptr<Task> task = box.get(id);   // Read
    if (task) {
        task->text += " & some bread";
        box.put(*task);                         // Update
        box.remove(id);                         // Delete
    }
    return 0;
}

It's required to have exactly one .cpp file in your project that defines OBX_CPP_FILE right before the inclusion of the "objectbox.hpp" header.

This #define instructs the "objectbox.hpp" header to emit implementation definitions. If you accidentally have it in multiple files, the linker will complain about multiple symbols (having the same name).

main.c
#include "objectbox.h"
#include "objectbox-model.h"
#include "tasklist.obx.h"

obx_err print_last_error() {
    printf("Unexpected error: %d %s\n", 
        obx_last_error_code(), obx_last_error_message());
    return obx_last_error_code();
}

int main(int argc, char* args[]) {
    int rc = 0;
    OBX_store* store = NULL;
    OBX_box* box = NULL;
    Task* task = NULL;

    // Firstly, we need to create a model for our data and the store
    {
        OBX_model* model = create_obx_model();  // generated in objectbox-model.h
        if (!model) goto handle_error;
        if (obx_model_error_code(model)) {
            printf("Model definition error: %d %s\n", 
                obx_model_error_code(model), obx_model_error_message(model));
            obx_model_free(model);
            goto handle_error;
        }

        OBX_store_options* opt = obx_opt();
        obx_opt_model(opt, model);
        store = obx_store_open(opt);
        if (!store) goto handle_error;

        // obx_store_open() takes ownership of model and opt and frees them.
    }

    box = obx_box(store, Task_ENTITY_ID);  // Note the generated "Task_ENTITY_ID"

    obx_id id = 0;

    {  // Create
        Task new_task = {.text = "Buy milk"};
        id = Task_put(box, &new_task); // generated in tasklist.obx.h
        if (!id) goto handle_error;
        printf("New task inserted with ID %d\n", id);
    }

    {  // Read
        task = Task_Get(store, box, id); // generated in tasklist.obx.h
        if (!task) goto handle_error;
        printf("Task %d read with text: %s\n", id, task->text);
    }

    {  // Update
        const char* appendix = " & some bread";

        // Updating a string property is a little more involved 
        //  because of C memory management.
        size_t old_text_len = task->text ? strlen(task->text) : 0;
        char* new_text = 
            (char*) malloc((old_text_len + strlen(appendix) + 1) * sizeof(char));

        if (task->text) {
            memcpy(new_text, task->text, old_text_len);

            // free the memory allocated previously before overwritting below
            free(task->text);
        }
        memcpy(new_text + old_text_len, appendix, strlen(appendix) + 1);
        task->text = new_text;
        printf("Updated task %d with a new text: %s\n", id, task->text);
    }

    // Delete
    if (obx_box_remove(box, id) != OBX_SUCCESS) goto handle_error;

free_resources:  // free any remaining allocated resources
    if (task) Task_free(task); // free allocs by Task_new_from_flatbuffer()
    if (store) obx_store_close(store); // and close the store
    return rc;

handle_error:  // print error and clean up
    rc = print_last_error();
    if (rc <= 0) rc = 1;
    goto free_resources;
}

If you've followed the installation instructions, you should be able to compile the example

The add_executable call in the CMake file now looks like this:

add_executable(myapp main.cpp tasklist.obx.cpp)

The rest of the CMakeLists.txt file stays unchanged. You can now use CMake as expected.

If you use a build system other than CMake, it has to do the proper action so the generated file is added to the build.

g++ main.cpp tasklist.obx.cpp -I. -std=c++11 -lobjectbox
gcc main.c -I. -lobjectbox -lflatccrt

Wherever you have access to a Box, you can use it to persist objects and fetch objects from disk. Boxes are thread-safe. Here are some of the basic operations, have a look at the objectbox.h(pp) for more:

  • put: persist an object at the given ID: either creating a new one or overwriting an existing one.

  • get: read an object from the database. There's also a variant that takes a list of IDs as an argument and returns multiple objects.

  • remove: deletes a previously persisted object from its box.

  • count: the number of objects stored in this box.

Examples

Have a look at the following TaskList example apps, depending on your programming language and preference:

If you are using CMake, like shown in the , just add the generated tasklist.obx.cpp file to the myapp target.

The command snippet assumes you have in a path recognized by your OS (e.g. /usr/local/lib/) and all the referenced headers are in the same folder alongside the main.c/.cpp file.

- plain C; using flatcc directly; without any generated code

- plain C, using code generated by objectbox-generator

- C++, using code generated by objectbox-generator

the libraries installed
C, cursor, no generated code
C, with generated code
C++, with generated code
downloaded separately
installation
installation section
#cmake-support