-
Notifications
You must be signed in to change notification settings - Fork 45
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
C callback signature requires C++ bindings to allocate #201
Comments
// inline
f((WGPUBufferMapCallbackInfo){mode, callback, userdata, userdata2});
// out-of-line
WGPUBufferMapCallbackInfo info = {mode, callback, userdata, userdata2};
f(info);
// C++
f({mode, callback, userdata, userdata2}); We also discussed just having a single struct for {mode, userdata, userdata2} and putting the callback separately. But we thought this was slightly better and it doesn't matter a ton. |
April 25th meeting:
|
How do you handle the lifetime of lambdas in the C++ version? This is something I've never been happy with in my wrapper (allocating a unique_ptr and returning it as a handle that I leave the user responsible of keeping alive). |
@eliemichel dawn hasn't done it yet, but in the linked gist, it's done manually with |
Oh ok now I realize that FTR what my wrapper is doing is like this: using ErrorCallback = std::function<void(ErrorType type, char const * message)>;
std::unique_ptr<ErrorCallback> Device::setUncapturedErrorCallback(ErrorCallback&& callback) {
auto handle = std::make_unique<ErrorCallback>(callback);
static auto cCallback = [](WGPUErrorType type, char const * message, void * userdata) -> void {
ErrorCallback& callback = *reinterpret_cast<ErrorCallback*>(userdata);
callback(static_cast<ErrorType>(type), message);
};
wgpuDeviceSetUncapturedErrorCallback(m_raw, cCallback, reinterpret_cast<void*>(handle.get()));
return handle;
} This only uses a single userdata API (which is all there is so far, right?) but then requires to make sure the user pointer returned by |
We would always do a single allocation: use std::ffi::c_void;
extern "C" {
fn wgpuMyOneShotCallback(func: extern "C" fn(*mut c_void), user_data: *mut c_void);
}
extern "C" fn my_oneshot_callback_wrapper<F>(raw: *mut c_void)
where
F: FnOnce(),
{
let raw_typed: *mut F = raw.cast();
let alloc: Box<F> = unsafe { Box::from_raw(raw_typed) };
alloc()
}
pub fn my_oneshot_callback<F>(func: F)
where
F: FnOnce(),
{
// If F captures nothing, Box::new doesn't allocate.
let alloc: Box<F> = Box::new(func);
let raw_typed: *mut F = Box::into_raw(alloc);
let raw: *mut c_void = raw_typed.cast();
unsafe {
wgpuMyOneShotCallback(my_oneshot_callback_wrapper::<F>, raw);
};
} So we wouldn't hit this super magically zero allocation case, but that case really wouldn't work well in rust. |
@lokokung is starting to add the second userdata in Dawn, and with it, C++ niceties for CallCallback2_T (using 2 userdatas) and CallCallback_LT (also with an optimization to avoid the allocation when the lambda is just a function pointer - the caller needed zero userdatas). Once stuff starts landing I can link to the generated code in codesearch to see what it actually looks like (won't have CallCallback_LT yet though). |
This was a long offshoot of #158. I should have moved it to a new thread a long time ago, but now that we have a proposed resolution I'm filing this so I can label it separately.
The text was updated successfully, but these errors were encountered: