Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Extended return type of search with std::tuple<Derived*, bool> method. #8

Merged
merged 1 commit into from
Jun 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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