Thrift offers a convenient way to define structures and services. Structures are bundles of data, and services are bundles of methods. Structures fit nicely into streaming communication, while services are intended for RPC communication. But sometimes we want to offer a set of services around a particular structure. One recurring desire is a way to manipulate or read just part of a structure. This is convenient if that structure represents configuration data for a program.
With YARP, it is possible to generate an Editor service that can be used to manipulate or read parts of a corresponding thrift structure, by passing the yarp.editor = "true" annotation. For example, suppose we have the following structure in settings.thrift
struct Settings
{
1: i32 id;
2: string name;
3: list<i32> ints;
}
When compiled, that will give is a Settings class that can read or write from a port. That class will read the structure in full. For example, here's a test program that will read the structure from a port and show its value:
#include "Settings.h"
{
Settings settings;
while(true) {
printf("Settings %s\n", settings.toString().c_str());
}
return 0;
}
Utilities for manipulating the YARP network, including initialization and shutdown.
A mini-server for network communication.
void setReader(PortReader &reader) override
Set an external reader for port data.
bool setCallbackLock(std::mutex *mutex=nullptr) override
Add a lock to use when invoking callbacks.
bool lockCallback() override
Lock callbacks until unlockCallback() is called.
bool open(const std::string &name) override
Start port operation, with a specific name, with automatically-chosen network parameters.
void unlockCallback() override
Unlock callbacks.
int main(int argc, char *argv[])
void delay(double seconds)
Wait for a certain number of seconds.
The main, catch-all namespace for YARP.
(If you want to quickly compile this program, just put it in a file called <something>.cpp in an empty directory, then add settings.thrift, then run yarp cmake && cmake . && make && ./yarpy, specifying -DYARP_DIR=<path_to_yarp> to cmake if needed.)
Once that is running, from another terminal we can write to the settings:
echo "1 sam (1 2 3)" | yarp write /write /settings
And we see the resulting change of the entire structure:
yarp: Port /settings active at tcp://192.168.1.2:10002
Settings 0 "" ()
Settings 0 "" ()
Settings 1 sam (1 2 3)
...
Now, suppose we want to offer a way to change parts of the structure individually. We can make the following modification:
struct Settings
{
1: i32 id;
2: string name;
3: list<i32> ints;
} (
yarp.editor = "true"
)
{
Settings settings;
Settings::Editor editor(settings);
while(true) {
printf("Settings %s\n", settings.toString().c_str());
}
return 0;
}
Now, we can send a new kind of message to the port:
echo 'set name "sam"' | yarp write /write /settings
(It is now also possible to use yarp rpc /settings to send commands and get status feedback). Now we can see that just the part of the structure we care about will change:
yarp: Port /settings active at tcp://192.168.1.2:10002
Settings 0 "" ()
Settings 0 "" ()
Settings 0 sam ()
...
The editor also lets us capture events generated just before and just after a field is changed. For example, if we want to do something just before or just after the name field is changed, we could do this:
class MySettings : public Settings::Editor
{
public:
bool will_set_name() override
{
printf("About to set the name, it is currently '%s'\n", state().name.c_str());
return true;
}
bool did_set_name() override
{
printf("Just set the name, it is now '%s'\n", state().name.c_str());
return true;
}
};
{
Settings settings;
MySettings editor;
editor.edit(settings);
}
Now if we send a message to change the name field, we'll see:
yarp: Port /settings active at tcp://192.168.1.2:10002
Settings 0 "" ()
Settings 0 "" ()
About to set the name, it is currently ''
Just set the name, it is now 'sam'
Settings 0 sam ()
...