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