#question about callback
1 messages · Page 1 of 1 (latest)
You can probably do this with some metaprogramming. You can use @typeInfo(container_type) on the container to get the declaration names of the callbacks, and then @field(container_type, decl_name) to get the callbacks
With more context of where the callbacks come from, I might be able to give a better example
I still don't really have enough context to know what you're doing. Do the listening types like camera and player controller have instances that have to be passed as arguments to the callback, and if so where do the instances come from in fooCallBack()? Basically like what are all the parameters for the functions
Right now I'm thinking it looks something like
pub const InputManager = struct {
listeners: struct {
cam: *Camera,
player_controller: *PlayerController,
},
fn fooCallback(im: *InputManager, event_data: EventType, ...) void {
inline for (@typeInfo(@TypeOf(im.listeners)).Struct.fields) |field| {
const listener = @field(im.listeners, field.name);
if (@hasDecl(listener, "fooCallback")) {
listener.fooCallback(event_data, ...);
}
}
}
};
Wrote that before your screenshot but I think the idea is similar
I can go through it, it's basically the second part of this message: there is a pointer to an instance of each listener in the input manager object
You call fooCallback() on an instance of an input_manager. There's some comptime logic in fooCallback to go through each of the listener's types, and determine if they care about whether they have fooCallback with @hasDecl. The listener variable is essentially something like im.listeners.cam or im.listeners.player_controller
So it's essentially doing: if im.listeners.cam.fooCallback is a real function, then call it, if im.listeners.player_controller.fooCallback is a real function, then call it.
the @hasDecl stuff will run at compile time, during semantic analysis, so it's basically as performant as just calling the ones that have the function manually
You mean it's not a comptime expression?
That should be fine, if I understand correctly. You can have SetKeyCallback run at runtime, and it sets the key callback to a "main" key callback which calls all the other ones like this
If I recall correctly, glfw only lets you set one key callback at a time anyways, so you'll need a single main callback to dispatch the other ones
Even though the logic for this function happens at comptime, you can call it at runtime
Essentially how it works is that the inline for gets unrolled in codegen into all the iterations in series, instead of doing a loop. Each iteration will have all the comptime code evaluated, leaving behind just runtime code
You shouldn't have to do that
setKeyCallback takes a function pointer, and you have a function in this class which calls all the listener's callbacks. You should just be able to pass this function
Are you using mach-glfw? I know the glfw C library has a way to pass a user pointer into a window object, so you can get it back in callbacks like this
If there's only one instance and you're willing to use globals, you could also make the instance manager a global variable
Looks like mach has window.setUserPointer and window.getUserPointer. You could have that point to the input manager, or some context that contains it
Yeah, good choice. mach-glfw is pretty nice
A user pointer is just a pointer to some extra data you want to associate with a glfw.Window
You can get and set it for a glfw.Window
How it'll work is:
When initializing the window, you'll set the callbacks to input_manager's callbacks with glfwSetXXCallback. You will set the user pointer to point to the input_manager instance with glfwSetUserPointer.
In the callback (keyPressCallback in your last screenshot), you will only be given a glfw.Window and the event data. Can't change the parameters here because it's determined by the GLFW API. But, you can get your input_manager instance back by doing const input_manager = window.getUserPointer(InputManager).?;. You can then do input_manager.keyPressed(window, key, scancode, action, mods);
Make sure the user pointer your passing doesn't change location
Like
fn setupStuff(...) void {
const input_manager = ...; // local variable
...
window.setUserPointer(&input_manager);
...
return input_manager; // or some struct that has it as a field, in any case it's changing location so the pointer is invalidated
}
invalid enum value probably means it wasn't initialized - the number it sees for the enum is outside of the options the enum has
you can try adding _ to any enums in input manager to see what value it thinks it is?
try adding _ in the enum: enum {a, b, c, _}
means the value of key is 0 which isn't valid for that enum
no
what are the fields in the enum?
well it's getting initialized to '0' which is invalid and is none of those
where do you set the user pointer?
that's fine as long as self sticks around
wait are you printing right below self.input_manager.init()?
which one is the print that crashes?
what is that initialized to?
so then either it's not initialized or the value is changed between when you initialied it and when you accessed it
it being 0 implies that it was zero initialized somehow (eg c_allocator) or maybe that it was on the stack and got overwritten? although I would expect a more random value for that
can't be overridden there because that's after the print that crashes
so the next time it's still the same?
does last_key_action change?
where is keyPressed called from?
where is this code?
I'm confused by keyPressCallback here - it calls keyPressed which looks in listeners? is one of them Camera and is that where the log is? or where is the print?
InputManager's init function is empty?
that should only be a problem for the first run though, why is it still 0 the next time?
it was getting zero-initialized somewhere