summaryrefslogtreecommitdiff
path: root/readme.md
blob: eb3f631762e0bb20ccbdadf81308ff33472232e0 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
# funCtional

Generic functional programming in C with support for statically allocated memory. You should probably use a `for` loop but this is fun!

## Usage

### Setup

To get started, pack your `Functional` struct with the required pointers and callbacks:

```C
Person people[] = {{.age = 19, .name = "Alex"},
                     {.age = 42, .name = "Jenna"},
                     {.age = 55, .name = "Tom"}};
int ages[3];
Person filteredPeople[3];

Functional f = {
    .args = {.i = 0,
             .n = 3,
             .arr = (void*)people,
             .ctxp = (void*)"Harry",
             .storage = filteredPeople},
    .filter = shouldIncludePerson,
    .map = copyPerson,
    .derefInput = derefPerson,
};
```

#### f.filter

Called to determine if `f.args.arr[f.args.i]` should be included in the filtered collection (for `func_filter` or `func_find`). The caller is responsible for copying filtered items using `f.map` when calling `func_filter`.

#### f.map

Called to copy or transform `f.args.arr[f.args.i]`. This callback is required for `func_filter` and `func_map`, however when used with `func_filter`, it's only responsibility should be copying.

#### f.derefInput

Required by `func_find` to dereference the correct element of `f.args.arr`.

#### f.args.arr

A `void*` pointer to the first element of the array to be iterated over.

#### f.args.storage

A `void*` pointer to the first element of the storage array where mapped and filtered elements will be copied.

#### f.args.i

Iteration index used to iterate over `args.arr` where `args.i < args.n`.

#### f.args.n

Total length of `arg.arr`. After running, this will be changed to the length of `args.storage`

#### f.args.offset

When filtering, `args.i` increments to cover all of `args.arr`, but `arr.storage` may contain fewer items. Therefore, the callback responsible for copying when filtering (`f.map`) may need to account for this difference (see `example.c`).

#### f.args.ctxp

Context pointer that is freely available for user use.

### Example

```C
#include "functional.h"
#define MAX_STR_SIZE 400

typedef struct {
  unsigned int age;
  char* name;
} Person;

bool shouldIncludePerson(FunctionalArgs* args) {
  const char* toExclude = args->ctxp;
  Person* first = args->arr;
  Person* p = &first[args->i];
  return p->age > 30 && strcmp(toExclude, p->name);
}

void* derefPerson(int i, void* arr) {
  Person* p = arr;
  return (void*)&p[i];
}

int stringifyPerson(Person* p, char* out, size_t n) {
  return snprintf(out, n, "Person %s is %d years of age, yo\n", p->name,
                  p->age);
}

void printPerson(Person* p) {
  char personStr[MAX_STR_SIZE];
  stringifyPerson(p, personStr, MAX_STR_SIZE);
  printf("%s", personStr);
}

void shiftPersonLeft(FunctionalArgs* args) {
  Person* people = args->arr;
  for (int i = args->i; i < args->n - 1; i++) {
    people[i] = people[i + 1];
  }
  args->n--;
}

void mapPersonToAge(FunctionalArgs* args) {
  int* storage = args->storage;
  Person* people = args->arr;
  storage[args->i] = people[args->i].age;
}

void copyPerson(FunctionalArgs* args) {
  Person* src = args->arr;
  Person* dest = args->storage;
  dest[args->i - args->offset] = src[args->i];
}

void mapExample(Functional* f) {
  func_map(f);
  for (int i = 0; i < f->args.n; i++) {
    int* ages = f->args.storage;
    printf("age[%d] = %d\n", i, ages[i]);
  }
}

void filterExample(Functional* f) {
  func_filter(f);
  for (int i = 0; i < f->args.n; i++) {
    Person* people = f->args.storage;
    printPerson(&people[i]);
  }
}

void findExample(Functional* f) {
  Person* olderAndNotHarry = func_find(f);
  printPerson(olderAndNotHarry);
}

int main() {
  Person people[] = {{.age = 19, .name = "Alex"},
                     {.age = 42, .name = "Jenna"},
                     {.age = 55, .name = "Tom"}};
  int ages[3];
  Person filteredPeople[3];
  Functional func = {
      .args = {.i = 0,
               .n = 3,
               .arr = (void*)people,
               .ctxp = (void*)"Harry",
               .storage = filteredPeople},
      .filter = shouldIncludePerson,
      .map = copyPerson,
      .derefInput = derefPerson,
  };
  findExample(&func);
  filterExample(&func);
  func.args.storage = ages;
  func.map = mapPersonToAge;
  func.args.n = 3;
  mapExample(&func);
}
```