Skip to content

Commit cd35d39

Browse files
move convenience macros out of typedef'd block. README for macros.
1 parent aad9540 commit cd35d39

File tree

2 files changed

+109
-29
lines changed

2 files changed

+109
-29
lines changed

README.md

Lines changed: 91 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
# C Vector Library
2-
A simple vector library for C. This library's vectors work in a similar manner to C++ vectors: they can store any type, their elements can be accessed via the `[]` operator, and elements may be added or removed with simple library calls.
2+
3+
A simple vector library for C. This library's vectors work in a similar manner to C++ vectors: they can store any type,
4+
their elements can be accessed via the `[]` operator, and elements may be added or removed with simple library calls.
35

46
You can easily create a vector like so:
57

@@ -32,7 +34,9 @@ vector_add(&char_vec, 'a');
3234
printf("first item: %c\n", char_vec[0]);
3335
```
3436
35-
Vectors require that their pointer type corresponds to the contents of the vector (e.g. `int*` for a vector containing `int`s); otherwise, you have to explicitly cast the vector pointer to the correct type when accessing elements or making library calls:
37+
Vectors require that their pointer type corresponds to the contents of the vector (e.g. `int*` for a vector containing
38+
`int`s); otherwise, you have to explicitly cast the vector pointer to the correct type when accessing elements or making
39+
library calls:
3640
3741
```c
3842
// recommended vector use
@@ -51,13 +55,16 @@ vector_add(&(int*)bar, 3);
5155
5256
((int*)bar)[0] = 5; // we have to cast to an int* so we can properly access vector elements
5357
```
58+
5459
Note: the `vector` type is just an alias for `void*` defined in `vec.h`.
5560

5661
# How It Works
5762

58-
These vectors can be manipulated in a similar manner to C++ vectors; their elements can be accessed via the `[]` operator and elements may be added or removed through the use of simple library calls.
63+
These vectors can be manipulated in a similar manner to C++ vectors; their elements can be accessed via the `[]`
64+
operator and elements may be added or removed through the use of simple library calls.
5965

60-
This works because these vectors are stored directly alongside special header in memory, which keeps track of the vector's size and capacity:
66+
This works because these vectors are stored directly alongside special header in memory, which keeps track of the
67+
vector's size and capacity:
6168

6269
+--------+-------------+
6370
| Header | Vector data |
@@ -67,17 +74,28 @@ This works because these vectors are stored directly alongside special header in
6774

6875
This design was inspired by anitrez's [Simple Dynamic Strings](https://github.com/antirez/sds/).
6976

70-
This library uses the preprocessor to perform compile-time type checks. The type checks are done using C23's `typeof` operator, which was actually implemented in some compilers before C23 (such as GCC and Clang). [Older versions of MSVC](https://learn.microsoft.com/en-us/cpp/c-language/typeof-c?view=msvc-170#requirements) do not support the `typeof` operator. See [Missing typeof Reference Sheet](#missing-typeof-reference-sheet) for info about vector usage when `typeof` is not present.
77+
This library uses the preprocessor to perform compile-time type checks. The type checks are done using C23's `typeof`
78+
operator, which was actually implemented in some compilers before C23 (such as GCC and
79+
Clang). [Older versions of MSVC](https://learn.microsoft.com/en-us/cpp/c-language/typeof-c?view=msvc-170#requirements)
80+
do not support the `typeof` operator. See [Missing typeof Reference Sheet](#missing-typeof-reference-sheet) for info
81+
about vector usage when `typeof` is not present.
7182

72-
If you're using this library in a C++ project, and `decltype` is supported, a macro substitute for `typeof` will automatically be applied.
83+
If you're using this library in a C++ project, and `decltype` is supported, a macro substitute for `typeof` will
84+
automatically be applied.
7385

7486
# Usage
7587

76-
Just because these vectors can be accessed and modified like regular arrays doesn't mean they should be treated the same in all cases.
88+
Just because these vectors can be accessed and modified like regular arrays doesn't mean they should be treated the same
89+
in all cases.
7790

7891
Because of the hidden header data, these vectors can only be properly freed by calling `vector_free(vec)`.
7992

80-
Since the header data is stored in the same location as the the vector's elements, the entire vector might be moved to a new location when its size is changed. This means the library calls that modify the vector's capacity need to take the address of the vector pointer, e.g `&vec`, so it can be reassigned. This obviously means that you can't have copies of the same vector pointer in multiple locations (unless the vector's capacity is constant), but this issue can be easily remedied by indirectly referencing the vector, either by referencing some kind of structure that contains the vector pointer, or by referencing the vector pointer itself.
93+
Since the header data is stored in the same location as the the vector's elements, the entire vector might be moved to a
94+
new location when its size is changed. This means the library calls that modify the vector's capacity need to take the
95+
address of the vector pointer, e.g `&vec`, so it can be reassigned. This obviously means that you can't have copies of
96+
the same vector pointer in multiple locations (unless the vector's capacity is constant), but this issue can be easily
97+
remedied by indirectly referencing the vector, either by referencing some kind of structure that contains the vector
98+
pointer, or by referencing the vector pointer itself.
8199

82100
Here is an example using a function that reassigns the vector pointer:
83101

@@ -88,7 +106,11 @@ int* baz = vector_create();
88106
vector_add(&baz, 5); // takes the address of the `int*` in case the pointer needs to be changed
89107
```
90108
91-
This is another similarity to *Simple Dynamic Strings*, which has library calls that are a little more clear in their reassignment, e.g. `s = sdscat(s,"foo")`, but this functionality would break the `vector_add` and `vector_insert` macros, which already expand to an assignment expression. If you're using a compiler that supports the `typeof` operator, the vector macros will perform compile-time checks that will produce an error when the vector pointer is used incorrectly.
109+
This is another similarity to *Simple Dynamic Strings*, which has library calls that are a little more clear in their
110+
reassignment, e.g. `s = sdscat(s,"foo")`, but this functionality would break the `vector_add` and `vector_insert`
111+
macros, which already expand to an assignment expression. If you're using a compiler that supports the `typeof`
112+
operator, the vector macros will perform compile-time checks that will produce an error when the vector pointer is used
113+
incorrectly.
92114
93115
Some functions never move a vector to a new location, so they just take a regular vector pointer as an argument:
94116
@@ -99,11 +121,13 @@ int* age_vec = vector_create();
99121
int num_ages = vector_size(age_vec); // just pass the `int*` without taking its address
100122
```
101123

102-
If the vector parameter for a function or macro is named `vec`, you don't need to take the address, but if the parameter is named `vec_addr`, then you do need to take it.
124+
If the vector parameter for a function or macro is named `vec`, you don't need to take the address, but if the parameter
125+
is named `vec_addr`, then you do need to take it.
103126

104127
# Best Practices
105128

106-
Because of the differences between regular arrays and vectors, it's probably a good idea to try to distinguish them from one another.
129+
Because of the differences between regular arrays and vectors, it's probably a good idea to try to distinguish them from
130+
one another.
107131

108132
One way to do this is to create an alias for various vector types using `typedef`:
109133

@@ -113,11 +137,14 @@ typedef float* vec_float; // vector alias for float
113137
vec_float qux = vector_create();
114138
```
115139

