From 9ea6568cdbbc3e029827ba7af14cecfa2155c519 Mon Sep 17 00:00:00 2001 From: Brett Laptop Date: Fri, 18 Apr 2025 18:13:59 -0400 Subject: [PATCH] wow --- .idea/editor.xml | 483 ++++++++++++++++++++++++++++++++++- CMakeLists.txt | 2 +- include/blt/gp/program.h | 114 ++++++++- src/program.cpp | 81 +++--- src/transformers.cpp | 6 +- tests/serialization_test.cpp | 14 +- 6 files changed, 639 insertions(+), 61 deletions(-) diff --git a/.idea/editor.xml b/.idea/editor.xml index 5603a5a..a164eb6 100644 --- a/.idea/editor.xml +++ b/.idea/editor.xml @@ -1,13 +1,488 @@ + \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index 8084d95..dc58c56 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.12) +project(blt-gp VERSION 0.5.13) include(CTest) diff --git a/include/blt/gp/program.h b/include/blt/gp/program.h index f747ff3..85927ee 100644 --- a/include/blt/gp/program.h +++ b/include/blt/gp/program.h @@ -106,8 +106,118 @@ namespace blt::gp type_provider system; }; + namespace errors::serialization + { + struct invalid_read_t + { + i64 read; + i64 expected; + + [[nodiscard]] std::string to_string() const + { + return "Invalid read; unable to read sufficient bytes to populate the object. Expected " + std::to_string(expected) + " but read " + + std::to_string(read); + } + }; + + struct unexpected_size_t + { + size_t read_size; + size_t expected_size; + + [[nodiscard]] std::string to_string() const + { + return "Invalid read of a size type. Expected to find " + std::to_string(expected_size) + " found " + std::to_string(read_size); + } + }; + + struct invalid_operator_id_t + { + operator_id read; + operator_id expected; + + [[nodiscard]] std::string to_string() const + { + return "Loaded invalid operator ID. Expected " + std::to_string(expected) + " but found " + std::to_string(read); + } + }; + + struct invalid_name_t + { + operator_id id; + std::string found; + std::string expected; + + [[nodiscard]] std::string to_string() const + { + return "Operator ID " + std::to_string(id) + " expected to have name '" + expected + "' but found '" + found + "'"; + } + }; + + struct mismatched_bytes_t + { + operator_id id; + size_t read_size; + size_t expected_size; + + [[nodiscard]] std::string to_string() const + { + return "Operator ID " + std::to_string(id) + " expected bytes " + std::to_string(expected_size) + " but found " + + std::to_string(read_size); + } + }; + + struct mismatched_argc_t + { + operator_id id; + size_t read_argc; + size_t expected_argc; + + [[nodiscard]] std::string to_string() const + { + return "Operator ID " + std::to_string(id) + " expected argc " + std::to_string(expected_argc) + " but found " + + std::to_string(read_argc); + } + }; + + struct mismatched_return_type_t + { + operator_id id; + type_id read_type; + type_id expected_type; + + [[nodiscard]] std::string to_string() const + { + return "Operator ID " + std::to_string(id) + + " expected return type " + std::to_string(expected_type) + + " but got " + std::to_string(read_type); + } + }; + + struct mismatched_arg_type_t + { + operator_id id; + size_t arg_index; + type_id read_type; + type_id expected_type; + + [[nodiscard]] std::string to_string() const + { + return "Operator ID " + std::to_string(id) + + " expected argument " + std::to_string(arg_index) + + " to be of type " + std::to_string(expected_type) + + " but got " + std::to_string(read_type); + } + }; + + using serializer_error_t = std::variant; + + std::string to_string(const serializer_error_t& error); + } - using serializer_error_t = std::variant<>; template class operator_builder @@ -838,7 +948,7 @@ namespace blt::gp bool load_generation(fs::reader_t& reader); - std::optional load_state(fs::reader_t& reader); + std::optional load_state(fs::reader_t& reader); private: template diff --git a/src/program.cpp b/src/program.cpp index e17702a..197f0e6 100644 --- a/src/program.cpp +++ b/src/program.cpp @@ -18,11 +18,24 @@ #include #include -#define BLT_ASSERT_RET(expr, ret) if (!(expr)) { return ret; } +#ifndef BLT_ASSERT_RET #define BLT_ASSERT_RET(expr) if (!(expr)) { return false; } +#endif + +#define BLT_READ(read_statement, size) do { auto read = read_statement; if (read != size) { return blt::gp::errors::serialization::invalid_read_t{read, size}; } } while (false) namespace blt::gp { + std::string errors::serialization::to_string(const serializer_error_t& error) + { + return std::visit(lambda_visitor{ + [](const auto& val) + { + return val.to_string(); + } + }, error); + } + // default static references for mutation, crossover, and initializer // 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 @@ -152,87 +165,71 @@ namespace blt::gp save_generation(writer); } - std::optional gp_program::load_state(fs::reader_t& reader) + std::optional gp_program::load_state(fs::reader_t& reader) { size_t operator_count; - BLT_ASSERT(reader.read(&operator_count, sizeof(operator_count)) == sizeof(operator_count)); + BLT_READ(reader.read(&operator_count, sizeof(operator_count)), sizeof(operator_count)); if (operator_count != storage.operators.size()) - throw std::runtime_error( - "Invalid number of operators. Expected " + std::to_string(storage.operators.size()) + " found " + std::to_string(operator_count)); + return errors::serialization::unexpected_size_t{operator_count, storage.operators.size()}; for (size_t i = 0; i < operator_count; i++) { size_t expected_i; - BLT_ASSERT(reader.read(&expected_i, sizeof(expected_i)) == sizeof(expected_i)); + BLT_READ(reader.read(&expected_i, sizeof(expected_i)), sizeof(expected_i)); if (expected_i != i) - throw std::runtime_error("Loaded invalid operator ID. Expected " + std::to_string(i) + " found " + std::to_string(expected_i)); + return errors::serialization::invalid_operator_id_t{i, expected_i}; bool has_name; - BLT_ASSERT(reader.read(&has_name, sizeof(has_name)) == sizeof(has_name)); + BLT_READ(reader.read(&has_name, sizeof(has_name)), sizeof(has_name)); if (has_name) { size_t size; - BLT_ASSERT(reader.read(&size, sizeof(size)) == sizeof(size)); + BLT_READ(reader.read(&size, sizeof(size)), sizeof(size)); std::string name; name.resize(size); - BLT_ASSERT(reader.read(name.data(), size) == static_cast(size)); + BLT_READ(reader.read(name.data(), size), static_cast(size)); if (!storage.names[i].has_value()) - throw std::runtime_error("Expected operator ID " + std::to_string(i) + " to have name " + name); + return errors::serialization::invalid_name_t{i, name, "NO NAME"}; if (name != *storage.names[i]) - throw std::runtime_error( - "Operator ID " + std::to_string(i) + " expected to be named " + name + " found " + std::string(*storage.names[i])); + return errors::serialization::invalid_name_t{i, name, std::string{*storage.names[i]}}; const auto& op = storage.operators[i]; const auto& op_meta = storage.operator_metadata[i]; decltype(std::declval().arg_size_bytes) arg_size_bytes; decltype(std::declval().return_size_bytes) return_size_bytes; - BLT_ASSERT(reader.read(&arg_size_bytes, sizeof(arg_size_bytes)) == sizeof(arg_size_bytes)); - BLT_ASSERT(reader.read(&return_size_bytes, sizeof(return_size_bytes)) == sizeof(return_size_bytes)); + BLT_READ(reader.read(&arg_size_bytes, sizeof(arg_size_bytes)), sizeof(arg_size_bytes)); + BLT_READ(reader.read(&return_size_bytes, sizeof(return_size_bytes)), sizeof(return_size_bytes)); if (op_meta.arg_size_bytes != arg_size_bytes) - throw std::runtime_error( - "Operator ID " + std::to_string(i) + " expected operator to take " + std::to_string(op_meta.arg_size_bytes) + " but got " + - std::to_string(arg_size_bytes)); + return errors::serialization::mismatched_bytes_t{i, arg_size_bytes, op_meta.arg_size_bytes}; if (op_meta.return_size_bytes != return_size_bytes) - throw std::runtime_error( - "Operator ID " + std::to_string(i) + " expected operator to return " + std::to_string(op_meta.return_size_bytes) + " but got " - + - std::to_string(return_size_bytes)); + return errors::serialization::mismatched_bytes_t{i, return_size_bytes, op_meta.return_size_bytes}; argc_t argc; - BLT_ASSERT(reader.read(&argc, sizeof(argc)) == sizeof(argc)); + BLT_READ(reader.read(&argc, sizeof(argc)), sizeof(argc)); if (argc.argc != op.argc.argc) - throw std::runtime_error( - "Operator ID " + std::to_string(i) + " expected " + std::to_string(op.argc.argc) + " arguments but got " + std::to_string( - argc.argc)); + return errors::serialization::mismatched_argc_t{i, argc.argc, op.argc.argc}; if (argc.argc_context != op.argc.argc_context) - throw std::runtime_error( - "Operator ID " + std::to_string(i) + " expected " + std::to_string(op.argc.argc_context) + " arguments but got " + - std::to_string(argc.argc_context)); + return errors::serialization::mismatched_argc_t{i, argc.argc_context, op.argc.argc_context}; + type_id return_type; - BLT_ASSERT(reader.read(&return_type, sizeof(return_type)) == sizeof(return_type)); + BLT_READ(reader.read(&return_type, sizeof(return_type)), sizeof(return_type)); if (return_type != op.return_type) - throw std::runtime_error( - "Operator ID " + std::to_string(i) + " expected return type " + std::to_string(op.return_type) + " but got " + std::to_string( - return_type)); + return errors::serialization::mismatched_return_type_t{i, return_type, op.return_type}; size_t arg_type_count; - BLT_ASSERT(reader.read(&arg_type_count, sizeof(arg_type_count)) == sizeof(return_type)); + BLT_READ(reader.read(&arg_type_count, sizeof(arg_type_count)), sizeof(return_type)); if (arg_type_count != op.argument_types.size()) - throw std::runtime_error( - "Operator ID " + std::to_string(i) + " expected " + std::to_string(op.argument_types.size()) + " arguments but got " + - std::to_string(arg_type_count)); + return errors::serialization::unexpected_size_t{arg_type_count, op.argument_types.size()}; for (size_t j = 0; j < arg_type_count; j++) { type_id type; - BLT_ASSERT(reader.read(&type, sizeof(type)) == sizeof(type)); + BLT_READ(reader.read(&type, sizeof(type)), sizeof(type)); if (type != op.argument_types[j]) - throw std::runtime_error( - "Operator ID " + std::to_string(i) + " expected argument " + std::to_string(j) + " to be of type " + std::to_string( - op.argument_types[j]) + " but got " + std::to_string(type)); + return errors::serialization::mismatched_arg_type_t{i, j, type, op.argument_types[j]}; } } } size_t history_count; - BLT_ASSERT(reader.read(&history_count, sizeof(history_count)) == sizeof(history_count)); + BLT_READ(reader.read(&history_count, sizeof(history_count)), sizeof(history_count)); statistic_history.resize(history_count); for (size_t i = 0; i < history_count; i++) load_stat(reader, statistic_history[i]); diff --git a/src/transformers.cpp b/src/transformers.cpp index 36da17a..b6f6b3b 100644 --- a/src/transformers.cpp +++ b/src/transformers.cpp @@ -93,8 +93,8 @@ namespace blt::gp std::optional crossover_t::get_crossover_point(const tree_t& c1, const tree_t& c2) const { - auto first = c1.select_subtree(config.terminal_chance); - auto second = c2.select_subtree(first.type, config.max_crossover_tries, config.terminal_chance); + const auto first = c1.select_subtree(config.terminal_chance); + const auto second = c2.select_subtree(first.type, config.max_crossover_tries, config.terminal_chance); if (!second) return {}; @@ -108,7 +108,7 @@ namespace blt::gp auto c1_point_o = get_point_traverse_retry(c1, {}); if (!c1_point_o) return {}; - auto c2_point_o = get_point_traverse_retry(c2, c1_point_o->type); + const auto c2_point_o = get_point_traverse_retry(c2, c1_point_o->type); if (!c2_point_o) return {}; return {{*c1_point_o, *c2_point_o}}; diff --git a/tests/serialization_test.cpp b/tests/serialization_test.cpp index 9bf5327..9965f25 100644 --- a/tests/serialization_test.cpp +++ b/tests/serialization_test.cpp @@ -156,15 +156,11 @@ int main() } } } - try { - std::ifstream stream{"serialization_test2.data", std::ios::binary}; - blt::fs::fstream_reader_t reader{stream}; - bad_program.load_state(reader); - } catch (const std::runtime_error&) + std::ifstream stream{"serialization_test2.data", std::ios::binary}; + blt::fs::fstream_reader_t reader{stream}; + if (auto err = bad_program.load_state(reader)) { - // TODO: use blt::expected so this isn't required + better design. - goto exit; + BLT_ERROR(blt::gp::errors::serialization::to_string(*err)); + BLT_ASSERT(false && "Expected program to throw an exception when parsing state data into an incompatible program!"); } - BLT_ASSERT(false && "Expected program to throw an exception when parsing state data into an incompatible program!"); - exit: }