Compare commits

..

No commits in common. "c294b70150d985fdc8a5b6a62eba74042489fa32" and "be90039a5ab812ebe2769672d730726bdc037e15" have entirely different histories.

10 changed files with 62 additions and 122 deletions

View File

@ -1,5 +1,5 @@
cmake_minimum_required(VERSION 3.25) cmake_minimum_required(VERSION 3.25)
project(blt-gp VERSION 0.0.112) project(blt-gp VERSION 0.0.110)
include(CTest) include(CTest)

View File

@ -37,7 +37,7 @@ blt::gp::prog_config_t config = blt::gp::prog_config_t()
.set_elite_count(2) .set_elite_count(2)
.set_crossover_chance(0.9) .set_crossover_chance(0.9)
.set_mutation_chance(0.1) .set_mutation_chance(0.1)
.set_reproduction_chance(0) .set_reproduction_chance(0.1)
.set_max_generations(50) .set_max_generations(50)
.set_pop_size(500) .set_pop_size(500)
.set_thread_count(0); .set_thread_count(0);
@ -75,7 +75,7 @@ constexpr auto fitness_function = [](blt::gp::tree_t& current_tree, blt::gp::fit
fitness.raw_fitness += value_cutoff; fitness.raw_fitness += value_cutoff;
} }
fitness.standardized_fitness = fitness.raw_fitness; fitness.standardized_fitness = fitness.raw_fitness;
fitness.adjusted_fitness = (1.0 / (1.0 + fitness.standardized_fitness)); fitness.adjusted_fitness = 1.0 / (1.0 + fitness.standardized_fitness);
//BLT_TRACE("fitness: %lf raw: %lf", fitness.adjusted_fitness, fitness.raw_fitness); //BLT_TRACE("fitness: %lf raw: %lf", fitness.adjusted_fitness, fitness.raw_fitness);
}; };

View File

@ -78,8 +78,7 @@ namespace blt::gp
{ {
blt::size_t offset = 0; blt::size_t offset = 0;
blt::size_t current_index = 0; blt::size_t current_index = 0;
((offset += (current_index++ < index ? stack_allocator::aligned_size<detail::remove_cv_ref<Args>>() : 0)), ...); ((offset += (current_index++ > index ? stack_allocator::aligned_size<detail::remove_cv_ref<Args>>() : 0)), ...);
//BLT_INFO("offset %ld for index %ld", offset, index);
return offset; return offset;
} }
@ -87,8 +86,6 @@ namespace blt::gp
inline static constexpr Return exec_sequence_to_indices(Func&& func, stack_allocator& allocator, std::integer_sequence<blt::u64, indices...>, inline static constexpr Return exec_sequence_to_indices(Func&& func, stack_allocator& allocator, std::integer_sequence<blt::u64, indices...>,
ExtraArgs&& ... args) ExtraArgs&& ... args)
{ {
//blt::size_t arg_size = (stack_allocator::aligned_size<detail::remove_cv_ref<Args>>() + ...);
//BLT_TRACE(arg_size);
// expands Args and indices, providing each argument with its index calculating the current argument byte offset // expands Args and indices, providing each argument with its index calculating the current argument byte offset
return std::forward<Func>(func)(std::forward<ExtraArgs>(args)..., return std::forward<Func>(func)(std::forward<ExtraArgs>(args)...,
allocator.from<detail::remove_cv_ref<Args>>(getByteOffset<indices>())...); allocator.from<detail::remove_cv_ref<Args>>(getByteOffset<indices>())...);
@ -177,7 +174,6 @@ namespace blt::gp
return name; return name;
} }
operator_id id = -1;
private: private:
function_t func; function_t func;
std::optional<std::string_view> name; std::optional<std::string_view> name;

View File

@ -40,7 +40,6 @@
#include <blt/std/hashmap.h> #include <blt/std/hashmap.h>
#include <blt/std/types.h> #include <blt/std/types.h>
#include <blt/std/utility.h> #include <blt/std/utility.h>
#include <blt/std/meta.h>
#include <blt/std/memory.h> #include <blt/std/memory.h>
#include <blt/std/thread.h> #include <blt/std/thread.h>
#include <blt/gp/fwdecl.h> #include <blt/gp/fwdecl.h>
@ -106,11 +105,10 @@ namespace blt::gp
{} {}
template<typename ArgType, typename Return, typename... Args> template<typename ArgType, typename Return, typename... Args>
operator_builder& add_operator(operation_t<ArgType, Return(Args...)>& op, bool is_static = false) operator_builder& add_operator(const operation_t<ArgType, Return(Args...)>& op, bool is_static = false)
{ {
auto return_type_id = system.get_type<Return>().id(); auto return_type_id = system.get_type<Return>().id();
auto operator_id = blt::gp::operator_id(storage.operators.size()); auto operator_id = blt::gp::operator_id(storage.operators.size());
op.id = operator_id;
operator_info info; operator_info info;
@ -130,15 +128,27 @@ namespace blt::gp
BLT_ASSERT(info.argc.argc_context - info.argc.argc <= 1 && "Cannot pass multiple context as arguments!"); BLT_ASSERT(info.argc.argc_context - info.argc.argc <= 1 && "Cannot pass multiple context as arguments!");
info.function = op.template make_callable<Context>(); info.function = op.template make_callable<Context>();
// info.transfer = [](std::optional<std::reference_wrapper<stack_allocator>> to, stack_allocator& from) {
//#if BLT_DEBUG_LEVEL >= 3
// auto value = from.pop<Return>();
// //BLT_TRACE_STREAM << value << "\n";
// if (to){
// to->get().push(value);
// }
//#else
// if (to)
// {
// to->get().push(from.pop<Return>());
// } else
// {
// from.pop<Return>();
// }
//#endif
//
// };
storage.operators.push_back(info); storage.operators.push_back(info);
storage.print_funcs.push_back([&op](std::ostream& out, stack_allocator& stack) { storage.print_funcs.push_back([](std::ostream& out, stack_allocator& stack) {
if constexpr (blt::meta::is_streamable_v<Return>) { out << stack.pop<Return>();
out << stack.pop<Return>();
(void)(op); // remove warning
} else {
out << "[Printing Value on '" << (op.get_name() ? *op.get_name() : "") << "' Not Supported!]";
}
}); });
storage.names.push_back(op.get_name()); storage.names.push_back(op.get_name());
if (is_static) if (is_static)

View File

@ -151,11 +151,8 @@ namespace blt::gp
if (blk == nullptr) if (blk == nullptr)
throw std::runtime_error("Some nonsense is going on. This function already smells"); throw std::runtime_error("Some nonsense is going on. This function already smells");
if (blk->used_bytes_in_block() < static_cast<blt::ptrdiff_t>(TYPE_SIZE)) if (blk->used_bytes_in_block() < static_cast<blt::ptrdiff_t>(TYPE_SIZE))
{
BLT_WARN_STREAM << size() << "\n";
throw std::runtime_error((std::string("Mismatched Types! Not enough space left in block! Bytes: ") += std::to_string( throw std::runtime_error((std::string("Mismatched Types! Not enough space left in block! Bytes: ") += std::to_string(
blk->used_bytes_in_block()) += " Size: " + std::to_string(sizeof(NO_REF_T))).c_str()); blk->used_bytes_in_block()) += " Size: " + std::to_string(sizeof(NO_REF_T))).c_str());
}
return *reinterpret_cast<NO_REF_T*>(blk->metadata.offset - remaining_bytes); return *reinterpret_cast<NO_REF_T*>(blk->metadata.offset - remaining_bytes);
} }

View File

@ -26,6 +26,7 @@
#include <blt/std/memory.h> #include <blt/std/memory.h>
#include <blt/gp/fwdecl.h> #include <blt/gp/fwdecl.h>
#include <blt/gp/stack.h> #include <blt/gp/stack.h>
#include <blt/gp/operations.h>
#include <random> #include <random>
namespace blt::gp namespace blt::gp

@ -1 +1 @@
Subproject commit befd5e0ca1135c467f194d1d0e5691d301f93a34 Subproject commit fb17ff16c0e10193454921af0d27d1f5afd4ddd6

View File

