-
Notifications
You must be signed in to change notification settings - Fork 0
/
vrpn_BaseClass.h
487 lines (413 loc) · 19.4 KB
/
vrpn_BaseClass.h
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
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
/** @file vrpn_BaseClass.h
All types of client/server/peer objects in VRPN should be derived from the
vrpn_BaseClass type described here. This includes Tracker, Button, Analog,
Clock, Dial, ForceDevice, Sound, and Text; it should include any user-defined
objects as well.
This class both implements code that will be shared by most (if not all)
objects in the system and forms a skeleton for the definition of new objects
by requiring certain virtual member functions to be defined.
See the VRPN web pages or another simple type (such as vrpn_Analog) for an
example of how to create a new VRPN object type using this as a base class.
*/
#ifndef VRPN_BASECLASS
#define VRPN_BASECLASS
#include <stdio.h> // for NULL, fprintf, stderr, FILE
#include "vrpn_Configure.h" // for VRPN_API, VRPN_CALLBACK
#include "vrpn_Connection.h"
#include "vrpn_Shared.h" // for timeval, vrpn_gettimeofday
#include "vrpn_Types.h" // for vrpn_int32, vrpn_uint32
/*
-----------------------------------------------------------------------------
Answer to the question:
"Why is there both a UNIQUE and NON-UNIQUE base class?",
or
"Why can't everything from vrpn_BaseClass be moved into
vrpn_BaseClassUnique?"
The first reason is that removing vrpn_BaseClass would require the
vrpn_BaseClassUnique constructor to take a name and connection object as
parameters, which would cause some problems due to the way virtual base
classes are implemented in C++.
Any class that inherits from a virtual base (either directly or several
generations removed) must provide an explicit call to the constructor
of the virtual base. This is done because the virtual base constructor
is invoked from the very first class in the constructor chain.
Take for example vrpn_Tng3, which inherits vrpn_Button and vrpn_Serial_Analog
(and thus vrpn_Analog). Creating a new instance of a vrpn_Tng3 object will
call the constructors in this order:
Tng3
BaseClassUnique (because it is a virtual base)
Button
BaseClass (coming from Button)
Serial_Analog
Analog
BaseClass (coming from Analog)
Right now, BaseClassUnique's constructor has no parameters. So the
Tng3 constructor does not have to explicitly invoke BaseClassUnique, although
implicitly it will call BaseClassUnique's 0-parameter constructor before
doing anything else. But if BaseClass is eliminated, then BaseClassUnique's
constructor must do the work of creating the connection and copying the
service name. So BaseClassUnique's constructor must now take a couple
parameters, which means that every class (including Tng3, Button, Analog, and
Serial_Analog) would have to explicitly name the constructor for BaseClassUnique
in the code and specify parameters for connection and service-name, even though
only one such call to the BaseClassUnique's constructor would ever actually
occur at runtime (that of Tng3 since it's located at the lowest level of the
family tree; the rest of the calls would be ignored). This would mean inserting
"vrpn_BaseClassUnique(name,connection)" into the initializer section of every
constructor in *every* class under the BaseClassUnique subtree.
The second reason we have both a unique and non-unique base class is that
the "register_types" virtual function must be called several times for
multiply-inherited devices, with a different virtual target in each case.
Presently, register_types() is called from vrpn_BaseClass::init().
init() may be called multiple times using a different vftable entry for
register_types() each time (e.g. for the Tng3 it will refer once to
vrpn_Analog::register_types() and once to vrpn_Button::register_types()).
Both init() and the pure-virtual declaration of register_types() are found
in BaseClass. Moving init() up into BaseClassUnique instead of BaseClass
means that register_types() would have to move up as well. And if
register_types() is declared in the virtual base class, BaseClassUnique,
it can only have one virtual target.
So it might appear that vrpn_BaseClass has no data members and would
therefore be easy to eliminate. However it actually does have a data
member: the vftable entry for "register_types". And this data member
*must* be duplicated in the case of multiply-inherited device because a
single object will need several distinct virtual targets for
"register_types".
[Jeff Feasel 19 May 2005]
-----------------------------------------------------------------------------
*/
const int vrpn_MAX_BCADRS = 100;
///< Internal value for number of BaseClass addresses
/// Since the sending of text messages has been pulled into the base class (so
/// that every object can send error/warning/info messages this way), these
/// definitions have been pulled in here as well.
typedef enum {
vrpn_TEXT_NORMAL = 0,
vrpn_TEXT_WARNING = 1,
vrpn_TEXT_ERROR = 2
} vrpn_TEXT_SEVERITY;
const unsigned vrpn_MAX_TEXT_LEN = 1024;
class VRPN_API vrpn_BaseClass;
/// Class that handles text/warning/error printing for all objects in the
/// system.
// It is a system class, with one instance of it in existence. Each object in
// the system registers with this class when it is constructed. By default,
// this class prints all Warning and Error messages to stdout, prefaced by
// "vrpn Warning(0) from MUMBLE: ", where the 0 indicates the level of the
// message and Warning the severity, and MUMBLE the name of the object that sent
// the message. The user could create their own TextPrinter, and attach whatever
// objects they want to it.
// NOTE: Because there is a vrpn_System_TextPrinter that all vrpn_BaseClass
// objects talk to, and because those objects may be in multiple threads, the
// vrpn_TextPrinter class has to be thread-safe. This requires all user-
// callable methods to be thread-safe because the destructor may be called
// during a method call.
class VRPN_API vrpn_TextPrinter {
public:
vrpn_TextPrinter();
~vrpn_TextPrinter();
/// Adds an object to the list of watched objects (multiple registration
/// of the same object will result in only one printing for each message
/// from the object). Returns 0 on success and -1 on failure.
/// YOU MUST REMOVE any objects from a vrpn_TextPrinter that you create
/// before destroying the printer if any connection objects survive,
/// otherwise they may call a callback function on the destroyed object.
int add_object(vrpn_BaseClass *o);
/// Remove an object from the list of watched objects (multiple deletions
/// of the object will not cause any error condition; deletions of
/// unregistered objects will not cause errors).
void remove_object(vrpn_BaseClass *o);
/// Change the level of printing for the object (sets the minimum level to
/// print). Default is Warnings and Errors of all levels.
void set_min_level_to_print(vrpn_TEXT_SEVERITY severity,
vrpn_uint32 level = 0);
/// Change the ostream that will be used to print messages. Setting a
/// NULL ostream results in no printing.
void set_ostream_to_use(FILE *o);
protected:
/// Mutex to ensure thread safety;
vrpn_Semaphore d_semaphore;
/// Structure to hold the objects that are being watched.
class VRPN_API vrpn_TextPrinter_Watch_Entry {
public:
vrpn_BaseClass *obj; ///< Object being watched
vrpn_TextPrinter *me;
///< Pointer to this, because used in a static function
vrpn_TextPrinter_Watch_Entry *next;
///< Pointer to the next one in the list
};
vrpn_TextPrinter_Watch_Entry *d_first_watched_object;
///< Head of list of objects being watched
FILE *d_ostream; ///< Output stream to use
vrpn_TEXT_SEVERITY d_severity_to_print; ///< Minimum severity to print
vrpn_uint32 d_level_to_print; ///< Minimum level to print
/// Handles the text messages that come from the connections for
/// objects we are watching.
static int VRPN_CALLBACK
text_message_handler(void *userdata, vrpn_HANDLERPARAM p);
};
// SWIG does not like this declaration.
#ifndef SWIG
extern VRPN_API vrpn_TextPrinter &vrpn_System_TextPrinter;
#endif
/// INTERNAL class to hold members that there should only be one copy of
/// even when a class inherits from multiple vrpn_BaseClasses because it
/// inherits from multiple user-level classes. Note that not everything in
/// vrpnBaseClass should be here, because (for example) the registration of
/// types should be done for each parent class.
class VRPN_API vrpn_BaseClassUnique {
friend class VRPN_API vrpn_TextPrinter;
public:
vrpn_BaseClassUnique();
virtual ~vrpn_BaseClassUnique();
/// Returns a pointer to the connection this object is using
vrpn_Connection *connectionPtr() { return d_connection; };
bool shutup; // if True, don't print the "No response from server" messages.
friend class SendTextMessageBoundCall;
class SendTextMessageBoundCall {
private:
vrpn_BaseClassUnique *_p;
vrpn_TEXT_SEVERITY _severity;
public:
SendTextMessageBoundCall(vrpn_BaseClassUnique *device,
vrpn_TEXT_SEVERITY type)
: _p(device)
, _severity(type)
{
}
SendTextMessageBoundCall(SendTextMessageBoundCall const &other)
: _p(other._p)
, _severity(other._severity)
{
}
int operator()(const char *msg) const
{
struct timeval timestamp;
vrpn_gettimeofday(×tamp, NULL);
return _p->send_text_message(msg, timestamp, _severity);
}
};
protected:
vrpn_Connection *d_connection; ///< Connection that this object talks to
char *d_servicename; ///< Name of this device, not including the connection
/// part
vrpn_int32 d_sender_id; ///< Sender ID registered with the connection
vrpn_int32 d_text_message_id; ///< ID for text messages
vrpn_int32 d_ping_message_id; ///< Ask the server if they are there
vrpn_int32 d_pong_message_id; ///< Server telling that it is there
/// Registers a handler with the connection, and remembers to delete at
/// destruction.
// This is a wrapper for the vrpn_Connection call that registers
// message handlers. It should be used rather than the connection's
// function because this one will remember to unregister all of its handlers
// at object deletion time.
int register_autodeleted_handler(vrpn_int32 type,
vrpn_MESSAGEHANDLER handler,
void *userdata,
vrpn_int32 sender = vrpn_ANY_SENDER);
/// Encodes the body of the text message into a buffer, preparing for
/// sending
static int encode_text_message_to_buffer(char *buf,
vrpn_TEXT_SEVERITY severity,
vrpn_uint32 level,
const char *msg);
/// Decodes the body of the text message from a buffer from the connection
static int decode_text_message_from_buffer(char *msg,
vrpn_TEXT_SEVERITY *severity,
vrpn_uint32 *level,
const char *buf);
/// Sends a NULL-terminated text message from the device d_sender_id
int send_text_message(const char *msg, struct timeval timestamp,
vrpn_TEXT_SEVERITY type = vrpn_TEXT_NORMAL,
vrpn_uint32 level = 0);
/// Returns an object you can stream into to send a text message from the
/// device
/// like send_text_message(vrpn_TEXT_WARNING) << "Value of i is: " << i;
/// This use requires including vrpn_SendTextMessageStreamProxy.h
SendTextMessageBoundCall
send_text_message(vrpn_TEXT_SEVERITY type = vrpn_TEXT_NORMAL)
{
return SendTextMessageBoundCall(this, type);
}
/// Handles functions that all servers should provide in their mainloop()
/// (ping/pong, for example)
/// Should be called by all servers in their mainloop()
void server_mainloop(void);
/// Handles functions that all clients should provide in their mainloop()
/// (warning of no server, for example)
/// Should be called by all clients in their mainloop()
void client_mainloop(void);
private:
struct {
vrpn_MESSAGEHANDLER handler;
vrpn_int32 sender;
vrpn_int32 type;
void *userdata;
} d_handler_autodeletion_record[vrpn_MAX_BCADRS];
int d_num_autodeletions;
int d_first_mainloop; ///< First time client_mainloop() or server_mainloop()
/// called?
struct timeval d_time_first_ping; ///< When was the first ping of this
/// unanswered group sent?
struct timeval
d_time_last_warned; ///< When is the last time we sent a warning?
int d_unanswered_ping; ///< Do we have an outstanding ping request?
int d_flatline; ///< Has it been 10+ seconds without a response?
/// Used by client/server code to request/send "server is alive" (pong)
/// message
static int VRPN_CALLBACK handle_ping(void *userdata, vrpn_HANDLERPARAM p);
static int VRPN_CALLBACK handle_pong(void *userdata, vrpn_HANDLERPARAM p);
static int VRPN_CALLBACK
handle_connection_dropped(void *userdata, vrpn_HANDLERPARAM p);
void initiate_ping_cycle(void);
};
//---------------------------------------------------------------
/// Class from which all user-level (and other) classes that communicate
/// with vrpn_Connections should derive.
class VRPN_API vrpn_BaseClass : virtual public vrpn_BaseClassUnique {
public:
/// Names the device and assigns or opens connection,
/// calls registration methods
vrpn_BaseClass(const char *name, vrpn_Connection *c = NULL);
virtual ~vrpn_BaseClass();
/// Called once through each main loop iteration to handle updates.
/// Remote object mainloop() should call client_mainloop() and
/// then call d_connection->mainloop().
/// Server object mainloop() should service the device and then
/// call server_mainloop(), but should not normally call
/// d_connection->mainloop().
virtual void mainloop() = 0;
protected:
/// Initialize things that the constructor can't. Returns 0 on
/// success, -1 on failure.
virtual int init(void);
/// Register the sender for this device (by default, the name of the
/// device). Return 0 on success, -1 on fail.
virtual int register_senders(void);
/// Register the types of messages this device sends/receives.
/// Return 0 on success, -1 on fail.
virtual int register_types(void) = 0;
};
//---------------------------------------------------------------
// Within VRPN (and other libraries), it is wise to avoid using the
// Standard Template Library. This is very annoying, but required
// by the fact that some systems have incompatible versions of STL.
// This caused problems with any program that uses the GHOST library
// (which had its own STL on Windows), and I've heard tell of problems
// with other systems as well. On the other hand, nothing says that
// we can't have our OWN template types and use them. This next type
// is used to handle callback lists within objects. It is templated
// over the struct that is passed to the user callback.
// See vrpn_Button.h's usage for an example.
// Disables a warning that the class requires DLL linkage to be
// used by clients of classes that include one: The classes themselves
// have DLL linkage, the code below asks for (but apparently does not
// get) DLL linkage, and the DLL-linked test programs work when things
// are as they are. Do not use this class outside of a derived class.
#ifdef _MSC_VER
#pragma warning(disable : 4251)
#endif
template <class CALLBACK_STRUCT> class VRPN_API vrpn_Callback_List {
public:
typedef void(VRPN_CALLBACK *HANDLER_TYPE)(void *userdata,
const CALLBACK_STRUCT info);
/// This class requires deep copies.
void operator=(const vrpn_Callback_List &from)
{
// Delete any existing elements in the list.
CHANGELIST_ENTRY *current, *next;
current = d_change_list;
while (current != NULL) {
next = current->next;
delete current;
current = next;
}
// Copy all elements from the other list. XXX Side effect, this inverts
// the order
current = from.d_change_list;
while (current != NULL) {
register_handler(current->userdata, current->handler);
current = current->next;
}
}
/// Call this to add a handler to the list.
int register_handler(void *userdata, HANDLER_TYPE handler)
{
CHANGELIST_ENTRY *new_entry;
// Ensure that the handler is non-NULL
if (handler == NULL) {
fprintf(stderr,
"vrpn_Callback_List::register_handler(): NULL handler\n");
return -1;
}
// Allocate and initialize the new entry
if ((new_entry = new CHANGELIST_ENTRY) == NULL) {
fprintf(stderr,
"vrpn_Callback_List::register_handler(): Out of memory\n");
return -1;
}
new_entry->handler = handler;
new_entry->userdata = userdata;
// Add this handler to the chain at the beginning (don't check to see
// if it is already there, since duplication is okay).
new_entry->next = d_change_list;
d_change_list = new_entry;
return 0;
};
/// Call this to remove a handler from the list (if it exists)
int unregister_handler(void *userdata, HANDLER_TYPE handler)
{
// The pointer at *snitch points to victim
CHANGELIST_ENTRY *victim, **snitch;
// Find a handler with this registry in the list (any one will do,
// since all duplicates are the same).
snitch = &d_change_list;
victim = *snitch;
while ((victim != NULL) && ((victim->handler != handler) ||
(victim->userdata != userdata))) {
snitch = &((*snitch)->next);
victim = victim->next;
}
// Make sure we found one
if (victim == NULL) {
fprintf(
stderr,
"vrpn_Callback_List::unregister_handler: No such handler\n");
return -1;
}
// Remove the entry from the list
*snitch = victim->next;
delete victim;
return 0;
};
/// This will pass the referenced parameter as a const to all the callbacks.
void call_handlers(const CALLBACK_STRUCT &info)
{
CHANGELIST_ENTRY *handler = d_change_list;
while (handler != NULL) {
handler->handler(handler->userdata, info);
handler = handler->next;
}
};
/// The list starts out empty
vrpn_Callback_List()
: d_change_list(NULL){};
/// Clear the list upon destruction if it is not empty already
~vrpn_Callback_List()
{
while (d_change_list != NULL) {
CHANGELIST_ENTRY *next = d_change_list->next;
delete d_change_list;
d_change_list = next;
}
};
protected:
typedef struct vrpn_CBS {
void *userdata;
HANDLER_TYPE handler;
struct vrpn_CBS *next;
} CHANGELIST_ENTRY;
CHANGELIST_ENTRY *d_change_list;
};
// End of defined VRPN_BASECLASS for vrpn_BaseClass.h
#endif