vrpn 07.35
Virtual Reality Peripheral Network
Loading...
Searching...
No Matches
vrpn_BaseClass.h
Go to the documentation of this file.
1
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
125public:
128
135 int add_object(vrpn_BaseClass *o);
136
140 void remove_object(vrpn_BaseClass *o);
141
144 void set_min_level_to_print(vrpn_TEXT_SEVERITY severity,
145 vrpn_uint32 level = 0);
146
149 void set_ostream_to_use(FILE *o);
150
151protected:
154
166
167 FILE *d_ostream;
169 vrpn_uint32 d_level_to_print;
170
173 static int VRPN_CALLBACK
174 text_message_handler(void *userdata, vrpn_HANDLERPARAM p);
175};
176// SWIG does not like this declaration.
177#ifndef SWIG
179#endif
180
188
189public:
191 virtual ~vrpn_BaseClassUnique();
192
194 vrpn_Connection *connectionPtr() { return d_connection; };
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
213 : _p(other._p)
214 , _severity(other._severity)
215 {
216 }
217
218 int operator()(const char *msg) const
219 {
220 struct timeval timestamp;
221 vrpn_gettimeofday(&timestamp, NULL);
222 return _p->send_text_message(msg, timestamp, _severity);
223 }
224 };
225
226protected:
230
231 vrpn_int32 d_sender_id;
232 vrpn_int32 d_text_message_id;
233 vrpn_int32 d_ping_message_id;
234 vrpn_int32 d_pong_message_id;
235
238 // This is a wrapper for the vrpn_Connection call that registers
239 // message handlers. It should be used rather than the connection's
240 // function because this one will remember to unregister all of its handlers
241 // at object deletion time.
242 int register_autodeleted_handler(vrpn_int32 type,
243 vrpn_MESSAGEHANDLER handler,
244 void *userdata,
245 vrpn_int32 sender = vrpn_ANY_SENDER);
246
249 static int encode_text_message_to_buffer(char *buf,
250 vrpn_TEXT_SEVERITY severity,
251 vrpn_uint32 level,
252 const char *msg);
253
255 static int decode_text_message_from_buffer(char *msg,
256 vrpn_TEXT_SEVERITY *severity,
257 vrpn_uint32 *level,
258 const char *buf);
259
261 int send_text_message(const char *msg, struct timeval timestamp,
263 vrpn_uint32 level = 0);
264
274
278 void server_mainloop(void);
279
283 void client_mainloop(void);
284
285private:
286 struct {
288 vrpn_int32 sender;
289 vrpn_int32 type;
290 void *userdata;
291 } d_handler_autodeletion_record[vrpn_MAX_BCADRS];
292 int d_num_autodeletions;
293
294 int d_first_mainloop;
296 struct timeval d_time_first_ping;
298 struct timeval
299 d_time_last_warned;
300 int d_unanswered_ping;
301 int d_flatline;
302
305 static int VRPN_CALLBACK handle_ping(void *userdata, vrpn_HANDLERPARAM p);
306 static int VRPN_CALLBACK handle_pong(void *userdata, vrpn_HANDLERPARAM p);
307 static int VRPN_CALLBACK
308 handle_connection_dropped(void *userdata, vrpn_HANDLERPARAM p);
309 void initiate_ping_cycle(void);
310};
311
312//---------------------------------------------------------------
315
317
318public:
321 vrpn_BaseClass(const char *name, vrpn_Connection *c = NULL);
322
323 virtual ~vrpn_BaseClass();
324
331 virtual void mainloop() = 0;
332
333protected:
336 virtual int init(void);
337
340 virtual int register_senders(void);
341
344 virtual int register_types(void) = 0;
345};
346
347//---------------------------------------------------------------
348// Within VRPN (and other libraries), it is wise to avoid using the
349// Standard Template Library. This is very annoying, but required
350// by the fact that some systems have incompatible versions of STL.
351// This caused problems with any program that uses the GHOST library
352// (which had its own STL on Windows), and I've heard tell of problems
353// with other systems as well. On the other hand, nothing says that
354// we can't have our OWN template types and use them. This next type
355// is used to handle callback lists within objects. It is templated
356// over the struct that is passed to the user callback.
357// See vrpn_Button.h's usage for an example.
358
359// Disables a warning that the class requires DLL linkage to be
360// used by clients of classes that include one: The classes themselves
361// have DLL linkage, the code below asks for (but apparently does not
362// get) DLL linkage, and the DLL-linked test programs work when things
363// are as they are. Do not use this class outside of a derived class.
364#ifdef _MSC_VER
365#pragma warning(disable : 4251)
366#endif
367template <class CALLBACK_STRUCT> class VRPN_API vrpn_Callback_List {
368public:
369 typedef void(VRPN_CALLBACK *HANDLER_TYPE)(void *userdata,
370 const CALLBACK_STRUCT info);
371
374 {
375 // Delete any existing elements in the list.
376 CHANGELIST_ENTRY *current, *next;
377 current = d_change_list;
378 while (current != NULL) {
379 next = current->next;
380 try {
381 delete current;
382 } catch (...) {
383 fprintf(stderr,
384 "vrpn_Callback_List::operator =: Deletion failure\n");
385 return;
386 }
387 current = next;
388 }
389
390 // Copy all elements from the other list. XXX Side effect, this inverts
391 // the order
392 current = from.d_change_list;
393 while (current != NULL) {
394 register_handler(current->userdata, current->handler);
395 current = current->next;
396 }
397 }
398
400 int register_handler(void *userdata, HANDLER_TYPE handler)
401 {
402 CHANGELIST_ENTRY *new_entry;
403
404 // Ensure that the handler is non-NULL
405 if (handler == NULL) {
406 fprintf(stderr,
407 "vrpn_Callback_List::register_handler(): NULL handler\n");
408 return -1;
409 }
410
411 // Allocate and initialize the new entry
412 try {
413 new_entry = new CHANGELIST_ENTRY;
414 } catch (...) {
415 fprintf(stderr,
416 "vrpn_Callback_List::register_handler(): Out of memory\n");
417 return -1;
418 }
419 new_entry->handler = handler;
420 new_entry->userdata = userdata;
421
422 // Add this handler to the chain at the beginning (don't check to see
423 // if it is already there, since duplication is okay).
424 new_entry->next = d_change_list;
425 d_change_list = new_entry;
426
427 return 0;
428 };
429
431 int unregister_handler(void *userdata, HANDLER_TYPE handler)
432 {
433 // The pointer at *snitch points to victim
434 CHANGELIST_ENTRY *victim, **snitch;
435
436 // Find a handler with this registry in the list (any one will do,
437 // since all duplicates are the same).
438 snitch = &d_change_list;
439 victim = *snitch;
440 while ((victim != NULL) && ((victim->handler != handler) ||
441 (victim->userdata != userdata))) {
442 snitch = &((*snitch)->next);
443 victim = victim->next;
444 }
445
446 // Make sure we found one
447 if (victim == NULL) {
448 fprintf(
449 stderr,
450 "vrpn_Callback_List::unregister_handler: No such handler\n");
451 return -1;
452 }
453
454 // Remove the entry from the list
455 *snitch = victim->next;
456 try {
457 delete victim;
458 } catch (...) {
459 fprintf(stderr, "vrpn_Callback_List::unregister_handler: delete failed\n");
460 return -1;
461 }
462
463 return 0;
464 };
465
467 void call_handlers(const CALLBACK_STRUCT &info)
468 {
469 CHANGELIST_ENTRY *handler = d_change_list;
470 while (handler != NULL) {
471 handler->handler(handler->userdata, info);
472 handler = handler->next;
473 }
474 };
475
478 : d_change_list(NULL){};
479
482 {
483 while (d_change_list != NULL) {
484 CHANGELIST_ENTRY *next = d_change_list->next;
485 try {
486 delete d_change_list;
487 } catch (...) {
488 fprintf(stderr, "vrpn_Callback_List::~vrpn_Callback_List: delete failed\n");
489 return;
490 }
491 d_change_list = next;
492 }
493 };
494
495protected:
496 typedef struct vrpn_CBS {
497 void *userdata;
498 HANDLER_TYPE handler;
499 struct vrpn_CBS *next;
502};
503
504// End of defined VRPN_BASECLASS for vrpn_BaseClass.h
505#endif
SendTextMessageBoundCall(SendTextMessageBoundCall const &other)
SendTextMessageBoundCall(vrpn_BaseClassUnique *device, vrpn_TEXT_SEVERITY type)
INTERNAL class to hold members that there should only be one copy of even when a class inherits from ...
vrpn_Connection * connectionPtr()
Returns a pointer to the connection this object is using.
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.
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.
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_types(void)=0
Register the types of messages this device sends/receives. Return 0 on success, -1 on fail.
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()
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)
This class requires deep copies.
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.
vrpn_TEXT_SEVERITY d_severity_to_print
Minimum severity to print.
vrpn_Semaphore d_semaphore
Mutex to ensure thread safety;.
vrpn_TextPrinter_Watch_Entry * d_first_watched_object
Head of list of objects being watched.
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.
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.
VRPN_API vrpn_TextPrinter & vrpn_System_TextPrinter
#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
Definition vrpn_Shared.h:99