@ -56,12 +56,9 @@ namespace blt::gp
operations_stack.pop_back(); operations_stack.pop_back();
if (operation.is_value) if (operation.is_value)
{ {
//BLT_DEBUG("Processed Value %ld with size %ld", operation.id, stack_allocator::aligned_size(operation.type_size));
value_stack.transfer_bytes(values_process, operation.type_size); value_stack.transfer_bytes(values_process, operation.type_size);
continue; continue;
} }
//BLT_DEBUG("Processed function %ld with size %ld", operation.id, stack_allocator::aligned_size(operation.type_size));
//BLT_TRACE("Stack info: %ld %ld", values_process.size().total_used_bytes, value_stack.size().total_used_bytes);
operation.func(context, values_process, value_stack); operation.func(context, values_process, value_stack);
operations_stack.emplace_back(empty_callable, operation.type_size, operation.id, true); operations_stack.emplace_back(empty_callable, operation.type_size, operation.id, true);
} }

View File

@ -50,8 +50,6 @@ struct large_18290
blt::u8 data[18290]; blt::u8 data[18290];
}; };
large_18290 base{};
blt::gp::type_provider type_system; blt::gp::type_provider type_system;
blt::gp::gp_program program{type_system, SEED}; blt::gp::gp_program program{type_system, SEED};
@ -67,29 +65,14 @@ blt::gp::op_container_t make_value(const blt::gp::type& id)
return {empty, id.size(), 0, true}; return {empty, id.size(), 0, true};
} }
blt::gp::operation_t add([](float a, float b) { blt::gp::operation_t basic_2([](float a, float b) {
return a + b; return a + b;
}); });
blt::gp::operation_t sub([](float a, float b) {
return a - b;
});
blt::gp::operation_t basic_2t([](float a, bool b) { blt::gp::operation_t basic_2t([](float a, bool b) {
return b ? a : 0.0f; return b ? a : 0.0f;
}); });
blt::gp::operation_t cross_large_type([](const large_18290& input, const float a, const float b) {
BLT_TRACE("%f, %f", a, b);
large_18290 output{};
for (const auto& [index, ref] : blt::enumerate(input.data))
{
if (ref > static_cast<blt::u8>(a) && ref < static_cast<blt::u8>(b))
output.data[index] = ref;
}
return output;
});
blt::gp::operation_t f_literal([]() { blt::gp::operation_t f_literal([]() {
return 0.0f; return 0.0f;
}); });
@ -98,68 +81,35 @@ blt::gp::operation_t b_literal([]() {
return false; return false;
}); });
blt::gp::operation_t large_literal([]() {
return base;
});
void basic_tree() void basic_tree()
{ {
BLT_INFO("Testing if we can get a basic tree going."); BLT_INFO("Testing if we can get a basic tree going.");
blt::gp::tree_t tree; blt::gp::tree_t tree;
tree.get_operations().push_back(make_container(sub.id)); tree.get_operations().push_back(make_container(2));
tree.get_operations().push_back(make_value(type_system.get_type<float>())); tree.get_operations().push_back(make_value(type_system.get_type<float>()));
tree.get_operations().push_back(make_value(type_system.get_type<float>())); tree.get_operations().push_back(make_value(type_system.get_type<float>()));
tree.get_values().push(120.0f);
tree.get_values().push(50.0f); tree.get_values().push(50.0f);
tree.get_values().push(120.0f);
auto val = tree.get_evaluation_value<float>(nullptr); auto val = tree.get_evaluation_value<float>(nullptr);
BLT_TRACE(val); BLT_TRACE(val);
BLT_ASSERT(val == (120 - 50)); BLT_ASSERT(val == (50 + 120));
}
void large_cross_type_tree()
{
blt::gp::tree_t tree;
auto& ops = tree.get_operations();
auto& vals = tree.get_values();
ops.push_back(make_container(cross_large_type.id));
ops.push_back(make_container(large_literal.id));
ops.push_back(make_container(sub.id));
ops.push_back(make_value(type_system.get_type<float>()));
ops.push_back(make_value(type_system.get_type<float>()));
ops.push_back(make_value(type_system.get_type<float>()));
vals.push(50.0f);
vals.push(120.0f);
vals.push(5555.0f);
auto val = tree.get_evaluation_value<large_18290>(nullptr);
blt::black_box(val);
} }
int main() int main()
{ {
for (auto& v : base.data)
v = static_cast<blt::u8>(blt::random::murmur_random64c(691, std::numeric_limits<blt::u8>::min(), std::numeric_limits<blt::u8>::max()));
type_system.register_type<float>(); type_system.register_type<float>();
type_system.register_type<bool>(); type_system.register_type<bool>();
type_system.register_type<large_18290>();
blt::gp::operator_builder builder{type_system}; blt::gp::operator_builder builder{type_system};
builder.add_operator(f_literal); // 0 builder.add_operator(f_literal); // 0
builder.add_operator(b_literal); // 1 builder.add_operator(b_literal); // 1
builder.add_operator(add); // 2 builder.add_operator(basic_2); // 2
builder.add_operator(basic_2t); // 3 builder.add_operator(basic_2t); // 3
builder.add_operator(sub); // 4
builder.add_operator(large_literal); // 5
builder.add_operator(cross_large_type); // 6
program.set_operations(builder.build()); program.set_operations(builder.build());
basic_tree(); basic_tree();
large_cross_type_tree();
} }

