-
Notifications
You must be signed in to change notification settings - Fork 0
/
dstc.hpp
162 lines (140 loc) · 5.67 KB
/
dstc.hpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
// Copyright (C) 2019, Jaguar Land Rover
// This program is licensed under the terms and conditions of the
// Mozilla Public License, version 2.0. The full text of the
// Mozilla Public License is at https://www.mozilla.org/MPL/2.0/
//
// Author:Steven Martin ([email protected])
#pragma once
extern "C" {
#include "dstc.h"
}
#include <type_traits>
#include <chrono>
#include <thread>
#include "dstccallback.hpp"
#include "dstceventloop.hpp"
namespace dstc {
extern CallbackHandler _callback_handler;;
namespace utils {
template<typename HEAD>
uint32_t getArgSize(HEAD head)
{
if constexpr (std::is_same<dstc_dynamic_data_t, HEAD>::value) {
return sizeof(uint16_t) + head.length;
}
else {
return sizeof(HEAD);
}
}
template<typename HEAD, typename ... TAIL>
uint32_t _getArgsSize(HEAD head, TAIL ... args) {
if constexpr (sizeof...(TAIL) > 0) {
return getArgSize<HEAD>(head) + _getArgsSize<TAIL...>(args...);
}
else {
return getArgSize<HEAD>(head);
}
}
template<typename ... TYPES>
uint32_t getArgsSize(TYPES ... args) {
if constexpr (sizeof...(TYPES) > 0) {
return _getArgsSize<TYPES...>(args...);
}
else {
return 0;
}
}
template<typename HEAD, typename ... TAIL>
void copyArgs(uint8_t* ptr, HEAD head, TAIL ... args) {
if constexpr (std::is_same<dstc_dynamic_data_t, HEAD>::value) {
memcpy(ptr, (void*) &head.length, sizeof(uint16_t));
ptr += sizeof(uint16_t);
memcpy(ptr, head.data, head.length);
ptr += head.length;
}
else if constexpr (std::is_array<HEAD>::value) {
memcpy(ptr, (void*)head, sizeof(HEAD));
ptr += sizeof(HEAD);
}
else if constexpr (std::is_base_of<CallbackFunctionBase, HEAD>::value) {
auto callback_id = _callback_handler.registerCallback(head);
memcpy(ptr, (void*)&callback_id, sizeof(callback_id));
ptr += sizeof(callback_id);
}
else {
memcpy(ptr, (void*)&head, sizeof(HEAD));
ptr += sizeof(HEAD);
}
if constexpr (sizeof...(TAIL) > 0) {
return copyArgs<TAIL...>(ptr, args...);
}
}
}
/*!
* Objects of this class expose a function available on a remote server via DSTC
* /tparam DSTC_Name Name of function on remote server, matching required DSTC string
* /tparam Types Argument types of remote function
*/
template<const char* DSTC_Name, class ... Types>
class RemoteFunction {
public:
RemoteFunction() {
dstc_register_client_function(NULL, (char*) DSTC_Name, (void*)RemoteFunction::execute);
}
/*!
* Executes remote function
* \param args Arguments to function
* \returns return value of `dstc_queue_func`.
*/
int operator()(Types ... args) {
return execute(args...);
}
/*!
* Checks if server is available
* \returns true if a server has advertised the remote function, false otherwise
*/
bool serverAvailable() {
return dstc_remote_function_available_by_name((char*)DSTC_Name);
}
/*!
* Blocks until a remote function is availble. Requires a `EventLoopRunner` object. If doing a custom event loop that executes `dstc_process_events`, then use `serverAvailable()` in polling mode instead.
* \param runner EventLoopRunner object to ensure that `dstc_process_events` is being called in the background.
* \param timeout_ms How long, in ms, to wait for server to become available. Negative values mean this blocks forever
* \returns true if server function is available, false if timeout occurred before function became available
*/
bool blockUntilServerAvailable(const EventLoopRunner& runner, int timeout_ms = -1) {
(void) runner; // cast to avoid not used warning--it does not get used in this function
// but the callee must prove they have created the object so that
// this function can assume dstc_process_events is being called in the background
if (timeout_ms < 0) {
while(!serverAvailable()) {} // block forever if server never comes up
return true;
}
else {
auto timeout = std::chrono::system_clock::now() + std::chrono::milliseconds(timeout_ms);
while (true) {
if (serverAvailable()) {
return true;
}
if (std::chrono::system_clock::now() >= timeout) {
return false;
}
std::this_thread::sleep_for(std::chrono::milliseconds(1));
}
}
}
private:
static uint32_t getArgSize(Types ... args) {
return utils::getArgsSize<Types...>(args...);
}
static int execute(Types... args) {
auto arg_size = getArgSize(args...);
uint8_t arg_buf[arg_size];
uint8_t* ptr = arg_buf;
if constexpr (sizeof...(Types) > 0) {
utils::copyArgs<Types...>(ptr, args...);
}
return dstc_queue_func(nullptr, (char*)DSTC_Name, arg_buf, arg_size);
}
};
}