The
in_order() function is definitely not MISRA compliant, and not intended to be; I only included it because it helps testing the generated tree structures quickly and visually. (I use Graphviz DOT language output, with the third parameter to the callback,
compares_as, providing the flag for the directed graph tail label (
< or
>).) I edited my post to make that clearer; one should not need it in final code.
I vaguely recall that there is a way to traverse trees without recursion or stack, with just fixed amount of storage (some sort of direction cookie or something, with checking which side one came from when going towards the root), but it requires a parent pointer in each node. If tree traversal is needed, then I'd modify the node structure accordingly, and implement that.
A quick glance showed that it uses new to allocate nodes, is that really MISRA C++ compliant?
I don't know, I don't have MISRA C++ either.
If it matters,
AUTOSAR AP Release 18-10 for C++14, which targets C++14 but builds upon MISRA C++ 2008, forbids
malloc()/
free() but allows
new/
delete in section 6.1.
If the tree nodes are of fixed size, I could change the code to use a template parameter for statically allocating the tree nodes, with a secondary tree as a singly-linked list holding the unused nodes for quick reuse. Populating the next tree node would move from
node::add_child() to
tree::add(), but the populated node would only be removed from the free list if the
node::add_child() call succeeded. So, I don't think it matters that much here, both dynamic and static allocation patterns are easily achieved with very minimal changes. It is why I didn't include any
delete operators or destructors.
p_btree->context.coin.method.cmp.isle = cmp_isle; /* A <= B */
p_btree->context.coin.method.cmp.islt = cmp_islt; /* A < B */
p_btree->context.coin.method.cmp.isge = cmp_isge; /* A >= B */
p_btree->context.coin.method.cmp.isgt = cmp_isgt; /* A > B */
p_btree->context.coin.method.cmp.iseq = cmp_iseq; /* A == B */
Why separate comparison functions, instead of just one that returns
less/below,
equal,
greater/above?
If
cmp(x,y) returns 0 if equal, -1 if less/below, or 1 if greater/above, then
logic │ expression ────────┼──────────────── x == y │ cmp(x, y) == 0 x != y │ cmp(x, y) != 0 x <= y │ cmp(x, y) <= 0 x >= y │ cmp(x, y) >= 0 x < y │ cmp(x, y) < 0 x > y | cmp(x, y) > 0Is there something in MISRA that forbids this?
You see, it is of crucial importance that the key set is totally ordered, meaning that if
x < y and
y <= z, then
z > x or the data structure will fail because the keys are not properly ordered.
Another option is to use a bit mask, so that 0 denotes "not comparable" or "unknown order" (which the unit tests would try to look for, since it should not happen). Then, say
/* Bit 0: Equal, Bit 1: Less/Below, Bit 2: Greater/Above */
enum order_bits {
MASK_EQ = 1,
MASK_LT = 2,
MASK_LE = 3,
MASK_GT = 4,
MASK_GE = 5,
MASK_NE = 6,
};
in which case we'd have
logic │ expression ────────┼─────────────────────────── x == y │ (cmpmask(x, y) & MASK_EQ) x != y │ (cmpmask(x, y) & MASK_NE) x <= y │ (cmpmask(x, y) & MASK_LE) x >= y │ (cmpmask(x, y) & MASK_GE) x < y │ (cmpmask(x, y) & MASK_LT) x > y | (cmpmask(x, y) & MASK_GT)and the unit test would ensure that for all possible keys,
cmpmask(x, y) returns a value between 1 and 6, inclusive.