Git Product home page Git Product logo

cpp-async-rpc's Introduction

cpp-async-rpc: Library for Asynchronicity, Serialization and Remoting

This is cpp-async-rpc, a C++17 library supporting template meta-programming, asynchronous network programming, binary serialization and RPC.

Disclaimer: This is not a Google supported product.

What does cpp-async-rpc look like?

RPC server and client

Here a single small binary includes an interface definition, the server and the client.

#include <iostream>
#include <string>
#include "arpc/client.h"
#include "arpc/interface.h"
#include "arpc/server.h"

/// This is the definition of the Greeter interface.
ARPC_INTERFACE(Greeter, (/* doesn't extend other interfaces */),
               (  // Return a salutation..
                   ((std::string), say_hello_to,
                    (((const std::string&), name)))));

/// This is the server-side implementation of the Greeter interface.
struct GreeterImpl : Greeter {
  std::string say_hello_to(const std::string& name) override {
    return "Hello " + name + "!";
  }
};

int main(int argc, char* argv[]) {
  // Create a server-side implementation object.
  arpc::server_object<GreeterImpl> greeter;

  // Set up the server on TCP port 9999 and register the object in it with under
  // the "greeter" name.
  arpc::server server({/* default options */}, arpc::endpoint().port(9999));
  server.register_object("greeter", greeter);

  // Start serving RPCs.
  server.start();

  // Create a client connection to the server.
  arpc::client_connection client(arpc::endpoint().name("localhost").port(9999));

  // Get a proxy for the "greeter" remote object.
  auto greeter_proxy = client.get_proxy<Greeter>("greeter");

  // Call the remote method and print the results.
  std::cout << greeter_proxy.say_hello_to("world") << std::endl;

  return 0;
}

Yeah, we use the C preprocessor as our IDL compiler, which makes the syntax awkward, but on the other hand your interface definition remains a valid (even if ugly) C++ header file.

Serialization of custom structures

cpp-async-rpc is able to serialize and deserialize most standard library classes out of the box. It also handles custom classes that implement standard interfaces, like the begin()/end() contract for containers. But it wouldn't be so useful if you couldn't teach it how to serialize your own data structures (for instance for their use as RPC arguments). Doing so is easy:

#include <iostream>
#include <string>
#include <vector>
#include "arpc/binary_codecs.h"
#include "arpc/iostream_adapters.h"
#include "arpc/serializable.h"

// To be serializable, MyClass must inherit arpc::serializable<MyClass> or
// arpc::dynamic<MyClass> if run-time polymorphism is required.
struct MyClass : arpc::serializable<MyClass> {
  int x;
  double y;
  std::vector<std::string> z;

  // Enumerate the fields that must be serialized.
  ARPC_FIELDS(x, y, z);
};

int main(int argc, char* argv[]) {
  // Create an instance of my data structure.
  MyClass data{{/* base class init */}, 4, 5.5, {"first", "second", "third"}};

  // Create a binary encoder outputting to stdout.
  arpc::ostream_output_stream oos(std::cout);
  arpc::little_endian_binary_encoder encoder(oos);

  // Write the binary data. Try piping the output of this program to xxd.
  encoder(data);

  return 0;
}

Asynchronous networking

This example sends an HTTP get request and prints out the response in an asynchronous manner. It uses a local context to set a timeout of 10 seconds for the whole set of calls.

#include <chrono>
#include <exception>
#include <iostream>
#include <string>
#include "arpc/awaitable.h"
#include "arpc/context.h"
#include "arpc/errors.h"
#include "arpc/select.h"
#include "arpc/socket.h"

