diff --git a/.idea/editor.xml b/.idea/editor.xml
index a8c6caa..eb7507a 100644
--- a/.idea/editor.xml
+++ b/.idea/editor.xml
@@ -7,6 +7,8 @@
+
+
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 6313ebd..7c22918 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -27,7 +27,7 @@ macro(compile_options target_name)
sanitizers(${target_name})
endmacro()
-project(blt-gp VERSION 0.5.27)
+project(blt-gp VERSION 0.5.28)
include(CTest)
diff --git a/include/blt/gp/fwdecl.h b/include/blt/gp/fwdecl.h
index 40ee4ad..2162fff 100644
--- a/include/blt/gp/fwdecl.h
+++ b/include/blt/gp/fwdecl.h
@@ -43,6 +43,15 @@ namespace blt::gp
struct op_container_t;
class evaluation_context;
+
+ template
+ class evaluation_ref;
+
+ class tree_manipulator_t;
+
+ class single_operation_tree_manipulator_t;
+
+ class multi_operation_tree_manipulator_t;
class tree_t;
diff --git a/include/blt/gp/tree.h b/include/blt/gp/tree.h
index 3d6eb44..c11b51f 100644
--- a/include/blt/gp/tree.h
+++ b/include/blt/gp/tree.h
@@ -97,15 +97,14 @@ namespace blt::gp
operator_special_flags m_flags;
};
- class evaluation_context
- {
- public:
- explicit evaluation_context() = default;
-
- stack_allocator values;
- };
-
- inline size_t accumulate_type_sizes(const detail::op_iter_t begin, const detail::op_iter_t end)
+ /**
+ * Calculate the number of bytes stored inside the tree's stack between the begin and end iterators
+ *
+ * @param begin Begin iterator to the container storing the tree's operators
+ * @param end End iterator
+ * @return bytes used by operators between [begin, end)
+ */
+ inline size_t calculate_ephemeral_size(const detail::op_iter_t begin, const detail::op_iter_t end)
{
size_t total = 0;
for (auto it = begin; it != end; ++it)
@@ -116,6 +115,21 @@ namespace blt::gp
return total;
}
+ /**
+ * Stores the stack used to evaluate a tree. This is done such that executing a tree doesn't modify the internal stack
+ */
+ class evaluation_context
+ {
+ public:
+ explicit evaluation_context() = default;
+
+ stack_allocator values;
+ };
+
+ /**
+ * Provides a method for accessing an evaluated tree's returned type;
+ * this class ensures that the drop function is properly called for the evaluation context
+ */
template
class evaluation_ref
{
@@ -185,8 +199,54 @@ namespace blt::gp
evaluation_context* m_context;
};
+ struct temporary_tree_storage_t
+ {
+ explicit temporary_tree_storage_t(tree_t& tree);
+
+ temporary_tree_storage_t(tracked_vector& operations, stack_allocator& values): operations(&operations), values(&values)
+ {
+ }
+
+ void clear() const
+ {
+ operations->clear();
+ values->reset();
+ }
+
+ tracked_vector* operations;
+ stack_allocator* values;
+ };
+
+ class single_operation_tree_manipulator_t
+ {
+ };
+
+ class multi_operation_tree_manipulator_t
+ {
+ };
+
+ /**
+ * This is the parent class responsible for managing a tree's internal state at runtime.
+ * While it is possible to create a tree without a need for this class, you should not attempt to modify a tree's internal state after creation.
+ * This class provides helper methods to ensure that a tree remains well-ordered and that drop functions are called correctly.
+ */
+ class tree_manipulator_t
+ {
+ public:
+ explicit tree_manipulator_t(tree_t& tree): m_tree(&tree)
+ {
+ }
+
+ private:
+ tree_t* m_tree;
+ };
+
class tree_t
{
+ friend struct temporary_tree_storage_t;
+ friend class single_operation_tree_manipulator_t;
+ friend class multi_operation_tree_manipulator_t;
+ friend class tree_manipulator_t;
public:
struct subtree_point_t
{
@@ -209,51 +269,6 @@ namespace blt::gp
ptrdiff_t start;
// one past the end
ptrdiff_t end;
-
-
- };
-
- struct byte_only_transaction_t
- {
- byte_only_transaction_t(tree_t& tree, const size_t bytes): tree(tree), data(nullptr), bytes(bytes)
- {
- move(bytes);
- }
-
- explicit byte_only_transaction_t(tree_t& tree): tree(tree), data(nullptr), bytes(0)
- {
- }
-
- byte_only_transaction_t(const byte_only_transaction_t& copy) = delete;
- byte_only_transaction_t& operator=(const byte_only_transaction_t& copy) = delete;
-
- byte_only_transaction_t(byte_only_transaction_t&& move) noexcept: tree(move.tree), data(std::exchange(move.data, nullptr)),
- bytes(std::exchange(move.bytes, 0))
- {
- }
-
- byte_only_transaction_t& operator=(byte_only_transaction_t&& move) noexcept = delete;
-
- void move(size_t bytes_to_move);
-
- [[nodiscard]] bool empty() const
- {
- return bytes == 0;
- }
-
- ~byte_only_transaction_t()
- {
- if (!empty())
- {
- tree.values.copy_from(data, bytes);
- bytes = 0;
- }
- }
-
- private:
- tree_t& tree;
- u8* data;
- size_t bytes;
};
explicit tree_t(gp_program& program): m_program(&program)
@@ -278,62 +293,9 @@ namespace blt::gp
* This function copies the data from the provided tree, will attempt to reserve and copy in one step.
* will avoid reallocation if enough space is already present.
*
- * This function is meant to copy into and replaces data inside the tree.
+ * This function is meant to copy into and replace data inside the tree.
*/
- void copy_fast(const tree_t& copy)
- {
- if (this == ©)
- return;
-
- operations.reserve(copy.operations.size());
-
- auto copy_it = copy.operations.begin();
- auto op_it = operations.begin();
-
- size_t total_op_bytes = 0;
- size_t total_copy_bytes = 0;
-
- for (; op_it != operations.end(); ++op_it)
- {
- if (copy_it == copy.operations.end())
- break;
- if (copy_it->is_value())
- {
- copy.handle_refcount_increment(copy_it, total_copy_bytes);
- total_copy_bytes += copy_it->type_size();
- }
- if (op_it->is_value())
- {
- handle_refcount_decrement(op_it, total_op_bytes);
- total_op_bytes += op_it->type_size();
- }
- *op_it = *copy_it;
- ++copy_it;
- }
- const auto op_it_cpy = op_it;
- for (; op_it != operations.end(); ++op_it)
- {
- if (op_it->is_value())
- {
- handle_refcount_decrement(op_it, total_op_bytes);
- total_op_bytes += op_it->type_size();
- }
- }
- operations.erase(op_it_cpy, operations.end());
- for (; copy_it != copy.operations.end(); ++copy_it)
- {
- if (copy_it->is_value())
- {
- copy.handle_refcount_increment(copy_it, total_copy_bytes);
- total_copy_bytes += copy_it->type_size();
- }
- operations.emplace_back(*copy_it);
- }
-
- values.reserve(copy.values.stored());
- values.reset();
- values.insert(copy.values);
- }
+ void copy_fast(const tree_t& copy);
tree_t(tree_t&& move) = default;
diff --git a/src/program.cpp b/src/program.cpp
index 747e792..c3bcf88 100644
--- a/src/program.cpp
+++ b/src/program.cpp
@@ -31,8 +31,8 @@ namespace blt::gp
// this is largely to not break the tests :3
// it's also to allow for quick setup of a gp program if you don't care how crossover or mutation is handled
static advanced_mutation_t s_mutator;
- // static subtree_crossover_t s_crossover;
- static one_point_crossover_t s_crossover;
+ static subtree_crossover_t s_crossover;
+ // static one_point_crossover_t s_crossover;
static ramped_half_initializer_t s_init;
prog_config_t::prog_config_t(): mutator(s_mutator), crossover(s_crossover), pop_initializer(s_init)
diff --git a/src/tree.cpp b/src/tree.cpp
index fd45888..e730534 100644
--- a/src/tree.cpp
+++ b/src/tree.cpp
@@ -267,7 +267,7 @@ namespace blt::gp
const auto point_begin_itr = operations.begin() + point.pos;
const auto point_end_itr = operations.begin() + extent;
- const size_t after_bytes = accumulate_type_sizes(point_end_itr, operations.end());
+ const size_t after_bytes = calculate_ephemeral_size(point_end_itr, operations.end());
const size_t ops = std::distance(point_begin_itr, point_end_itr);
operators.reserve(operators.size() + ops);
@@ -342,8 +342,8 @@ namespace blt::gp
c2_subtree_operators.push_back(it);
}
- const size_t c1_stack_after_bytes = accumulate_type_sizes(c1_subtree_end_itr, operations.end());
- const size_t c2_stack_after_bytes = accumulate_type_sizes(c2_subtree_end_itr, other_tree.operations.end());
+ const size_t c1_stack_after_bytes = calculate_ephemeral_size(c1_subtree_end_itr, operations.end());
+ const size_t c2_stack_after_bytes = calculate_ephemeral_size(c2_subtree_end_itr, other_tree.operations.end());
const auto c1_total = static_cast(c1_stack_after_bytes + c1_subtree_bytes);
const auto c2_total = static_cast(c2_stack_after_bytes + c2_subtree_bytes);
const auto copy_ptr_c1 = get_thread_pointer_for_size(c1_total);
@@ -387,7 +387,7 @@ namespace blt::gp
const auto point_begin_itr = operations.begin() + point.pos;
const auto point_end_itr = operations.begin() + extent;
- const size_t after_bytes = accumulate_type_sizes(point_end_itr, operations.end());
+ const size_t after_bytes = calculate_ephemeral_size(point_end_itr, operations.end());
size_t for_bytes = 0;
for (auto& it : iterate(point_begin_itr, point_end_itr).rev())
@@ -434,7 +434,7 @@ namespace blt::gp
const auto point_begin_itr = operations.begin() + point.pos;
const auto point_end_itr = operations.begin() + extent;
- const size_t after_bytes = accumulate_type_sizes(point_end_itr, operations.end());
+ const size_t after_bytes = calculate_ephemeral_size(point_end_itr, operations.end());
size_t for_bytes = 0;
for (auto& it : iterate(point_begin_itr, point_end_itr).rev())
@@ -461,7 +461,7 @@ namespace blt::gp
ptrdiff_t tree_t::insert_subtree(const subtree_point_t point, tree_t& other_tree)
{
- const size_t after_bytes = accumulate_type_sizes(operations.begin() + point.pos, operations.end());
+ const size_t after_bytes = calculate_ephemeral_size(operations.begin() + point.pos, operations.end());
byte_only_transaction_t transaction{*this, after_bytes};
auto insert = operations.begin() + point.pos;
@@ -635,6 +635,65 @@ namespace blt::gp
}
}
+ temporary_tree_storage_t::temporary_tree_storage_t(tree_t& tree): operations(&tree.operations), values(&tree.values)
+ {
+ }
+
+ void tree_t::copy_fast(const tree_t& copy)
+ {
+ if (this == ©)
+ return;
+
+ operations.reserve(copy.operations.size());
+
+ auto copy_it = copy.operations.begin();
+ auto op_it = operations.begin();
+
+ size_t total_op_bytes = 0;
+ size_t total_copy_bytes = 0;
+
+ for (; op_it != operations.end(); ++op_it)
+ {
+ if (copy_it == copy.operations.end())
+ break;
+ if (copy_it->is_value())
+ {
+ copy.handle_refcount_increment(copy_it, total_copy_bytes);
+ total_copy_bytes += copy_it->type_size();
+ }
+ if (op_it->is_value())
+ {
+ handle_refcount_decrement(op_it, total_op_bytes);
+ total_op_bytes += op_it->type_size();
+ }
+ *op_it = *copy_it;
+ ++copy_it;
+ }
+ const auto op_it_cpy = op_it;
+ for (; op_it != operations.end(); ++op_it)
+ {
+ if (op_it->is_value())
+ {
+ handle_refcount_decrement(op_it, total_op_bytes);
+ total_op_bytes += op_it->type_size();
+ }
+ }
+ operations.erase(op_it_cpy, operations.end());
+ for (; copy_it != copy.operations.end(); ++copy_it)
+ {
+ if (copy_it->is_value())
+ {
+ copy.handle_refcount_increment(copy_it, total_copy_bytes);
+ total_copy_bytes += copy_it->type_size();
+ }
+ operations.emplace_back(*copy_it);
+ }
+
+ values.reserve(copy.values.stored());
+ values.reset();
+ values.insert(copy.values);
+ }
+
void tree_t::clear(gp_program& program)
{
auto* f = &program;
@@ -766,7 +825,7 @@ namespace blt::gp
byte_only_transaction_t move_data{*this};
if (operations[point].is_value())
{
- const size_t after_bytes = accumulate_type_sizes(operations.begin() + static_cast(point) + 1, operations.end());
+ const size_t after_bytes = calculate_ephemeral_size(operations.begin() + static_cast(point) + 1, operations.end());
move_data.move(after_bytes);
if (operations[point].get_flags().is_ephemeral() && operations[point].has_ephemeral_drop())
{
@@ -787,7 +846,7 @@ namespace blt::gp
{
if (move_data.empty())
{
- const size_t after_bytes = accumulate_type_sizes(operations.begin() + static_cast(point) + 1, operations.end());
+ const size_t after_bytes = calculate_ephemeral_size(operations.begin() + static_cast(point) + 1, operations.end());
move_data.move(after_bytes);
}
handle_operator_inserted(operations[point]);