View File

@ -33,7 +33,7 @@ struct log_box
~log_box() ~log_box()
{ {
for (auto& _ : text) for (auto& c : text)
logger << '-'; logger << '-';
logger << '\n'; logger << '\n';
} }
@ -51,18 +51,9 @@ T make_data(T t, Func&& func)
return t; return t;
} }
template<typename T, typename Class, blt::size_t size>
static inline auto constexpr array_size(const T(Class::*)[size])
{
return size;
}
template<typename T, typename U> template<typename T, typename U>
blt::ptrdiff_t compare(const T& t, const U& u) blt::ptrdiff_t compare(const T& t, const U& u)
{ {
constexpr auto ts = array_size(&T::data);
constexpr auto us = array_size(&U::data);
BLT_ASSERT_MSG(ts == us, ("Array sizes don't match! " + std::to_string(ts) + " vs " + std::to_string(us)).c_str());
for (const auto& [index, v] : blt::enumerate(t.data)) for (const auto& [index, v] : blt::enumerate(t.data))
{ {
if (u.data[index] != v) if (u.data[index] != v)
@ -298,8 +289,8 @@ void test_basic()
BLT_INFO("Testing basic with stack"); BLT_INFO("Testing basic with stack");
{ {
blt::gp::stack_allocator stack; blt::gp::stack_allocator stack;
stack.push(10.0f);
stack.push(50.0f); stack.push(50.0f);
stack.push(10.0f);
BLT_TRACE_STREAM << stack.size() << "\n"; BLT_TRACE_STREAM << stack.size() << "\n";
basic_2.make_callable<blt::gp::detail::empty_t>()(nullptr, stack, stack); basic_2.make_callable<blt::gp::detail::empty_t>()(nullptr, stack, stack);
BLT_TRACE_STREAM << stack.size() << "\n"; BLT_TRACE_STREAM << stack.size() << "\n";
@ -312,8 +303,8 @@ void test_basic()
{ {
blt::gp::stack_allocator stack; blt::gp::stack_allocator stack;
stack.push(std::array<blt::u8, blt::gp::stack_allocator::page_size_no_block() - sizeof(float)>{}); stack.push(std::array<blt::u8, blt::gp::stack_allocator::page_size_no_block() - sizeof(float)>{});
stack.push(10.0f);
stack.push(50.0f); stack.push(50.0f);
stack.push(10.0f);
auto size = stack.size(); auto size = stack.size();
BLT_TRACE_STREAM << size << "\n"; BLT_TRACE_STREAM << size << "\n";
BLT_ASSERT(size.blocks > 1 && "Stack doesn't have more than one block!"); BLT_ASSERT(size.blocks > 1 && "Stack doesn't have more than one block!");
@ -332,11 +323,10 @@ void test_mixed()
BLT_INFO("Testing mixed with stack"); BLT_INFO("Testing mixed with stack");
{ {
blt::gp::stack_allocator stack; blt::gp::stack_allocator stack;
stack.push(false);
stack.push(true);
stack.push(10.0f);
stack.push(50.0f); stack.push(50.0f);
stack.push(10.0f);
stack.push(true);
stack.push(false);
BLT_TRACE_STREAM << stack.size() << "\n"; BLT_TRACE_STREAM << stack.size() << "\n";
basic_mixed_4.make_callable<blt::gp::detail::empty_t>()(nullptr, stack, stack); basic_mixed_4.make_callable<blt::gp::detail::empty_t>()(nullptr, stack, stack);
BLT_TRACE_STREAM << stack.size() << "\n"; BLT_TRACE_STREAM << stack.size() << "\n";
@ -349,10 +339,10 @@ void test_mixed()
{ {
blt::gp::stack_allocator stack; blt::gp::stack_allocator stack;
stack.push(std::array<blt::u8, blt::gp::stack_allocator::page_size_no_block() - sizeof(float)>{}); stack.push(std::array<blt::u8, blt::gp::stack_allocator::page_size_no_block() - sizeof(float)>{});
stack.push(false);
stack.push(true);
stack.push(10.0f);
stack.push(50.0f); stack.push(50.0f);
stack.push(10.0f);
stack.push(true);
stack.push(false);
auto size = stack.size(); auto size = stack.size();
BLT_TRACE_STREAM << size << "\n"; BLT_TRACE_STREAM << size << "\n";
BLT_ASSERT(size.blocks > 1 && "Stack doesn't have more than one block!"); BLT_ASSERT(size.blocks > 1 && "Stack doesn't have more than one block!");
@ -371,10 +361,9 @@ void test_large_256()
BLT_INFO("Testing large 256 with stack"); BLT_INFO("Testing large 256 with stack");
{ {
blt::gp::stack_allocator stack; blt::gp::stack_allocator stack;
stack.push(69.420f);
stack.push(691.0f);
stack.push(base_256); stack.push(base_256);
stack.push(691.0f);
stack.push(69.420f);
BLT_TRACE_STREAM << stack.size() << "\n"; BLT_TRACE_STREAM << stack.size() << "\n";
large_256_basic_3.make_callable<blt::gp::detail::empty_t>()(nullptr, stack, stack); large_256_basic_3.make_callable<blt::gp::detail::empty_t>()(nullptr, stack, stack);
BLT_TRACE_STREAM << stack.size() << "\n"; BLT_TRACE_STREAM << stack.size() << "\n";
@ -387,9 +376,9 @@ void test_large_256()
{ {
blt::gp::stack_allocator stack; blt::gp::stack_allocator stack;
stack.push(std::array<blt::u8, blt::gp::stack_allocator::page_size_no_block() - sizeof(large_256)>{}); stack.push(std::array<blt::u8, blt::gp::stack_allocator::page_size_no_block() - sizeof(large_256)>{});
stack.push(69.420f);
stack.push(691.0f);
stack.push(base_256); stack.push(base_256);
stack.push(691.0f);
stack.push(69.420f);
auto size = stack.size(); auto size = stack.size();
BLT_TRACE_STREAM << size << "\n"; BLT_TRACE_STREAM << size << "\n";
BLT_ASSERT(size.blocks > 1 && "Stack doesn't have more than one block!"); BLT_ASSERT(size.blocks > 1 && "Stack doesn't have more than one block!");
@ -408,9 +397,9 @@ void test_large_4096()
BLT_INFO("Testing large 4096 with stack"); BLT_INFO("Testing large 4096 with stack");
{ {
blt::gp::stack_allocator stack; blt::gp::stack_allocator stack;
stack.push(true);
stack.push(33.0f);
stack.push(base_4096); stack.push(base_4096);
stack.push(33.0f);
stack.push(true);
BLT_TRACE_STREAM << stack.size() << "\n"; BLT_TRACE_STREAM << stack.size() << "\n";
large_4096_basic_3b.make_callable<blt::gp::detail::empty_t>()(nullptr, stack, stack); large_4096_basic_3b.make_callable<blt::gp::detail::empty_t>()(nullptr, stack, stack);
BLT_TRACE_STREAM << stack.size() << "\n"; BLT_TRACE_STREAM << stack.size() << "\n";
@ -422,16 +411,16 @@ void test_large_4096()
BLT_INFO("Testing large 4096 with stack over boundary"); BLT_INFO("Testing large 4096 with stack over boundary");
{ {
blt::gp::stack_allocator stack; blt::gp::stack_allocator stack;
stack.push(true);
stack.push(33.0f);
stack.push(base_4096); stack.push(base_4096);
stack.push(33.0f);
stack.push(true);
auto size = stack.size(); auto size = stack.size();
BLT_TRACE_STREAM << size << "\n"; BLT_TRACE_STREAM << size << "\n";
BLT_ASSERT(size.blocks > 1 && "Stack doesn't have more than one block!"); BLT_ASSERT(size.blocks > 1 && "Stack doesn't have more than one block!");
large_4096_basic_3b.make_callable<blt::gp::detail::empty_t>()(nullptr, stack, stack); large_4096_basic_3b.make_callable<blt::gp::detail::empty_t>()(nullptr, stack, stack);
BLT_TRACE_STREAM << stack.size() << "\n"; BLT_TRACE_STREAM << stack.size() << "\n";
auto val = stack.pop<large_4096>(); auto val = stack.pop<large_4096>();
RUN_TEST(!compare(val, base_4096), stack, "Large 4096 3 Boundary Test Passed", "Large 4096 3 Test Failed. Unexpected value produced '%lf'", val); RUN_TEST(!compare(val, base_256), stack, "Large 4096 3 Boundary Test Passed", "Large 4096 3 Test Failed. Unexpected value produced '%lf'", val);
BLT_TRACE_STREAM << stack.size() << "\n"; BLT_TRACE_STREAM << stack.size() << "\n";
BLT_ASSERT(stack.empty() && "Stack was not empty after evaluation over stack boundary"); BLT_ASSERT(stack.empty() && "Stack was not empty after evaluation over stack boundary");
} }
@ -442,9 +431,9 @@ void test_large_18290()
BLT_INFO("Testing large 18290 with stack"); BLT_INFO("Testing large 18290 with stack");
{ {
blt::gp::stack_allocator stack; blt::gp::stack_allocator stack;
stack.push(true);
stack.push(-2543.0f);
stack.push(base_18290); stack.push(base_18290);
stack.push(-2543.0f);
stack.push(true);
BLT_TRACE_STREAM << stack.size() << "\n"; BLT_TRACE_STREAM << stack.size() << "\n";
large_18290_basic_3b.make_callable<blt::gp::detail::empty_t>()(nullptr, stack, stack); large_18290_basic_3b.make_callable<blt::gp::detail::empty_t>()(nullptr, stack, stack);
BLT_TRACE_STREAM << stack.size() << "\n"; BLT_TRACE_STREAM << stack.size() << "\n";
@ -457,9 +446,9 @@ void test_large_18290()
{ {
blt::gp::stack_allocator stack; blt::gp::stack_allocator stack;
stack.push(std::array<blt::u8, 20480 - 18290 - blt::gp::stack_allocator::block_size()>()); stack.push(std::array<blt::u8, 20480 - 18290 - blt::gp::stack_allocator::block_size()>());
stack.push(true);
stack.push(-2543.0f);
stack.push(base_18290); stack.push(base_18290);
stack.push(-2543.0f);
stack.push(true);
auto size = stack.size(); auto size = stack.size();
BLT_TRACE_STREAM << size << "\n"; BLT_TRACE_STREAM << size << "\n";
BLT_ASSERT(size.blocks > 1 && "Stack doesn't have more than one block!"); BLT_ASSERT(size.blocks > 1 && "Stack doesn't have more than one block!");
@ -474,15 +463,15 @@ void test_large_18290()
BLT_INFO("Testing large 18290 with stack over multiple boundaries"); BLT_INFO("Testing large 18290 with stack over multiple boundaries");
{ {
blt::gp::stack_allocator stack; blt::gp::stack_allocator stack;
stack.push(true);
stack.push(-2543.0f);
stack.push(true);
stack.push(-2543.0f);
stack.push(base_18290); stack.push(base_18290);
stack.push(-2543.0f);
stack.push(true);
auto size = stack.size(); auto size = stack.size();
BLT_TRACE_STREAM << size << "\n"; BLT_TRACE_STREAM << size << "\n";
large_18290_basic_3b.make_callable<blt::gp::detail::empty_t>()(nullptr, stack, stack); large_18290_basic_3b.make_callable<blt::gp::detail::empty_t>()(nullptr, stack, stack);
BLT_TRACE_STREAM << stack.size() << "\n"; BLT_TRACE_STREAM << stack.size() << "\n";
stack.push(-2543.0f);
stack.push(true);
BLT_TRACE_STREAM << stack.size() << "\n"; BLT_TRACE_STREAM << stack.size() << "\n";
large_18290_basic_3b.make_callable<blt::gp::detail::empty_t>()(nullptr, stack, stack); large_18290_basic_3b.make_callable<blt::gp::detail::empty_t>()(nullptr, stack, stack);
BLT_TRACE_STREAM << stack.size() << "\n"; BLT_TRACE_STREAM << stack.size() << "\n";