Skip to content

Calculator

A server with multiple unary methods demonstrating logging and error handling.

examples/calculator.cpp
// © Copyright 2025-2026, Query.Farm LLC - https://query.farm
// SPDX-License-Identifier: Apache-2.0

#include <vgi_rpc/server.h>
#include <vgi_rpc/request.h>
#include <vgi_rpc/result.h>
#include <vgi_rpc/call_context.h>
#include <vgi_rpc/log.h>
#include <vgi_rpc/arrow_utils.h>

#include <arrow/array/builder_primitive.h>
#include <arrow/type.h>

#include <stdexcept>

namespace {

auto params_ab() {
    return arrow::schema({
        arrow::field("a", arrow::float64()),
        arrow::field("b", arrow::float64()),
    });
}

auto result_double() {
    return arrow::schema({
        arrow::field("result", arrow::float64()),
    });
}

vgi_rpc::Result make_double_result(double value) {
    arrow::DoubleBuilder builder;
    VGI_RPC_THROW_NOT_OK(builder.Append(value));
    auto array = vgi_rpc::unwrap(builder.Finish());
    return vgi_rpc::Result::value(result_double(), {array});
}

}  // anonymous namespace

int main() {
    auto server = vgi_rpc::ServerBuilder()
        .add_unary("add", params_ab(), result_double(),
            [](const vgi_rpc::Request& req, vgi_rpc::CallContext& ctx) {
                double a = req.get<double>("a");
                double b = req.get<double>("b");
                ctx.client_log(vgi_rpc::LogLevel::DEBUG,
                               "Computing " + std::to_string(a) + " + " + std::to_string(b));
                return make_double_result(a + b);
            },
            "Add two numbers.")
        .add_unary("multiply", params_ab(), result_double(),
            [](const vgi_rpc::Request& req, vgi_rpc::CallContext& ctx) {
                double a = req.get<double>("a");
                double b = req.get<double>("b");
                return make_double_result(a * b);
            },
            "Multiply two numbers.")
        .add_unary("divide", params_ab(), result_double(),
            [](const vgi_rpc::Request& req, vgi_rpc::CallContext& ctx) {
                double a = req.get<double>("a");
                double b = req.get<double>("b");
                if (b == 0.0) {
                    throw std::runtime_error("division by zero");
                }
                return make_double_result(a / b);
            },
            "Divide a by b. Raises error if b is zero.")
        .enable_describe("Calculator")
        .build();

    server->run();
    return 0;
}

Key Concepts

Multiple methods — Call add_unary() multiple times to register several methods on the same server. Each gets its own schema and handler.

Client loggingctx.client_log(LogLevel::DEBUG, "message") sends a log message to the client as an in-band LOG batch. Available log levels: TRACE, DEBUG, INFO, WARN, ERROR.

Error handling — Throwing std::runtime_error (or any exception) from a handler automatically converts it to a protocol-level EXCEPTION response. The client receives the error message without crashing the server.

Helper functions — Factor out common result construction (like make_double_result) to reduce boilerplate across handlers.