vrpn 07.36
Virtual Reality Peripheral Network
Loading...
Searching...
No Matches
vrpn_BaseClass.h
Go to the documentation of this file.
1
15
16#ifndef VRPN_BASECLASS
17#define VRPN_BASECLASS
18
19#include <stdio.h> // for NULL, fprintf, stderr, FILE
20
21#include "vrpn_Configure.h" // for VRPN_API, VRPN_CALLBACK
22#include "vrpn_Connection.h"
23#include "vrpn_Shared.h" // for timeval, vrpn_gettimeofday
24#include "vrpn_Types.h" // for vrpn_int32, vrpn_uint32
25
26/*
27-----------------------------------------------------------------------------
28Answer to the question:
29 "Why is there both a UNIQUE and NON-UNIQUE base class?",
30 or
31 "Why can't everything from vrpn_BaseClass be moved into
32vrpn_BaseClassUnique?"
33
34 The first reason is that removing vrpn_BaseClass would require the
35 vrpn_BaseClassUnique constructor to take a name and connection object as
36 parameters, which would cause some problems due to the way virtual base
37 classes are implemented in C++.
38
39 Any class that inherits from a virtual base (either directly or several
40 generations removed) must provide an explicit call to the constructor
41 of the virtual base. This is done because the virtual base constructor
42 is invoked from the very first class in the constructor chain.
43
44 Take for example vrpn_Tng3, which inherits vrpn_Button and vrpn_Serial_Analog
45 (and thus vrpn_Analog). Creating a new instance of a vrpn_Tng3 object will
46 call the constructors in this order:
47 Tng3
48 BaseClassUnique (because it is a virtual base)
49 Button
50 BaseClass (coming from Button)
51 Serial_Analog
52 Analog
53 BaseClass (coming from Analog)
54
55 Right now, BaseClassUnique's constructor has no parameters. So the
56 Tng3 constructor does not have to explicitly invoke BaseClassUnique, although
57 implicitly it will call BaseClassUnique's 0-parameter constructor before
58 doing anything else. But if BaseClass is eliminated, then BaseClassUnique's
59 constructor must do the work of creating the connection and copying the
60 service name. So BaseClassUnique's constructor must now take a couple
61 parameters, which means that every class (including Tng3, Button, Analog, and
62Serial_Analog) would have to explicitly name the constructor for BaseClassUnique
63in the code and specify parameters for connection and service-name, even though
64only one such call to the BaseClassUnique's constructor would ever actually
65occur at runtime (that of Tng3 since it's located at the lowest level of the
66family tree; the rest of the calls would be ignored). This would mean inserting
67"vrpn_BaseClassUnique(name,connection)" into the initializer section of every
68constructor in *every* class under the BaseClassUnique subtree.
69
70 The second reason we have both a unique and non-unique base class is that
71 the "register_types" virtual function must be called several times for
72 multiply-inherited devices, with a different virtual target in each case.
73 Presently, register_types() is called from vrpn_BaseClass::init().
74 init() may be called multiple times using a different vftable entry for
75 register_types() each time (e.g. for the Tng3 it will refer once to
76 vrpn_Analog::register_types() and once to vrpn_Button::register_types()).
77 Both init() and the pure-virtual declaration of register_types() are found
78 in BaseClass. Moving init() up into BaseClassUnique instead of BaseClass
79 means that register_types() would have to move up as well. And if
80 register_types() is declared in the virtual base class, BaseClassUnique,
81 it can only have one virtual target.
82
83 So it might appear that vrpn_BaseClass has no data members and would
84 therefore be easy to eliminate. However it actually does have a data
85 member: the vftable entry for "register_types". And this data member
86 *must* be duplicated in the case of multiply-inherited device because a
87 single object will need several distinct virtual targets for
88 "register_types".
89
90 [Jeff Feasel 19 May 2005]
91-----------------------------------------------------------------------------
92*/
93
94const int vrpn_MAX_BCADRS = 100;
96
105const unsigned vrpn_MAX_TEXT_LEN = 1024;
106
108
111// It is a system class, with one instance of it in existence. Each object in
112// the system registers with this class when it is constructed. By default,
113// this class prints all Warning and Error messages to stdout, prefaced by
114// "vrpn Warning(0) from MUMBLE: ", where the 0 indicates the level of the
115// message and Warning the severity, and MUMBLE the name of the object that sent
116// the message. The user could create their own TextPrinter, and attach whatever
117// objects they want to it.
118// NOTE: Because there is a vrpn_System_TextPrinter that all vrpn_BaseClass
119// objects talk to, and because those objects may be in multiple threads, the
120// vrpn_TextPrinter class has to be thread-safe. This requires all user-
121// callable methods to be thread-safe because the destructor may be called
122// during a method call.
123
176// SWIG does not like this declaration.
177#ifndef SWIG
179#endif
180
188
189public:
191 virtual ~vrpn_BaseClassUnique();
192
195
196 bool shutup; // if True, don't print the "No response from server" messages.
197
200 private:
202 vrpn_TEXT_SEVERITY _severity;
203
204 public:
207 : _p(device)
208 , _severity(type)
209 {
210 }
211
212 int operator()(const char *msg) const
213 {
214 struct timeval timestamp;
215 vrpn_gettimeofday(&timestamp, NULL);
216 return _p->send_text_message(msg, timestamp, _severity);
217 }
218 };
219
220protected:
224
225 vrpn_int32 d_sender_id;
226 vrpn_int32 d_text_message_id;
227 vrpn_int32 d_ping_message_id;
228 vrpn_int32 d_pong_message_id;
229
232 // This is a wrapper for the vrpn_Connection call that registers
233 // message handlers. It should be used rather than the connection's
234 // function because this one will remember to unregister all of its handlers
235 // at object deletion time.
236 int register_autodeleted_handler(vrpn_int32 type,
238 void *userdata,
239 vrpn_int32 sender = vrpn_ANY_SENDER);
240
243 static int encode_text_message_to_buffer(char *buf,
244 vrpn_TEXT_SEVERITY severity,
245 vrpn_uint32 level,
246 const char *msg);
247
249 static int decode_text_message_from_buffer(char *msg,
250 vrpn_TEXT_SEVERITY *severity,
251 vrpn_uint32 *level,
252 const char *buf);
253
255 int send_text_message(const char *msg, struct timeval timestamp,
257 vrpn_uint32 level = 0);
258
268
272 void server_mainloop(void);
273
277 void client_mainloop(void);
278
279private:
280 struct {
282 vrpn_int32 sender;
283 vrpn_int32 type;
284 void *userdata;
285 } d_handler_autodeletion_record[vrpn_MAX_BCADRS];
286 int d_num_autodeletions;
287
288 int d_first_mainloop;
290 struct timeval d_time_first_ping;
292 struct timeval
293 d_time_last_warned;
294 int d_unanswered_ping;
295 int d_flatline;
296
299 static int VRPN_CALLBACK handle_ping(void *userdata, vrpn_HANDLERPARAM p);
300 static int VRPN_CALLBACK handle_pong(void *userdata, vrpn_HANDLERPARAM p);
301 static int VRPN_CALLBACK
302 handle_connection_dropped(void *userdata, vrpn_HANDLERPARAM p);
303 void initiate_ping_cycle(void);
304};
305
306//---------------------------------------------------------------
309
311
312public:
315 vrpn_BaseClass(const char *name, vrpn_Connection *c = NULL);
316
317 virtual ~vrpn_BaseClass();
318
325 virtual void mainloop() = 0;
326
327protected:
330 virtual int init(void);
331
334 virtual int register_senders(void);
335
338 virtual int register_types(void) = 0;
339};
340
341//---------------------------------------------------------------
342// Within VRPN (and other libraries), it is wise to avoid using the
343// Standard Template Library. This is very annoying, but required
344// by the fact that some systems have incompatible versions of STL.
345// This caused problems with any program that uses the GHOST library
346// (which had its own STL on Windows), and I've heard tell of problems
347// with other systems as well. On the other hand, nothing says that
348// we can't have our OWN template types and use them. This next type
349// is used to handle callback lists within objects. It is templated
350// over the struct that is passed to the user callback.
351// See vrpn_Button.h's usage for an example.
352
353// Disables a warning that the class requires DLL linkage to be
354// used by clients of classes that include one: The classes themselves
355// have DLL linkage, the code below asks for (but apparently does not
356// get) DLL linkage, and the DLL-linked test programs work when things
357// are as they are. Do not use this class outside of a derived class.
358#ifdef _MSC_VER
359#pragma warning(disable : 4251)
360#endif
361template <class CALLBACK_STRUCT> class VRPN_API vrpn_Callback_List {
362public:
363 typedef void(VRPN_CALLBACK *HANDLER_TYPE)(void *userdata,
364 const CALLBACK_STRUCT info);
365
367 int register_handler(void *userdata, HANDLER_TYPE handler)
368 {
369 CHANGELIST_ENTRY *new_entry;
370
371 // Ensure that the handler is non-NULL
372 if (handler == NULL) {
373 fprintf(stderr,
374 "vrpn_Callback_List::register_handler(): NULL handler\n");
375 return -1;
376 }
377
378 // Allocate and initialize the new entry
379 try {
380 new_entry = new CHANGELIST_ENTRY;
381 } catch (...) {
382 fprintf(stderr,
383 "vrpn_Callback_List::register_handler(): Out of memory\n");
384 return -1;
385 }
386 new_entry->handler = handler;
387 new_entry->userdata = userdata;
388
389 // Add this handler to the chain at the beginning (don't check to see
390 // if it is already there, since duplication is okay).
391 new_entry->next = d_change_list;
392 d_change_list = new_entry;
393
394 return 0;
395 };
396
398 int unregister_handler(void *userdata, HANDLER_TYPE handler)
399 {
400 // The pointer at *snitch points to victim
401 CHANGELIST_ENTRY *victim, **snitch;
402
403 // Find a handler with this registry in the list (any one will do,
404 // since all duplicates are the same).
405 snitch = &d_change_list;
406 victim = *snitch;
407 while ((victim != NULL) && ((victim->handler != handler) ||
408 (victim->userdata != userdata))) {
409 snitch = &((*snitch)->next);
410 victim = victim->next;
411 }
412
413 // Make sure we found one
414 if (victim == NULL) {
415 fprintf(
416 stderr,
417 "vrpn_Callback_List::unregister_handler: No such handler\n");
418 return -1;
419 }
420
421 // Remove the entry from the list
422 *snitch = victim->next;
423 try {
424 delete victim;
425 } catch (...) {
426 fprintf(stderr, "vrpn_Callback_List::unregister_handler: delete failed\n");
427 return -1;
428 }
429
430 return 0;
431 };
432
434 void call_handlers(const CALLBACK_STRUCT &info)
435 {
437 while (handler != NULL) {
438 handler->handler(handler->userdata, info);
439 handler = handler->next;
440 }
441 };
442
446
449 : d_change_list(NULL)
450 {
451 // Copy all elements from the other list. XXX Side effect, this inverts
452 // the order
453 CHANGELIST_ENTRY *current, *next;
454 current = from.d_change_list;
455 while (current != NULL) {
456 register_handler(current->userdata, current->handler);
457 current = current->next;
458 }
459 }
460
462 {
463 // Delete any existing elements in the list.
464 CHANGELIST_ENTRY* current, * next;
465 current = d_change_list;
466 while (current != NULL) {
467 next = current->next;
468 try {
469 delete current;
470 }
471 catch (...) {
472 fprintf(stderr,
473 "vrpn_Callback_List::operator =: Deletion failure\n");
474 return;
475 }
476 current = next;
477 }
478
479 // Copy all elements from the other list. XXX Side effect, this inverts
480 // the order
481 current = from.d_change_list;
482 while (current != NULL) {
483 register_handler(current->userdata, current->handler);
484 current = current->next;
485 }
486 }
487
490 {
491 while (d_change_list != NULL) {
492 CHANGELIST_ENTRY *next = d_change_list->next;
493 try {
494 delete d_change_list;
495 } catch (...) {
496 fprintf(stderr, "vrpn_Callback_List::~vrpn_Callback_List: delete failed\n");
497 return;
498 }
499 d_change_list = next;
500 }
501 };
502
503protected:
504 typedef struct vrpn_CBS {
505 void *userdata;
506 HANDLER_TYPE handler;
507 struct vrpn_CBS *next;
510};
511
512// End of defined VRPN_BASECLASS for vrpn_BaseClass.h
513#endif
SendTextMessageBoundCall(vrpn_BaseClassUnique *device, vrpn_TEXT_SEVERITY type)
static int decode_text_message_from_buffer(char *msg, vrpn_TEXT_SEVERITY *severity, vrpn_uint32 *level, const char *buf)
Decodes the body of the text message from a buffer from the connection.
vrpn_Connection * connectionPtr()
Returns a pointer to the connection this object is using.
int register_autodeleted_handler(vrpn_int32 type, vrpn_MESSAGEHANDLER handler, void *userdata, vrpn_int32 sender=vrpn_ANY_SENDER)
Registers a handler with the connection, and remembers to delete at destruction.
vrpn_Connection * d_connection
Connection that this object talks to.
SendTextMessageBoundCall send_text_message(vrpn_TEXT_SEVERITY type=vrpn_TEXT_NORMAL)
Returns an object you can stream into to send a text message from the device like send_text_message(v...
vrpn_MESSAGEHANDLER handler
vrpn_int32 d_pong_message_id
Server telling that it is there.
vrpn_int32 d_sender_id
Sender ID registered with the connection.
vrpn_int32 d_text_message_id
ID for text messages.
friend class VRPN_API vrpn_TextPrinter
int send_text_message(const char *msg, struct timeval timestamp, vrpn_TEXT_SEVERITY type=vrpn_TEXT_NORMAL, vrpn_uint32 level=0)
Sends a NULL-terminated text message from the device d_sender_id.
static int encode_text_message_to_buffer(char *buf, vrpn_TEXT_SEVERITY severity, vrpn_uint32 level, const char *msg)
Encodes the body of the text message into a buffer, preparing for sending.
vrpn_int32 d_ping_message_id
Ask the server if they are there.
char * d_servicename
Name of this device, not including the connection part.
Class from which all user-level (and other) classes that communicate with vrpn_Connections should der...
virtual void mainloop()=0
Called once through each main loop iteration to handle updates. Remote object mainloop() should call ...
virtual int register_senders(void)
Register the sender for this device (by default, the name of the device). Return 0 on success,...
virtual int register_types(void)=0
Register the types of messages this device sends/receives. Return 0 on success, -1 on fail.
vrpn_BaseClass(const char *name, vrpn_Connection *c=NULL)
Names the device and assigns or opens connection, calls registration methods.
virtual int init(void)
Initialize things that the constructor can't. Returns 0 on success, -1 on failure.
void(VRPN_CALLBACK * HANDLER_TYPE)(void *userdata, const CALLBACK_STRUCT info)
int register_handler(void *userdata, HANDLER_TYPE handler)
Call this to add a handler to the list.
~vrpn_Callback_List()
Clear the list upon destruction if it is not empty already.
int unregister_handler(void *userdata, HANDLER_TYPE handler)
Call this to remove a handler from the list (if it exists)
vrpn_Callback_List(const vrpn_Callback_List &from)
This class requires deep copies.
vrpn_Callback_List()
The list starts out empty.
void call_handlers(const CALLBACK_STRUCT &info)
This will pass the referenced parameter as a const to all the callbacks.
void operator=(const vrpn_Callback_List &from)
CHANGELIST_ENTRY * d_change_list
Generic connection class not specific to the transport mechanism.
Structure to hold the objects that are being watched.
vrpn_TextPrinter_Watch_Entry * next
Pointer to the next one in the list.
vrpn_BaseClass * obj
Object being watched.
vrpn_TextPrinter * me
Pointer to this, because used in a static function.
Class that handles text/warning/error printing for all objects in the system.
FILE * d_ostream
Output stream to use.
static int VRPN_CALLBACK text_message_handler(void *userdata, vrpn_HANDLERPARAM p)
Handles the text messages that come from the connections for objects we are watching.
vrpn_TEXT_SEVERITY d_severity_to_print
Minimum severity to print.
vrpn_Semaphore d_semaphore
Mutex to ensure thread safety;.
void set_ostream_to_use(FILE *o)
Change the ostream that will be used to print messages. Setting a NULL ostream results in no printing...
void remove_object(vrpn_BaseClass *o)
Remove an object from the list of watched objects (multiple deletions of the object will not cause an...
vrpn_TextPrinter_Watch_Entry * d_first_watched_object
Head of list of objects being watched.
int add_object(vrpn_BaseClass *o)
Adds an object to the list of watched objects (multiple registration of the same object will result i...
void set_min_level_to_print(vrpn_TEXT_SEVERITY severity, vrpn_uint32 level=0)
Change the level of printing for the object (sets the minimum level to print). Default is Warnings an...
vrpn_uint32 d_level_to_print
Minimum level to print.
struct vrpn_CBS * next
void * userdata
HANDLER_TYPE handler
This structure is what is passed to a vrpn_Connection message callback.
vrpn_TextPrinter & vrpn_System_TextPrinter
class VRPN_API vrpn_BaseClass
vrpn_TEXT_SEVERITY
Since the sending of text messages has been pulled into the base class (so that every object can send...
@ vrpn_TEXT_NORMAL
@ vrpn_TEXT_WARNING
@ vrpn_TEXT_ERROR
const unsigned vrpn_MAX_TEXT_LEN
const int vrpn_MAX_BCADRS
Internal value for number of BaseClass addresses.
#define VRPN_API
#define VRPN_CALLBACK
const int vrpn_ANY_SENDER
vrpn_ANY_SENDER can be used to register callbacks on a given message type from any sender.
int(VRPN_CALLBACK * vrpn_MESSAGEHANDLER)(void *userdata, vrpn_HANDLERPARAM p)
Type of a message handler for vrpn_Connection messages.
#define vrpn_gettimeofday