Compare commits
9 Commits
3f957b0be5
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fd589d4e00 | ||
|
|
3317a7cc33 | ||
|
|
bc93460d04 | ||
|
|
0110b585b4 | ||
|
|
cf627eac0d | ||
|
|
ab4ab04957 | ||
|
|
5f41dc2baf | ||
|
|
26823cb754 | ||
|
|
2ff1d0e94b |
6
.gitignore
vendored
Normal file
6
.gitignore
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
nob.exe*
|
||||
nob
|
||||
a.out
|
||||
a.exe
|
||||
temp.txt
|
||||
build/
|
||||
21
README.md
21
README.md
@@ -1,3 +1,22 @@
|
||||
# c_command_pattern
|
||||
|
||||
a simple example of how to implement the command pattern in c
|
||||
a simple example of how to implement the command 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.
|
||||
69
nob.c
Normal file
69
nob.c
Normal file
@@ -0,0 +1,69 @@
|
||||
// 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"input.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;
|
||||
}
|
||||
11
src/command.h
Normal file
11
src/command.h
Normal file
@@ -0,0 +1,11 @@
|
||||
#pragma once
|
||||
|
||||
#define exe(cmd) cmd.command(cmd.args, cmd.ret);
|
||||
|
||||
typedef void (*CommandFunction)(void* args, void* ret);
|
||||
|
||||
typedef struct Command_s{
|
||||
void* args;
|
||||
void* ret;
|
||||
CommandFunction command;
|
||||
}Command_t;
|
||||
16
src/input.c
Normal file
16
src/input.c
Normal file
@@ -0,0 +1,16 @@
|
||||
#include <stdio.h>
|
||||
#include "input.h"
|
||||
#include "command.h"
|
||||
|
||||
// in theroy your input should be a single ton but i want to support multiple interfaces
|
||||
void handleInput(Input_t input_device) {
|
||||
Inputs_e input = input_device.handle.getInput();
|
||||
|
||||
switch (input) {
|
||||
case BUTTON_UP: exe(input_device.button_up_cmd); break;
|
||||
case BUTTON_LEFT: exe(input_device.button_left_cmd); break;
|
||||
case BUTTON_RIGHT: exe(input_device.button_right_cmd); break;
|
||||
case BUTTON_DOWN: exe(input_device.button_down_cmd); break;
|
||||
default: printf("HOW THE FUCK DID YOU PRESS A NON EXISTANT BUTTON"); break;
|
||||
}
|
||||
}
|
||||
26
src/input.h
Normal file
26
src/input.h
Normal file
@@ -0,0 +1,26 @@
|
||||
#pragma once
|
||||
|
||||
#include "command.h"
|
||||
|
||||
typedef enum {
|
||||
BUTTON_UP,
|
||||
BUTTON_LEFT,
|
||||
BUTTON_RIGHT,
|
||||
BUTTON_DOWN,
|
||||
TOTAL_COMMANDS,
|
||||
} Inputs_e;
|
||||
|
||||
typedef struct InputHandler_s {
|
||||
Inputs_e (*getInput)(void);
|
||||
}InputHandler_t;
|
||||
|
||||
typedef struct Input_s{
|
||||
Command_t button_up_cmd;
|
||||
Command_t button_left_cmd;
|
||||
Command_t button_right_cmd;
|
||||
Command_t button_down_cmd;
|
||||
InputHandler_t handle;
|
||||
}Input_t;
|
||||
|
||||
void handleInput(Input_t input_device);
|
||||
|
||||
90
src/main.c
Normal file
90
src/main.c
Normal file
@@ -0,0 +1,90 @@
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "command.h"
|
||||
#include "input.h"
|
||||
|
||||
// this is the underlying function
|
||||
int printButton(const char* string) {
|
||||
// printf actually returns the number of chars printed to the screen
|
||||
return printf(string);
|
||||
}
|
||||
|
||||
// this should live in the app logic
|
||||
void commandWrapper(void* args, void* ret) {
|
||||
// we cast ret to a int and then place the return val in to it
|
||||
*(int*)(ret) = printButton(*(const char**)(args));
|
||||
}
|
||||
|
||||
|
||||
// this should be in your input driver, so in here you may have a task
|
||||
// that is getting these inputs and adding them to a queue
|
||||
static Inputs_e current_input = BUTTON_DOWN;
|
||||
Inputs_e getinput() {
|
||||
return current_input;
|
||||
}
|
||||
|
||||
|
||||
const char* button_up_cmd_args = "up button pressed\n";
|
||||
int button_up_cmd_return_val = 0; // dont leave thing uninstalised
|
||||
|
||||
const char* button_left_cmd_args = "left button pressed\n";
|
||||
int button_left_cmd_return_val = 0; // dont leave thing uninstalised
|
||||
|
||||
const char* button_right_cmd_args = "right button pressed\n";
|
||||
int button_right_cmd_return_val = 0; // dont leave thing uninstalised
|
||||
|
||||
const char* button_down_cmd_args = "down button pressed\n";
|
||||
int button_down_cmd_return_val = 0; // dont leave thing uninstalised
|
||||
|
||||
// this should be implemented with your app logic
|
||||
static Input_t input_device = (Input_t){
|
||||
.button_up_cmd = (Command_t){
|
||||
.args = (void*)(&button_up_cmd_args),
|
||||
.ret = (void*)(&button_up_cmd_return_val),
|
||||
.command = &commandWrapper,
|
||||
},
|
||||
.button_left_cmd = (Command_t){
|
||||
.args = (void*)(&button_left_cmd_args),
|
||||
.ret = (void*)(&button_left_cmd_return_val),
|
||||
.command = &commandWrapper,
|
||||
},
|
||||
.button_right_cmd = (Command_t){
|
||||
.args = (void*)(&button_right_cmd_args),
|
||||
.ret = (void*)(&button_right_cmd_return_val),
|
||||
.command = &commandWrapper,
|
||||
},
|
||||
.button_down_cmd = (Command_t){
|
||||
.args = (void*)(&button_down_cmd_args),
|
||||
.ret = (void*)(&button_down_cmd_return_val),
|
||||
.command = &commandWrapper,
|
||||
},
|
||||
.handle = (InputHandler_t) {
|
||||
.getInput = &getinput,
|
||||
}
|
||||
};
|
||||
|
||||
int main() {
|
||||
|
||||
// we have our input now we will handle it
|
||||
|
||||
// arange
|
||||
// set that current input is being pressed
|
||||
current_input = BUTTON_UP;
|
||||
|
||||
// act
|
||||
// poll the input a
|
||||
handleInput(input_device);
|
||||
|
||||
// assert
|
||||
if ((int)(strlen(button_up_cmd_args)) == button_up_cmd_return_val) {
|
||||
printf("return value correct");
|
||||
} else {
|
||||
printf("return value incorrect");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user