116-
The *recommended* way to differentiate between vectors and arrays is to simply name them differently, for example, an array of eggs could be named `eggs` while a vector of eggs could be named `egg_vec`.
140+
The *recommended* way to differentiate between vectors and arrays is to simply name them differently, for example, an
141+
array of eggs could be named `eggs` while a vector of eggs could be named `egg_vec`.
117142

118143
# What About Structures?
119144

120-
If you have a vector storing some kind of structure, you can't initialize new elements of the vector like you would a variable, e.g. `{ a, b, c }`. To get around this, there's a set of special macros that allow for more control over element initialization.
145+
If you have a vector storing some kind of structure, you can't initialize new elements of the vector like you would a
146+
variable, e.g. `{ a, b, c }`. To get around this, there's a set of special macros that allow for more control over
147+
element initialization.
121148

122149
Here is an example:
123150

@@ -138,7 +165,10 @@ temp = NULL; // stop using temp now that the element is initialized
138165
139166
Below is a cheat sheet for this library's functions and macros.
140167
141-
Some functions and macros take a normal vector argument, e.g. `vec`, while others can change the vector's memory location and therefore require the address of the vector pointer, e.g. `&vec`. You should get a compile-time error if you use a vector pointer incorrectly. Parameter names will also indicate whether or not to take the address of the vector pointer.
168+
Some functions and macros take a normal vector argument, e.g. `vec`, while others can change the vector's memory
169+
location and therefore require the address of the vector pointer, e.g. `&vec`. You should get a compile-time error if
170+
you use a vector pointer incorrectly. Parameter names will also indicate whether or not to take the address of the
171+
vector pointer.
142172
143173
| Action | Code | Changes vector address? |
144174
|-----------------------------------------|--------------------------------------------|-------------------------|
@@ -155,9 +185,55 @@ Some functions and macros take a normal vector argument, e.g. `vec`, while other
155185
| reserve space for 255 items in `vec` | `vector_reserve(&vec, 255);` | yes |
156186
| make a copy of `vec` | `type* vec_copy = vector_copy(vec);` | no |
157187
188+
# Convenience Iterator and Access Macros
189+
190+
Several macros are implemented to make iteration and element access more convenient
191+
192+
### `vector_front(vec)`
193+
194+
Access the first element in the vector by value (`vec[0]`).
195+
196+
Will cause an out-of-bounds access on an empty vector.
197+
198+
### `vector_back(vec)`
199+
200+
Access the last element in the vector by value (`vec[vector_size(vec) - 1]`).
201+
202+
Will cause an out-of-bounds access on an empty vector.
203+
204+
### `vector_begin(vec)`
205+
206+
Address of first element in vector (`vec`).
207+
208+
### `vector_end(vec)`
209+
210+
Address of element "past the end" of the vector. (`vec + vector_size(vec)`).
211+
212+
### `vector_foreach(*T, vec)`
213+
214+
Iterate over the vector using the provided item pointer.
215+
216+
```c
217+
int *int_vec = vector_create();
218+
219+
for (size_t i = 0; i < 100; i += 1) {
220+
vector_add(&int_vec, i);
221+
}
222+
223+
int sum = 0;
224+
vector_foreach(int *ip, int_vec) {
225+
sum += *ip;
226+
}
227+
228+
printf ("The sum of items is %d", sum);
229+
```
230+
158231
# Missing typeof Reference Sheet
159232

