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;
19
25
mod message_structure;
26
+ pub use dynamic_publisher:: * ;
27
+ pub use dynamic_subscription:: * ;
20
28
pub use error:: * ;
29
+ pub use field_access:: * ;
21
30
pub use message_structure:: * ;
22
31
23
32
/// Factory for constructing messages in a certain package dynamically.
@@ -54,19 +63,33 @@ struct MessageTypeName {
54
63
/// can be used as a factory to create message instances.
55
64
#[ derive( Clone ) ]
56
65
pub struct DynamicMessageMetadata {
57
- #[ allow( dead_code) ]
58
66
message_type : MessageTypeName ,
59
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.
60
69
#[ allow( dead_code) ]
61
70
introspection_type_support_library : Arc < libloading:: Library > ,
62
- #[ allow( dead_code) ]
63
71
type_support_ptr : * const rosidl_message_type_support_t ,
64
- #[ allow( dead_code) ]
65
72
structure : MessageStructure ,
66
- #[ allow( dead_code) ]
67
73
fini_function : unsafe extern "C" fn ( * mut std:: os:: raw:: c_void ) ,
68
74
}
69
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
+
70
93
// ========================= impl for DynamicMessagePackage =========================
71
94
72
95
/// This is an analogue of rclcpp::get_typesupport_library.
@@ -258,12 +281,138 @@ impl DynamicMessageMetadata {
258
281
pkg. message_metadata ( type_name)
259
282
}
260
283
284
+ /// Instantiates a new message.
285
+ pub fn create ( & self ) -> Result < DynamicMessage , DynamicMessageError > {
286
+ // Get an aligned boxed slice. This is inspired by the maligned crate.
287
+ use std:: alloc:: Layout ;
288
+ // As mentioned in the struct definition, the maximum alignment required is 8.
289
+ let layout = Layout :: from_size_align ( self . structure . size , 8 ) . unwrap ( ) ;
290
+ let mut storage = unsafe {
291
+ assert_ne ! ( self . structure. size, 0 ) ;
292
+ // SAFETY: The layout has non-zero size.
293
+ let ptr = std:: alloc:: alloc_zeroed ( layout) ;
294
+ // SAFETY: This is valid, memory in ptr has appropriate size and is initialized
295
+ let slice = std:: slice:: from_raw_parts_mut ( ptr, self . structure . size ) ;
296
+ // The mutable reference decays into a (fat) *mut [u8]
297
+ Box :: from_raw ( slice)
298
+ } ;
299
+ // SAFETY: The pointer returned by get_type_support_handle() is always valid.
300
+ let type_support = unsafe { & * self . type_support_ptr } ;
301
+ let message_members: & rosidl_message_members_t =
302
+ // SAFETY: The data pointer is supposed to be always valid.
303
+ unsafe { & * ( type_support. data as * const rosidl_message_members_t ) } ;
304
+ // SAFETY: The init function is passed zeroed memory of the correct alignment.
305
+ unsafe {
306
+ ( message_members. init_function . unwrap ( ) ) (
307
+ storage. as_mut_ptr ( ) as _ ,
308
+ rosidl_runtime_c__message_initialization:: ROSIDL_RUNTIME_C_MSG_INIT_ALL ,
309
+ ) ;
310
+ } ;
311
+ let dyn_msg = DynamicMessage {
312
+ metadata : self . clone ( ) ,
313
+ storage,
314
+ needs_fini : true ,
315
+ } ;
316
+ Ok ( dyn_msg)
317
+ }
318
+
261
319
/// Returns a description of the message structure.
262
320
pub fn structure ( & self ) -> & MessageStructure {
263
321
& self . structure
264
322
}
265
323
}
266
324
325
+ // ========================= impl for DynamicMessage =========================
326
+
327
+ impl Drop for DynamicMessage {
328
+ fn drop ( & mut self ) {
329
+ if self . needs_fini {
330
+ // SAFETY: The fini_function expects to be passed a pointer to the message
331
+ unsafe { ( self . metadata . fini_function ) ( self . storage . as_mut_ptr ( ) as _ ) }
332
+ }
333
+ }
334
+ }
335
+
336
+ impl PartialEq for DynamicMessage {
337
+ fn eq ( & self , other : & Self ) -> bool {
338
+ self . metadata . type_support_ptr == other. metadata . type_support_ptr
339
+ && self . storage == other. storage
340
+ }
341
+ }
342
+
343
+ impl Eq for DynamicMessage { }
344
+
345
+ impl DynamicMessage {
346
+ /// Dynamically loads a type support library for the specified type and creates a message instance.
347
+ ///
348
+ /// The full message type is of the form `<package>/msg/<type_name>`, e.g.
349
+ /// `std_msgs/msg/String`.
350
+ ///
351
+ /// The message instance will contain the default values of the message type.
352
+ pub fn new ( full_message_type : & str ) -> Result < Self , DynamicMessageError > {
353
+ DynamicMessageMetadata :: new ( full_message_type) ?. create ( )
354
+ }
355
+
356
+ /// Returns a description of the message structure.
357
+ pub fn structure ( & self ) -> & MessageStructure {
358
+ & self . metadata . structure
359
+ }
360
+
361
+ /// Converts a statically typed RMW-native message into a `DynamicMessage`.
362
+ pub fn convert_from_rmw_message < T > ( mut msg : T ) -> Result < Self , DynamicMessageError >
363
+ where
364
+ T : RmwMessage ,
365
+ {
366
+ let mut dyn_msg = Self :: new ( <T as RmwMessage >:: TYPE_NAME ) ?;
367
+ let align = std:: mem:: align_of :: < T > ( ) ;
368
+ assert_eq ! ( dyn_msg. storage. as_ptr( ) . align_offset( align) , 0 ) ;
369
+ {
370
+ // SAFETY: This transmutes the slice of bytes into a &mut T. This is fine, since
371
+ // under the hood it *is* a T.
372
+ // However, the resulting value is not seen as borrowing from dyn_msg by the borrow checker,
373
+ // so we are careful to not create a second mutable reference before dropping this one,
374
+ // since that would be UB.
375
+ let dyn_msg_transmuted = unsafe { & mut * ( dyn_msg. storage . as_mut_ptr ( ) as * mut T ) } ;
376
+ // We cannot simply overwrite one message with the other, or we will get a memory leak/double-free.
377
+ // Swapping is the solution.
378
+ std:: mem:: swap ( & mut msg, dyn_msg_transmuted) ;
379
+ }
380
+ Ok ( dyn_msg)
381
+ }
382
+
383
+ /// Converts a `DynamicMessage` into a statically typed RMW-native message.
384
+ ///
385
+ /// If the RMW-native message type does not match the underlying message type of this `DynamicMessage`,
386
+ /// it is not converted but instead returned unchanged.
387
+ pub fn convert_into_rmw_message < T > ( mut self ) -> Result < T , Self >
388
+ where
389
+ T : RmwMessage ,
390
+ {
391
+ if <T as RmwMessage >:: TYPE_NAME == self . metadata . message_type . to_string ( ) {
392
+ // SAFETY: Even though a zero-initialized message might not match RMW expectations for
393
+ // what a message should look like, it is safe to temporarily have a zero-initialized
394
+ // value, i.e. it is not undefined behavior to do this since it's a C struct, and an
395
+ // all-zeroes bit pattern is always a valid instance of any C struct.
396
+ let mut dest = unsafe { std:: mem:: zeroed :: < T > ( ) } ;
397
+ let dest_ptr = & mut dest as * mut T as * mut u8 ;
398
+ // This reinterprets the struct as a slice of bytes.
399
+ // The bytes copied into the dest slice are a valid value of T, as ensured by comparison
400
+ // of the type support pointers.
401
+ let dest_slice =
402
+ unsafe { std:: slice:: from_raw_parts_mut ( dest_ptr, std:: mem:: size_of :: < T > ( ) ) } ;
403
+ // This creates a shallow copy, with ownership of the "deep" (or inner) parts moving
404
+ // into the destination.
405
+ dest_slice. copy_from_slice ( & * self . storage ) ;
406
+ // Don't run the fini function on the src data anymore, because the inner parts would be
407
+ // double-freed by dst and src.
408
+ self . needs_fini = false ;
409
+ Ok ( dest)
410
+ } else {
411
+ Err ( self )
412
+ }
413
+ }
414
+ }
415
+
267
416
#[ cfg( test) ]
268
417
mod tests {
269
418
use super :: * ;
@@ -275,6 +424,8 @@ mod tests {
275
424
fn all_types_are_sync_and_send ( ) {
276
425
assert_send :: < DynamicMessageMetadata > ( ) ;
277
426
assert_sync :: < DynamicMessageMetadata > ( ) ;
427
+ assert_send :: < DynamicMessage > ( ) ;
428
+ assert_sync :: < DynamicMessage > ( ) ;
278
429
}
279
430
280
431
#[ test]
0 commit comments