Skip to content

Commit aa7b89c

Browse files
committed
Upload source to Git from archive.
0 parents  commit aa7b89c

File tree

8 files changed

+3249
-0
lines changed

8 files changed

+3249
-0
lines changed

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
*.out
2+
*vscode
3+
*.dSYM

README.md

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
# array_alg.h
2+
3+
Unintrusive algorithms for C arrays OR a C implementation of \<algorithm> from C++
4+
5+
## Pitch
6+
7+
The C++ STL is one of the most complete and reusable algorithm libraries available.
8+
This single header file brings 80% of that functionality to C99 in a non-intrusive way.
9+
There are no new data structures. Just include the library and call functions on C arrays.
10+
11+
Features:
12+
13+
- Sets (intersection, union, subset, etc)
14+
- Heaps (priority queues)
15+
- Binary search (lower bound, upper bound, etc)
16+
- Sorts (insertion sort, quick sort, merge/stable sort, heap sort, partial sort, etc)
17+
- Partitioning (partition, unique, etc)
18+
- Predicates (all of, any of, none of, etc)
19+
- Uniform random sampling and shuffling
20+
21+
## Usage
22+
23+
This library uses the preprocessor to implement generic functions.
24+
Each time you include the library, you will need to define the array element type and a function prefix:
25+
26+
#define ARRAY_ALG_TYPE int
27+
#define ARRAY_ALG_PREFIX intv_
28+
#include "array_alg.h"
29+
30+
The above will only generate the declarations.
31+
In at least one C file, you will also need to generate implementations.
32+
To generate implementations, define `ARRAY_ALG_IMPLEMENTATION` in a C file and include the library:
33+
34+
#define ARRAY_ALG_TYPE int
35+
#define ARRAY_ALG_PREFIX intv_
36+
#define ARRAY_ALG_IMPLEMENTATION
37+
#include "array_alg.h"
38+
39+
Alternatively, you can include the algorithms as static functions to avoid the need for separate implementations:
40+
41+
#define ARRAY_ALG_STATIC
42+
#define ARRAY_ALG_TYPE int
43+
#define ARRAY_ALG_PREFIX intv_
44+
#define ARRAY_ALG_IMPLEMENTATION
45+
#include "array_alg.h"
46+
47+
Repeat this process for each array type you want to use.
48+
49+
## Examples
50+
51+
Remove duplicate entries:
52+
53+
#define ARRAY_ALG_TYPE int
54+
#define ARRAY_ALG_PREFIX intv_
55+
#include "array_alg.h"
56+
57+
int compare_int(const int *a, const int *b, void *ctx) {
58+
return *a - *b;
59+
}
60+
61+
...
62+
63+
int nums[100] = ...;
64+
intv_sort(nums, nums + 100, compare_int, NULL);
65+
int* end = intv_unique(nums, nums + 100, compare_int, NULL);
66+
67+
68+
## Design
69+
70+
1. Iterators and Arrays
71+
72+
The C++ STL is designed around the concept of iterators.
73+
With iterators, one algorithm can be reused not just for multiple types, but also for many data structures.
74+
This is an ingenious design.
75+
However, in practice, this capability is rarely needed.
76+
The vast majority of real world \<algorithm> invocations are on contiguous arrays/vectors.
77+
78+
For those cases where you do have a fancy data structure (graphs, trees, etc),
79+
copy its contents to an array, perform the algorithm, and then copy the contents back.
80+
This will often help it perform better anyway!
81+
82+
2. Bounds vs counted ranges
83+
84+
STL algorithms typically operate on half-open ranges bounded by iterators [first, last).
85+
This convention is not used as often in C, but we think it offers some benefits.
86+
Internally, the functions can maintain less state by simply incrementing pointers
87+
rather than keeping track of pointers, indices, and counts.
88+
89+
Operations also compose a little easier.
90+
When a pointer is returned to an element of interest,
91+
that same pointer can be used as an argument for another algorithm.
92+
93+
3. What's left out
94+
95+
Because it's a bit verbose to define a C closure (function pointer and context), some STL algorithms are less useful in C.
96+
If an algorithm can be written as a simple for loop with no additional state or control flow, this library doesn't implement it.
97+
98+
transform -> for (int i = 0; i < n; ++i) out[i] = f(in[i])
99+
fill -> for (int i = 0; i < n; ++i) out[i] = x;
100+
iota -> for (int i = 0; i < n; ++i) out[i] = i;
101+
generate -> for (int i = 0; i < n; ++i) out[i] = f();
102+
103+
The algorithms which rely on ordered types always require a comparison function.
104+
We do not include any variants that operate on the `==` operator, as operators cannot be overloaded in C.
105+
106+
4. Generics vs `void*`
107+
108+
Including a header multiple times with various `#define`s is a little cumbersome.
109+
However, we think it's a superior way to achieve C generics compared to the `void*` style used by `qsort` and `bsearch`.
110+
The preprocessor approach provides:
111+
112+
- Better type safety and avoids verbose casting logic.
113+
114+
- Better peformance (as `void*` functions are difficult to optimize).
115+
116+
Note: The C compiler can only create one non-inlined version of each function.
117+
For example, it could not choose to use `int` instructions, even if it knew the type at compile time.
118+
With the single header approach you get a new instance of each function optimized for each application.

0 commit comments

Comments
 (0)