Examples#

Login Server#

/**
 * @brief Example of a web server built with UCall in C++.
 */
#include <charconv> // `std::to_chars`
#include <cstdio>   // `std::fprintf`
#include <thread>
#include <vector>

#include <cxxopts.hpp>

#include "ucall/ucall.h"

static void validate_session(ucall_call_t call, ucall_callback_tag_t) {
    int64_t a{}, b{};
    char c_str[256]{};
    bool got_a = ucall_param_named_i64(call, "user_id", 0, &a);
    bool got_b = ucall_param_named_i64(call, "session_id", 0, &b);
    if (!got_a || !got_b)
        return ucall_call_reply_error_invalid_params(call);

    const char* res = ((a ^ b) % 23 == 0) ? "true" : "false";
    ucall_call_reply_content(call, res, strlen(res));
}

int main(int argc, char** argv) {

    cxxopts::Options options("Summation Server", "If device can't sum integers, just send them over with JSON-RPC :)");
    options.add_options()                                                                                             //
        ("h,help", "Print usage")                                                                                     //
        ("nic", "Networking Interface Internal IP to use", cxxopts::value<std::string>()->default_value("127.0.0.1")) //
        ("p,port", "On which port to server JSON-RPC", cxxopts::value<int>()->default_value("8545"))                  //
        ("j,threads", "How many threads to run", cxxopts::value<int>()->default_value("1"))                           //
        ("s,silent", "Silence statistics output", cxxopts::value<bool>()->default_value("false"))                     //
        ;
    auto result = options.parse(argc, argv);
    if (result.count("help")) {
        std::cout << options.help() << std::endl;
        exit(0);
    }

    ucall_server_t server{};
    ucall_config_t config{};
    config.hostname = result["nic"].as<std::string>().c_str();
    config.port = result["port"].as<int>();
    config.max_threads = result["threads"].as<int>();
    config.max_concurrent_connections = 1024;
    config.queue_depth = 4096 * config.max_threads;
    config.max_lifetime_exchanges = UINT32_MAX;
    config.logs_file_descriptor = result["silent"].as<bool>() ? -1 : fileno(stdin);
    config.logs_format = "human";
    // config.use_ssl = true;
    // config.ssl_private_key_path = "./examples/login/certs/main.key";
    // const char* crts[] = {"./examples/login/certs/srv.crt", "./examples/login/certs/cas.pem"};
    // config.ssl_certificates_paths = crts;
    // config.ssl_certificates_count = 2;

    ucall_init(&config, &server);
    if (!server) {
        std::printf("Failed to start server: %s:%i\n", config.hostname, config.port);
        return -1;
    }

    std::printf("Initialized server: %s:%i\n", config.hostname, config.port);
    std::printf("- %zu threads\n", static_cast<std::size_t>(config.max_threads));
    std::printf("- %zu max concurrent connections\n", static_cast<std::size_t>(config.max_concurrent_connections));
    if (result["silent"].as<bool>())
        std::printf("- silent\n");

    // Add all the callbacks we need
    ucall_add_procedure(server, "validate_session", &validate_session, nullptr);

    if (config.max_threads > 1) {
        std::vector<std::thread> threads;
        for (uint16_t i = 0; i != config.max_threads; ++i)
            threads.emplace_back(&ucall_take_calls, server, i);
        for (auto& thread : threads)
            thread.join();
    } else
        ucall_take_calls(server, 0);

    ucall_free(server);
    return 0;
}

PyTorch Server#

/**
 * @brief Example of building a Redis-like in-memory store with UCall.
 *
 * @see Reading materials on using the C++ PyTorch Frontend.
 * https://pytorch.org/tutorials/advanced/cpp_frontend.html
 * https://pytorch.org/cppdocs/installing.html
 */
#include <cstdio> // `std::printf`
#include <string>
#include <unordered_map>

#include <torch/torch.h>

#include "ucall/ucall.h"

static std::unordered_map<std::string, std::string> store;

static void summarize(ucall_call_t call) {
    char const* text_ptr{};
    size_t text_len{};
    bool text_found = ucall_param_named_str(call, "text", 3, &text_ptr, &text_len);
    if (!text_found)
        return ucall_call_reply_error_invalid_params(call);

    return ucall_call_reply_content(call, "OK", 2);
}

static void continue_(ucall_call_t call) {
    char const* text_ptr{};
    size_t text_len{};
    bool text_found = ucall_param_named_str(call, "text", 4, &text_ptr, &text_len);
    if (!text_found)
        return ucall_call_reply_error_invalid_params(call);

    return ucall_call_reply_content(call, "", 0);
}

int main(int argc, char** argv) {
    ucall_server_t server{};
    ucall_config_t config{};
    config.port = 6379;
    ucall_init(&config, &server);
    if (!server) {
        std::printf("Failed to initialize server!\n");
        return -1;
    }

    std::printf("Initialized server!\n");
    ucall_add_procedure(server, "summarize", &summarize);
    ucall_add_procedure(server, "continue", &continue_);

    ucall_take_calls(server, 0);
    ucall_free(server);
    return 0;
}

Redis Server#

/**
 * @brief Example of building a Redis-like in-memory store with UCall.
 */
#include <cstdio> // `std::printf`
#include <string>
#include <unordered_map>

#include "ucall/ucall.h"

static std::unordered_map<std::string, std::string> store;

static void set(ucall_call_t call) {
    char const* key_ptr{};
    char const* value_ptr{};
    size_t key_len{};
    size_t value_len{};
    bool key_found = ucall_param_named_str(call, "key", 3, &key_ptr, &key_len);
    bool value_found = ucall_param_named_str(call, "value", 5, &value_ptr, &value_len);
    if (!key_found || !value_found)
        return ucall_call_reply_error_invalid_params(call);

    store.insert_or_assign(std::string_view{key_ptr, key_len}, std::string_view{value_ptr, value_len});
    return ucall_call_reply_content(call, "OK", 2);
}

static void get(ucall_call_t call) {
    char const* key_ptr{};
    size_t key_len{};
    bool key_found = ucall_param_named_str(call, "key", 4, &key_ptr, &key_len);
    if (!key_found)
        return ucall_call_reply_error_invalid_params(call);

    auto iterator = store.find(std::string_view{key_ptr, key_len});
    if (iterator == store.end())
        return ucall_call_reply_content(call, "", 0);
    else
        return ucall_call_reply_content(call, iterator.second.c_str(), iterator.second.size());
}

int main(int argc, char** argv) {
    ucall_server_t server{};
    ucall_config_t config{};
    config.port = 6379;
    ucall_init(&config, &server);
    if (!server) {
        std::printf("Failed to initialize server!\n");
        return -1;
    }

    std::printf("Initialized server!\n");
    ucall_add_procedure(server, "set", &set);
    ucall_add_procedure(server, "get", &get);

    ucall_take_calls(server, 0);
    ucall_free(server);
    return 0;
}