{"id":525,"date":"2016-06-06T08:29:48","date_gmt":"2016-06-06T08:29:48","guid":{"rendered":"http:\/\/www.crazygaze.com\/blog\/?p=525"},"modified":"2020-02-26T17:30:49","modified_gmt":"2020-02-26T17:30:49","slug":"modern-c-lightweight-binary-rpc-framework-without-code-generation","status":"publish","type":"post","link":"https:\/\/www.crazygaze.com\/blog\/2016\/06\/06\/modern-c-lightweight-binary-rpc-framework-without-code-generation\/","title":{"rendered":"Modern C++ lightweight binary RPC framework without code generation"},"content":{"rendered":"<h1>Table of Contents<\/h1>\n<ol>\n<li><a href=\"#id-introduction\">Introduction<\/a>\n<ol>\n<li><a href=\"#id-why-i-needed-this\">Why I needed this<\/a><\/li>\n<\/ol>\n<\/li>\n<li><a href=\"#id-rpc-parameters\">RPC parameters<\/a>\n<ol>\n<li><a href=\"#id-parameter-traits\">Parameter Traits<\/a><\/li>\n<li><a href=\"#id-serialization\">Serialization<\/a><\/li>\n<li><a href=\"#id-deserialization\">Deserialization<\/a><\/li>\n<li><a href=\"#id-from-tuple-to-function-parameters\">From tuple to function parameters<\/a><\/li>\n<\/ol>\n<\/li>\n<li><a href=\"#id-the-rpc-api\">The RPC API<\/a>\n<ol>\n<li><a href=\"#id-header\">Header<\/a><\/li>\n<li><a href=\"#id-table\">Table<\/a><\/li>\n<li><a href=\"#id-transport\">Transport<\/a><\/li>\n<li><a href=\"#id-result\">Result<\/a><\/li>\n<li><a href=\"#id-out-processor\">OutProcessor<\/a><\/li>\n<li><a href=\"#id-in-processor\">InProcessor<\/a><\/li>\n<li><a href=\"#id-connection\">Connection<\/a><\/li>\n<\/ol>\n<\/li>\n<li><a href=\"#id-improvements\">Improvements<\/a><\/li>\n<li><a href=\"#id-final-words\">Final words<\/a><\/li>\n<li><a href=\"#id-license\">License <\/a><\/li>\n<\/ol>\n<p><a name=\"id-introduction\"><\/a><\/p>\n<h2>Introduction<\/h2>\n<p><img alt='' class='alignnone size-full wp-image-652 ' src='https:\/\/www.crazygaze.com\/blog\/wp-content\/uploads\/2016\/06\/img_57561179204f5.png' \/><\/p>\n<p>This article explores a C++ RPC framework I&#8217;ve been working on which requires no code generation step for glue code.<br \/>\nBefore I start rambling on implementation details, and so you know what to expect, here is a feature list:<\/p>\n<ul>\n<li>Source available at <a href=\"https:\/\/bitbucket.org\/ruifig\/czrpc\">https:\/\/bitbucket.org\/ruifig\/czrpc<\/a>\n<ul>\n<li>The source code shown in this article is by no means complete. It&#8217;s meant to show the foundations upon which the framework was built. Also, to shorten things a bit, it&#8217;s a mix of code from the repository at the time of writing and custom sample code, so it might have errors.<\/li>\n<li>Some of the source code which is not directly related to the problem at hand is left intentionally simple with disregard for performance. Any improvements will later be added to source code repository.<\/li>\n<\/ul>\n<\/li>\n<li>Modern C++ (C++11\/14)\n<ul>\n<li>Requires at least <strong>Visual Studio 2015<\/strong>. Clang\/GCC is fine too, but might not work as-is, since VS is less strict.<\/li>\n<\/ul>\n<\/li>\n<li>Type-safe\n<ul>\n<li>The framework detects at <strong>compile time<\/strong> invalid RPC calls, such as unknown RPC names, wrong number of parameters, or wrong parameter types.<\/li>\n<\/ul>\n<\/li>\n<li>Relatively small API and not too verbose (considering it requires no code generation) <\/li>\n<li>Multiple ways to handle RPC replies\n<ul>\n<li>Asynchronous handler<\/li>\n<li>Futures<\/li>\n<li>A client can detect if an RPC caused an exception server side<\/li>\n<\/ul>\n<\/li>\n<li>Allows the use of potentially any type in RPC parameters\n<ul>\n<li>Provided the user implements the required functions to deal with that type.<\/li>\n<\/ul>\n<\/li>\n<li>Bidirectional RPCs (A server can call RPCs on a client)\n<ul>\n<li>Typically, client code cannot be trusted, but since the framework is to be used between trusted parties, this is not a problem.<\/li>\n<\/ul>\n<\/li>\n<li>Non intrusive\n<ul>\n<li>An object being used for RPC calls doesn&#8217;t need to know anything about RPCs or network.<\/li>\n<li>This makes it possible to wrap third party classes for RPC calls.<\/li>\n<\/ul>\n<\/li>\n<li>Minimal bandwidth overhead per RPC call<\/li>\n<li>No external dependencies\n<ul>\n<li>Although the supplied transport (in the source code repository) uses Asio\/Boost Asio, the framework itself does not depend on it. You can plug in your own transport.<\/li>\n<\/ul>\n<\/li>\n<li>No security features provided\n<ul>\n<li>Because the framework is intended to be used between trusted parties (e.g: between servers).<\/li>\n<li>The application can specify its own transport, therefore having a chance to encrypt anything if required.<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<p>Even though the source code shown is not complete, the article is still very heavy on code.<br \/>\nCode is presented in small portions and every section builds on the previous, but is still an overwhelming amount of code.<br \/>\nSo that you have an idea of how it will look like in the end, here is a fully functional sample using the source code repository at the time of writing:<\/p>\n<pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\n\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\n\/\/ Useless RPC-agnostic class that performs calculations.\n\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\nclass Calculator {\npublic:\n    double add(double a, double b) { return a + b; }\n};\n\n\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\n\/\/ Define the RPC table for the Calculator class\n\/\/ This needs to be seen by both the server and client code\n\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\n#define RPCTABLE_CLASS Calculator\n#define RPCTABLE_CONTENTS \\\n    REGISTERRPC(add)\n#include &quot;crazygaze\/rpc\/RPCGenerate.h&quot;\n\n\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\n\/\/ A Server that only accepts 1 client, then shuts down\n\/\/ when the client disconnects\n\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\nvoid RunServer() {\n    asio::io_service io;\n    \/\/ Start thread to run Asio&#039;s the io_service\n    \/\/ we will be using for the server\n    std::thread th = std::thread([&amp;io] {\n        asio::io_service::work w(io);\n        io.run();\n    });\n\n    \/\/ Instance we will be using to serve RPC calls.\n    \/\/ Note that it&#039;s an object that knows nothing about RPCs\n    Calculator calc;\n\n    \/\/ start listening for a client connection.\n    \/\/ We specify what Calculator instance clients will use,\n    auto acceptor = AsioTransportAcceptor&lt;Calculator, void&gt;::create(io, calc);\n    \/\/ Start listening on port 9000.\n    \/\/ For simplicity, we are only expecting 1 client\n    using ConType = Connection&lt;Calculator, void&gt;;\n    std::shared_ptr&lt;ConType&gt; con;\n    acceptor-&gt;start(9000, [&amp;io, &amp;con](std::shared_ptr&lt;ConType&gt; con_) {\n        con = con_;\n        \/\/ Since this is just a sample, close the server once the first client\n        \/\/ disconnects\n        reinterpret_cast&lt;BaseAsioTransport*&gt;(con-&gt;transport.get())\n            -&gt;setOnClosed([&amp;io] { io.stop(); });\n    });\n\n    th.join();\n}\n\n\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\n\/\/ A client that connects to the server, calls 1 RPC\n\/\/ then disconnects, causing everything to shut down\n\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\nvoid RunClient() {\n    \/\/ Start a thread to run our Asio io_service\n    asio::io_service io;\n    std::thread th = std::thread([&amp;io] {\n        asio::io_service::work w(io);\n        io.run();\n    });\n\n    \/\/ Connect to the server (localhost, port 9000)\n    auto con =\n        AsioTransport&lt;void, Calculator&gt;::create(io, &quot;127.0.0.1&quot;, 9000).get();\n\n    \/\/ Call one RPC (the add method), specifying an asynchronous handler for\n    \/\/ when the result arrives\n    CZRPC_CALL(*con, add, 1, 2)\n        .async([&amp;io](Result&lt;double&gt; res) {\n            printf(&quot;Result=%f\\n&quot;, res.get());  \/\/ Prints 3.0\n            \/\/ Since this is a just a sample, stop the io_service after we get\n            \/\/ the result,\n            \/\/ so everything shuts down\n            io.stop();\n        });\n\n    th.join();\n}\n\n\/\/ For testing simplicity, run both the server and client on the same machine,\nvoid RunServerAndClient() {\n    auto a = std::thread([] { RunServer(); });\n    auto b = std::thread([] { RunClient(); });\n    a.join();\n    b.join();\n}\n<\/pre>\n<p>This code is mostly setup code, since the provided transport uses Asio.<br \/>\nThe RPC calls itself can be as simple as:<\/p>\n<pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\n\/\/ RPC call using asynchronous handler to handle the result\nCZRPC_CALL(*con, add, 1, 2).async([](Result&lt;double&gt; res) {\n    printf(&quot;Result=%f\\n&quot;, res.get());  \/\/ Prints 3.0\n});\n\n\/\/ RPC call using std::future to handle the result\nResult&lt;double&gt; res = CZRPC_CALL(*con, add, 1, 2).ft().get();\nprintf(&quot;Result=%f\\n&quot;, res.get());  \/\/ Prints 3.0\n<\/pre>\n<p><a name=\"id-why-i-needed-this\"><\/a><\/p>\n<h3>Why I needed this<\/h3>\n<p>The game I&#8217;ve been working on for a couple of years now (<a href=\"https:\/\/bitbucket.org\/ruifig\/g4devkit\">code named G4<\/a>), gives players fully simulated little in-game computers they can code for whatever they want.<br \/>\nThat requires me to have a couple of server types running:<\/p>\n<ul>\n<li>Gameplay Server(s)<\/li>\n<li>VM Server(s) (Simulates the in-game computers)\n<ul>\n<li>So that in-game computers can be simulated even if the player is not currently online<\/li>\n<\/ul>\n<\/li>\n<li>VM Disk Server(s)\n<ul>\n<li>Deals with in-game computer&#8217;s storage, like floppies or hard drives.<\/li>\n<\/ul>\n<\/li>\n<li>Database server(s)<\/li>\n<li>Login server(s)<\/li>\n<\/ul>\n<p>All these servers need to exchange data, therefore the need for a flexible RPC framework.<\/p>\n<p>Initially I had a custom solution where I would tag methods of a class with certain attributes, then have a Clang based parser (<a href=\"https:\/\/github.com\/Celtoys\/clReflect\">clReflect<\/a>) generate any required serialization and glue code.<\/p>\n<p>Although it worked fine for the most part, for the past year or so I kept wondering how could I use the new C++11\/14 features to create a minimal type safe C++ RPC framework.<br \/>\nSomething that would not need a code generation step for glue code, while still keeping an acceptable API.<\/p>\n<p>For serialization of non-fundamental types, code generation is still useful, so I don&#8217;t need to manually define how to serialize all the fields of a given struct\/class. Although defining those manually is not a big deal, I believe.<\/p>\n<p><a name=\"id-rpc-parameters\"><\/a><\/p>\n<h2>RPC Parameters<\/h2>\n<p>Given a function, in order to have type safe RPC calls, there are a few things we need to be able to do:<\/p>\n<ul>\n<li>Identify at compile time if this function is a valid RPC function (Right number of parameters, right type of parameters, etc)<\/li>\n<li>Check if the supplied parameters match (or can be converted) to what the function signature specifies.<\/li>\n<li>Serialize all parameters<\/li>\n<li>Deserialize all parameters<\/li>\n<li>Call the desired function<\/li>\n<\/ul>\n<p><a name=\"id-parameter-traits\"><\/a><\/p>\n<h3>Parameter Traits<\/h3>\n<p>The first problem you&#8217;ll face is in deciding what type of parameters are accepted. Some RPC frameworks only accept a limited number of types, such as Thrift. Let&#8217;s check the problem.<\/p>\n<p>Given these function signatures:<\/p>\n<pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\nvoid func1(int a, float b, char c);\nvoid func2(const char* a, std::string b, const std::string&amp; c);\nvoid func3(Foo a, Bar* b);\n<\/pre>\n<p>How can we make compile time checks regarding the parameters?<br \/>\nFundamental types are easy enough and should definitely be supported by the framework. A dumb memory copy will do the trick in those cases unless you want to trade a bit of performance for bandwidth usage by cutting down the number of bits needed.<br \/>\nBut how about complex types such std::string, std::vector, or your own classes?<br \/>\nHow about pointers, references, const references, rvalues?<\/p>\n<p>We can get some inspiration from what the C++ Standard Library does in the <a href=\"http:\/\/en.cppreference.com\/w\/cpp\/types\">type_traits<\/a> header.<br \/>\nWe need to be able to query a given type regarding its RPC properties. Let&#8217;s put that concept in a template class <code>ParamTraits&lt;T&gt;<\/code>, with the following layout .<\/p>\n<table>\n<thead>\n<tr>\n<th><strong>Member constants<\/strong><\/th>\n<th><\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td><code>valid<\/code><\/td>\n<td><code>true<\/code> if T is valid for RPC parameters, <code>false<\/code> otherwise<\/td>\n<\/tr>\n<tr>\n<td><strong>Member types<\/strong><\/td>\n<td><\/td>\n<\/tr>\n<tr>\n<td><code>store_type<\/code><\/td>\n<td>Type used to hold the temporary copy needed when deserializing<\/td>\n<\/tr>\n<tr>\n<td><strong>Member functions<\/strong><\/td>\n<td><\/td>\n<\/tr>\n<tr>\n<td><code>write<\/code><\/td>\n<td>Writes the parameter to a stream<\/td>\n<\/tr>\n<tr>\n<td><code>read<\/code><\/td>\n<td>Reads a parameter into a <code>store_type<\/code><\/td>\n<\/tr>\n<tr>\n<td><code>get<\/code><\/td>\n<td>Given a <code>store_type<\/code> parameter, it returns what can be passed to the RPC function as a parameter<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>As an example, let&#8217;s implement <code>ParamTraits&lt;T&gt;<\/code> for arithmetic types, considering we have a stream class with a <code>write<\/code> and <code>read<\/code> methods:<\/p>\n<pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\nnamespace cz {\nnamespace rpc {\n\/\/ By default, all types for which ParamTraits is not specialized are invalid\ntemplate &lt;typename T, typename ENABLED = void&gt;\nstruct ParamTraits {\n    using store_type = int;\n    static constexpr bool valid = false;\n};\n\n\/\/ Specialization for arithmetic types\ntemplate &lt;typename T&gt;\nstruct ParamTraits&lt;\n    T, typename std::enable_if&lt;std::is_arithmetic&lt;T&gt;::value&gt;::type&gt; {\n    using store_type = typename std::decay&lt;T&gt;::type;\n    static constexpr bool valid = true;\n\n    template &lt;typename S&gt;\n    static void write(S&amp; s, typename std::decay&lt;T&gt;::type v) {\n        s.write(&amp;v, sizeof(v));\n    }\n\n    template &lt;typename S&gt;\n    static void read(S&amp; s, store_type&amp; v) {\n        s.read(&amp;v, sizeof(v));\n    }\n\n    static store_type get(store_type v) {\n        return v;\n    }\n};\n\n}  \/\/ namespace rpc\n}  \/\/ namespace cz\n<\/pre>\n<p>And a simple test:<\/p>\n<pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\n#define TEST(exp) printf(&quot;%s = %s\\n&quot;, #exp, exp ? &quot;true&quot; : &quot;false&quot;);\n\nvoid testArithmetic() {\n    TEST(ParamTraits&lt;int&gt;::valid);         \/\/ true\n    TEST(ParamTraits&lt;const int&gt;::valid);   \/\/ true\n    TEST(ParamTraits&lt;int&amp;&gt;::valid);        \/\/ false\n    TEST(ParamTraits&lt;const int&amp;&gt;::valid);  \/\/ false\n}\n<\/pre>\n<p><code>ParamTraits&lt;T&gt;<\/code> is also used to check if return types are valid, and since a void function is valid, we need to specialize <code>ParamTraits<\/code> for void too.<\/p>\n<pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\nnamespace cz {\nnamespace rpc {\n\n\/\/ void type is valid\ntemplate &lt;&gt;\nstruct ParamTraits&lt;void&gt; {\n    static constexpr bool valid = true;\n    using store_type = void;\n};\n\n}  \/\/ namespace rpc\n}  \/\/ namespace cz\n<\/pre>\n<p>The apparently strange thing with the specialization for <code>void<\/code> is that it also specifies a <code>store_type<\/code>. We can&#8217;t use it to store anything, but will make some of the later template code easier.<\/p>\n<p>With these <code>ParamTraits<\/code> examples, references are not valid RPC parameters. In practice you do want to allow const references at least, especially for fundamental types. A tweak can be added to enable support for <code>const T&amp;<\/code> for any valid T if your application needs it.<\/p>\n<pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\n\/\/ Make &quot;const T&amp;&quot; valid for any valid T\n#define CZRPC_ALLOW_CONST_LVALUE_REFS                                          \\\n    namespace cz {                                                             \\\n    namespace rpc {                                                            \\\n    template &lt;typename T&gt;                                                      \\\n    struct ParamTraits&lt;const T&amp;&gt; : ParamTraits&lt;T&gt; {                            \\\n        static_assert(ParamTraits&lt;T&gt;::valid,                                   \\\n                      &quot;Invalid RPC parameter type. Specialize ParamTraits if &quot; \\\n                      &quot;required.&quot;);                                            \\\n    };                                                                         \\\n    }                                                                          \\\n    }\n<\/pre>\n<p>Similar tweaks can be made to enable support for <code>T&amp;<\/code> or <code>T&amp;&amp;<\/code> if required, although if the function makes changes to those parameters, those changes will be lost.<\/p>\n<p>Let&#8217;s try adding support for a complex type, such as <code>std::vector&lt;T&gt;<\/code>. For <code>std::vector&lt;T&gt;<\/code> to be supported, <code>T<\/code> needs to be supported too.<\/p>\n<pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\nnamespace cz {\nnamespace rpc {\n\ntemplate &lt;typename T&gt;\nstruct ParamTraits&lt;std::vector&lt;T&gt;&gt; {\n    using store_type = std::vector&lt;T&gt;;\n    static constexpr bool valid = ParamTraits&lt;T&gt;::valid;\n    static_assert(ParamTraits&lt;T&gt;::valid == true,\n                  &quot;T is not valid RPC parameter type.&quot;);\n\n    \/\/ std::vector serialization is done by writing the vector size, followed by\n    \/\/ each element\n    template &lt;typename S&gt;\n    static void write(S&amp; s, const std::vector&lt;T&gt;&amp; v) {\n        int len = static_cast&lt;int&gt;(v.size());\n        s.write(&amp;len, sizeof(len));\n        for (auto&amp;&amp; i : v) ParamTraits&lt;T&gt;::write(s, i);\n    }\n\n    template &lt;typename S&gt;\n    static void read(S&amp; s, std::vector&lt;T&gt;&amp; v) {\n        int len;\n        s.read(&amp;len, sizeof(len));\n        v.clear();\n        while (len--) {\n            T i;\n            ParamTraits&lt;T&gt;::read(s, i);\n            v.push_back(std::move(i));\n        }\n    }\n\n    static std::vector&lt;T&gt;&amp;&amp; get(std::vector&lt;T&gt;&amp;&amp; v) {\n        return std::move(v);\n    }\n};\n\n\n}  \/\/ namespace rpc\n}  \/\/ namespace cz\n\n\/\/ A simple test\nvoid testVector() {\n    TEST(ParamTraits&lt;std::vector&lt;int&gt;&gt;::valid);  \/\/ true\n    \/\/ true if support for const refs was enabled\n    TEST(ParamTraits&lt;const std::vector&lt;int&gt;&amp;&gt;::valid);\n}\n\n<\/pre>\n<p>For convenience, we can use the <code>&lt;&lt;<\/code> and <code>&gt;&gt;<\/code> operators with <code>Stream<\/code> class (not shown here). Those operators simply call the respective <code>ParamTraits&lt;T&gt;<\/code> <code>read<\/code> and <code>write<\/code> functions.<\/p>\n<p>Now that we can check if a specific type is allowed for RPC parameters, we can build on that and check if a function can be used for RPCs. This is implemented with variadic templates.<\/p>\n<p>First let&#8217;s create a template to tells us if a bunch of parameters are valid.<\/p>\n<pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\nnamespace cz {\nnamespace rpc {\n\n\/\/\n\/\/ Validate if all parameter types in a parameter pack can be used for RPC\n\/\/ calls\n\/\/\ntemplate &lt;typename... T&gt;\nstruct ParamPack {\n    static constexpr bool valid = true;\n};\n\ntemplate &lt;typename First&gt;\nstruct ParamPack&lt;First&gt; {\n    static constexpr bool valid = ParamTraits&lt;First&gt;::valid;\n};\n\ntemplate &lt;typename First, typename... Rest&gt;\nstruct ParamPack&lt;First, Rest...&gt; {\n    static constexpr bool valid =\n        ParamTraits&lt;First&gt;::valid &amp;&amp; ParamPack&lt;Rest...&gt;::valid;\n}\n\n}  \/\/ namespace rpc\n}  \/\/ namespace cz\n\n\/\/ Usage example:\nvoid testParamPack() {\n    TEST(ParamPack&lt;&gt;::valid);  \/\/ true (No parameters is a valid too)\n    TEST((ParamPack&lt;int, double&gt;::valid));  \/\/ true\n    TEST((ParamPack&lt;int, int*&gt;::valid));    \/\/ false\n}\n<\/pre>\n<p>Using <code>ParamPack<\/code>, we can now create a <code>FunctionTraits<\/code> template to query a function&#8217;s properties.<\/p>\n<pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\nnamespace cz {\nnamespace rpc {\n\ntemplate &lt;class F&gt;\nstruct FunctionTraits {};\n\n\/\/ For free function pointers\ntemplate &lt;class R, class... Args&gt;\nstruct FunctionTraits&lt;R (*)(Args...)&gt; : public FunctionTraits&lt;R(Args...)&gt; {};\n\n\/\/ For method pointers\ntemplate &lt;class R, class C, class... Args&gt;\nstruct FunctionTraits&lt;R (C::*)(Args...)&gt; : public FunctionTraits&lt;R(Args...)&gt; {\n    using class_type = C;\n};\n\n\/\/ For const method pointers\ntemplate &lt;class R, class C, class... Args&gt;\nstruct FunctionTraits&lt;R (C::*)(Args...) const&gt;\n    : public FunctionTraits&lt;R(Args...)&gt; {\n    using class_type = C;\n};\n\ntemplate &lt;class R, class... Args&gt;\nstruct FunctionTraits&lt;R(Args...)&gt; {\n    \/\/ Tells if both the return type and parameters are valid for RPC calls\n    static constexpr bool valid =\n        ParamTraits&lt;R&gt;::valid &amp;&amp; ParamPack&lt;Args...&gt;::valid;\n    using return_type = R;\n    \/\/ Number of parameters\n    static constexpr std::size_t arity = sizeof...(Args);\n\n    \/\/ A tuple that can store all parameters\n    using param_tuple = std::tuple&lt;typename ParamTraits&lt;Args&gt;::store_type...&gt;;\n\n    \/\/ Allows us to get the type of each parameter, given an index\n    template &lt;std::size_t N&gt;\n    struct argument {\n        static_assert(N &lt; arity, &quot;error: invalid parameter index.&quot;);\n        using type = typename std::tuple_element&lt;N, std::tuple&lt;Args...&gt;&gt;::type;\n    };\n};\n\n}  \/\/ namespace rpc\n}  \/\/ namespace cz\n\n\/\/ A simple test...\nstruct FuncTraitsTest {\n    void func1() const {}\n    void func2(int) {}\n    int func3(const std::vector&lt;int&gt;&amp;) { return 0; }\n    int* func4() { return 0; }\n};\n\nvoid testFunctionTraits() {\n    TEST(FunctionTraits&lt;decltype(&amp;FuncTraitsTest::func1)&gt;::valid);  \/\/ true\n    TEST(FunctionTraits&lt;decltype(&amp;FuncTraitsTest::func2)&gt;::valid);  \/\/ true\n    TEST(FunctionTraits&lt;decltype(&amp;FuncTraitsTest::func3)&gt;::valid);  \/\/ true\n    TEST(FunctionTraits&lt;decltype(&amp;FuncTraitsTest::func4)&gt;::valid);  \/\/ false\n}\n<\/pre>\n<p><code>FunctionTraits<\/code> gives us a couple of properties that will be used later. Note for example that <code>FunctionTraits::param_tuple<\/code> builds on <code>ParamTraits&lt;T&gt;::store_type<\/code> . This is needed, since at some point we need a way to deserialize all parameters into a tuple before calling the function.<\/p>\n<p><a name=\"id-serialization\"><\/a><\/p>\n<h3>Serialization<\/h3>\n<p>Since we now have the required code for querying parameters, return types and validating functions, we can put together the code to serialize a function call.<br \/>\nAlso, it is type safe. It will not compile if given the wrong number or type of parameters, or if the function itself is not valid for RPCs (e.g: unsupported return\/parameter types).<\/p>\n<pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\nnamespace cz {\nnamespace rpc {\n\nnamespace details {\n\ntemplate &lt;typename F, int N&gt;\nstruct Parameters {\n    template &lt;typename S&gt;\n    static void serialize(S&amp;) {}\n    template &lt;typename S, typename First, typename... Rest&gt;\n    static void serialize(S&amp; s, First&amp;&amp; first, Rest&amp;&amp;... rest) {\n        using Traits =\n            ParamTraits&lt;typename FunctionTraits&lt;F&gt;::template argument&lt;N&gt;::type&gt;;\n        Traits::write(s, std::forward&lt;First&gt;(first));\n        Parameters&lt;F, N + 1&gt;::serialize(s, std::forward&lt;Rest&gt;(rest)...);\n    }\n};\n\n}  \/\/ namespace details\n\ntemplate &lt;typename F, typename... Args&gt;\nvoid serializeMethod(Stream&amp; s, Args&amp;&amp;... args) {\n    using Traits = FunctionTraits&lt;F&gt;;\n    static_assert(Traits::valid,\n                  &quot;Function signature not valid for RPC calls. Check if &quot;\n                  &quot;parameter types are valid&quot;);\n    static_assert(Traits::arity == sizeof...(Args),\n                  &quot;Invalid number of parameters for RPC call.&quot;);\n    details::Parameters&lt;F, 0&gt;::serialize(s, std::forward&lt;Args&gt;(args)...);\n}\n\n}  \/\/ namespace rpc\n}  \/\/ namespace cz\n\n\n\/\/\n\/\/ A simple test\n\/\/ \nstruct SerializeTest {\n    void func1(int, const std::vector&lt;int&gt;) {}\n    void func2(int*) {}\n};\n\nvoid testSerializeCall() {\n    Stream s;\n\n    serializeMethod&lt;decltype(&amp;SerializeTest::func1)&gt;(s, 1,\n                                                     std::vector&lt;int&gt;{1, 2, 3});\n\n    \/\/ These fail to compile because of the wrong number of parameters\n    \/\/ serializeMethod&lt;decltype(&amp;SerializeTest::func1)&gt;(s);\n    \/\/ serializeMethod&lt;decltype(&amp;SerializeTest::func1)&gt;(s, 1);\n\n    \/\/ Doesn&#039;t compile because of wrong type of parameters\n    \/\/ serializeMethod&lt;decltype(&amp;SerializeTest::func1)&gt;(s, 1, 2);\n\n    \/\/ Doesn&#039;t compile because the function can&#039;t be used for RPCs.\n    \/\/ int a;\n    \/\/ serializeMethod&lt;decltype(&amp;SerializeTest::func2)&gt;(s, &amp;a);\n}\n\n<\/pre>\n<p><a name=\"id-deserialization\"><\/a><\/p>\n<h3>Deserialization<\/h3>\n<p>As mentioned above, <code>FunctionTraits&lt;F&gt;::param_tuple<\/code> is the <code>std::tuple<\/code> type we can use to hold all the function&#8217;s parameters. In order to be able to use this tuple to deserialize parameters, we need to specialize <code>ParamTraits<\/code> for tuples.<br \/>\nThis has the nice side effect of also making it possible to use <code>std::tuple<\/code> for RPC parameters.<\/p>\n<pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\nnamespace cz {\nnamespace rpc {\n\nnamespace details {\ntemplate &lt;typename T, bool Done, int N&gt;\nstruct Tuple {\n    template &lt;typename S&gt;\n    static void deserialize(S&amp; s, T&amp; v) {\n        s &gt;&gt; std::get&lt;N&gt;(v);\n        Tuple&lt;T, N == std::tuple_size&lt;T&gt;::value - 1, N + 1&gt;::deserialize(s, v);\n    }\n\n    template &lt;typename S&gt;\n    static void serialize(S&amp; s, const T&amp; v) {\n        s &lt;&lt; std::get&lt;N&gt;(v);\n        Tuple&lt;T, N == std::tuple_size&lt;T&gt;::value - 1, N + 1&gt;::serialize(s, v);\n    }\n};\n\ntemplate &lt;typename T, int N&gt;\nstruct Tuple&lt;T, true, N&gt; {\n    template &lt;typename S&gt;\n    static void deserialize(S&amp;, T&amp;) {}\n    template &lt;typename S&gt;\n    static void serialize(S&amp;, const T&amp;) {}\n};\n}  \/\/ namespace details\n\ntemplate &lt;typename... T&gt;\nstruct ParamTraits&lt;std::tuple&lt;T...&gt;&gt; {\n    using tuple_type = std::tuple&lt;T...&gt;;  \/\/ for internal use\n\n    using store_type = tuple_type;\n    static constexpr bool valid = ParamPack&lt;T...&gt;::valid;\n\n    static_assert(\n        ParamPack&lt;T...&gt;::valid == true,\n        &quot;One or more tuple elements is not a valid RPC parameter type.&quot;);\n\n    template &lt;typename S&gt;\n    static void write(S&amp; s, const tuple_type&amp; v) {\n        details::Tuple&lt;tuple_type, std::tuple_size&lt;tuple_type&gt;::value == 0,\n                       0&gt;::serialize(s, v);\n    }\n\n    template &lt;typename S&gt;\n    static void read(S&amp; s, tuple_type&amp; v) {\n        details::Tuple&lt;tuple_type, std::tuple_size&lt;tuple_type&gt;::value == 0,\n                       0&gt;::deserialize(s, v);\n    }\n\n    static tuple_type&amp;&amp; get(tuple_type&amp;&amp; v) {\n        return std::move(v);\n    }\n};\n\n}  \/\/ namespace rpc\n}  \/\/ namespace cz\n\n\/\/ A simple test\nvoid testDeserialization() {\n    Stream s;\n    serializeMethod&lt;decltype(&amp;SerializeTest::func1)&gt;(s, 1,\n                                                     std::vector&lt;int&gt;{1, 2});\n    \/\/ deserialize the parameters into a tuple.\n    \/\/ the tuple is of type std::tuple&lt;int,std::vector&lt;int&gt;&gt;\n    FunctionTraits&lt;decltype(&amp;SerializeTest::func1)&gt;::param_tuple params;\n    s &gt;&gt; params;\n};\n\n<\/pre>\n<p><a name=\"id-from-tuple-to-function-parameters\"><\/a><\/p>\n<h3>From tuple to function parameters<\/h3>\n<p>After we deserialize all the parameters into a tuple, we now need to figure out how to unpack the tuple to call a matching function. This is once again done with variadic templates.<\/p>\n<pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\nnamespace cz {\nnamespace rpc {\n\nnamespace detail {\ntemplate &lt;typename F, typename Tuple, bool Done, int Total, int... N&gt;\nstruct callmethod_impl {\n    static decltype(auto) call(typename FunctionTraits&lt;F&gt;::class_type&amp; obj, F f,\n                               Tuple&amp;&amp; t) {\n        return callmethod_impl&lt;F, Tuple, Total == 1 + sizeof...(N), Total, N...,\n                               sizeof...(N)&gt;::call(obj, f,\n                                                   std::forward&lt;Tuple&gt;(t));\n    }\n};\n\ntemplate &lt;typename F, typename Tuple, int Total, int... N&gt;\nstruct callmethod_impl&lt;F, Tuple, true, Total, N...&gt; {\n    static decltype(auto) call(typename FunctionTraits&lt;F&gt;::class_type&amp; obj, F f,\n                               Tuple&amp;&amp; t) {\n        using Traits = FunctionTraits&lt;F&gt;;\n        return (obj.*f)(\n            ParamTraits&lt;typename Traits::template argument&lt;N&gt;::type&gt;::get(\n                std::get&lt;N&gt;(std::forward&lt;Tuple&gt;(t)))...);\n    }\n};\n}  \/\/ namespace details\n\ntemplate &lt;typename F, typename Tuple&gt;\ndecltype(auto) callMethod(typename FunctionTraits&lt;F&gt;::class_type&amp; obj, F f,\n                          Tuple&amp;&amp; t) {\n    static_assert(FunctionTraits&lt;F&gt;::valid, &quot;Function not usable as RPC&quot;);\n    typedef typename std::decay&lt;Tuple&gt;::type ttype;\n    return detail::callmethod_impl&lt;\n        F, Tuple, 0 == std::tuple_size&lt;ttype&gt;::value,\n        std::tuple_size&lt;ttype&gt;::value&gt;::call(obj, f, std::forward&lt;Tuple&gt;(t));\n}\n\n}  \/\/ namespace rpc\n}  \/\/ namespace cz\n\n\/\/ A simple test\nvoid testCall() {\n    Stream s;\n    \/\/ serialize\n    serializeMethod&lt;decltype(&amp;SerializeTest::func1)&gt;(s, 1,\n                                                     std::vector&lt;int&gt;{1, 2});\n    \/\/ deserialize\n    FunctionTraits&lt;decltype(&amp;SerializeTest::func1)&gt;::param_tuple params;\n    s &gt;&gt; params;\n    \/\/ Call func1 on an object, unpacking the tuple into parameters\n    SerializeTest obj;\n    callMethod(obj, &amp;SerializeTest::func1, std::move(params));\n};\n\n<\/pre>\n<p>So, we now know how to validate a function, serialize, deserialize, and call it. That&#8217;s the low level code done. The layer we&#8217;ll now build on top of this code will be the actual RPC API.<\/p>\n<p><a name=\"id-the-rpc-api\"><\/a><\/p>\n<h2>The RPC API<\/h2>\n<p><a name=\"id-header\"><\/a><\/p>\n<h3>Header<\/h3>\n<p>The header contains the following information:<\/p>\n<p><img alt='' class='alignnone size-full wp-image-575 ' src='https:\/\/www.crazygaze.com\/blog\/wp-content\/uploads\/2016\/05\/img_574cbb33e8851.png' \/><\/p>\n<table>\n<thead>\n<tr>\n<th>Field<\/th>\n<th><\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td>size<\/td>\n<td>Total size in bytes of the RPC. Having the size as part of the header greatly simplifies things, since we can check if we received all the data before trying to process the RPC.<\/td>\n<\/tr>\n<tr>\n<td>counter<\/td>\n<td>Call number. Every time we call an RPC, a counter is incremented and assigned to that RPC call.<\/td>\n<\/tr>\n<tr>\n<td>rpcid<\/td>\n<td>The function to call<\/td>\n<\/tr>\n<tr>\n<td>isReply<\/td>\n<td>If true, it&#8217;s a reply to an RPC. If false, it&#8217;s an RPC call.<\/td>\n<\/tr>\n<tr>\n<td>success<\/td>\n<td>This only applies to replies (isReply==true). If true, the call was successful and the data is the reply. If false, the data is the exception information<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>The counter and rpcid form a key that identifies an RPC call instance. This is needed to match an incoming RPC reply to the RPC call that caused it.<\/p>\n<pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\nnamespace cz {\nnamespace rpc {\n\n\/\/ Small utility struct to make it easier to work with the RPC headers\nstruct Header {\n    enum {\n        kSizeBits = 32,\n        kRPCIdBits = 8,\n        kCounterBits = 22,\n    };\n    explicit Header() {\n        static_assert(sizeof(*this) == sizeof(uint64_t),\n                      &quot;Invalid size. Check the bitfields&quot;);\n        all_ = 0;\n    }\n\n    struct Bits {\n        unsigned size : kSizeBits;\n        unsigned counter : kCounterBits;\n        unsigned rpcid : kRPCIdBits;\n        unsigned isReply : 1;  \/\/ Is it a reply to a RPC call ?\n        unsigned success : 1;  \/\/ Was the RPC call a success ?\n    };\n\n    uint32_t key() const {\n        return (bits.counter &lt;&lt; kRPCIdBits) | bits.rpcid;\n    }\n    union {\n        Bits bits;\n        uint64_t all_;\n    };\n};\n\ninline Stream&amp; operator&lt;&lt;(Stream&amp; s, const Header&amp; v) {\n    s &lt;&lt; v.all_;\n    return s;\n}\n\ninline Stream&amp; operator&gt;&gt;(Stream&amp; s, Header&amp; v) {\n    s &gt;&gt; v.all_;\n    return s;\n}\n\n}  \/\/ namespace rpc\n}  \/\/ namespace cz\n\n<\/pre>\n<p><a name=\"id-table\"><\/a><\/p>\n<h3>Table<\/h3>\n<p>We already managed to serialize and deserialize an RPC, but not a way to map a serialized RPC to the right function on the server side.<br \/>\nTo solve this, we need to assign an ID to each function. The client knows what function it wants to call and fills the header with the right ID. The server checks the header, and knowing the ID, it dispatches to the right handler.<br \/>\nLet&#8217;s create the basics to define such dispatching tables.<\/p>\n<pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\nnamespace cz {\nnamespace rpc {\n\n\/\/\n\/\/ Helper code to dispatch a call.\nnamespace details {\n\/\/ Handle RPCs with return values\ntemplate &lt;typename R&gt;\nstruct CallHelper {\n    template &lt;typename OBJ, typename F, typename P&gt;\n    static void impl(OBJ&amp; obj, F f, P&amp;&amp; params, Stream&amp; out) {\n        out &lt;&lt; callMethod(obj, f, std::move(params));\n    }\n};\n\n\/\/ Handle void RPCs\ntemplate &lt;&gt;\nstruct CallHelper&lt;void&gt; {\n    template &lt;typename OBJ, typename F, typename P&gt;\n    static void impl(OBJ&amp; obj, F f, P&amp;&amp; params, Stream&amp; out) {\n        callMethod(obj, f, std::move(params));\n    }\n};\n}\n\nstruct BaseRPCInfo {\n    BaseRPCInfo() {}\n    virtual ~BaseRPCInfo(){};\n    std::string name;\n};\n\nclass BaseTable {\npublic:\n    BaseTable() {}\n    virtual ~BaseTable() {}\n    bool isValid(uint32_t rpcid) const {\n        return rpcid &lt; m_rpcs.size();\n    }\n\nprotected:\n    std::vector&lt;std::unique_ptr&lt;BaseRPCInfo&gt;&gt; m_rpcs;\n};\n\ntemplate &lt;typename T&gt;\nclass TableImpl : public BaseTable {\npublic:\n    using Type = T;\n\n    struct RPCInfo : public BaseRPCInfo {\n        std::function&lt;void(Type&amp;, Stream&amp; in, Stream&amp; out)&gt; dispatcher;\n    };\n\n    template &lt;typename F&gt;\n    void registerRPC(uint32_t rpcid, const char* name, F f) {\n        assert(rpcid == m_rpcs.size());\n        auto info = std::make_unique&lt;RPCInfo&gt;();\n        info-&gt;name = name;\n        info-&gt;dispatcher = [f](Type&amp; obj, Stream&amp; in, Stream&amp; out) {\n            using Traits = FunctionTraits&lt;F&gt;;\n            typename Traits::param_tuple params;\n            in &gt;&gt; params;\n            using R = typename Traits::return_type;\n            details::CallHelper&lt;R&gt;::impl(obj, f, std::move(params), out);\n        };\n        m_rpcs.push_back(std::move(info));\n    }\n};\n\ntemplate &lt;typename T&gt;\nclass Table : public TableImpl&lt;T&gt; {\n    static_assert(sizeof(T) == 0, &quot;RPC Table not specified for the type.&quot;);\n};\n\n\n}  \/\/ namespace rpc\n}  \/\/ namespace cz\n<\/pre>\n<p>The <code>Table<\/code> template needs to be specialized for the class we want to use for RPC calls.<br \/>\nSay we have a <code>Calculator<\/code> class we want to be able to use for RPC calls:<\/p>\n<pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\nclass Calculator {\npublic:\n    double add(double a, double b) {\n        m_ans = a + b;\n        return m_ans;\n    }\n    double sub(double a, double b) {\n        m_ans = a - b;\n        return m_ans;\n    }\n    double ans() {\n        return m_ans;\n    }\n\nprivate:\n    double m_ans = 0;\n};\n<\/pre>\n<p>We can specialize the <code>Table<\/code> template for <code>Calculator<\/code>, so both the client and server have something to work with :<\/p>\n<pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\n\/\/ Table specialization for Calculator\ntemplate &lt;&gt;\nclass cz::rpc::Table&lt;Calculator&gt; : cz::rpc::TableImpl&lt;Calculator&gt; {\npublic:\n    enum class RPCId { add, sub, ans };\n\n    Table() {\n        registerRPC((int)RPCId::add, &quot;add&quot;, &amp;Calculator::add);\n        registerRPC((int)RPCId::sub, &quot;sub&quot;, &amp;Calculator::sub);\n        registerRPC((int)RPCId::ans, &quot;ans&quot;, &amp;Calculator::ans);\n    }\n\n    static const RPCInfo* get(uint32_t rpcid) {\n        static Table&lt;Calculator&gt; tbl;\n        assert(tbl.isValid(rpcid));\n        return static_cast&lt;RPCInfo*&gt;(tbl.m_rpcs[rpcid].get());\n    }\n};\n<\/pre>\n<p>Given an ID, the <code>get<\/code> function returns the dispatcher for the right <code>Calculator<\/code> method. We can then pass our <code>Calculator<\/code> instance, input and output streams to the dispatcher, which takes care of the rest.<\/p>\n<p>These specializations are quite verbose and error-prone, since the enums and the <code>registerRPC<\/code> calls need to match. But we can greatly shorten that with some macros.<br \/>\nBut first, let&#8217;s see a verbose example on how to use this table :<\/p>\n<pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\nvoid testCalculatorTable() {\n    \/\/ Both the client and server need to have access to the necessary table\n    using CalcT = Table&lt;Calculator&gt;;\n\n    \/\/\n    \/\/ Client sends RPC\n    Stream toServer;    \n    RPCHeader hdr;\n    hdr.bits.rpcid = (int)CalcT::RPCId::add;\n    toServer &lt;&lt; hdr;\n    serializeMethod&lt;decltype(&amp;Calculator::add)&gt;(toServer, 1.0, 9.0);\n\n    \/\/\n    \/\/ Server receives RPC, and sends back a reply\n    Calculator calc;  \/\/ object used to receive the RPCs\n    toServer &gt;&gt; hdr;\n    auto&amp;&amp; info = CalcT::get(hdr.bits.rpcid);\n    Stream toClient;  \/\/ Stream for the reply\n    \/\/ Call the desired Calculator function.\n    info-&gt;dispatcher(calc, toServer, toClient);\n\n    \/\/\n    \/\/ Client receives a reply\n    double r;\n    toClient &gt;&gt; r;\n    printf(&quot;%2.0f\\n&quot;, r);  \/\/ Will print &quot;10&quot;\n}\n<\/pre>\n<p>Again, this is quite verbose, but its just to show the code flow. That will be improved later.<\/p>\n<p>Now, how can we simplify the table specializations?<br \/>\nIf we put the gist of tables specialization in an unguarded header, all you need is a couple of defines followed by an include of that unguarded header to generate what the equivalent of what we did manually.<\/p>\n<p>Example:<\/p>\n<pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\n#define RPCTABLE_CLASS Calculator\n#define RPCTABLE_CONTENTS \\\n    REGISTERRPC(add) \\\n    REGISTERRPC(sub) \\\n    REGISTERRPC(ans)\n#include &quot;RPCGenerate.h&quot;\n<\/pre>\n<p>That&#8217;s surprisingly simple, no?<\/p>\n<p>The <code>RPCGenerate.h<\/code> is an unguarded header that looks like this:<\/p>\n<pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\n#ifndef RPCTABLE_CLASS\n    #error &quot;Macro RPCTABLE_CLASS needs to be defined&quot;\n#endif\n#ifndef RPCTABLE_CONTENTS\n    #error &quot;Macro RPCTABLE_CONTENTS needs to be defined&quot;\n#endif\n\n\n#define RPCTABLE_TOOMANYRPCS_STRINGIFY(arg) #arg\n#define RPCTABLE_TOOMANYRPCS(arg) RPCTABLE_TOOMANYRPCS_STRINGIFY(arg)\n\ntemplate&lt;&gt; class cz::rpc::Table&lt;RPCTABLE_CLASS&gt; : cz::rpc::TableImpl&lt;RPCTABLE_CLASS&gt;\n{\npublic:\n    using Type = RPCTABLE_CLASS;\n    #define REGISTERRPC(rpc) rpc,\n    enum class RPCId {\n        RPCTABLE_CONTENTS\n        NUMRPCS\n    };\n\n    Table()\n    {\n        static_assert((unsigned)((int)RPCId::NUMRPCS-1)&lt;(1&lt;&lt;Header::kRPCIdBits),\n            RPCTABLE_TOOMANYRPCS(Too many RPCs registered for class RPCTABLE_CLASS));\n        #undef REGISTERRPC\n        #define REGISTERRPC(func) registerRPC((uint32_t)RPCId::func, #func, &amp;Type::func);\n        RPCTABLE_CONTENTS\n    }\n\n    static const RPCInfo* get(uint32_t rpcid)\n    {\n        static Table&lt;RPCTABLE_CLASS&gt; tbl;\n        assert(tbl.isValid(rpcid));\n        return static_cast&lt;RPCInfo*&gt;(tbl.m_rpcs[rpcid].get());\n    }\n};\n\n#undef REGISTERRPC\n#undef RPCTABLE_START\n#undef RPCTABLE_END\n#undef RPCTABLE_CLASS\n#undef RPCTABLE_CONTENTS\n#undef RPCTABLE_TOOMANYRPCS_STRINGIFY\n#undef RPCTABLE_TOOMANYRPCS\n<\/pre>\n<p>As a bonus, specializing tables like this makes it easy to support inheritance.<br \/>\nImagine we have a <code>ScientificCalculator<\/code> that inherits from Calculator:<\/p>\n<pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\nclass ScientificCalculator : public Calculator {\npublic:\n    double sqrt(double a) {\n        return std::sqrt(a);\n    }\n};\n<\/pre>\n<p>By separately defining the contents of <code>Calculator<\/code>, we can reuse that define:<\/p>\n<pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\n\/\/ Separately define the Calculator contents so it can be reused\n#define RPCTABLE_CALCULATOR_CONTENTS \\\n    REGISTERRPC(add) \\\n    REGISTERRPC(sub) \\\n    REGISTERRPC(ans)\n\n\/\/ Calculator table\n#define RPCTABLE_CLASS Calculator\n#define RPCTABLE_CONTENTS \\\n    RPCTABLE_CALCULATOR_CONTENTS\n#include &quot;RPCGenerate.h&quot;\n\n\/\/ ScientificCalculator table\n#define RPCTABLE_CLASS ScientificCalculator\n#define RPCTABLE_CONTENTS \\\n    RPCTABLE_CALCULATOR_CONTENTS \\\n    REGISTERRPC(sqrt)\n#include &quot;RPCGenerate.h&quot;\n<\/pre>\n<p><a name=\"id-transport\"><\/a><\/p>\n<h3>Transport<\/h3>\n<p>We need to define how data will be transported between client and server. Let&#8217;s put that in a <code>Transport<\/code> interface class.<br \/>\nThe interface is intentionally left very simple so the application can specify a custom transport . All we need is a method to send, receive, and to close.<\/p>\n<pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\nnamespace cz {\nnamespace rpc {\n\nclass Transport {\npublic:\n    virtual ~Transport() {}\n\n    \/\/ Send one single RPC\n    virtual void send(std::vector&lt;char&gt; data) = 0;\n\n    \/\/ Receive one single RPC\n    \/\/ dst : Will contain the data for one single RPC, or empty if no RPC\n    \/\/ available\n    \/\/ return: true if the transport is still alive, false if the transport\n    \/\/ closed\n    virtual bool receive(std::vector&lt;char&gt;&amp; dst) = 0;\n\n    \/\/ Close connection to the peer\n    virtual void close() = 0;\n};\n\n}  \/\/ namespace rpc\n}  \/\/ namespace cz\n<\/pre>\n<p><a name=\"id-result\"><\/a><\/p>\n<h3>Result<\/h3>\n<p>How should a RPC result look like?<br \/>\nWhenever we make an RPC call, the result can come in 3 forms.<\/p>\n<table>\n<thead>\n<tr>\n<th>Form<\/th>\n<th>Meaning<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td>Valid<\/td>\n<td>We got a reply from the server with the result value of the RPC call<\/td>\n<\/tr>\n<tr>\n<td>Aborted<\/td>\n<td>The connection failed or was closed, and therefore we don&#8217;t have result value<\/td>\n<\/tr>\n<tr>\n<td>Exception<\/td>\n<td>We got a reply from the server with an exception string (The RPC call caused an exception server side)<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\nnamespace cz {\nnamespace rpc {\n\nclass Exception : public std::exception {\npublic:\n    Exception(const std::string&amp; msg) : std::exception(msg.c_str()) {}\n};\n\ntemplate &lt;typename T&gt;\nclass Result {\npublic:\n    using Type = T;\n\n    Result() : m_state(State::Aborted) {}\n\n    explicit Result(Type&amp;&amp; val) : m_state(State::Valid), m_val(std::move(val)) {}\n\n    Result(Result&amp;&amp; other) {\n        moveFrom(std::move(other));\n    }\n\n    Result(const Result&amp; other) {\n        copyFrom(other);\n    }\n\n    ~Result() {\n        destroy();\n    }\n\n    Result&amp; operator=(Result&amp;&amp; other) {\n        if (this == &amp;other)\n            return *this;\n        destroy();\n        moveFrom(std::move(other));\n        return *this;\n    }\n\n    \/\/ Construction from an exception needs to be separate. so\n    \/\/ RPCReply&lt;std::string&gt; works.\n    \/\/ Otherwise we would have no way to tell if constructing from a value, or\n    \/\/ from an exception\n    static Result fromException(std::string ex) {\n        Result r;\n        r.m_state = State::Exception;\n        new (&amp;r.m_ex) std::string(std::move(ex));\n        return r;\n    }\n\n    template &lt;typename S&gt;\n    static Result fromStream(S&amp; s) {\n        Type v;\n        s &gt;&gt; v;\n        return Result(std::move(v));\n    };\n\n    bool isValid() const {\n        return m_state == State::Valid;\n    }\n    bool isException() const {\n        return m_state == State::Exception;\n    };\n    bool isAborted() const {\n        return m_state == State::Aborted;\n    }\n\n    T&amp; get() {\n        if (!isValid())\n            throw Exception(isException() ? m_ex : &quot;RPC reply was aborted&quot;);\n        return m_val;\n    }\n\n    const T&amp; get() const {\n        if (!isValid())\n            throw Exception(isException() ? m_ex : &quot;RPC reply was aborted&quot;);\n        return m_val;\n    }\n\n    const std::string&amp; getException() {\n        assert(isException());\n        return m_ex;\n    };\n\nprivate:\n    void destroy() {\n        if (m_state == State::Valid)\n            m_val.~Type();\n        else if (m_state == State::Exception) {\n            using String = std::string;\n            m_ex.~String();\n        }\n        m_state = State::Aborted;\n    }\n\n    void moveFrom(Result&amp;&amp; other) {\n        m_state = other.m_state;\n        if (m_state == State::Valid)\n            new (&amp;m_val) Type(std::move(other.m_val));\n        else if (m_state == State::Exception)\n            new (&amp;m_ex) std::string(std::move(other.m_ex));\n    }\n\n    void copyFrom(const Result&amp; other) {\n        m_state = other.m_state;\n        if (m_state == State::Valid)\n            new (&amp;m_val) Type(other.m_val);\n        else if (m_state == State::Exception)\n            new (&amp;m_ex) std::string(other.m_ex);\n    }\n\n    enum class State { Valid, Aborted, Exception };\n\n    State m_state;\n    union {\n        Type m_val;\n        std::string m_ex;\n    };\n};\n\n\/\/ void specialization\ntemplate &lt;&gt;\nclass Result&lt;void&gt; {\npublic:\n    Result() : m_state(State::Aborted) {}\n\n    Result(Result&amp;&amp; other) {\n        moveFrom(std::move(other));\n    }\n\n    Result(const Result&amp; other) {\n        copyFrom(other);\n    }\n\n    ~Result() {\n        destroy();\n    }\n\n    Result&amp; operator=(Result&amp;&amp; other) {\n        if (this == &amp;other)\n            return *this;\n        destroy();\n        moveFrom(std::move(other));\n        return *this;\n    }\n\n    static Result fromException(std::string ex) {\n        Result r;\n        r.m_state = State::Exception;\n        new (&amp;r.m_ex) std::string(std::move(ex));\n        return r;\n    }\n\n    template &lt;typename S&gt;\n    static Result fromStream(S&amp; s) {\n        Result r;\n        r.m_state = State::Valid;\n        return r;\n    }\n\n    bool isValid() const {\n        return m_state == State::Valid;\n    }\n    bool isException() const {\n        return m_state == State::Exception;\n    };\n    bool isAborted() const {\n        return m_state == State::Aborted;\n    }\n\n    const std::string&amp; getException() {\n        assert(isException());\n        return m_ex;\n    };\n\n    void get() const {\n        if (!isValid())\n            throw Exception(isException() ? m_ex : &quot;RPC reply was aborted&quot;);\n    }\n\nprivate:\n    void destroy() {\n        if (m_state == State::Exception) {\n            using String = std::string;\n            m_ex.~String();\n        }\n        m_state = State::Aborted;\n    }\n\n    void moveFrom(Result&amp;&amp; other) {\n        m_state = other.m_state;\n        if (m_state == State::Exception)\n            new (&amp;m_ex) std::string(std::move(other.m_ex));\n    }\n\n    void copyFrom(const Result&amp; other) {\n        m_state = other.m_state;\n        if (m_state == State::Exception)\n            new (&amp;m_ex) std::string(other.m_ex);\n    }\n\n    enum class State { Valid, Aborted, Exception };\n\n    State m_state;\n    union {\n        bool m_dummy;\n        std::string m_ex;\n    };\n};\n\n}  \/\/ namespace rpc\n}  \/\/ namespace cz\n\n\n<\/pre>\n<p>The <code>Result&lt;void&gt;<\/code> specialization is needed, since for that case there isn&#8217;t a result, but the caller still wants to know if the RPC call was properly processed.<br \/>\nInitially, I considered using <a href=\"https:\/\/en.wikipedia.org\/wiki\/Andrei_Alexandrescu\"><code>Expected&lt;T&gt;<\/code><\/a> for RPC replies. But <code>Expected&lt;T&gt;<\/code> has basically 2 states (Value or Exception), and we need 3 (Value, Exception, and Aborted). One can think that Aborted could be considered an exception, but from a client point of view, it&#8217;s not always the case. In some cases you want to know an RPC failed because the connection was closed, and not because the server replied with an exception.<\/p>\n<p><a name=\"id-out-processor\"><\/a><\/p>\n<h3>OutProcessor<\/h3>\n<p>We need to track ongoing RPC calls so the user code can get the results when they arrive.<br \/>\nHandling a result can be done in two ways. Throught an asynchronous handler (similar to <a href=\"https:\/\/en.wikipedia.org\/wiki\/Asio_C%2B%2B_library\">Asio<\/a> ), or with a future.<\/p>\n<p>Two classes are needed for this. An outgoing processor and a wrapper for a single RPC call.<br \/>\nAnother class is needed (<code>Connection<\/code>) that ties together outgoing and incoming processors. It will be introduced later.<\/p>\n<pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\nnamespace cz {\nnamespace rpc {\n\nclass BaseOutProcessor {\npublic:\n    virtual ~BaseOutProcessor() {}\n\nprotected:\n    template &lt;typename R&gt;\n    friend class Call;\n    template &lt;typename L, typename R&gt;\n    friend struct Connection;\n\n    template &lt;typename F, typename H&gt;\n    void commit(Transport&amp; transport, uint32_t rpcid, Stream&amp; data,\n                H&amp;&amp; handler) {\n        std::unique_lock&lt;std::mutex&gt; lk(m_mtx);\n        Header hdr;\n        hdr.bits.size = data.writeSize();\n        hdr.bits.counter = ++m_replyIdCounter;\n        hdr.bits.rpcid = rpcid;\n        *reinterpret_cast&lt;Header*&gt;(data.ptr(0)) = hdr;\n        m_replies[hdr.key()] = [handler = std::move(handler)](Stream * in,\n                                                              Header hdr) {\n            using R = typename ParamTraits&lt;\n                typename FunctionTraits&lt;F&gt;::return_type&gt;::store_type;\n            if (in) {\n                if (hdr.bits.success) {\n                    handler(Result&lt;R&gt;::fromStream((*in)));\n                } else {\n                    std::string str;\n                    (*in) &gt;&gt; str;\n                    handler(Result&lt;R&gt;::fromException(std::move(str)));\n                }\n            } else {\n                \/\/ if the stream is nullptr, it means the result is being aborted\n                handler(Result&lt;R&gt;());\n            }\n        };\n        lk.unlock();\n\n        transport.send(data.extract());\n    }\n\n    void processReply(Stream&amp; in, Header hdr) {\n        std::function&lt;void(Stream*, Header)&gt; h;\n        {\n            std::unique_lock&lt;std::mutex&gt; lk(m_mtx);\n            auto it = m_replies.find(hdr.key());\n            assert(it != m_replies.end());\n            h = std::move(it-&gt;second);\n            m_replies.erase(it);\n        }\n\n        h(&amp;in, hdr);\n    }\n\n    void abortReplies() {\n        decltype(m_replies) replies;\n        {\n            std::unique_lock&lt;std::mutex&gt; lk(m_mtx);\n            replies = std::move(m_replies);\n        }\n\n        for (auto&amp;&amp; r : replies) {\n            r.second(nullptr, Header());\n        }\n    };\n\n    std::mutex m_mtx;\n    uint32_t m_replyIdCounter = 0;\n    std::unordered_map&lt;uint32_t, std::function&lt;void(Stream*, Header)&gt;&gt;\n        m_replies;\n};\n\ntemplate &lt;typename T&gt;\nclass OutProcessor : public BaseOutProcessor {\npublic:\n    using Type = T;\n\n    template &lt;typename F, typename... Args&gt;\n    auto call(Transport&amp; transport, uint32_t rpcid, Args&amp;&amp;... args) {\n        using Traits = FunctionTraits&lt;F&gt;;\n        static_assert(\n            std::is_member_function_pointer&lt;F&gt;::value &amp;&amp;\n                std::is_base_of&lt;typename Traits::class_type, Type&gt;::value,\n            &quot;Not a member function of the wrapped class&quot;);\n        Call&lt;F&gt; c(*this, transport, rpcid);\n        c.serializeParams(std::forward&lt;Args&gt;(args)...);\n        return std::move(c);\n    }\n\nprotected:\n};\n\n\/\/ Specialization for when there is no outgoing RPC calls\n\/\/ If we have no outgoing RPC calls, receiving a reply is therefore an error.\ntemplate &lt;&gt;\nclass OutProcessor&lt;void&gt; {\npublic:\n    OutProcessor() {}\n    void processReply(Stream&amp;, Header) {\n        assert(0 &amp;&amp; &quot;Incoming replies not allowed for OutProcessor&lt;void&gt;&quot;);\n    }\n    void abortReplies() {}\n};\n\n}  \/\/ namespace rpc\n}  \/\/ namespace cz\n<\/pre>\n<p>The entry point to start an RPC call is the <code>OutProcessor&lt;T&gt;::call<\/code> method. That packs together the data in a <code>Call<\/code> object. We can then use that object to set the result handling.<br \/>\nThe reason for separating calls with a processor and a call object, is to make it easier for the user to specify the handling.<br \/>\nIn other words&#8230; The user code asks the processor to prepare an RPC call, then specifies how to handle the result (which triggers the send).<\/p>\n<p>The <code>abortReplies<\/code> method is called one single time when the transport is closed, so the user code gets the abort notifications for any RPCs that are waiting for a result.<\/p>\n<p>The <code>OutProcessor&lt;void&gt;<\/code> specialization will come in handy later, as we deal with bidirectional RPCS.<\/p>\n<p>The source code for the <code>Call<\/code> class used by the <code>OutProcessor<\/code>:<\/p>\n<pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\nnamespace cz {\nnamespace rpc {\n\nclass BaseOutProcessor;\n\ntemplate &lt;typename F&gt;\nclass Call {\nprivate:\n    using RType = typename FunctionTraits&lt;F&gt;::return_type;\n    using RTraits = typename ParamTraits&lt;RType&gt;;\n\npublic:\n    Call(Call&amp;&amp; other)\n        : m_outer(other.m_outer)\n        , m_transport(other.m_transport)\n        , m_rpcid(other.m_rpcid)\n        , m_data(std::move(other.m_data)) {}\n\n    Call(const Call&amp;) = delete;\n    Call&amp; operator=(const Call&amp;) = delete;\n    Call&amp; operator=(Call&amp;&amp;) = delete;\n\n    ~Call() {\n        if (m_data.writeSize() &amp;&amp; !m_commited)\n            async([](Result&lt;RTraits::store_type&gt;&amp;) {});\n    }\n\n    template &lt;typename H&gt;\n    void async(H&amp;&amp; handler) {\n        m_outer.commit&lt;F&gt;(m_transport, m_rpcid, m_data,\n                          std::forward&lt;H&gt;(handler));\n        m_commited = true;\n    }\n\n    std::future&lt;typename Result&lt;typename RTraits::store_type&gt;&gt; ft() {\n        auto pr = std::make_shared&lt;std::promise&lt;Result&lt;RTraits::store_type&gt;&gt;&gt;();\n        auto ft = pr-&gt;get_future();\n        async([pr = std::move(pr)](Result&lt;RTraits::store_type&gt; &amp;&amp; res) {\n            pr-&gt;set_value(std::move(res));\n        });\n\n        return ft;\n    }\n\nprotected:\n    template &lt;typename T&gt;\n    friend class OutProcessor;\n\n    explicit Call(BaseOutProcessor&amp; outer, Transport&amp; transport, uint32_t rpcid)\n        : m_outer(outer), m_transport(transport), m_rpcid(rpcid) {\n        m_data &lt;&lt; uint64_t(0);  \/\/ rpc header\n    }\n\n    template &lt;typename... Args&gt;\n    void serializeParams(Args&amp;&amp;... args) {\n        serializeMethod&lt;F&gt;(m_data, std::forward&lt;Args&gt;(args)...);\n    }\n\n    BaseOutProcessor&amp; m_outer;\n    Transport&amp; m_transport;\n    uint32_t m_rpcid;\n    Stream m_data;\n    \/\/ Used in the destructor to do a commit with an empty handler if the rpc\n    \/\/ was not committed.\n    bool m_commited = false;\n};\n\n}  \/\/ namespace rpc\n}  \/\/ namespace cz\n<\/pre>\n<p>Note that <code>OutProcessor&lt;T&gt;<\/code> doesn&#8217;t need a reference\/pointer to an object of <code>T<\/code>. It only needs to know the type we are sending the RPCs to, so it knows what <code>Table&lt;T&gt;<\/code> to use.<\/p>\n<p>This is an example how to use the OutProcessor:<\/p>\n<pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\n\/\/\n\/\/ &quot;trp&quot; is a transport that sends data to a &quot;Calculator&quot; server\nvoid testOutProcessor(Transport&amp; trp) {\n    \/\/ A processor that calls RPCs on a &quot;Calculator&quot; server\n    OutProcessor&lt;Calculator&gt; outPrc;\n\n    \/\/ Handle with an asynchronous handler\n    outPrc.call&lt;decltype(&amp;Calculator::add)&gt;(\n              trp, (int)Table&lt;Calculator&gt;::RPCId::add, 1.0, 2.0)\n        .async([](Result&lt;double&gt; res) {\n            printf(&quot;%2.0f\\n&quot;, res.get());  \/\/ prints &#039;3&#039;\n        });\n\n    \/\/ Handle with a future\n    Result&lt;double&gt; res = outPrc.call&lt;decltype(&amp;Calculator::add)&gt;(\n                        trp, (int)Table&lt;Calculator&gt;::RPCId::add, 1.0, 3.0)\n                  .ft().get();\n    printf(&quot;%2.0f\\n&quot;, res.get());  \/\/ prints &#039;4&#039;\n}\n<\/pre>\n<p>Again, a bit verbose, since I haven&#8217;t introduced all the code that wraps things up. But shows how the <code>OutProcessor&lt;T&gt;<\/code> and <code>Call<\/code> interfaces work.<br \/>\nThe <code>std::future<\/code> implementation simply builds on the asynchronous implementation.<\/p>\n<p><a name=\"id-in-processor\"><\/a><\/p>\n<h3>InProcessor<\/h3>\n<p>Now that we can send an RPC and wait for the result, let&#8217;s look at what we need to do on the other side. What to do when an RPC call is received on the server.<\/p>\n<p>Let&#8217;s create a <code>InProcessor&lt;T&gt;<\/code> class.<br \/>\nContrary to <code>OutProcessor&lt;T&gt;<\/code>, <code>InProcessor&lt;T&gt;<\/code> needs to keep a reference to an object of type <code>T<\/code>. This is so when an RPC is received, it can call the requested method on that object, and send the result back to the client.<\/p>\n<pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\nnamespace cz {\nnamespace rpc {\n\nclass BaseInProcessor {\npublic:\n    virtual ~BaseInProcessor() {}\n};\n\ntemplate &lt;typename T&gt;\nclass InProcessor : public BaseInProcessor {\npublic:\n    using Type = T;\n    InProcessor(Type* obj, bool doVoidReplies = true)\n        : m_obj(*obj), m_voidReplies(doVoidReplies) {}\n\n    void processCall(Transport&amp; transport, Stream&amp; in, Header hdr) {\n        Stream out;\n        \/\/ Reuse the header as the header for the reply, so we keep the counter\n        \/\/ and rpcid\n        hdr.bits.size = 0;\n        hdr.bits.isReply = true;\n        hdr.bits.success = true;\n\n        auto&amp;&amp; info = Table&lt;Type&gt;::get(hdr.bits.rpcid);\n\n#if CZRPC_CATCH_EXCEPTIONS\n        try {\n#endif\n            out &lt;&lt; hdr;  \/\/ Reserve space for the header\n            info-&gt;dispatcher(m_obj, in, out);\n#if CZRPC_CATCH_EXCEPTIONS\n        } catch (std::exception&amp; e) {\n            out.clear();\n            out &lt;&lt; hdr;  \/\/ Reserve space for the header\n            hdr.bits.success = false;\n            out &lt;&lt; e.what();\n        }\n#endif\n\n        if (m_voidReplies || (out.writeSize() &gt; sizeof(hdr))) {\n            hdr.bits.size = out.writeSize();\n            *reinterpret_cast&lt;Header*&gt;(out.ptr(0)) = hdr;\n            transport.send(out.extract());\n        }\n    }\n\nprotected:\n    Type&amp; m_obj;\n    bool m_voidReplies = false;\n};\n\ntemplate &lt;&gt;\nclass InProcessor&lt;void&gt; {\npublic:\n    InProcessor(void*) {}\n    void processCall(Transport&amp;, Stream&amp;, Header) {\n        assert(0 &amp;&amp; &quot;Incoming RPC not allowed for void local type&quot;);\n    }\n};\n\n}  \/\/ namespace rpc\n}  \/\/ namespace cz\n<\/pre>\n<p>The <code>CZRPC_CATCH_EXCEPTIONS<\/code> define allows us to tweak if we want server side exceptions to be passed to the clients.<\/p>\n<p>It&#8217;s the use of <code>InProcessor&lt;T&gt;<\/code> (and <code>Table&lt;T&gt;<\/code>) that allows calling RPCs on objects that don&#8217;t know anything about RPCs or network. For example, consider this dummy example:<\/p>\n<pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\nvoid calculatorServer() {\n    \/\/ The object we want to use for RPC calls\n    Calculator calc;\n    \/\/ The server processor. It will call the appropriate methods on &#039;calc&#039; when\n    \/\/ an RPC is received\n    InProcessor&lt;Calculator&gt; serverProcessor(&amp;calc);\n    while (true) {\n        \/\/ calls to serverProcessor::processCall whenever there is data\n    }\n}\n<\/pre>\n<p>The <code>Calculator<\/code> object used for RPCs doesn&#8217;t know anything about RPCs. The <code>InProcessor&lt;Calculator&gt;<\/code> does all the required work. This makes it possible use third party classes for RPCs.<br \/>\nIn some situations, we do want have the class used for RPCs to know about RPCs and\/or network. For example, if you&#8217;re creating a chat system, you have the clients sending messages (RPC calls) to the server. The server needs to know what clients are connected, so it can broadcast messages.<\/p>\n<p><a name=\"id-connection\"><\/a><\/p>\n<h3>Connection<\/h3>\n<p>We can now send and receive RPCs, although with a bit of a verbose API. The <code>OutProcessor&lt;T&gt;<\/code> and <code>InProcessor&lt;T&gt;<\/code> template classes deal with what happens to data at both ends of the connection.<br \/>\nSo, what we need now is exactly that. A <code>Connection<\/code> to tie in one place everything needed to send and receive data, and simplify the API.<\/p>\n<pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\nnamespace cz {\nnamespace rpc {\n\nstruct BaseConnection {\n    virtual ~BaseConnection() {}\n\n    \/\/! Process any incoming RPCs or replies\n    \/\/ Return true if the connection is still alive, false otherwise\n    virtual bool process() = 0;\n};\n\ntemplate &lt;typename LOCAL, typename REMOTE&gt;\nstruct Connection : public BaseConnection {\n    using Local = LOCAL;\n    using Remote = REMOTE;\n    using ThisType = Connection&lt;Local, Remote&gt;;\n    Connection(Local* localObj, std::shared_ptr&lt;Transport&gt; transport)\n        : localPrc(localObj), transport(std::move(transport)) {}\n\n    template &lt;typename F, typename... Args&gt;\n    auto call(Transport&amp; transport, uint32_t rpcid, Args&amp;&amp;... args) {\n        return remotePrc.template call&lt;F&gt;(transport, rpcid,\n                                          std::forward&lt;Args&gt;(args)...);\n    }\n\n    static ThisType* getCurrent() {\n        auto it = Callstack&lt;ThisType&gt;::begin();\n        return (*it) == nullptr ? nullptr : (*it)-&gt;getKey();\n    }\n\n    virtual bool process() override {\n        \/\/ Place a callstack marker, so other code can detect we are serving an\n        \/\/ RPC\n        typename Callstack&lt;ThisType&gt;::Context ctx(this);\n        std::vector&lt;char&gt; data;\n        while (true) {\n            if (!transport-&gt;receive(data)) {\n                \/\/ Transport is closed\n                remotePrc.abortReplies();\n                return false;\n            }\n\n            if (data.size() == 0)\n                return true;  \/\/ No more pending data to process\n\n            Header hdr;\n            Stream in(std::move(data));\n            in &gt;&gt; hdr;\n\n            if (hdr.bits.isReply) {\n                remotePrc.processReply(in, hdr);\n            } else {\n                localPrc.processCall(*transport, in, hdr);\n            }\n        }\n    }\n\n    InProcessor&lt;Local&gt; localPrc;\n    OutProcessor&lt;Remote&gt; remotePrc;\n    std::shared_ptr&lt;Transport&gt; transport;\n};\n\n}  \/\/ namespace rpc\n}  \/\/ namespace cz\n<\/pre>\n<p>This puts together the output processor, the input processor, and the transport.<br \/>\nTo make it possible for user code to detect if it&#039;s in the middle of serving an RPC, it uses a class I introduced in an earlier post. The <a href=\"http:\/\/www.crazygaze.com\/blog\/2016\/03\/11\/callstack-markers-boostasiodetailcall_stack\/\">Callstack<\/a> class.<br \/>\nThis allows the creation of RPC\/network aware code if necessary, like server classes.<\/p>\n<p>So, how this simplifies the API ?<br \/>\nSince the <code>Connection&lt;T&gt;<\/code> has everything we need, one macro taking as parameters the connection object, a function name and the parameters, does everything, including type checks so it doesn&#8217;t compile if it&#8217;s an invalid call.<\/p>\n<pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\n#define CZRPC_CALL(con, func, ...)                                        \\\n    (con).call&lt;decltype(&amp;std::decay&lt;decltype(con)&gt;::type::Remote::func)&gt;( \\\n        *(con).transport,                                                 \\\n        (uint32_t)cz::rpc::Table&lt;                                         \\\n            std::decay&lt;decltype(con)&gt;::type::Remote&gt;::RPCId::func,        \\\n        ##__VA_ARGS__)\n<\/pre>\n<p>Using this macro, RPC calls syntax is surprisingly simple. For example, consider this client code:<\/p>\n<pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\n\/\/ Some class to use for RPC calls\nclass MagicSauce {\npublic:\n    int func1(int a, int b) {\n        return a + b;\n    }\n\n    int func2(int a, int b) {\n        return a + b;\n    }\n};\n\n\/\/ Define RPC table for MagicSauce\n#define RPCTABLE_CLASS MagicSauce\n#define RPCTABLE_CONTENTS REGISTERRPC(func1)\n#include &quot;RPCGenerate.h&quot;\n\n\/\/ &#039;trp&#039; is a fully functional transport\nvoid test_Connection(std::shared_ptr&lt;Transport&gt; trp) {\n    Connection&lt;void, MagicSauce&gt; con(nullptr, trp);\n\n    \/\/ Doesn&#039;t compile : Invalid number of parameters\n    \/\/ CZRPC_CALL(con, func1, 1);\n\n    \/\/ Doesn&#039;t compile : Wrong type of parameters\n    \/\/ CZRPC_CALL(con, func1, 1, &quot;hello&quot;);\n\n    \/\/ Doesn&#039;t compile: func3 is not a MagicSauce method\n    \/\/ CZRPC_CALL(con, func3, 1, 2);\n\n    \/\/ Doesn&#039;t compile: func2 is a method of MagicSauce, but not registered as\n    \/\/ RPC\n    \/\/ CZRPC_CALL(con, func2, 1, 2);\n\n    \/\/ Compiles fine, since everything is valid\n    CZRPC_CALL(con, func1, 1, 2).async([](Result&lt;int&gt; res) {\n            printf(&quot;%d\\n&quot;, res.get());  \/\/ print &#039;3&#039;\n        });\n}\n<\/pre>\n<p>Notice the <code>void<\/code> and <code>nullptr<\/code> used when creating the connection with <code>Connection&lt;void, MagicSauce&gt; con(nullptr, trp);<\/code> ?<br \/>\nThis accommodates for bidirectional RPCs (the server can also call RPCs on a client). In this case, we don&#8217;t expect client side RPCs, so the client side <code>Connection<\/code> object doesn&#8217;t have a local object to call RPCs on.<\/p>\n<p>A simplified example (not functional) of bidirectional RPCs can be something like this:<\/p>\n<pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\nclass ChatClient;\n\nclass ChatServer {\npublic:\n    \/\/ Called by clients to post new messages\n    void msg(const char* msg);\n    void addNewClient(std::shared_ptr&lt;Transport&gt; trp);\n\nprivate:\n    \/\/ Connection specifies both a LOCAL, and REMOTE object types\n    std::vector&lt;std::unique_ptr&lt;Connection&lt;ChatServer, ChatClient&gt;&gt;&gt; m_clients;\n};\n\n#define RPCTABLE_CLASS ChatServer\n#define RPCTABLE_CONTENTS REGISTERRPC(msg)\n#include &quot;RPCGenerate.h&quot;\n\nclass ChatClient {\npublic:\n    void onMsg(const char* msg);\n};\n\n#define RPCTABLE_CLASS ChatClient\n#define RPCTABLE_CONTENTS REGISTERRPC(onMsg)\n#include &quot;RPCGenerate.h&quot;\n\nvoid ChatServer::msg(const char* msg) {\n    \/\/ Simply broadcast the message to all clients\n    for (auto&amp;&amp; c : m_clients) {\n        CZRPC_CALL(*c, onMsg, msg);\n    }\n}\nvoid ChatServer::addNewClient(std::shared_ptr&lt;Transport&gt; trp) {\n    auto con = std::make_unique&lt;Connection&lt;ChatServer, ChatClient&gt;&gt;(this, trp);\n    m_clients.push_back(std::move(con));\n}\n\nvoid ChatClient::onMsg(const char* msg) {\n    printf(&quot;%s\\n&quot;, msg);\n}\n\nvoid test_ChatServer() {\n    ChatServer server;\n    while (true) {\n        \/\/ Wait for connections, and call ChatServer::addClient\n    }\n}\n\n\/\/ &#039;trp&#039; is some fully functional transport connected to the ChatServer\nvoid test_ChatClient(std::shared_ptr&lt;Transport&gt; trp) {\n    ChatClient client;\n    \/\/ In this case, we have a client side object to answer RPCs\n    Connection&lt;ChatClient, ChatServer&gt; con(&amp;client, trp);\n    while (true) {\n        \/\/ call the &#039;msg&#039; RPC whenever the user types something, like this:\n        CZRPC_CALL(con, msg, &quot;some message&quot;);\n\n        \/\/ The server will call our client &#039;onMsg&#039; when other clients send a\n        \/\/ message\n    }\n}\n<\/pre>\n<p>The <code>Connection<\/code> template parameters in the server and client are reversed. Whenever data is received, the <code>Connection<\/code> object forwards processing to the <code>InProcessor<\/code> if it&#8217;s an incoming RPC call (to call on our side), or to the <code>OutProcessor<\/code> if it&#8217;s a reply to a previous outgoing RPC call. Data flow for bidirectional RPCs looks like this:<\/p>\n<p><img alt='' class='alignnone size-full wp-image-653 ' src='https:\/\/www.crazygaze.com\/blog\/wp-content\/uploads\/2016\/06\/img_57561269f2a4b.png' \/><\/p>\n<p><a name=\"id-improvements\"><\/a><\/p>\n<h2>Improvements<\/h2>\n<p>A couple of things were left out of the framework intentionally, so the application can decide what&#8217;s best. For example:<\/p>\n<ul>\n<li>Transport initialization\n<ul>\n<li>The transport interface is very simple, so it doesn&#8217;t impose any specific way of initialization  or detecting incoming data. It&#8217;s up to the application to provide a fully functional and connected transport to the <code>Connection<\/code> class. This is also the reason why I avoided showing any transport initialization, since I would have to present a fully functional transport implementation for that.<\/li>\n<li>At the time of writing, the source code repository has one transport implementation using Boost Asio (or standalone Asio)<\/li>\n<\/ul>\n<\/li>\n<li>Disconnection detection\n<ul>\n<li>As with initialization, there is minimal code for dealing with or detecting shutdowns. It is up to the application to decide how to do this with whatever custom transports it provides.<\/li>\n<\/ul>\n<\/li>\n<li>At some point I had support for server side functions that return std::futures. But as I was writing this article I removed that to simplify things. That will be added back to the repository soon, since I need it for my own projects.\n<ul>\n<li>This is needed to support wrapping classes whose API returns std::future instances. Say you have a <code>Login<\/code> server class with a <code>login<\/code> method. That <code>login<\/code> method is in itself probably asynchronous (returns a std::future) since you&#8217;ll be checking a database for the login details.<\/li>\n<li>It&#8217;s not particularly hard to implement, but it has one niggle I couldn&#8217;t get rid of without introducing a lot more code. The bulk of the changes would be in the <code>InProcessor&lt;T&gt;<\/code> class. It would have to keep track of ongoing calls whose <code>std::future&lt;T&gt;<\/code> instances are not ready yet. The problem here is how would <code>InProcessor&lt;T&gt;<\/code> detect when those futures are ready so it can send the result back to the client? I can think of two approaches:\n<ul>\n<li>Regular polling all pending std::futures to see if they are ready.<\/li>\n<li>Use continuations (<code>std::future::then<\/code>), which are not available in the standard yet. That would be the easiest solution. <code>InProcessor&lt;T&gt;<\/code> would set a continuation to send the result back to the client whenever it is ready. No need for polling anything.<\/li>\n<\/ul>\n<\/li>\n<li>This would affect no client side code whatsoever. Say a server side function returns <code>std::future&lt;bool&gt;<\/code> . All the client sees and needs is still just a <code>Result&lt;bool&gt;<\/code>.<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<p><a name=\"id-final-words\"><\/a><\/p>\n<h2>Final words<\/h2>\n<p>Feel free to nitpick at the code. This amount of code certainly needs a couple of fresh pair of eyes (and brain). Although any major changes will only go into the source code repository, and not this article.<\/p>\n<p>After a short poll on Twitter <a href=\"https:\/\/twitter.com\/RuiMVFigueira\">https:\/\/twitter.com\/RuiMVFigueira<\/a>, it seems the next article will be about cache friendly ticking.<br \/>\nI&#8217;ll be explaining and sharing some self-contained code I use in my project to tick a couple of things (in-game computers included).<br \/>\nI will have to measure performance, but hopefully it should be more cache friendly than the classic ticking. I&#8217;ll share the results either way.<br \/>\nStill, the code is useful as-is even if it turns out it&#8217;s not improving performance that much.<\/p>\n<p>Subscribe to the newsletter to get a notification when a new technical article is up, and\/or follow me on twitter <a href=\"https:\/\/twitter.com\/RuiMVFigueira\">https:\/\/twitter.com\/RuiMVFigueira<\/a> .<\/p>\n<p><a name=\"id-license\"><\/a><\/p>\n<h2>License<\/h2>\n<p>The license for the code in this article can be found at <a href=\"https:\/\/bitbucket.org\/ruifig\/czrpc\">https:\/\/bitbucket.org\/ruifig\/czrpc<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Table of Contents Introduction Why I needed this RPC parameters Parameter Traits Serialization Deserialization From tuple to function parameters The RPC API Header Table Transport Result OutProcessor InProcessor Connection Improvements Final words License Introduction This article explores a C++ RPC framework I&#8217;ve been working on which requires no code generation step for glue code. Before [&hellip;]<\/p>\n","protected":false},"author":3,"featured_media":652,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_mi_skip_tracking":false,"spay_email":"","jetpack_publicize_message":"","jetpack_is_tweetstorm":false,"jetpack_publicize_feature_enabled":true},"categories":[50,3],"tags":[14,15],"jetpack_featured_media_url":"https:\/\/www.crazygaze.com\/blog\/wp-content\/uploads\/2016\/06\/img_57561179204f5.png","jetpack_publicize_connections":[],"jetpack_sharing_enabled":true,"jetpack_shortlink":"https:\/\/wp.me\/p7jpe0-8t","_links":{"self":[{"href":"https:\/\/www.crazygaze.com\/blog\/wp-json\/wp\/v2\/posts\/525"}],"collection":[{"href":"https:\/\/www.crazygaze.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.crazygaze.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.crazygaze.com\/blog\/wp-json\/wp\/v2\/users\/3"}],"replies":[{"embeddable":true,"href":"https:\/\/www.crazygaze.com\/blog\/wp-json\/wp\/v2\/comments?post=525"}],"version-history":[{"count":7,"href":"https:\/\/www.crazygaze.com\/blog\/wp-json\/wp\/v2\/posts\/525\/revisions"}],"predecessor-version":[{"id":824,"href":"https:\/\/www.crazygaze.com\/blog\/wp-json\/wp\/v2\/posts\/525\/revisions\/824"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.crazygaze.com\/blog\/wp-json\/wp\/v2\/media\/652"}],"wp:attachment":[{"href":"https:\/\/www.crazygaze.com\/blog\/wp-json\/wp\/v2\/media?parent=525"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.crazygaze.com\/blog\/wp-json\/wp\/v2\/categories?post=525"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.crazygaze.com\/blog\/wp-json\/wp\/v2\/tags?post=525"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}