Skip to content

Commit 2e7436a

Browse files
committed
Add templatized Table implementation
1 parent 28d83e7 commit 2e7436a

File tree

3 files changed

+201
-0
lines changed

3 files changed

+201
-0
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
1212
- Pins now accept an array (or string) of input bits for providing pin values across multiple reads
1313
- FlashStringHelper (and related macros) compilation mocks
1414
- SoftwareSerial. That took a while.
15+
- Queue template implementation
16+
- Table template implementation
1517

1618
### Changed
1719
- Unit test executables print to STDERR just in case there are segfaults. Uh, just in case I ever write any.
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
#include <ArduinoUnitTests.h>
2+
#include <WString.h>
3+
#include <ci/Table.h>
4+
5+
// for testing a work function
6+
// note swapped args because as "isMatch", it's "firstArg, key"
7+
bool isSubstr(String firstArg, String haystack) {
8+
return strstr(haystack.c_str(), firstArg.c_str());
9+
}
10+
11+
// for testing a work function
12+
int results[5];
13+
void setResult2(int k, int v) {
14+
results[k] = v;
15+
}
16+
void setResult3(long l, int k, int v) {
17+
results[k] = v + l;
18+
}
19+
20+
21+
unittest(basic_table)
22+
{
23+
Table<String, int> t;
24+
assertTrue(t.empty());
25+
26+
int data[5] = {11, 22, 33, 44, 55};
27+
28+
for (int i = 0; i < 5; ++i) {
29+
assertEqual(i, t.size());
30+
assertTrue(t.add(String(data[i]), data[i]));
31+
assertEqual(i + 1, t.size());
32+
}
33+
34+
assertTrue(t.has("44"));
35+
assertFalse(t.has("66"));
36+
assertEqual(44, t.get("44"));
37+
38+
assertEqual("33", t.getMatchingKey(String("3"), isSubstr));
39+
40+
for (int i = 0; i < 5; ++i) {
41+
assertEqual(5 - i, t.size());
42+
assertTrue(t.remove(String(data[i])));
43+
assertFalse(t.has(String(data[i])));
44+
assertEqual(4 - i, t.size());
45+
}
46+
47+
}
48+
49+
unittest(iteration_no_arg) {
50+
Table<int, int> t;
51+
for (int i = 0; i < 5; ++i) {
52+
results[i] = 0;
53+
t.add(i, 11 * (i + 1));
54+
}
55+
56+
t.iterate(&setResult2);
57+
58+
for (int i = 0; i < 5; ++i) assertEqual(11 * (i + 1), results[i]);
59+
}
60+
61+
unittest(iteration_one_arg) {
62+
Table<int, int> t;
63+
for (int i = 0; i < 5; ++i) {
64+
results[i] = 0;
65+
t.add(i, 11 * (i + 1));
66+
}
67+
68+
long offset = 9;
69+
t.iterate(&setResult3, offset);
70+
71+
for (int i = 0; i < 5; ++i) assertEqual(11 * (i + 1), results[i] - offset);
72+
}
73+
74+
unittest_main()

cpp/arduino/ci/Table.h

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
#pragma once
2+
3+
// A template-ized lookup table implementation
4+
//
5+
// this is this stupidest table implementation ever but it's
6+
// an MVP for unit testing. O(n).
7+
template <typename K, typename V>
8+
class Table {
9+
private:
10+
struct Node {
11+
K key;
12+
V val;
13+
Node *next;
14+
};
15+
16+
Node* mStart;
17+
unsigned long mSize;
18+
// to alow const reference signatures, pre-allocate nil values
19+
K mNilK;
20+
V mNilV;
21+
22+
void init() {
23+
mStart = NULL;
24+
mSize = 0;
25+
}
26+
27+
public:
28+
Table() : mNilK(), mNilV() { init(); }
29+
30+
// number of things in the table
31+
inline unsigned long size() const { return mSize; }
32+
33+
// whether there are no things
34+
inline bool empty() const { return 0 == mSize; }
35+
36+
// whether there is a thing stored at the given key
37+
bool has(K const key) const {
38+
for (Node* p = mStart; p; p = p->next) {
39+
if (p->key == key) return true;
40+
}
41+
return false;
42+
}
43+
44+
// allow find operations on keys
45+
template <typename T>
46+
const K& getMatchingKey(T const firstArg, bool (*isMatch)(const T, const K)) const {
47+
for (Node* p = mStart; p; p = p->next) {
48+
if (isMatch(firstArg, p->key)) return p->key;
49+
}
50+
return mNilK;
51+
}
52+
53+
// allow iteration over entire table, with a work function that takes key/value pairs
54+
void iterate(void (*work)(const K&, const V&)) const {
55+
for (Node* p = mStart; p; p = p->next) work(p->key, p->val);
56+
}
57+
void iterate(void (*work)(K, V)) const {
58+
for (Node* p = mStart; p; p = p->next) work(p->key, p->val);
59+
}
60+
61+
// allow iteration over entire table, with a work function that takes key/value pairs
62+
// plus an initial argument. this enables member function passing (via workaround)
63+
template <typename T>
64+
void iterate(void (*work)(T&, const K&, const V&), T& firstArg) const {
65+
for (Node* p = mStart; p; p = p->next) work(firstArg, p->key, p->val);
66+
}
67+
68+
template <typename T>
69+
void iterate(void (*work)(T&, K, V), T& firstArg) const {
70+
for (Node* p = mStart; p; p = p->next) work(firstArg, p->key, p->val);
71+
}
72+
73+
template <typename T>
74+
void iterate(void (*work)(T, K, V), T firstArg) const {
75+
for (Node* p = mStart; p; p = p->next) work(firstArg, p->key, p->val);
76+
}
77+
78+
// return the value for a given key
79+
const V& get(K const key) const {
80+
for (Node* p = mStart; p; p = p->next) {
81+
if (p->key == key) return p->val;
82+
}
83+
return mNilV;
84+
}
85+
86+
// remove an item by key
87+
bool remove(K const key) {
88+
Node *o = NULL;
89+
for (Node* p = mStart; p; p = p->next) {
90+
if (p->key == key) {
91+
(o ? o->next : mStart) = p->next;
92+
delete p;
93+
--mSize;
94+
return true;
95+
}
96+
o = p;
97+
}
98+
return false;
99+
}
100+
101+
// add a key/value pair. deletes any existing key by that name.
102+
bool add(K const key, V const val) {
103+
remove(key);
104+
Node *n = new Node;
105+
if (n == NULL) return false;
106+
n->key = key;
107+
n->val = val;
108+
n->next = mStart;
109+
mStart = n;
110+
++mSize;
111+
return true;
112+
}
113+
114+
// remove everything
115+
void clear() {
116+
Node* p;
117+
while (mStart) {
118+
p = mStart;
119+
mStart = mStart->next;
120+
delete p;
121+
}
122+
}
123+
124+
~Table() { clear(); }
125+
};

0 commit comments

Comments
 (0)