-
Notifications
You must be signed in to change notification settings - Fork 137
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add context builder #312
Open
routiful
wants to merge
18
commits into
ros2-rust:main
Choose a base branch
from
routiful:context_builder
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Add context builder #312
Changes from all commits
Commits
Show all changes
18 commits
Select commit
Hold shift + click to select a range
fc89816
Add context builder
routiful 935d9d8
Fix typo
routiful 98eb2f6
Delete brackets
routiful fd7323a
Add cfg to avoid compile error at old distro
routiful 729fd3c
Split implementation about get domain id according to ros distro
routiful 93b10c5
Modify cfg
routiful 4e1b46d
Add set_env() to change domain_id at foxy
routiful b647fe9
Apply advices from clippy
routiful 4481f99
Add blocklist to rosidl_dynamic_support
routiful 58e4d75
Modify orders
routiful ad70492
Move domain_id func to node builder in Foxy
routiful 9c50198
Delete unnecessary mut
routiful 47a4b51
Add rcl_init_options_t field in context builder
routiful 169edd1
Change formatting
routiful caf0f33
Modify formats
routiful 7c200b3
Modify test of domain_id func in each ros distro
routiful 82e8f3a
Modify wrong tests
routiful 4f1af02
Modify failed tests
routiful File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,164 @@ | ||
use std::ffi::CString; | ||
use std::os::raw::c_char; | ||
use std::sync::{Arc, Mutex}; | ||
|
||
use crate::rcl_bindings::*; | ||
use crate::{Context, RclrsError, ToResult}; | ||
|
||
/// A builder for creating a [`Context`][1]. | ||
/// | ||
/// The builder pattern allows selectively setting some fields, and leaving all others at their default values. | ||
/// This struct instance can be created via [`Context::builder()`][2]. | ||
/// | ||
/// # Example | ||
/// ``` | ||
/// # use rclrs::{Context, ContextBuilder, RclrsError}; | ||
/// // Building a context in a single expression | ||
/// let args = ["ROS 1 ROS 2"].map(String::from); | ||
/// assert!(ContextBuilder::new(args.clone()).build().is_ok()); | ||
/// // Building a context via Context::builder() | ||
/// assert!(Context::builder(args.clone()).build().is_ok()); | ||
/// // Building a context step-by-step | ||
/// let mut builder = Context::builder(args.clone()); | ||
/// assert!(builder.build().is_ok()); | ||
/// # Ok::<(), RclrsError>(()) | ||
/// ``` | ||
/// | ||
/// [1]: crate::Context | ||
/// [2]: crate::Context::builder | ||
pub struct ContextBuilder { | ||
arguments: Vec<String>, | ||
domain_id: usize, | ||
rcl_init_options: rcl_init_options_t, | ||
} | ||
|
||
impl ContextBuilder { | ||
/// Creates a builder for a context with arguments. | ||
/// | ||
/// Usually, this would be called with `std::env::args()`, analogously to `rclcpp::init()`. | ||
/// See also the official "Passing ROS arguments to nodes via the command-line" tutorial. | ||
/// | ||
/// Creating a context can fail in case the args contain invalid ROS arguments. | ||
/// | ||
/// # Example | ||
/// ``` | ||
/// # use rclrs::{ContextBuilder, RclrsError}; | ||
/// let invalid_remapping = ["--ros-args", "-r", ":=:*/]"].map(String::from); | ||
/// assert!(ContextBuilder::new(invalid_remapping).build().is_err()); | ||
/// let valid_remapping = ["--ros-args", "--remap", "__node:=my_node"].map(String::from); | ||
/// assert!(ContextBuilder::new(valid_remapping).build().is_ok()); | ||
/// # Ok::<(), RclrsError>(()) | ||
/// ``` | ||
pub fn new(args: impl IntoIterator<Item = String>) -> ContextBuilder { | ||
let mut domain_id = 0; | ||
// SAFETY: Getting the default domain ID, based on the environment | ||
let ret = unsafe { rcl_get_default_domain_id(&mut domain_id) }; | ||
debug_assert_eq!(ret, 0); | ||
// SAFETY: No preconditions for this function. | ||
let allocator = unsafe { rcutils_get_default_allocator() }; | ||
// SAFETY: Getting a zero-initialized value is always safe. | ||
let mut rcl_init_options = unsafe { rcl_get_zero_initialized_init_options() }; | ||
// SAFETY: Passing in a zero-initialized value is expected. | ||
// In the case where this returns not ok, there's nothing to clean up. | ||
unsafe { | ||
rcl_init_options_init(&mut rcl_init_options, allocator) | ||
.ok() | ||
.unwrap() | ||
}; | ||
|
||
ContextBuilder { | ||
arguments: args.into_iter().collect(), | ||
domain_id, | ||
rcl_init_options, | ||
} | ||
} | ||
|
||
/// Sets the context domain id. | ||
/// | ||
/// The domain ID controls which nodes can send messages to each other, see the [ROS 2 concept article][1]. | ||
/// | ||
/// [1]: https://docs.ros.org/en/rolling/Concepts/About-Domain-ID.html | ||
/// | ||
/// # Example | ||
/// ``` | ||
/// # use rclrs::{Context, ContextBuilder, RclrsError}; | ||
/// let context = ContextBuilder::new([]).domain_id(1).build()?; | ||
/// assert_eq!(context.domain_id(), 1); | ||
/// # Ok::<(), RclrsError>(()) | ||
/// ``` | ||
#[cfg(not(ros_distro = "foxy"))] | ||
pub fn domain_id(mut self, domain_id: usize) -> Self { | ||
self.domain_id = domain_id; | ||
self | ||
} | ||
|
||
/// Builds the context instance and `rcl_init_options` in order to initialize rcl | ||
/// | ||
/// For example usage, see the [`ContextBuilder`][1] docs. | ||
/// | ||
/// [1]: crate::ContextBuilder | ||
pub fn build(&mut self) -> Result<Context, RclrsError> { | ||
// SAFETY: Getting a zero-initialized value is always safe | ||
let mut rcl_context = unsafe { rcl_get_zero_initialized_context() }; | ||
let cstring_args: Vec<CString> = self | ||
.arguments | ||
.iter() | ||
.map(|arg| { | ||
CString::new(arg.as_str()).map_err(|err| RclrsError::StringContainsNul { | ||
err, | ||
s: arg.clone(), | ||
}) | ||
}) | ||
.collect::<Result<_, _>>()?; | ||
// Vector of pointers into cstring_args | ||
let c_args: Vec<*const c_char> = cstring_args.iter().map(|arg| arg.as_ptr()).collect(); | ||
self.rcl_init_options = self.create_rcl_init_options()?; | ||
unsafe { | ||
// SAFETY: This function does not store the ephemeral init_options and c_args | ||
// pointers. Passing in a zero-initialized rcl_context is expected. | ||
rcl_init( | ||
c_args.len() as i32, | ||
if c_args.is_empty() { | ||
std::ptr::null() | ||
} else { | ||
c_args.as_ptr() | ||
}, | ||
&self.rcl_init_options, | ||
&mut rcl_context, | ||
) | ||
.ok()?; | ||
} | ||
Ok(Context { | ||
rcl_context_mtx: Arc::new(Mutex::new(rcl_context)), | ||
}) | ||
} | ||
|
||
/// Creates a rcl_init_options_t struct from this builder. | ||
/// | ||
/// domain id validation is performed in this method. | ||
fn create_rcl_init_options(&self) -> Result<rcl_init_options_t, RclrsError> { | ||
unsafe { | ||
// SAFETY: No preconditions for this function. | ||
let allocator = rcutils_get_default_allocator(); | ||
// SAFETY: Getting a zero-initialized value is always safe. | ||
let mut rcl_init_options = rcl_get_zero_initialized_init_options(); | ||
// SAFETY: Passing in a zero-initialized value is expected. | ||
// In the case where this returns not ok, there's nothing to clean up. | ||
rcl_init_options_init(&mut rcl_init_options, allocator).ok()?; | ||
// SAFETY: Setting domain id in the init options provided. | ||
// In the case where this returns not ok, the domain id is invalid. | ||
rcl_init_options_set_domain_id(&mut rcl_init_options, self.domain_id).ok()?; | ||
|
||
Ok(rcl_init_options) | ||
} | ||
} | ||
} | ||
|
||
impl Drop for rcl_init_options_t { | ||
fn drop(&mut self) { | ||
// SAFETY: Do not finish this struct except here. | ||
unsafe { | ||
rcl_init_options_fini(self).ok().unwrap(); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Personally, I would make the builder have a field of
rcl_init_options_t
type, instead of only building it inbuild()
. But I don't have a good argument for it, so I'll leave it up to you whether you want to do that :)There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I accept your suggestion. That field give more flexible features to assign allocators and a domain_id.