#Observer Pattern in C
1 messages · Page 1 of 1 (latest)
When your question is answered use !solved to mark the question as resolved.
Remember to ask specific questions, provide necessary details, and reduce your question to its simplest form. For tips on how to ask a good question use !howto ask.
Or maybe an equivalent way of doing this in C, I see a few ways but they all try to do it by emulating OOP is there a way to not do it like that?
I see a few ways but they all try to do it by emulating OOP
well observer pattern is an OOP concept, so...
Might want to take a look at callback functions
I see a few ways but they all try to do it by emulating OOP is there a way to not do it like that?
How would you describe the Observer pattern without using any OOP terminology/concepts?
We have a function f which has logic and at some point uses some state S, we don't want f to have access to S because S is not really related to what f does.
Ex) f controls a players movement but at the same time it needs to emit a sound if the player has jumped and so we had to pass S in as an argument to f, f(S, ...) instead we want to remove this dependency and inside of f we just do emit_jump_event() that way we can test f easily because it simply moves the player and doesn't need access to S.
I'm open to solutions which aren't the observer pattern I just couldn't think of any other term to explain this in a few words
soooo event callbacks?
Yeah that seems like a better way to describe it without OOP
struct Listener {
void* data;
void (*callback)(void* ptr, /* args ... */);
};
void foo(const struct Listener* listeners, size_t count) {
for(size_t i = 0; i < count; ++i) {
listeners[i].callback(listeners[i].data);
}
}
so something like that?
you might not be aware of function pointers
but they allow you to have dynamic function calls
and we usually let the callback have a data pointer
So callback would be a function we can define in another file right?
since the functions all have to have the same signature you use void* and then cast inside the callback
struct Bar {};
void bar_callback(void* in) {
struct Bar* bar = (struct Bar*)in;
// ...
}
yeah you can define the function wherever and whenever
anyway this is quite a common way to do event listeners/callbacks in c
in fact most oop polymorphism is just implemented using function pointers if you go deep enough into the implementation
Could you help me understand this in a more concrete way with my example?
you have an array/container of callbacks and you ... call them back when something happens
like we would have
void update_movement(Player *player) {
if (jump_pressed) {
jump_occurred_callback(...)
}
}
well you'd probably want some dynamically growing container to store all these
and then ways to add a pointer and a callback function to the list
and ways to remove one
You're suggesting this list for the case that many things need to respond to one event right?
yeah
if just 1 thing then you only need 1
but also at that point you probably don't need one at all
We wouldn't need a callback? Or the list system?
well if its always the same thing being called then you don't need a dynamic callback you just call it like anything else
you only need the function pointer if you might be calling different types with different functionality
Ok, in this case my question might be more about state and what functions are dependent on what state and how to remove dependencies from certain functions. For example in my code now we have update_movement and I have to pass in the sound_system because I need to play a sound when they jump
For testing I'd rather not have to make a sound system in order to see if movement is working correctly
So my goal is to remove the dependency on the sound system
;compile
#include <stdio.h>
#include <stdint.h>
struct Listener {
void* data;
void (*callback)(void* ptr);
};
void emit(const struct Listener* listeners, size_t count) {
for(size_t i = 0; i < count; ++i) {
listeners[i].callback(listeners[i].data);
}
}
struct Foo{};
void foo_callback(void* in) {
//struct Foo* foo = (struct Foo*)in;
printf("Foo: %p\n", in);
}
struct Bar {};
void bar_callback(void* in) {
//struct Bar* bar = (struct Bar*)in;
printf("Bar: %p\n", in);
}
int main() {
struct Foo foo;
struct Bar bar;
struct Listener listeners[2] = { { &foo, foo_callback }, { &bar, bar_callback } };
emit(listeners, 2);
}
Foo: 0x7ffcb2567a3f
Bar: 0x7ffcb2567a3e
Ah ok
so like the sound system doesn't need to exist
until you need one
and when you do you just add it as a listener
another name for this is "hooks" I believe
Ok, that makes sense
When I just have one thing that reacts to it you said it was better off not using a callback right?
well you're worried about this "dependency"
Yes
but like if it needs to go there it needs to go there
Right
usually what you do is write it the dumb way, the clean it up later if you need to
if you dont need to be clever then dont be clever
being clever is usually slow
Ok, that's what I thought. I was actually asking about something along these lines on the software engineering SO do you have any comments on this discussion: https://softwareengineering.stackexchange.com/questions/452813/game-systems-interaction-design ?
From what I spoke about with you it seems like design 1 is preferred, because it's not trying to do something smart?
well its hard
because what counts as a "system" is super vague
systems are not created equal
in my opinion (and yes this is an opinion there is no right way) thinking too much about the design is just a waste of time. You could have spent that time actually trying things to see which works
There will be better solutions
but you don't know what they are until you try them
and in trying them you learn more about the specific problem and why certain things are good or bad
and maybe can even come up with more options
e.g. maybe you want a combined approach
where some things are hard-coded and some are dynamic
Ok, yeah I tend to get frozen up because I'm scared that this deicision will turn out to be the wrong one and then 1000's of lines in I have to totally redo stuff
yeah well thats just programming
true
sometimes you have to do the big rewrite
ok I'm just going to write the code
also thinking and writing code aren't mutually exclusive :D
you can always keep the design in the back of your mind and review it as you go
either way youll probably be able to re-use some of the code you wrote
its likely the final structure will be similar
Right
but also thats partly what version control is for
we tried some stuff, it didn't work, lets go back
I also depend on some libraries that use c++ do you think it's still ok/good idea to write my code in c ?
well as long as they only use c++ internally it should probably work
I see so if their api's expose c++ specific stuff there'd be no real option I have to use c++ for at least that part
But I guess in terms of like my general code do you think it's a good idea to be using c for like this engine code we're talking about? I want to keep things simple that's why I was using c
well yes. If you can't describe it in c you can't put it in c
I want to keep things simple that's why I was using c
You use c if you want control over exactly what happens
c and c++ fulfil similar niches in that regard
simplicity in a language imo does't really matter for a personal thing
because you probably know most of what is going on, and as long as you're competent in the language then youre good
buuuut while c syntactically and semantically is simple its reallllllly easy to 1. shoot yourself in the foot (by writing illegal code) and 2. write confusing code
one of the reasons we didn't stop making languages when we got to c is that c is hard lol
thanks for discussing this with me
@valid linden Has your question been resolved? If so, type !solved :)
!solve