From e61be765315292e58c1b27ed9f603a0bc3f50e21 Mon Sep 17 00:00:00 2001 From: leftibot Date: Mon, 13 Apr 2026 20:20:11 -0600 Subject: [PATCH] Fix #601: Allow operator functions to accept any type with an add() method (#686) The operator functions in chaiscript::bootstrap::operators (equal, not_equal, assign, etc.) hardcoded Module& as their first parameter, preventing chaiscript::utility::add_class for enum types from working when passed a ChaiScript& reference directly. Added a second template parameter ModuleType to all operator functions so they accept any type that provides an add() method, matching the already-templatized add_class functions in utility.hpp. Co-authored-by: leftibot Co-authored-by: Claude Opus 4.6 (1M context) --- include/chaiscript/dispatchkit/operators.hpp | 132 +++++++++---------- unittests/compiled_tests.cpp | 39 ++++++ 2 files changed, 105 insertions(+), 66 deletions(-) diff --git a/include/chaiscript/dispatchkit/operators.hpp b/include/chaiscript/dispatchkit/operators.hpp index 2545bfce..d32ee9d6 100644 --- a/include/chaiscript/dispatchkit/operators.hpp +++ b/include/chaiscript/dispatchkit/operators.hpp @@ -15,168 +15,168 @@ #include "register_function.hpp" namespace chaiscript::bootstrap::operators { - template - void assign(Module &m) { + template + void assign(ModuleType &m) { m.add(chaiscript::fun([](T &lhs, const T &rhs) -> T & { return lhs = rhs; }), "="); } - template - void assign_bitwise_and(Module &m) { + template + void assign_bitwise_and(ModuleType &m) { m.add(chaiscript::fun([](T &lhs, const T &rhs) -> T & { return lhs &= rhs; }), "&="); } - template - void assign_xor(Module &m) { + template + void assign_xor(ModuleType &m) { m.add(chaiscript::fun([](T &lhs, const T &rhs) -> T & { return lhs ^= rhs; }), "^="); } - template - void assign_bitwise_or(Module &m) { + template + void assign_bitwise_or(ModuleType &m) { m.add(chaiscript::fun([](T &lhs, const T &rhs) -> T & { return lhs |= rhs; }), "|="); } - template - void assign_difference(Module &m) { + template + void assign_difference(ModuleType &m) { m.add(chaiscript::fun([](T &lhs, const T &rhs) -> T & { return lhs -= rhs; }), "-="); } - template - void assign_left_shift(Module &m) { + template + void assign_left_shift(ModuleType &m) { m.add(chaiscript::fun([](T &lhs, const T &rhs) -> T & { return lhs <<= rhs; }), "<<="); } - template - void assign_product(Module &m) { + template + void assign_product(ModuleType &m) { m.add(chaiscript::fun([](T &lhs, const T &rhs) -> T & { return lhs <<= rhs; }), "*="); } - template - void assign_quotient(Module &m) { + template + void assign_quotient(ModuleType &m) { m.add(chaiscript::fun([](T &lhs, const T &rhs) -> T & { return lhs /= rhs; }), "/="); } - template - void assign_remainder(Module &m) { + template + void assign_remainder(ModuleType &m) { m.add(chaiscript::fun([](T &lhs, const T &rhs) -> T & { return lhs %= rhs; }), "%="); } - template - void assign_right_shift(Module &m) { + template + void assign_right_shift(ModuleType &m) { m.add(chaiscript::fun([](T &lhs, const T &rhs) -> T & { return lhs >>= rhs; }), ">>="); } - template - void assign_sum(Module &m) { + template + void assign_sum(ModuleType &m) { m.add(chaiscript::fun([](T &lhs, const T &rhs) -> T & { return lhs += rhs; }), "+="); } - template - void prefix_decrement(Module &m) { + template + void prefix_decrement(ModuleType &m) { m.add(chaiscript::fun([](T &lhs) -> T & { return --lhs; }), "--"); } - template - void prefix_increment(Module &m) { + template + void prefix_increment(ModuleType &m) { m.add(chaiscript::fun([](T &lhs) -> T & { return ++lhs; }), "++"); } - template - void equal(Module &m) { + template + void equal(ModuleType &m) { m.add(chaiscript::fun([](const T &lhs, const T &rhs) { return lhs == rhs; }), "=="); } - template - void greater_than(Module &m) { + template + void greater_than(ModuleType &m) { m.add(chaiscript::fun([](const T &lhs, const T &rhs) { return lhs > rhs; }), ">"); } - template - void greater_than_equal(Module &m) { + template + void greater_than_equal(ModuleType &m) { m.add(chaiscript::fun([](const T &lhs, const T &rhs) { return lhs >= rhs; }), ">="); } - template - void less_than(Module &m) { + template + void less_than(ModuleType &m) { m.add(chaiscript::fun([](const T &lhs, const T &rhs) { return lhs < rhs; }), "<"); } - template - void less_than_equal(Module &m) { + template + void less_than_equal(ModuleType &m) { m.add(chaiscript::fun([](const T &lhs, const T &rhs) { return lhs <= rhs; }), "<="); } - template - void logical_compliment(Module &m) { + template + void logical_compliment(ModuleType &m) { m.add(chaiscript::fun([](const T &lhs) { return !lhs; }), "!"); } - template - void not_equal(Module &m) { + template + void not_equal(ModuleType &m) { m.add(chaiscript::fun([](const T &lhs, const T &rhs) { return lhs != rhs; }), "!="); } - template - void addition(Module &m) { + template + void addition(ModuleType &m) { m.add(chaiscript::fun([](const T &lhs, const T &rhs) { return lhs + rhs; }), "+"); } - template - void unary_plus(Module &m) { + template + void unary_plus(ModuleType &m) { m.add(chaiscript::fun([](const T &lhs) { return +lhs; }), "+"); } - template - void subtraction(Module &m) { + template + void subtraction(ModuleType &m) { m.add(chaiscript::fun([](const T &lhs, const T &rhs) { return lhs - rhs; }), "-"); } - template - void unary_minus(Module &m) { + template + void unary_minus(ModuleType &m) { m.add(chaiscript::fun([](const T &lhs) { return -lhs; }), "-"); } - template - void bitwise_and(Module &m) { + template + void bitwise_and(ModuleType &m) { m.add(chaiscript::fun([](const T &lhs, const T &rhs) { return lhs & rhs; }), "&"); } - template - void bitwise_compliment(Module &m) { + template + void bitwise_compliment(ModuleType &m) { m.add(chaiscript::fun([](const T &lhs) { return ~lhs; }), "~"); } - template - void bitwise_xor(Module &m) { + template + void bitwise_xor(ModuleType &m) { m.add(chaiscript::fun([](const T &lhs, const T &rhs) { return lhs ^ rhs; }), "^"); } - template - void bitwise_or(Module &m) { + template + void bitwise_or(ModuleType &m) { m.add(chaiscript::fun([](const T &lhs, const T &rhs) { return lhs | rhs; }), "|"); } - template - void division(Module &m) { + template + void division(ModuleType &m) { m.add(chaiscript::fun([](const T &lhs, const T &rhs) { return lhs / rhs; }), "/"); } - template - void left_shift(Module &m) { + template + void left_shift(ModuleType &m) { m.add(chaiscript::fun([](const T &lhs, const T &rhs) { return lhs << rhs; }), "<<"); } - template - void multiplication(Module &m) { + template + void multiplication(ModuleType &m) { m.add(chaiscript::fun([](const T &lhs, const T &rhs) { return lhs * rhs; }), "*"); } - template - void remainder(Module &m) { + template + void remainder(ModuleType &m) { m.add(chaiscript::fun([](const T &lhs, const T &rhs) { return lhs % rhs; }), "%"); } - template - void right_shift(Module &m) { + template + void right_shift(ModuleType &m) { m.add(chaiscript::fun([](const T &lhs, const T &rhs) { return lhs >> rhs; }), ">>"); } } // namespace chaiscript::bootstrap::operators diff --git a/unittests/compiled_tests.cpp b/unittests/compiled_tests.cpp index 516c8bd5..09e14d9a 100644 --- a/unittests/compiled_tests.cpp +++ b/unittests/compiled_tests.cpp @@ -604,6 +604,45 @@ TEST_CASE("Utility_Test utility class wrapper for enum") { CHECK_NOTHROW(chai.eval("var o = ONE; o = TWO")); } +// Issue #601: add_class for enums should work directly with ChaiScript reference +enum class Issue601_EnumClass { Apple, Banana, Pear }; + +TEST_CASE("Issue 601: add_class enum with ChaiScript reference directly") { + chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(), create_chaiscript_parser()); + + // This should compile and work — previously it failed because the operator + // functions in chaiscript::bootstrap::operators hardcoded Module& as their + // first parameter instead of using a template parameter. + chaiscript::utility::add_class(chai, + "Issue601_EnumClass", + {{Issue601_EnumClass::Apple, "Apple"}, + {Issue601_EnumClass::Banana, "Banana"}, + {Issue601_EnumClass::Pear, "Pear"}}); + + CHECK(chai.eval("Apple == Apple")); + CHECK(chai.eval("Apple != Banana")); + CHECK_NOTHROW(chai.eval("var e = Apple; e = Pear")); + CHECK(chai.eval("Banana") == Issue601_EnumClass::Banana); +} + +// Also test non-scoped enum directly with ChaiScript reference +enum Issue601_PlainEnum { Issue601_Red = 0, Issue601_Green = 1, Issue601_Blue = 2 }; + +TEST_CASE("Issue 601: add_class plain enum with ChaiScript reference directly") { + chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(), create_chaiscript_parser()); + + chaiscript::utility::add_class(chai, + "Issue601_PlainEnum", + {{Issue601_Red, "Red"}, + {Issue601_Green, "Green"}, + {Issue601_Blue, "Blue"}}); + + CHECK(chai.eval("Red == Red")); + CHECK(chai.eval("Red == 0")); + CHECK(chai.eval("Red != Green")); + CHECK_NOTHROW(chai.eval("var c = Red; c = Blue")); +} + ////// Object copy count test class Object_Copy_Count_Test {