#Observer Pattern in C

1 messages · Page 1 of 1 (latest)

valid linden
#

Hey I am used to C++ where I used inheritance to build an observer pattern, how do we do this in C?

main dustBOT
#

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.

valid linden
#

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?

mossy basin
#

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

steady verge
valid linden
# steady verge > I see a few ways but they all try to do it by emulating OOP is there a way to ...

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

topaz pike
#

soooo event callbacks?

valid linden
#

Yeah that seems like a better way to describe it without OOP

topaz pike
#
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

valid linden
#

So callback would be a function we can define in another file right?

topaz pike
#

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;
  // ...
}
topaz pike
#

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

valid linden
#

Could you help me understand this in a more concrete way with my example?

topaz pike
#

you have an array/container of callbacks and you ... call them back when something happens

valid linden
#

like we would have

void update_movement(Player *player) {
  if (jump_pressed) {
    jump_occurred_callback(...)
  }
}
topaz pike
#

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

valid linden
#

You're suggesting this list for the case that many things need to respond to one event right?

topaz pike
#

yeah

#

if just 1 thing then you only need 1

#

but also at that point you probably don't need one at all

valid linden
topaz pike
#

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

valid linden
#

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

topaz pike
#

;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);
}
orchid lightBOT
#
Program Output
Foo: 0x7ffcb2567a3f
Bar: 0x7ffcb2567a3e
topaz pike
#

hmm

#

whats the function pointer syntax again

#

ah wait comma

#

thats how you use it

valid linden
#

Ah ok

topaz pike
#

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

valid linden
#

When I just have one thing that reacts to it you said it was better off not using a callback right?

topaz pike
valid linden
#

Yes

topaz pike
#

but like if it needs to go there it needs to go there

valid linden
#

Right

topaz pike
#

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

valid linden
#

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?

topaz pike
#

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

valid linden
#

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

topaz pike
#

yeah well thats just programming

valid linden
#

true

topaz pike
#

sometimes you have to do the big rewrite

valid linden
#

ok I'm just going to write the code

topaz pike
#

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

valid linden
#

Right

topaz pike
#

but also thats partly what version control is for

#

we tried some stuff, it didn't work, lets go back

valid linden
#

I also depend on some libraries that use c++ do you think it's still ok/good idea to write my code in c ?

topaz pike
#

well as long as they only use c++ internally it should probably work

valid linden
#

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

topaz pike
topaz pike
#

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

valid linden
#

thanks for discussing this with me

main dustBOT
#

@valid linden Has your question been resolved? If so, type !solved :)

valid linden
#

!solve