Implementation of interface layer on top of DSTC with C++ bindings to eliminate need of macros/code generation that, while compatible with gcc
, is not compatible with g++
. This also provides tools and helper routines geared toward C++ developers who want to use DSTC.
Currently, only client-side interfaces are available, although there are plans to provide similar server functionality.
The primary difference is that DSTCPP does not use macros for any of the code generation, preferring instead to use templates and objects in their stead.
This means that accessing a server's function can be done by creating a dstc::RemoteFunction
object. Macros such as DSTC_DECL_DYNAMIC_ARG
are replaced by the underlying data structure (dstc_dynamic_data_t
). Callbacks have been re-implemented to allow callback routines to be declared on the fly using lambda expressions or be bound to existing global functions or member functions (std::function
). This effectively replaces the DSTC_DECL_CALLBACK_ARG
with a dstc::CallbackFunction
which wraps a std::function
object that gets invoked when the callback occurs. This has the added benefit of declaring the callback arguments when the original dstc::RemoteFunction
object gets created, as discussed later. (See more examples in test/client_tests.cpp
).
For this example, imagine that you have a server function that accepts a variable-length string, and then returns an unsigned int
that is the number of characters in the string. The DSTC server may have a macro generation that looks like this:
DSTC_SERVER(calc_strlen, DSTC_DECL_DYNAMIC_ARG, DSTC_DECL_CALLBACK_ARG)
To execute this function, first we must create a RemoteFunction object, using code like this:
const char calc_strlen_name[] = "calc_strlen";
dstc::RemoteFunction<calc_strlen_name, dstc_dynamic_data_t, dstc::CallbackFunction<int>> remoteCalcStrlen;
Note that calc_strlen_name
much match the string in the DSTC_SERVER
macro (because DSTC
uses strings for function identification.) It also must be const because the template generates code using this as a global const
. Please also note that instead of using the macro DSTC_DECL_DYNAMIC_ARG
, we simply specify that one of the arguments is a dstc_dynamic_data_t
, which makes the argument list more clear. Also, as discussed in a preceding section, the arguments of the callback routine are embedded into the object creation, a dst::CallbackFunction
. (In this example, we expect the callback function to have an int
as an argument).
Now, we can ensure the server has started up by using code like this:
dstc::EventLoopRunner runner;
remoteCalcStrlen.blockUntilServerAvailable(runner);
Please note that using an EventLoopRunner
is optional... the only thing this object does is ensure a thread is running that is periodically calling dstc_process_events
as long as the EventLoopRunner
is in scope. You can alternatively create your own loop and use dstc::RemoteFunction::serverAvailable() to check if the server is running.
Now, to execute the function, we simply call the RemoteFunction
with the appropriate parameters. (Note that since this function uses standard templates, modern IDEs such as VS Code will remind you of argument lists as you type):
char string[] = "string to calculate length of";
dstc_dynamic_data_t arg;
arg.length = strlen(string) + 1; // +1 for `\0`
arg.data = (void*) string;
remoteCalcStrlen(
arg,
dstc::CallbackFunction<int> (
[](int str_len) {
std::cout << "strlen: " << str_len << std::endl;
}
)
);
Please note that in this example we used a lambda expression for the callback. However, you can also construct a dstc::CallbackFunction object using any standard std::function that has the required argument list and has no return (void
).
Detailed examples of interfacing with a DSTC server with various types of parameters can be seen in test/client_tests.cpp
.
Build instructions are for Ubuntu 18.04, although these should be similar to other platforms.
Install Reliable Multicast
(RMC
) using the instructions found on its github page.
Install DSTC
using the instructions found on its github page.
sudo apt install -y cmake
sudo apt install -y googletest
pushd /usr/src/googletest
sudo cmake CMakeLists.txt
sudo make -j$(nproc)
sudo cp googlemock/*.a /usr/lib
sudo cp googlemock/gtest/*.a /usr/lib
popd
cmake .
make -j ${nproc}
If necessary for how you installed DSTC
/RMC
, set your LD_LIBRARY_PATH
so the appropriate .so
files can be found.
These tests rely on DSTC being shut down between some tests so that remote functions are no longer being advertised at different points of the test. Therefore, a wrapper function has been ran that executes each test within the run_tests
binary that gets generated above.
./run_tests_separate_processes.py