int main(int argc, char* argv[]) {
  try {
    arpc::context ctx;
    ctx.set_timeout(std::chrono::seconds(10));

    auto s =
        arpc::dial(arpc::endpoint().name("www.kernel.org").service("http"));

    std::string request = "GET / HTTP/1.0\r\nHost: www.kernel.org\r\n\r\n";
    char buf[256];

    while (true) {
      auto [sent, received] = arpc::select(
          request.size() ? s.async_write(request.data(), request.size())
                         : arpc::never().then([]() { return std::size_t{0}; }),
          s.async_read(buf, sizeof(buf)));

      if (sent) {
        std::cout << "S(" << *sent << ")" << std::endl;
        // Remove the bytes we already sent.
        request.erase(0, *sent);
      }

      if (received) {
        std::cout << "R(" << *received << ")" << std::endl
                  << std::string(buf, buf + *received) << std::endl;
      }
    }

    return 0;
  } catch (const arpc::errors::base_error& e) {
    std::cerr << "Exception of type " << e.portable_error_class_name()
              << " with message: " << e.what() << std::endl;
    return 1;
  } catch (...) {
    std::cerr << "Some other exception." << std::endl;
    return 1;
  }
}

Why cpp-async-rpc?

This library evolved from plans to communicate two distinct microcontrollers over a serial line.

It eventually became a generic serialization framework (because none of the existing ones would be easy to adapt to the target embedded environments), and from there on the jump to an RPC framework with support with cancellation and asynchronous networking was easier.

As cpp-async-rpc built on template metaprogramming to reduce the amount of code the user would need to write, the library eventually moved from targeting C++11 to target C++17, which simplified a number of things and made some syntax way more natural.

At of today, the library supports POSIX environments (it's developed on Linux), but the plan is to eventually make it work for Espressif's ESP-IDF environment, so that RPC servers can be implemented in the ESP32 hardware platform.

What's in cpp-async-rpc?

  • Preprocessor-based code generation (sequences, iteration and conditionals). This is used so that cpp-async-rpc doesn't require an IDL compiler to define its serializable structures or RPC interfaces. Thanks to the preprocessor, these are just defined in C++ native headers (albeit with some syntax contortions).

  • Meta-programming toolkit: support for compile-time sequences with associated compile-time or run-time values. The library supports compile-time iteration on lists of types or values, and compile-time and constexpr mapping (transform()) and aggregation (accumulate()) of data sequences.

  • A serialization framework that supports portable binary serialization of most of the standard types in modern C++, including primitive types, strings, sequences and associative containers. cpp-async-rpc can also serialize smart pointers (including run-time polymorphic types and possibly cyclic object graphs).

  • Asynchronous primitives for operating on files, sockets and timers, coupled with synchronization primitives (mutexes, semaphores, flags, queues, futures) that also support asynchronicity and event composition. A select() function allows to compose events so that the first one to trigger is notified. The synchronization primitives are drop-in replacements for the standard library ones, but they are built on POSIX pipes so that they can be used in select() or poll().

  • A subclass of std::thread with support for stackable execution contexts. The context primitive can hold data or deadlines that are propagated across RPC call boundaries. If a context is cancelled, any I/O operation or asynchronous wait related to it will throw an exception immediately.

  • Networking support for resolving domain names and ports, and establishing both client and server connections.

  • Preprocessor-based generation of RPC-oriented "C++ interfaces" (classes with just pure virtual methods), including asynchronous variants and the associated RPC proxy implementations.

  • RPC client and server support for remoting over RPC.

What's still missing?

  • Discovering and fixing all the bugs.

  • A usable port to the ESP32 (which will likely involve some VFS component to support vestigial pipes as select-able locks.

  • Non-token test coverage.

  • Documentation.

Inspiration

  • Cereal: A C++11 library for serialization. This was the original source of inspiration for cpp-async-rpc.

  • Boost MPL, Fusion and Hana libraries. These provided inspiration on how to process sequences of types or data at compile time (for example for iterating on struct fields).

  • The Go language. The cancellable contexts, the channel multiplexing and the clean networking libraries in Go have inspired some of the work in cpp-async-rpc.

  • JavaScript Promises as the chosen way to encapsulate behaviour to be run once asynchronous conditions are met.

Dependencies

cpp-async-rpc's People

Contributors

skandalfo avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.