diff --git a/CMakeLists.txt b/CMakeLists.txt index 84709f5..5c0fd5e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,5 +1,5 @@ cmake_minimum_required(VERSION 3.25) -project(blt-gp VERSION 0.0.27) +project(blt-gp VERSION 0.0.28) option(ENABLE_ADDRSAN "Enable the address sanitizer" OFF) option(ENABLE_UBSAN "Enable the ub sanitizer" OFF) diff --git a/examples/gp_test_2.cpp b/examples/gp_test_2.cpp index 5c64961..894e2ad 100644 --- a/examples/gp_test_2.cpp +++ b/examples/gp_test_2.cpp @@ -20,7 +20,6 @@ static constexpr long SEED = 41912; blt::gp::type_system type_system; -std::random_device dev; blt::gp::gp_program program(type_system, std::mt19937_64{SEED}); // NOLINT blt::gp::operation_t add([](float a, float b) { return a + b; }); @@ -37,11 +36,14 @@ int main() type_system.register_type(); type_system.register_type(); - program.add_operator(add); - program.add_operator(sub); - program.add_operator(mul); - program.add_operator(pro_div); - program.add_operator(lit, true); + blt::gp::gp_operations silly{type_system}; + silly.add_operator(add); + silly.add_operator(sub); + silly.add_operator(mul); + silly.add_operator(pro_div); + silly.add_operator(lit, true); + + program.set_operations(std::move(silly)); return 0; } \ No newline at end of file diff --git a/include/blt/gp/operations.h b/include/blt/gp/operations.h index 51fe5ac..480f3f5 100644 --- a/include/blt/gp/operations.h +++ b/include/blt/gp/operations.h @@ -23,9 +23,66 @@ #include #include #include +#include namespace blt::gp { + namespace detail + { + using callable_t = std::function; + struct empty_t + { + }; + } + + template + struct call_with + { + template + [[nodiscard]] inline constexpr static blt::size_t getByteOffset() + { + blt::size_t offset = 0; + blt::size_t current_index = 0; + ((offset += (current_index++ > index ? stack_allocator::aligned_size() : 0)), ...); + return offset; + } + + template + inline static constexpr Return exec_sequence_to_indices(Func&& func, stack_allocator& allocator, std::integer_sequence, + ExtraArgs&& ... args) + { + // expands Args and indices, providing each argument with its index calculating the current argument byte offset + return std::forward(func)(std::forward(args)..., allocator.from(getByteOffset())...); + } + + template + Return operator()(Func&& func, stack_allocator& allocator, ExtraArgs&& ... args) + { + constexpr auto seq = std::make_integer_sequence(); + Return ret = exec_sequence_to_indices(std::forward(func), allocator, seq, std::forward(args)...); + allocator.call_destructors(); + allocator.pop_bytes((stack_allocator::aligned_size() + ...)); + return ret; + } + }; + + template + struct first_arg + { + using type = First; + }; + + template<> + struct first_arg + { + }; + + template + struct call_without_first : public call_with + { + using call_with::call_with; + }; + template class operation_t; @@ -43,22 +100,6 @@ namespace blt::gp constexpr explicit operation_t(const Functor& functor): func(functor) {} - template - [[nodiscard]] inline constexpr static blt::size_t getByteOffset() - { - blt::size_t offset = 0; - blt::size_t current_index = 0; - ((offset += (current_index++ > index ? stack_allocator::aligned_size() : 0)), ...); - return offset; - } - - template - inline constexpr Return exec_sequence_to_indices(stack_allocator& allocator, std::integer_sequence) const - { - // expands Args and indices, providing each argument with its index calculating the current argument byte offset - return func(allocator.from(getByteOffset())...); - } - [[nodiscard]] constexpr inline Return operator()(stack_allocator& allocator) const { if constexpr (sizeof...(Args) == 0) @@ -66,18 +107,47 @@ namespace blt::gp return func(); } else { - constexpr auto seq = std::make_integer_sequence(); - Return ret = exec_sequence_to_indices(allocator, seq); - allocator.call_destructors(); - allocator.pop_bytes((stack_allocator::aligned_size() + ...)); - return ret; + return call_with()(func, allocator); } } - [[nodiscard]] std::function make_callable() const + [[nodiscard]] constexpr inline Return operator()(void* context, stack_allocator& allocator) const { - return [this](stack_allocator& values) { - values.push(this->operator()(values)); + // should be an impossible state + if constexpr (sizeof...(Args) == 0) + { + BLT_ABORT("Cannot pass context to function without arguments!"); + } + auto& ctx_ref = *static_cast::type*>(context); + if constexpr (sizeof...(Args) == 1) + { + return func(ctx_ref); + } else + { + return call_without_first()(func, allocator, ctx_ref); + } + } + + template + [[nodiscard]] detail::callable_t make_callable() const + { + return [this](void* context, stack_allocator& values) { + if constexpr (sizeof...(Args) == 0) + { + values.push(this->operator()(values)); + } else + { + // annoying hack. + if constexpr (std::is_same_v::type>) + { + // first arg is context + values.push(this->operator()(context, values)); + } else + { + // first arg isn't context + values.push(this->operator()(values)); + } + } }; } @@ -101,7 +171,7 @@ namespace blt::gp operation_t(Lambda) -> operation_t; template - operation_t(Return (*)(Args...)) -> operation_t; + operation_t(Return(*)(Args...)) -> operation_t; // templat\e // operation_t make_operator(Return (Class::*)(Args...) const lambda) diff --git a/include/blt/gp/program.h b/include/blt/gp/program.h index 2f9da23..826c847 100644 --- a/include/blt/gp/program.h +++ b/include/blt/gp/program.h @@ -46,6 +46,42 @@ namespace blt::gp static constexpr blt::size_t STATIC_T = 0x1; static constexpr blt::size_t TERMINAL_T = 0x2; + template + class gp_operations + { + friend class gp_program; + + public: + explicit gp_operations(type_system& system): system(system) + {} + + template + void add_operator(const operation_t& op, bool is_static = false) + { + auto return_type_id = system.get_type().id(); + auto operator_id = blt::gp::operator_id(operators.size()); + + auto& operator_list = op.get_argc() == 0 ? terminals : non_terminals; + operator_list[return_type_id].push_back(operator_id); + + (argument_types[operator_id].push_back(system.get_type()), ...); + operators.push_back(op.template make_callable()); + if (is_static) + static_types.insert(operator_id); + } + + private: + type_system& system; + + // indexed from return TYPE ID, returns index of operator + blt::expanding_buffer> terminals; + blt::expanding_buffer> non_terminals; + // indexed from OPERATOR ID (operator number) + blt::expanding_buffer> argument_types; + blt::hashset_t static_types; + std::vector operators; + }; + class gp_program { public: @@ -105,19 +141,14 @@ namespace blt::gp return static_types.contains(static_cast(id)); } - template - void add_operator(const operation_t& op, bool is_static = false) + template + inline void set_operations(gp_operations&& op) { - auto return_type_id = system.get_type().id(); - auto operator_id = blt::gp::operator_id(operators.size()); - - auto& operator_list = op.get_argc() == 0 ? terminals : non_terminals; - operator_list[return_type_id].push_back(operator_id); - - (argument_types[operator_id].push_back(system.get_type()), ...); - operators.push_back(op.make_callable()); - if (is_static) - static_types.insert(operator_id); + terminals = std::move(op.terminals); + non_terminals = std::move(op.non_terminals); + argument_types = std::move(op.argument_types); + static_types = std::move(op.static_types); + operators = std::move(op.operators); } private: @@ -131,7 +162,7 @@ namespace blt::gp // indexed from OPERATOR ID (operator number) blt::expanding_buffer> argument_types; blt::hashset_t static_types; - std::vector> operators; + std::vector operators; }; } diff --git a/lib/blt b/lib/blt index cc788e9..ac163a3 160000 --- a/lib/blt +++ b/lib/blt @@ -1 +1 @@ -Subproject commit cc788e98f4a739f450f487f6a7c758990128a698 +Subproject commit ac163a34b9d70346df80fcac44054e8dffe9ad43 diff --git a/src/generators.cpp b/src/generators.cpp index 53d5ffc..5e63cda 100644 --- a/src/generators.cpp +++ b/src/generators.cpp @@ -40,13 +40,13 @@ namespace blt::gp base_type = system.select_type(program.get_random()); } while (program.get_type_non_terminals(base_type.id()).empty()); - tree_generator.emplace(program.select_non_terminal(base_type.id()), 0); + tree_generator.push(stack{program.select_non_terminal(base_type.id()), 0}); return tree_generator; } template - tree_t create_tree(Func func, gp_program& program, blt::size_t min_depth, blt::size_t max_depth) + tree_t create_tree(Func, gp_program& program, blt::size_t, blt::size_t) { std::stack tree_generator = get_base_generator(program); tree_t tree;