Skip to content

Commit

Permalink
Extended return type of search with std::tuple<Derived*, bool> me…
Browse files Browse the repository at this point in the history
…thod.
  • Loading branch information
serges147 committed Jun 26, 2024
1 parent 8110aaa commit e12a99e
Show file tree
Hide file tree
Showing 2 changed files with 50 additions and 44 deletions.
63 changes: 35 additions & 28 deletions c++/cavl.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
#pragma once

#include <cstdint>
#include <tuple>
#include <type_traits>

/// If CAVL is used in throughput-critical code, then it is recommended to disable assertion checks as they may
Expand Down Expand Up @@ -93,10 +94,10 @@ class Node
template <typename Pre>
static auto search(Node* const root, const Pre& predicate) noexcept -> Derived*
{
Derived* p = down(root);
Derived* const out = search<Pre>(p, predicate, []() -> Derived* { return nullptr; });
Derived* p = down(root);
std::tuple<Derived*, bool> const out = search<Pre>(p, predicate, []() -> Derived* { return nullptr; });
CAVL_ASSERT(p == root);
return out;
return std::get<0>(out);
}

/// Same but const.
Expand All @@ -120,10 +121,12 @@ class Node

/// This is like the regular search function except that if the node is missing, the factory will be invoked
/// (without arguments) to construct a new one and insert it into the tree immediately.
/// The root node may be replaced in the process. If the factory returns true, the tree is not modified.
/// The factory does not need to be noexcept (may throw).
/// The root node may be replaced in the process. If this method returns true, the tree is not modified;
/// otherwise, the factory was (successfully!) invoked and a new node has been inserted into the tree.
/// The factory does not need to be noexcept (may throw). It may also return nullptr to indicate intentional
/// refusal to modify the tree, or f.e. in case of out of memory - result will be `(nullptr, true)` tuple.
template <typename Pre, typename Fac>
static auto search(Derived*& root, const Pre& predicate, const Fac& factory) -> Derived*;
static auto search(Derived*& root, const Pre& predicate, const Fac& factory) -> std::tuple<Derived*, bool>;

/// Remove the specified node from its tree. The root node may be replaced in the process.
/// The function has no effect if the node pointer is nullptr.
Expand Down Expand Up @@ -279,7 +282,7 @@ class Node

template <typename Derived>
template <typename Pre, typename Fac>
auto Node<Derived>::search(Derived*& root, const Pre& predicate, const Fac& factory) -> Derived*
auto Node<Derived>::search(Derived*& root, const Pre& predicate, const Fac& factory) -> std::tuple<Derived*, bool>
{
Node* out = nullptr;
Node* up = root;
Expand All @@ -298,29 +301,33 @@ auto Node<Derived>::search(Derived*& root, const Pre& predicate, const Fac& fact
n = n->lr[r];
CAVL_ASSERT((nullptr == n) || (n->up == up));
}
if (nullptr != out)
{
return std::make_tuple(down(out), true);
}

out = factory();
if (nullptr == out)
{
out = factory();
if (out != nullptr)
{
if (up != nullptr)
{
CAVL_ASSERT(up->lr[r] == nullptr);
up->lr[r] = out;
}
else
{
root = down(out);
}
out->unlink();
out->up = up;
if (Node* const rt = out->retraceOnGrowth())
{
root = down(rt);
}
}
return std::make_tuple(nullptr, true);
}

if (up != nullptr)
{
CAVL_ASSERT(up->lr[r] == nullptr);
up->lr[r] = out;
}
else
{
root = down(out);
}
out->unlink();
out->up = up;
if (Node* const rt = out->retraceOnGrowth())
{
root = down(rt);
}
return down(out);
return std::make_tuple(down(out), false);
}

template <typename Derived>
Expand Down Expand Up @@ -546,7 +553,7 @@ class Tree final
return NodeType::template search<Pre>(*this, predicate);
}
template <typename Pre, typename Fac>
auto search(const Pre& predicate, const Fac& factory) -> Derived*
auto search(const Pre& predicate, const Fac& factory) -> std::tuple<Derived*, bool>
{
CAVL_ASSERT(!traversal_in_progress_); // Cannot modify the tree while it is being traversed.
return NodeType::template search<Pre, Fac>(root_, predicate, factory);
Expand Down
31 changes: 15 additions & 16 deletions c++/test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -222,7 +222,9 @@ void testManual(const std::function<N*(std::uint8_t)>& factory)
const auto pred = [&](const N& v) { return t.at(i)->getValue() - v.getValue(); };
TEST_ASSERT_NULL(tr.search(pred));
TEST_ASSERT_NULL(static_cast<const TreeType&>(tr).search(pred));
TEST_ASSERT_EQUAL(t[i], tr.search(pred, [&]() { return t[i]; }));
auto result = tr.search(pred, [&]() { return t[i]; });
TEST_ASSERT_EQUAL(t[i], std::get<0>(result));
TEST_ASSERT_FALSE(std::get<1>(result));
TEST_ASSERT_EQUAL(t[i], tr.search(pred));
TEST_ASSERT_EQUAL(t[i], static_cast<const TreeType&>(tr).search(pred));
// Validate the tree after every mutation.
Expand Down Expand Up @@ -718,26 +720,23 @@ void testRandomized()
{
TEST_ASSERT_TRUE(mask.at(x));
TEST_ASSERT_EQUAL(x, existing->getValue());
TEST_ASSERT_EQUAL(x,
root.search(predicate,
[]() -> My* {
TEST_FAIL_MESSAGE(
"Attempted to create a new node when there is one already");
return nullptr;
})
->getValue());
auto result = root.search(predicate, []() -> My* {
TEST_FAIL_MESSAGE("Attempted to create a new node when there is one already");
return nullptr;
});
TEST_ASSERT_EQUAL(x, std::get<0>(result)->getValue());
TEST_ASSERT_TRUE(std::get<1>(result));
}
else
{
TEST_ASSERT_FALSE(mask.at(x));
bool factory_called = false;
TEST_ASSERT_EQUAL(x,
root.search(predicate,
[&]() -> My* {
factory_called = true;
return t.at(x).get();
})
->getValue());
auto result = root.search(predicate, [&]() -> My* {
factory_called = true;
return t.at(x).get();
});
TEST_ASSERT_EQUAL(x, std::get<0>(result)->getValue());
TEST_ASSERT_FALSE(std::get<1>(result));
TEST_ASSERT(factory_called);
size++;
cnt_addition++;
Expand Down

0 comments on commit e12a99e

Please sign in to comment.