160-
Because some compilers don't support the `typeof` operator, which is used for static type checks in some of this library's macros, you have to use a slightly different set of macros. Unfortunately, this also means some errors will be missed at compile time, so if you're getting runtime errors, make sure you are properly using `vec` and `&vec` for their corresponding calls.
233+
Because some compilers don't support the `typeof` operator, which is used for static type checks in some of this
234+
library's macros, you have to use a slightly different set of macros. Unfortunately, this also means some errors will be
235+
missed at compile time, so if you're getting runtime errors, make sure you are properly using `vec` and `&vec` for their
236+
corresponding calls.
161237

162238
| Action | Code | Changes vector address? |
163239
|-----------------------------------------|--------------------------------------------------|-------------------------|

vec.h

Lines changed: 18 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -72,20 +72,6 @@ typedef size_t vec_type_t;
7272
#define vector_insert(vec_addr, pos, value)\
7373
(*vector_insert_dst(vec_addr, pos) = value)
7474

75-
// Get last element in vector by value (warning--vector may be empty!)
76-
#define vector_back(vec) (vec[vector_size(vec) - 1])
77-
78-
// Get pointer to object past end of vector for iterator use
79-
#define vector_end(vec) (vec + vector_size(vec))
80-
81-
// Get first element in vector by value (warning--vector may be empty!)
82-
#define vector_front(vec) (vec[0])
83-
84-
#define vector_begin(vec) (vec)
85-
86-
#define vector_foreach(item, vec) for(int _cont = 1, _count = 0; _cont && _count < vector_size(vec); _cont = 1, _count += 1) \
87-
for (item = vector_begin(vec) + _count; _cont; _cont = 0)
88-
8975
#else
9076

9177
#define vector_add_dst(vec_addr, type)\
@@ -112,6 +98,24 @@ typedef size_t vec_type_t;
11298
#define vector_copy(vec)\
11399
(_vector_copy((vector)vec, sizeof(*vec)))
114100

101+
/*
102+
* Convenience macros for iteration and access
103+
*/
104+
105+
// Get last element in vector by value (warning--vector may be empty!)
106+
#define vector_back(vec) (vec[vector_size(vec) - 1])
107+
108+
// Get pointer to object past end of vector for iterator use
109+
#define vector_end(vec) (vec + vector_size(vec))
110+
111+
// Get first element in vector by value (warning--vector may be empty!)
112+
#define vector_front(vec) (vec[0])
113+
114+
#define vector_begin(vec) (vec)
115+
116+
#define vector_foreach(item, vec) for(int _cont = 1, _count = 0; _cont && _count < vector_size(vec); _cont = 1, _count += 1) \
117+
for (item = vector_begin(vec) + _count; _cont; _cont = 0)
118+
115119
vector vector_create(void);
116120

117121
void vector_free(vector vec);

0 commit comments

Comments
 (0)