summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJonathan Bradley <jcb@pikum.xyz>2025-03-20 13:59:29 -0400
committerJonathan Bradley <jcb@pikum.xyz>2025-03-21 11:03:34 -0400
commit153d9b600a11025fe653a45cb4f845c0dff0b145 (patch)
treedf055c68a0a9e9044c4c9c3364fed7ce2dc419f3
parent9cd55867de91013bdbfe0d73112df504eb7963ba (diff)
pkarr: add c++ template + bump version to 0.4.2
-rw-r--r--config.mk2
-rw-r--r--pk.h.in11
-rw-r--r--pkarr.h92
-rw-r--r--test/pkarr.cpp113
4 files changed, 206 insertions, 12 deletions
diff --git a/config.mk b/config.mk
index d2fb017..32ab53b 100644
--- a/config.mk
+++ b/config.mk
@@ -1,5 +1,5 @@
# pk.h version
-VERSION = 0.4.1
+VERSION = 0.4.2
# paths
PREFIX = /usr/local
diff --git a/pk.h.in b/pk.h.in
index 94129ab..0fcad06 100644
--- a/pk.h.in
+++ b/pk.h.in
@@ -144,6 +144,7 @@
*
* Initialize `stride`, `alignment`, and `bkt` (optional) members
* *before* calling any `pk_arr_*` methods.
+* Alternatively, if using c++, use the template ctor.
*
* Examples:
* ``` c
@@ -154,6 +155,11 @@
* pk_arr_reserve(&arr, 10); // optional
* pk_arr_append(&arr, &obj);
* ```
+* ``` c++
+* struct pk_arr<some_type> arr(bkt);
+* pk_arr_reserve(&arr, 10); // optional
+* pk_arr_append(&arr, &obj);
+* ```
* ``` c
* struct pk_arr arr = {0};
* arr.stride = sizeof(obj); // required
@@ -163,6 +169,11 @@
* obj* d = (obj*)arr->data;
* d[0] = ...;
* ```
+* ``` c++
+* struct pk_arr_t<some_type> arr();
+* pk_arr_resize(&arr, 10);
+* arr[0] = {};
+* ```
*
********************************************************************************
* pkstn.h: def PK_IMPL_STN before including pk.h to enable ad-hoc.
diff --git a/pkarr.h b/pkarr.h
index fa2d88a..baa91e8 100644
--- a/pkarr.h
+++ b/pkarr.h
@@ -22,8 +22,82 @@ void pk_arr_resize(struct pk_arr *arr, uint32_t count);
void pk_arr_move_to_back(struct pk_arr *arr, uint32_t index);
void pk_arr_append(struct pk_arr *arr, void *data);
void pk_arr_remove_at(struct pk_arr *arr, uint32_t index);
+void pk_arr_clone(struct pk_arr *lhs, struct pk_arr *rhs);
+void pk_arr_swap(struct pk_arr *lhs, struct pk_arr *rhs);
uint32_t pk_arr_find_first_index(struct pk_arr *arr, void *user_data, pk_arr_item_compare *fn);
+#if defined(__cplusplus)
+template<typename T>
+struct pk_arr_t : public pk_arr {
+ pk_arr_t();
+ pk_arr_t(struct pk_membucket *bkt);
+ pk_arr_t(const pk_arr_t<T> &other);
+ pk_arr_t(pk_arr_t<T> &&other);
+ pk_arr_t &operator=(const pk_arr_t<T> &other);
+ pk_arr_t &operator=(pk_arr_t<T> &&other);
+ ~pk_arr_t();
+ T &operator[](size_t index);
+};
+template<typename T>
+pk_arr_t<T>::pk_arr_t() {
+ this->next = 0;
+ this->reserved = 0;
+ this->stride = sizeof(T);
+ this->alignment = alignof(T);
+ this->bkt = NULL;
+ this->data = NULL;
+}
+template<typename T>
+pk_arr_t<T>::pk_arr_t(struct pk_membucket *bkt) : pk_arr_t<T>() {
+ this->bkt = bkt;
+}
+template<typename T>
+pk_arr_t<T>::pk_arr_t(const pk_arr_t<T> &other) {
+ // copy ctor
+ pk_arr_clone(static_cast<struct pk_arr_t *>(&const_cast<pk_arr_t<T>&>(other)), this);
+}
+template<typename T>
+pk_arr_t<T>::pk_arr_t(pk_arr_t<T> &&other) {
+ // move ctor
+ pk_arr_swap(this, &other);
+ other.data = NULL;
+}
+template<typename T>
+pk_arr_t<T> &
+pk_arr_t<T>::operator=(const pk_arr_t<T> &other) {
+ // copy assignment
+ if (this->data != NULL) {
+ pk_arr_reset(this);
+ }
+ pk_arr_clone(static_cast<struct pk_arr_t *>(&const_cast<pk_arr_t<T>&>(other)), this);
+ return *this;
+}
+template<typename T>
+pk_arr_t<T> &
+pk_arr_t<T>::operator=(pk_arr_t<T> &&other) {
+ // move assignment
+ if (this->data != NULL) {
+ pk_arr_reset(this);
+ }
+ pk_arr_swap(this, &other);
+ other.data = NULL;
+ return *this;
+}
+template<typename T>
+pk_arr_t<T>::~pk_arr_t() {
+ if (this->data != NULL) pk_delete(this->data, this->stride * this->reserved, this->bkt);
+}
+template<typename T>
+T &pk_arr_t<T>::operator[](size_t index) {
+ if(index >= this->next) throw "pk_arr_t<T>::operator[] out of range";
+ return reinterpret_cast<T*>(this->data)[index];
+}
+template<typename T>
+void pk_arr_append_t(pk_arr_t<T> *arr, const T &item) {
+ pk_arr_append(arr, &const_cast<T&>(item));
+}
+#endif
+
#endif /* PK_PKARR_H */
#ifdef PK_IMPL_ARR
@@ -194,6 +268,24 @@ pk_arr_remove_at(struct pk_arr *arr, uint32_t index)
arr->next -= 1;
}
+void
+pk_arr_clone(struct pk_arr *lhs, struct pk_arr *rhs) {
+ size_t sz;
+ *rhs = *lhs;
+ if (lhs->data == NULL) return;
+ sz = lhs->stride * lhs->reserved;
+ rhs->data = pk_new(sz, lhs->alignment, lhs->bkt);
+ memcpy(rhs->data, lhs->data, sz);
+}
+
+void
+pk_arr_swap(struct pk_arr *lhs, struct pk_arr *rhs)
+{
+ struct pk_arr tmp = *lhs;
+ *lhs = *rhs;
+ *rhs = tmp;
+}
+
uint32_t
pk_arr_find_first_index(struct pk_arr *arr, void *user_data, pk_arr_item_compare *fn)
{
diff --git a/test/pkarr.cpp b/test/pkarr.cpp
index caed839..bc8e3be 100644
--- a/test/pkarr.cpp
+++ b/test/pkarr.cpp
@@ -1,53 +1,144 @@
#include <cstring>
+#include <utility>
#include "../pkarr.h"
+// TODO might be able to define a "test" class that inherits from a given
+// `pk_arr_t<T>` to make some of this testing more streamlined.
+// Spinup in ctor, check bkt in dtor, log fn calls, etc.
+
void
-test_spinup(struct pk_arr *arr, struct pk_membucket **bkt)
+test_spinup(struct pk_membucket **bkt)
{
- memset(arr, 0, sizeof(struct pk_arr));
*bkt = pk_bucket_create("test", 1024 * 1024, false);
- arr->bkt = *bkt;
}
void
-test_teardown(struct pk_arr *arr, struct pk_membucket **bkt)
+test_teardown(struct pk_membucket **bkt)
{
pk_bucket_destroy(*bkt);
*bkt = NULL;
- arr->data = NULL;
}
int main(int argc, char *argv[])
{
(void)argc;
(void)argv;
- struct pk_arr arr = {};
struct pk_membucket *bkt = {};
+ uint8_t val1 = 69;
+ uint8_t val2 = 117;
/* template
{
- test_spinup(&arr, &bkt);
+ test_spinup(&bkt);
test_teardown(&arr, &bkt);
}
*/
+ fprintf(stdout, "[%s] Begin\n\n", __FILE__);
+
+ fprintf(stdout, "[%s] Starting append test\n", __FILE__);
+ test_spinup(&bkt);
{
- test_spinup(&arr, &bkt);
- arr.stride = sizeof(uint8_t);
+ pk_arr_t<uint8_t> arr(bkt);
uint8_t c = 255;
- pk_arr_append(&arr, &c);
+ pk_arr_append_t(&arr, c);
if (arr.bkt == NULL) exit(1);
if (arr.data == NULL) exit(1);
if (arr.reserved == 0) exit(1);
+ if (arr.stride != sizeof(uint8_t)) exit(1);
+ if (arr.alignment != alignof(uint8_t)) exit(1);
if (arr.next != 1) exit(1);
+ if (arr[0] != c) exit(1);
- test_teardown(&arr, &bkt);
+ fprintf(stdout, "[%s] parameterized bkt ctor + operator[]\n", __FILE__);
+ }
+ if (bkt->allocs != 0) exit(1);
+ fprintf(stdout, "[%s] dtor called.\n", __FILE__);
+ fprintf(stdout, "[%s] Ending append test\n\n", __FILE__);
+ test_teardown(&bkt);
+
+ // copy ctor
+ fprintf(stdout, "[%s] Starting copy ctor test\n", __FILE__);
+ test_spinup(&bkt);
+ {
+ pk_arr_t<uint8_t> arr1(bkt);
+ pk_arr_append_t(&arr1, val1);
+
+ // copy
+ pk_arr_t<uint8_t> arr2{arr1};
+
+ if (arr1[0] != val1) exit(1);
+ if (arr2[0] != val1) exit(1);
+ }
+ if (bkt->allocs != 0) exit(1);
+ fprintf(stdout, "[%s] dtor called.\n", __FILE__);
+ fprintf(stdout, "[%s] Ending copy ctor test\n\n", __FILE__);
+ test_teardown(&bkt);
+
+ // move ctor
+ fprintf(stdout, "[%s] Starting move ctor test\n", __FILE__);
+ test_spinup(&bkt);
+ {
+ pk_arr_t<uint8_t> arr1(bkt);
+ pk_arr_append_t(&arr1, val1);
+
+ // move
+ pk_arr_t<uint8_t> arr2{std::move(arr1)};
+
+ // if (arr1[0] == val1) exit(1);
+ if (arr2[0] != val1) exit(1);
+ }
+ if (bkt->allocs != 0) exit(1);
+ fprintf(stdout, "[%s] dtor called.\n", __FILE__);
+ fprintf(stdout, "[%s] Ending move ctor test\n\n", __FILE__);
+ test_teardown(&bkt);
+
+ // copy assignment
+ fprintf(stdout, "[%s] Starting copy assignment test\n", __FILE__);
+ test_spinup(&bkt);
+ {
+ pk_arr_t<uint8_t> arr1(bkt);
+ pk_arr_append_t(&arr1, val1);
+ pk_arr_t<uint8_t> arr2(bkt);
+ pk_arr_append_t(&arr2, val2);
+
+ // copy
+ arr1 = arr2;
+
+ if (arr1[0] != val2) exit(1);
+ if (arr2[0] != val2) exit(1);
}
+ if (bkt->allocs != 0) exit(1);
+ fprintf(stdout, "[%s] dtor called.\n", __FILE__);
+ fprintf(stdout, "[%s] Ending copy assignment test\n\n", __FILE__);
+ test_teardown(&bkt);
+
+ // move assignment
+ fprintf(stdout, "[%s] Starting move assignment test\n", __FILE__);
+ test_spinup(&bkt);
+ {
+ pk_arr_t<uint8_t> arr1(bkt);
+ pk_arr_append_t(&arr1, val1);
+ pk_arr_t<uint8_t> arr2(bkt);
+ pk_arr_append_t(&arr2, val2);
+
+ // move
+ arr1 = std::move(arr2);
+
+ if (arr1[0] != val2) exit(1);
+ // if (arr2[0] != val1) exit(1);
+ }
+ if (bkt->allocs != 0) exit(1);
+ fprintf(stdout, "[%s] dtor called.\n", __FILE__);
+ fprintf(stdout, "[%s] Ending move assignment test\n\n", __FILE__);
+ test_teardown(&bkt);
+
+ fprintf(stdout, "[%s] End\n", __FILE__);
return 0;
}