-
Notifications
You must be signed in to change notification settings - Fork 2
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 example for parallel sort #15
base: main
Are you sure you want to change the base?
Conversation
It is now clearer how to use this library. The example(s) are built by default.
@Connor-GH thanks for looking into this. @SAtacker would you mind haveing a look? |
@Connor-GH are you building hpx-rs on |
I build it on linux. |
could you please add instructions to build it on linux in the Readme. |
HPX has its tests, and it has its examples. Tests are meant to be entirely functional. People aren't really supposed to look at them until a regression pops up. On the other hand, HPX's examples are fairly small (can fit on one screen) and have some important comments. The examples are there for people who just need to quickly pick something up with the library and need to learn by doing, rather than searching for a specific flag or something (which is what the documentation is for). This is probably best demonstrated visibly. Here is a simple example from HPX that I learned from to help develop this example (notice the comments):
It should also be noted that examples should be separated from the implementation, but I don't think this matters wrt the testcases. Many languages have built-in unit tests, and I think putting them in the implementation file is fine. D does this for example in its standard library, but their tests are a bit more trivial but express the power of the API. There is a way of writing tests that can work as examples, and D does this this well, but these tests just serve to "get the thing done" with low regard for the potential of it being used to educate. Maybe I am bikeshedding here, but there is a genuine case for both examples and tests. |
i agree that examples are essential, but i don't think it's ideal to write one-liner examples as they don't add much value beyond what's already covered in the tests. It would be more beneficial to create more complex and practical examples. Since most of the functions maintain a similar structure and usage style as HPX it's ideal to refer to hpx documentation for understanding about that function (till the time we aren't done with hpx-rs documentation), more elaborate examples would help demonstrate their effective use. @SAtacker what are your thoughts on it? |
I agree that trivial, one-liner examples may not add much value at this stage, as they are largely covered by the existing tests. |
…init() Having to do all of the Args -> Vec<String> -> *mut *mut c_char conversions in every program using hpx-rs seems tedious. This wrapper helps to remove some of these ugly layers by moving them to the library rather than the user code
I apologize, I have been busy lately. I finally have a commit ready again for review though. By the time this PR is done, there should be no code that directly calls the ffi. IOW: all code needs to go through the "front door" of hpx_sys::, rather than hpx_sys::ffi::. Things I don't like about this code that I am asking guidance on:
|
…afe part of this assignment is itself, not the right-hand side.
@@ -39,9 +40,45 @@ pub mod ffi { | |||
// Wrapper for the above Bindings. | |||
// reffer to tests to understand how to use them. [NOTE: Not all bindings have wrapper.] | |||
// ================================================================================================ | |||
use std::ffi::CString; |
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.
@Connor-GH can you explain why have you removed std::ffi::String
?
I would really prefer not to pass this function pointer through by another static variable, but it was the only way I could find to keep the API of fn(Vec) to the user. It feels really bad, please work with me on fixing this.
CString
in Rust is a better approach, and it eliminate the need for the c_init function and the static variable.- or ig instead of
static variable
you can useclosure
like the following
pub fn init<F>(func: F, func_args: Vec<String>) -> i32
where
F: Fn(Vec<String>) -> i32 + 'static,
{
let c_init = ......
....
...
}
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.
can you explain why have you removed
std::ffi::String
?
It is right here: https://github.com/STEllAR-GROUP/hpx-rs/pull/15/files#diff-7ab105a9531556e61127ea7119050c01f680c4d93e2fbaa66cb1a4c19bbe66f7R44
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.
Please explain what you mean by "you can use closure
". A closure cannot be cast into a function pointer if it takes any arguments.
static mut FUNC_HOLDER: Option<fn (Vec<String>) -> i32> = None; | ||
|
||
// Convert arguments from *mut *mut c_char to Vec<String> | ||
// and call the saved Rust version of the function. | ||
fn c_init(argc: i32, argv: *mut *mut c_char) -> i32 { | ||
let mut vec_args: Vec<String> = Vec::new(); | ||
|
||
for i in 0..argc { | ||
// SAFETY: We get the argv from a conversion from Vec<String>. | ||
// The conversion, which is safe, perserves the arguments. | ||
// Therefore, no null pointers. | ||
|
||
let c_str: &CStr = unsafe { CStr::from_ptr(*argv.offset(i as isize)) }; | ||
let string = c_str.to_string_lossy().into_owned(); | ||
vec_args.push(string.to_string()); | ||
} | ||
|
||
unsafe { FUNC_HOLDER.unwrap()(vec_args) } | ||
} | ||
|
||
pub fn init(func: fn(Vec<String>) -> i32, func_args: Vec<String>) -> i32 | ||
{ | ||
unsafe { FUNC_HOLDER = Some(func) }; | ||
let str_args: Vec<&str> = func_args | ||
.iter() | ||
.map(|s| s.as_str()) | ||
.collect::<Vec<&str>>(); | ||
let (argc, rust_argv) = create_c_args(&str_args); | ||
let argv = rust_argv.into_raw_parts().0; | ||
|
||
unsafe { | ||
return self::ffi::init(c_init, argc, argv); | ||
} | ||
} |
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.
Please explain what you mean by "you can use closure". A closure cannot be cast into a function pointer if it takes any arguments.
remove the static mut
variable and replace it with boxed closure
to store callback. use lifetime
with closure because it needs to live until explicitly freed.
refer(not boxed in it): #15 (comment)
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.
Do we really need the FUNC_HOLDER
? Can we not be a bit more barbaric and construct vec_args
within here? This makes the FUNC_HOLDER
go away at no cost.
The need to have a static variable global to this file is more of a burden to verify with the best practices and I intend to just go around it until I know better.
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.
You cannot put the c_init
function inside of the rust init
function because it captures the dynamic environment (the Vec function pointer). You cannot use a closure because that's not what the FFI allows. So the only way to do this is as follows (in pseudocode):
fn some_rust_callback(argc: i32, argv: *mut *mut c_char) -> i32 {
let args = c_args_to_vec(argc, argv);
return rust_func(args); // this is the problem! how do we get this function in scope?
}
fn rust_init(rust_func: fn(Vec<String>) -> i32, args: Vec<String>) -> i32 {
let (argc, argv) = vec_to_c_args(args);
return self::ffi::init(some_rust_callback, argc, argv);
}
How would we feel about this implementation? |
I lost the context, could you please remind? |
It is now clearer how to use this library. The example(s) are built by default.