init
This commit is contained in:
6
.gitignore
vendored
Normal file
6
.gitignore
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
nob.exe*
|
||||
nob
|
||||
a.out
|
||||
a.exe
|
||||
temp.txt
|
||||
build/
|
||||
22
README.md
Normal file
22
README.md
Normal file
@@ -0,0 +1,22 @@
|
||||
# c_command_pattern
|
||||
|
||||
a simple example of how to implement the state pattern in c. check [`main.c`](src/main.c) for the implementation
|
||||
|
||||
# how to build and run it
|
||||
|
||||
this demo uses a simple build system called [nob](https://github.com/tsoding/nob.h) a header only build system for c projects.
|
||||
|
||||
to build run these command in the root of the project:
|
||||
```bash
|
||||
|
||||
# bootstraps the build system
|
||||
> gcc nob.c -o nob
|
||||
|
||||
# runs the build system
|
||||
> ./nob
|
||||
|
||||
# runs the program
|
||||
> ./build/main
|
||||
|
||||
```
|
||||
and if you have a differnt complier you want to use that is posix compliant just change the `CC` macro in the `nob.c` with your one.
|
||||
73
nob.c
Normal file
73
nob.c
Normal file
@@ -0,0 +1,73 @@
|
||||
// This is your build script. You only need to "bootstrap" it once with `cc -o nob nob.c`
|
||||
// (you can call the executable whatever actually) or `cl nob.c` on MSVC. After that every
|
||||
// time you run the `nob` executable if it detects that you modifed nob.c it will rebuild
|
||||
// itself automatically thanks to NOB_GO_REBUILD_URSELF (see below)
|
||||
|
||||
// nob.h is an stb-style library https://github.com/nothings/stb/blob/master/docs/stb_howto.txt
|
||||
// What that means is that it's a single file that acts both like .c and .h files, but by default
|
||||
// when you include it, it acts only as .h. To make it include implementations of the functions
|
||||
// you must define NOB_IMPLEMENTATION macro. This is done to give you full control over where
|
||||
// the implementations go.
|
||||
#define NOB_IMPLEMENTATION
|
||||
|
||||
// Always keep a copy of nob.h in your repo. One of my main pet peeves with build systems like CMake
|
||||
// and Autotools is that the codebases that use them naturally rot. That is if you do not actively update
|
||||
// your build scripts, they may not work with the latest version of the build tools. Here we basically
|
||||
// include the entirety of the source code of the tool along with the code base. It will never get
|
||||
// outdated (unless you got no standard compliant C compiler lying around, but at that point why are
|
||||
// you trying to build a C project?)
|
||||
//
|
||||
// (In these examples we actually symlinking nob.h, but this is to keep nob.h-s synced among all the
|
||||
// examples)
|
||||
#include "nob.h"
|
||||
|
||||
// Some folder paths that we use throughout the build process.
|
||||
#define BUILD_FOLDER "build/"
|
||||
#define SRC_FOLDER "src/"
|
||||
|
||||
#define CC "gcc"
|
||||
|
||||
#define SRCS \
|
||||
SRC_FOLDER"main.c", \
|
||||
SRC_FOLDER"device.c", \
|
||||
SRC_FOLDER"states/lock_state.c", \
|
||||
SRC_FOLDER"states/unlock_state.c", \
|
||||
SRC_FOLDER"states/debug_state.c", \
|
||||
SRC_FOLDER"states/off_state.c", \
|
||||
|
||||
#define C_ARGS
|
||||
// "-Wall", \
|
||||
// "-Wextra"
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
// This line enables the self-rebuilding. It detects when nob.c is updated and auto rebuilds it then
|
||||
// runs it again.
|
||||
NOB_GO_REBUILD_URSELF(argc, argv);
|
||||
|
||||
// It's better to keep all the building artifacts in a separate build folder. Let's create it if it
|
||||
// does not exist yet.
|
||||
//
|
||||
// Majority of the nob command return bool which indicates whether operation has failed or not (true -
|
||||
// success, false - failure). If the operation returned false you don't need to log anything, the
|
||||
// convention is usually that the function logs what happened to itself. Just do
|
||||
// `if (!nob_function()) return;`
|
||||
if (!nob_mkdir_if_not_exists(BUILD_FOLDER)) return 1;
|
||||
|
||||
// The working horse of nob is the Nob_Cmd structure. It's a Dynamic Array of strings which represent
|
||||
// command line that you want to execute.
|
||||
Nob_Cmd cmd = {0};
|
||||
|
||||
// nob.h ships with a bunch of nob_cc_* macros that try abstract away the specific compiler.
|
||||
// They are verify basic and not particularly flexible, but you can redefine them if you need to
|
||||
// or not use them at all and create your own abstraction on top of Nob_Cmd.
|
||||
|
||||
nob_cmd_append(&cmd, CC);
|
||||
nob_cmd_append(&cmd, C_ARGS);
|
||||
nob_cc_output(&cmd, BUILD_FOLDER "main");
|
||||
nob_cc_inputs(&cmd, SRCS);
|
||||
|
||||
if (!nob_cmd_run(&cmd)) return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
28
src/device.c
Normal file
28
src/device.c
Normal file
@@ -0,0 +1,28 @@
|
||||
#include "device.h"
|
||||
#include <string.h>
|
||||
|
||||
static void pressPwrMethod(Device_t *device) {
|
||||
device->state.methods.pressPwr(device);
|
||||
}
|
||||
|
||||
static void pressStrInputMethod(Device_t *device) {
|
||||
device->state.methods.pressStrInput(device);
|
||||
}
|
||||
|
||||
static void pressLockMethod(Device_t *device) {
|
||||
device->state.methods.pressLock(device);
|
||||
}
|
||||
|
||||
void initDevice(Device_t* device) {
|
||||
*device = (Device_t){
|
||||
.state = NULL,
|
||||
.entered_string = "frogs",
|
||||
.entered_string_len = 6,
|
||||
.methods = (DeviceInterface_t){
|
||||
.pressPwr = &pressPwrMethod,
|
||||
.pressStrInput = &pressStrInputMethod,
|
||||
.pressLock = &pressLockMethod,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
57
src/device.h
Normal file
57
src/device.h
Normal file
@@ -0,0 +1,57 @@
|
||||
#pragma once
|
||||
// lets assume we have some app with 4 states, 3 differnet input that change the state
|
||||
// states : [locked, unlocked, off, debug]
|
||||
// change actions : lock, str_input, power button
|
||||
//
|
||||
// pwr ┌──────────┐ pwr
|
||||
// ┌─────┴┬────►│off state │◄────┴┐
|
||||
// │ │ └────┬─────┘ │
|
||||
// │ │ │ │
|
||||
// │ │ ┌─────┘ │
|
||||
// │ │ ├ pwr │
|
||||
// │ │ ▼ │
|
||||
// │ ┌──┴─────┐ str=pwd ┌────┴───┐
|
||||
// │ │lock ├─────┴─────►│unlock │
|
||||
// │ │state │ │state │
|
||||
// │ └──────┬─┘◄───┬───────┴──┬─────┘
|
||||
// │ ^ │ lck │
|
||||
// │ lck ┤ ├ str=dbg ├ str=dbg
|
||||
// │ │ │ │
|
||||
// │ │ │ │
|
||||
// │ │ ▼ │
|
||||
// │ ┌─┴──────┐ │
|
||||
// └───┤debug │◄──────────────┘
|
||||
// │ │
|
||||
// └────────┘
|
||||
|
||||
typedef struct Device_s Device_t;
|
||||
|
||||
|
||||
typedef struct DeviceInterface_s{
|
||||
// all the actions that change the state
|
||||
// normally there wouldnt be any args
|
||||
// but as we dont have a protected keyword
|
||||
// we have to pass in the device to change
|
||||
// the state
|
||||
void (*pressPwr)(Device_t*);
|
||||
void (*pressStrInput)(Device_t*);
|
||||
void (*pressLock)(Device_t*);
|
||||
}DeviceInterface_t;
|
||||
|
||||
typedef struct DeviceState_s{
|
||||
const char* state_name;
|
||||
Device_t* device;
|
||||
DeviceInterface_t methods;
|
||||
}DeviceState_t;
|
||||
|
||||
struct Device_s{
|
||||
// in theroy this is also the flyweight pattern
|
||||
DeviceState_t state;
|
||||
char* entered_string;
|
||||
int entered_string_len;
|
||||
|
||||
DeviceInterface_t methods;
|
||||
};
|
||||
|
||||
|
||||
void initDevice(Device_t* device);
|
||||
22
src/main.c
Normal file
22
src/main.c
Normal file
@@ -0,0 +1,22 @@
|
||||
#include "states/state.h"
|
||||
#include "device.h"
|
||||
|
||||
int main() {
|
||||
Device_t device;
|
||||
initDevice(&device);
|
||||
// sets the device to off on start up
|
||||
setOffState(&device);
|
||||
|
||||
// turn on the device
|
||||
device.methods.pressPwr(&device);
|
||||
|
||||
// send the current string
|
||||
device.methods.pressStrInput(&device);
|
||||
|
||||
// send in the right string
|
||||
device.entered_string = "pwd";
|
||||
device.methods.pressStrInput(&device);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
33
src/states/debug_state.c
Normal file
33
src/states/debug_state.c
Normal file
@@ -0,0 +1,33 @@
|
||||
#include <stdio.h>
|
||||
|
||||
#include "state.h"
|
||||
#include "../device.h"
|
||||
|
||||
const char debug_state_name[] = __FILE_NAME__;
|
||||
|
||||
static void pressPwrMethod(Device_t *device) {
|
||||
printf("turning off device\n");
|
||||
setOffState(device);
|
||||
}
|
||||
|
||||
static void pressStrInputMethod(Device_t *device) {
|
||||
printf("nothing happens\n");
|
||||
}
|
||||
|
||||
static void pressLockMethod(Device_t *device) {
|
||||
printf("locking device\n");
|
||||
setOffState(device);
|
||||
}
|
||||
|
||||
void setDebugState(Device_t *device) {
|
||||
device->state = (DeviceState_t){
|
||||
.state_name = debug_state_name,
|
||||
.device = device,
|
||||
.methods = (DeviceInterface_t){
|
||||
.pressPwr = &pressPwrMethod,
|
||||
.pressStrInput = &pressStrInputMethod,
|
||||
.pressLock = &pressLockMethod,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
45
src/states/lock_state.c
Normal file
45
src/states/lock_state.c
Normal file
@@ -0,0 +1,45 @@
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "state.h"
|
||||
#include "../device.h"
|
||||
|
||||
const char lock_state_name[] = __FILE_NAME__;
|
||||
|
||||
static void pressPwrMethod(Device_t *device) {
|
||||
printf("turning off device\n");
|
||||
setOffState(device);
|
||||
}
|
||||
|
||||
static void pressStrInputMethod(Device_t *device) {
|
||||
if (strcmp(device->entered_string, "dbg") == 0) {
|
||||
printf("entering debug state\n");
|
||||
setDebugState(device);
|
||||
return;
|
||||
}
|
||||
if (strcmp(device->entered_string, "pwd") == 0) {
|
||||
printf("entering unlock state\n");
|
||||
setUnlockState(device);
|
||||
return;
|
||||
}
|
||||
|
||||
printf("unknown string %s\n", device->entered_string);
|
||||
|
||||
}
|
||||
|
||||
static void pressLockMethod(Device_t *device) {
|
||||
printf("nothing happens\n");
|
||||
}
|
||||
|
||||
void setLockState(Device_t *device) {
|
||||
device->state = (DeviceState_t){
|
||||
.state_name = lock_state_name,
|
||||
.device = device,
|
||||
.methods = (DeviceInterface_t){
|
||||
.pressPwr = &pressPwrMethod,
|
||||
.pressStrInput = &pressStrInputMethod,
|
||||
.pressLock = &pressLockMethod,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
31
src/states/off_state.c
Normal file
31
src/states/off_state.c
Normal file
@@ -0,0 +1,31 @@
|
||||
#include "state.h"
|
||||
#include "../device.h"
|
||||
#include <stdio.h>
|
||||
|
||||
const char off_state_name[] = __FILE_NAME__;
|
||||
|
||||
static void pressPwrMethod(Device_t *device) {
|
||||
printf("turning on device\n");
|
||||
setLockState(device);
|
||||
}
|
||||
|
||||
static void pressStrInputMethod(Device_t *device) {
|
||||
printf("nothing happens\n");
|
||||
}
|
||||
|
||||
static void pressLockMethod(Device_t *device) {
|
||||
printf("nothing happens\n");
|
||||
}
|
||||
|
||||
void setOffState(Device_t *device) {
|
||||
device->state = (DeviceState_t){
|
||||
.state_name = off_state_name,
|
||||
.device = device,
|
||||
.methods = (DeviceInterface_t){
|
||||
.pressPwr = &pressPwrMethod,
|
||||
.pressStrInput = &pressStrInputMethod,
|
||||
.pressLock = &pressLockMethod,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
7
src/states/state.h
Normal file
7
src/states/state.h
Normal file
@@ -0,0 +1,7 @@
|
||||
#pragma once
|
||||
#include "../device.h"
|
||||
|
||||
void setLockState(Device_t *device);
|
||||
void setUnlockState(Device_t *device);
|
||||
void setDebugState(Device_t *device);
|
||||
void setOffState(Device_t *device);
|
||||
37
src/states/unlock_state.c
Normal file
37
src/states/unlock_state.c
Normal file
@@ -0,0 +1,37 @@
|
||||
#include "state.h"
|
||||
#include "../device.h"
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
const char name[] = __FILE_NAME__;
|
||||
|
||||
static void pressPwrMethod(Device_t *device) {
|
||||
printf("turning off device\n");
|
||||
setOffState(device);
|
||||
}
|
||||
|
||||
static void pressStrInputMethod(Device_t *device) {
|
||||
if (strcmp(device->entered_string, "dbg") == 0) {
|
||||
printf("entering debug state\n");
|
||||
setDebugState(device);
|
||||
}
|
||||
printf("unknown string %s\n", device->entered_string);
|
||||
}
|
||||
|
||||
static void pressLockMethod(Device_t *device) {
|
||||
printf("locking device\n");
|
||||
setLockState(device);
|
||||
}
|
||||
|
||||
void setUnlockState(Device_t *device) {
|
||||
device->state = (DeviceState_t){
|
||||
.state_name = name,
|
||||
.device = device,
|
||||
.methods = (DeviceInterface_t){
|
||||
.pressPwr = &pressPwrMethod,
|
||||
.pressStrInput = &pressStrInputMethod,
|
||||
.pressLock = &pressLockMethod,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user