making-readme-more-tutorial-like #1

Merged
sirlilpanda merged 2 commits from making-readme-more-tutorial-like into main 2025-09-02 00:51:18 -07:00
Showing only changes of commit 98d39992a0 - Show all commits

View File

@@ -3,6 +3,8 @@
A simple example of how to implement the state pattern in c. check [`main.c`](src/main.c) for the implementation.
the state pattern is a pattern that aims to get rid of the mess that a state machine causes within your code. So i propose a problem for you, you are trying to write an state machine for a phone, sounds easy your phone only has 3 inputs that change the current state of the phone.
#### inputs:
- power button
- lock button
- string enter button
@@ -10,6 +12,8 @@ the state pattern is a pattern that aims to get rid of the mess that a state mac
the power button does what it says either turns the phone off or on, the lock button too does what it says on the tin locks the phone, lastly the string enter button enters what every string is in the phones buffer this is used for entering things like passwords to unlock the phone.
using these buttons you determine you need 4 states for your phone these are:
#### states:
- off
- unlock
- locked
@@ -56,7 +60,10 @@ $$
$$
that would be a 2k line state machine and a nightmare to debug (i should know ive seen them in the wild)
so to start implement this pattern we first draw up a uml diagram of how it should go together. we can see we have the device itself and which is composed of a device state that has the pointer to the device that it is in
so to start implement this pattern we first draw up a uml diagram of how it should go together. we can see we have the device itself and which is composed of a device state, and the entered string.
This "class" also has 3 methods pressPwrButton, pressStrInputButton, and pressLockButton. these methods are used to create an abstraction from the device state so the underlying state can change while using the same input functions.
```mermaid
classDiagram
@@ -88,12 +95,41 @@ classDiagram
```
(note not real uml diagram because no one knows how to read them)
1. so to implement this we will first start off with the [device struct](/src/device.h#L67)
```c
typedef struct Device_s{
DeviceState_t state,
char* entered_string,
} Device_t;
```
2. Next we will implement the [methods](/src/device.h#L34) which in this case will be an [interface](https://en.wikipedia.org/wiki/Interface_(object-oriented_programming)). These methods are all the changes that can be made to the device i.e. your [inputs](#inputs).
```c
typedef struct DeviceInterface_s{
void (*pressPwr)(Device_t*);
void (*pressStrInput)(Device_t*);
void (*pressLock)(Device_t*);
} DeviceInterface_t;
```
so to implement this we will first create a struct for the device well call this [Device_t](/src/device.h#L67) this device holds its current state ([DeviceState_s](/src/device.h#L52)) and the string that was entered. The [DeviceState_s](/src/device.h#L52) actually holds all the [functions](/src/device.h#L34) or [methods](/src/device.h#L34) that this device uses, with the pressPwrButton, pressStrInputButton, and pressLockButton methods just being alias to these methods as can be seen in [device.c](/src/device.c). these method are defined within the [DeviceInterface_t](/src/device.h#L34) struct which really just acts as a [vtable](https://en.wikipedia.org/wiki/Virtual_method_table) for the given inputs (power button, lock button, string enter button) that will effect the device.
This whole interface allows for the underlying state to change with out having to use different function calls depending on the current state.
3. and finally we will implement the [struct to hold the actual state](/src/device.h#L52) of the device. (note there is some funkiness in c with implementing this since you will need forward decls)
```c
typedef struct DeviceState_s{
const char* state_name;
DeviceInterface_t methods;
}DeviceState_t;
```
4. now we need to implement the 3 functions for device ive implemented one here as the rest are very [similar](/src/device.c#L14). So this function takes the give device and then uses the methods defined within the
```c
void pressPwrButton(Device_t *device) {
device->state.methods.pressPwr(device);
}
```
now to actually create the meat of this device the logic that changes the state, to do this we will first create a [header file](/src/states/state.h) to store all the functions that change the state of the device. now we will implement and state so we will start [off state](/src/states/off_state.c) with the easiest one the off state (no bother having the rest if the device cant turn on). to create this state we will implement the functions that are defined within the [interface](/src/device.h#L34) we created, this functions will define what happens to the current state based on the given input. so `pressPwrMethod` is what happens when the power button is pressed when its in the off state (to make the code [grep-able](https://morizbuesing.com/blog/greppability-code-metric/) these functions should probably be prefixed with the given state like `OffStatePressPwrMethod`). these function are prefix with the static keyword as they should only ever be used here and not exposed. the other methods should be self explanatory. lastly we must implement the function that actually changes the state of the device to this given state, so to change the state we just set the devices state to a new struct with the given methods that we defined within this file.
now to actually create the meat of this device, we need to implement the logic that changes the state. To do this we will first create a [header file](/src/states/state.h) to store all the functions that change the state of the device. now we will implement and state so we will start [off state](/src/states/off_state.c) with the easiest one the off state (no bother having the rest if the device cant turn on). to create this state we will implement the functions that are defined within the [interface](/src/device.h#L34) we created, this functions will define what happens to the current state based on the given input. so `pressPwrMethod` is what happens when the power button is pressed when its in the off state (to make the code [grep-able](https://morizbuesing.com/blog/greppability-code-metric/) these functions should probably be prefixed with the given state like `OffStatePressPwrMethod`). these function are prefix with the static keyword as they should only ever be used here and not exposed. the other methods should be self explanatory. lastly we must implement the function that actually changes the state of the device to this given state, so to change the state we just set the devices state to a new struct with the given methods that we defined within this file.
```c