6
6
//! The central type of this module is [`DynamicMessage`].
7
7
8
8
use std:: fmt:: { self , Display } ;
9
+ use std:: ops:: Deref ;
9
10
use std:: path:: PathBuf ;
10
11
use std:: sync:: Arc ;
11
12
13
+ use rosidl_runtime_rs:: RmwMessage ;
14
+
12
15
#[ cfg( any( ros_distro = "foxy" , ros_distro = "galactic" ) ) ]
13
16
use crate :: rcl_bindings:: rosidl_typesupport_introspection_c__MessageMembers as rosidl_message_members_t;
14
17
#[ cfg( all( not( ros_distro = "foxy" ) , not( ros_distro = "galactic" ) ) ) ]
15
18
use crate :: rcl_bindings:: rosidl_typesupport_introspection_c__MessageMembers_s as rosidl_message_members_t;
16
19
use crate :: rcl_bindings:: * ;
17
20
21
+ mod dynamic_publisher;
22
+ mod dynamic_subscription;
18
23
mod error;
24
+ mod field_access;
25
+ mod message_structure;
26
+ pub use dynamic_publisher:: * ;
27
+ pub use dynamic_subscription:: * ;
19
28
pub use error:: * ;
29
+ pub use field_access:: * ;
30
+ pub use message_structure:: * ;
20
31
21
32
/// Factory for constructing messages in a certain package dynamically.
22
33
///
@@ -52,17 +63,33 @@ struct MessageTypeName {
52
63
/// can be used as a factory to create message instances.
53
64
#[ derive( Clone ) ]
54
65
pub struct DynamicMessageMetadata {
55
- #[ allow( dead_code) ]
56
66
message_type : MessageTypeName ,
57
67
// The library needs to be kept loaded in order to keep the type_support_ptr valid.
68
+ // This is the introspection type support library, not the regular one.
58
69
#[ allow( dead_code) ]
59
70
introspection_type_support_library : Arc < libloading:: Library > ,
60
- #[ allow( dead_code) ]
61
71
type_support_ptr : * const rosidl_message_type_support_t ,
62
- # [ allow ( dead_code ) ]
72
+ structure : MessageStructure ,
63
73
fini_function : unsafe extern "C" fn ( * mut std:: os:: raw:: c_void ) ,
64
74
}
65
75
76
+ /// A message whose type is not known at compile-time.
77
+ ///
78
+ /// This type allows inspecting the structure of the message as well as the
79
+ /// values contained in it.
80
+ /// It also allows _modifying_ the values, but not the structure, because
81
+ /// even a dynamic message must always correspond to a specific message type.
82
+ // There is no clone function yet, we need to add that in rosidl.
83
+ pub struct DynamicMessage {
84
+ metadata : DynamicMessageMetadata ,
85
+ // This is aligned to the maximum possible alignment of a message (8)
86
+ // by the use of a special allocation function.
87
+ storage : Box < [ u8 ] > ,
88
+ // This type allows moving the message contents out into another message,
89
+ // in which case the drop impl is not responsible for calling fini anymore
90
+ needs_fini : bool ,
91
+ }
92
+
66
93
// ========================= impl for DynamicMessagePackage =========================
67
94
68
95
/// This is an analogue of rclcpp::get_typesupport_library.
@@ -172,6 +199,8 @@ impl DynamicMessagePackage {
172
199
let message_members: & rosidl_message_members_t =
173
200
// SAFETY: The data pointer is supposed to be always valid.
174
201
unsafe { & * ( type_support. data as * const rosidl_message_members_t ) } ;
202
+ // SAFETY: The message members coming from a type support library will always be valid.
203
+ let structure = unsafe { MessageStructure :: from_rosidl_message_members ( message_members) } ;
175
204
// The fini function will always exist.
176
205
let fini_function = message_members. fini_function . unwrap ( ) ;
177
206
let metadata = DynamicMessageMetadata {
@@ -180,6 +209,7 @@ impl DynamicMessagePackage {
180
209
& self . introspection_type_support_library ,
181
210
) ,
182
211
type_support_ptr,
212
+ structure,
183
213
fini_function,
184
214
} ;
185
215
Ok ( metadata)
@@ -230,6 +260,13 @@ impl Display for MessageTypeName {
230
260
231
261
// ========================= impl for DynamicMessageMetadata =========================
232
262
263
+ impl Deref for DynamicMessageMetadata {
264
+ type Target = MessageStructure ;
265
+ fn deref ( & self ) -> & Self :: Target {
266
+ & self . structure
267
+ }
268
+ }
269
+
233
270
// SAFETY: The functions accessing this type, including drop(), shouldn't care about the thread
234
271
// they are running in. Therefore, this type can be safely sent to another thread.
235
272
unsafe impl Send for DynamicMessageMetadata { }
@@ -250,6 +287,203 @@ impl DynamicMessageMetadata {
250
287
let pkg = DynamicMessagePackage :: new ( package_name) ?;
251
288
pkg. message_metadata ( type_name)
252
289
}
290
+
291
+ /// Instantiates a new message.
292
+ pub fn create ( & self ) -> Result < DynamicMessage , DynamicMessageError > {
293
+ // Get an aligned boxed slice. This is inspired by the maligned crate.
294
+ use std:: alloc:: Layout ;
295
+ // As mentioned in the struct definition, the maximum alignment required is 8.
296
+ let layout = Layout :: from_size_align ( self . structure . size , 8 ) . unwrap ( ) ;
297
+ let mut storage = unsafe {
298
+ assert_ne ! ( self . structure. size, 0 ) ;
299
+ // SAFETY: The layout has non-zero size.
300
+ let ptr = std:: alloc:: alloc_zeroed ( layout) ;
301
+ // SAFETY: This is valid, memory in ptr has appropriate size and is initialized
302
+ let slice = std:: slice:: from_raw_parts_mut ( ptr, self . structure . size ) ;
303
+ // The mutable reference decays into a (fat) *mut [u8]
304
+ Box :: from_raw ( slice)
305
+ } ;
306
+ // SAFETY: The pointer returned by get_type_support_handle() is always valid.
307
+ let type_support = unsafe { & * self . type_support_ptr } ;
308
+ let message_members: & rosidl_message_members_t =
309
+ // SAFETY: The data pointer is supposed to be always valid.
310
+ unsafe { & * ( type_support. data as * const rosidl_message_members_t ) } ;
311
+ // SAFETY: The init function is passed zeroed memory of the correct alignment.
312
+ unsafe {
313
+ ( message_members. init_function . unwrap ( ) ) (
314
+ storage. as_mut_ptr ( ) as _ ,
315
+ rosidl_runtime_c__message_initialization:: ROSIDL_RUNTIME_C_MSG_INIT_ALL ,
316
+ ) ;
317
+ } ;
318
+ let dyn_msg = DynamicMessage {
319
+ metadata : self . clone ( ) ,
320
+ storage,
321
+ needs_fini : true ,
322
+ } ;
323
+ Ok ( dyn_msg)
324
+ }
325
+
326
+ /// Returns a description of the message structure.
327
+ pub fn structure ( & self ) -> & MessageStructure {
328
+ & self . structure
329
+ }
330
+ }
331
+
332
+ // ========================= impl for DynamicMessage =========================
333
+
334
+ impl Deref for DynamicMessage {
335
+ type Target = MessageStructure ;
336
+ fn deref ( & self ) -> & Self :: Target {
337
+ & self . metadata . structure
338
+ }
339
+ }
340
+
341
+ impl Drop for DynamicMessage {
342
+ fn drop ( & mut self ) {
343
+ if self . needs_fini {
344
+ // SAFETY: The fini_function expects to be passed a pointer to the message
345
+ unsafe { ( self . metadata . fini_function ) ( self . storage . as_mut_ptr ( ) as _ ) }
346
+ }
347
+ }
348
+ }
349
+
350
+ impl PartialEq for DynamicMessage {
351
+ fn eq ( & self , other : & Self ) -> bool {
352
+ self . metadata . type_support_ptr == other. metadata . type_support_ptr
353
+ && self . storage == other. storage
354
+ }
355
+ }
356
+
357
+ impl Eq for DynamicMessage { }
358
+
359
+ impl DynamicMessage {
360
+ /// Dynamically loads a type support library for the specified type and creates a message instance.
361
+ ///
362
+ /// The full message type is of the form `<package>/msg/<type_name>`, e.g.
363
+ /// `std_msgs/msg/String`.
364
+ ///
365
+ /// The message instance will contain the default values of the message type.
366
+ pub fn new ( full_message_type : & str ) -> Result < Self , DynamicMessageError > {
367
+ DynamicMessageMetadata :: new ( full_message_type) ?. create ( )
368
+ }
369
+
370
+ /// See [`DynamicMessageView::get()`][1].
371
+ ///
372
+ /// [1]: crate::dynamic_message::DynamicMessageView::get
373
+ pub fn get ( & self , field_name : & str ) -> Option < Value < ' _ > > {
374
+ let field_info = self . metadata . structure . get_field_info ( field_name) ?;
375
+ // For the unwrap_or, see DynamicMessageViewMut::get_mut
376
+ let size = field_info. size ( ) . unwrap_or ( 1 ) ;
377
+ let bytes = & self . storage [ field_info. offset ..field_info. offset + size] ;
378
+ // SAFETY: The bytes contain a valid field of the type recorded in field_info.
379
+ unsafe { Value :: new ( bytes, field_info) }
380
+ }
381
+
382
+ /// See [`DynamicMessageViewMut::get_mut()`][1].
383
+ ///
384
+ /// [1]: crate::dynamic_message::DynamicMessageViewMut::get_mut
385
+ pub fn get_mut ( & mut self , field_name : & str ) -> Option < ValueMut < ' _ > > {
386
+ let field_info = self . metadata . structure . get_field_info ( field_name) ?;
387
+ // For the unwrap_or, see DynamicMessageViewMut::get_mut
388
+ let size = field_info. size ( ) . unwrap_or ( 1 ) ;
389
+ let bytes = & mut self . storage [ field_info. offset ..field_info. offset + size] ;
390
+ // SAFETY: The bytes contain a valid field of the type recorded in field_info.
391
+ Some ( unsafe { ValueMut :: new ( bytes, field_info) } )
392
+ }
393
+
394
+ /// Returns a description of the message structure.
395
+ pub fn structure ( & self ) -> & MessageStructure {
396
+ & self . metadata . structure
397
+ }
398
+
399
+ /// Iterate over all fields in declaration order.
400
+ pub fn iter ( & self ) -> impl Iterator < Item = ( & str , Value < ' _ > ) > + ' _ {
401
+ self . metadata . structure . fields . iter ( ) . map ( |field_info| {
402
+ let value = self . get ( & field_info. name ) . unwrap ( ) ;
403
+ ( field_info. name . as_str ( ) , value)
404
+ } )
405
+ }
406
+
407
+ /// Iterate over all fields in declaration order (mutable version).
408
+ pub fn iter_mut ( & mut self ) -> impl Iterator < Item = ( & str , ValueMut < ' _ > ) > + ' _ {
409
+ self . view_mut ( ) . iter_mut ( )
410
+ }
411
+
412
+ /// Returns a view object of this message.
413
+ ///
414
+ /// The purpose for this conversion is to allow uniform handling of this top-level message
415
+ /// and nested messages contained in it through a [`DynamicMessageView`].
416
+ pub fn view ( & self ) -> DynamicMessageView < ' _ > {
417
+ DynamicMessageView {
418
+ structure : & self . metadata . structure ,
419
+ storage : & self . storage ,
420
+ }
421
+ }
422
+
423
+ /// Returns a mutable view object of this message.
424
+ ///
425
+ /// The purpose for this conversion is to allow uniform handling of this top-level message
426
+ /// and nested messages contained in it through a [`DynamicMessageViewMut`].
427
+ pub fn view_mut ( & mut self ) -> DynamicMessageViewMut < ' _ > {
428
+ DynamicMessageViewMut {
429
+ structure : & self . metadata . structure ,
430
+ storage : & mut self . storage ,
431
+ }
432
+ }
433
+
434
+ /// Converts a statically typed RMW-native message into a `DynamicMessage`.
435
+ pub fn convert_from_rmw_message < T > ( mut msg : T ) -> Result < Self , DynamicMessageError >
436
+ where
437
+ T : RmwMessage ,
438
+ {
439
+ let mut dyn_msg = Self :: new ( <T as RmwMessage >:: TYPE_NAME ) ?;
440
+ let align = std:: mem:: align_of :: < T > ( ) ;
441
+ assert_eq ! ( dyn_msg. storage. as_ptr( ) . align_offset( align) , 0 ) ;
442
+ {
443
+ // SAFETY: This transmutes the slice of bytes into a &mut T. This is fine, since
444
+ // under the hood it *is* a T.
445
+ // However, the resulting value is not seen as borrowing from dyn_msg by the borrow checker,
446
+ // so we are careful to not create a second mutable reference before dropping this one,
447
+ // since that would be UB.
448
+ let dyn_msg_transmuted = unsafe { & mut * ( dyn_msg. storage . as_mut_ptr ( ) as * mut T ) } ;
449
+ // We cannot simply overwrite one message with the other, or we will get a memory leak/double-free.
450
+ // Swapping is the solution.
451
+ std:: mem:: swap ( & mut msg, dyn_msg_transmuted) ;
452
+ }
453
+ Ok ( dyn_msg)
454
+ }
455
+
456
+ /// Converts a `DynamicMessage` into a statically typed RMW-native message.
457
+ ///
458
+ /// If the RMW-native message type does not match the underlying message type of this `DynamicMessage`,
459
+ /// it is not converted but instead returned unchanged.
460
+ pub fn convert_into_rmw_message < T > ( mut self ) -> Result < T , Self >
461
+ where
462
+ T : RmwMessage ,
463
+ {
464
+ if <T as RmwMessage >:: TYPE_NAME == self . metadata . message_type . to_string ( ) {
465
+ // SAFETY: Even though a zero-initialized message might not match RMW expectations for
466
+ // what a message should look like, it is safe to temporarily have a zero-initialized
467
+ // value, i.e. it is not undefined behavior to do this since it's a C struct, and an
468
+ // all-zeroes bit pattern is always a valid instance of any C struct.
469
+ let mut dest = unsafe { std:: mem:: zeroed :: < T > ( ) } ;
470
+ let dest_ptr = & mut dest as * mut T as * mut u8 ;
471
+ // This reinterprets the struct as a slice of bytes.
472
+ // The bytes copied into the dest slice are a valid value of T, as ensured by comparison
473
+ // of the type support pointers.
474
+ let dest_slice =
475
+ unsafe { std:: slice:: from_raw_parts_mut ( dest_ptr, std:: mem:: size_of :: < T > ( ) ) } ;
476
+ // This creates a shallow copy, with ownership of the "deep" (or inner) parts moving
477
+ // into the destination.
478
+ dest_slice. copy_from_slice ( & * self . storage ) ;
479
+ // Don't run the fini function on the src data anymore, because the inner parts would be
480
+ // double-freed by dst and src.
481
+ self . needs_fini = false ;
482
+ Ok ( dest)
483
+ } else {
484
+ Err ( self )
485
+ }
486
+ }
253
487
}
254
488
255
489
#[ cfg( test) ]
@@ -263,6 +497,8 @@ mod tests {
263
497
fn all_types_are_sync_and_send ( ) {
264
498
assert_send :: < DynamicMessageMetadata > ( ) ;
265
499
assert_sync :: < DynamicMessageMetadata > ( ) ;
500
+ assert_send :: < DynamicMessage > ( ) ;
501
+ assert_sync :: < DynamicMessage > ( ) ;
266
502
}
267
503
268
504
#[ test]
0 commit comments