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 <leftibot@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
leftibot 2026-04-13 20:20:11 -06:00 committed by GitHub
parent d4c5bdb3e4
commit e61be76531
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 105 additions and 66 deletions

View File

@ -15,168 +15,168 @@
#include "register_function.hpp"
namespace chaiscript::bootstrap::operators {
template<typename T>
void assign(Module &m) {
template<typename T, typename ModuleType>
void assign(ModuleType &m) {
m.add(chaiscript::fun([](T &lhs, const T &rhs) -> T & { return lhs = rhs; }), "=");
}
template<typename T>
void assign_bitwise_and(Module &m) {
template<typename T, typename ModuleType>
void assign_bitwise_and(ModuleType &m) {
m.add(chaiscript::fun([](T &lhs, const T &rhs) -> T & { return lhs &= rhs; }), "&=");
}
template<typename T>
void assign_xor(Module &m) {
template<typename T, typename ModuleType>
void assign_xor(ModuleType &m) {
m.add(chaiscript::fun([](T &lhs, const T &rhs) -> T & { return lhs ^= rhs; }), "^=");
}
template<typename T>
void assign_bitwise_or(Module &m) {
template<typename T, typename ModuleType>
void assign_bitwise_or(ModuleType &m) {
m.add(chaiscript::fun([](T &lhs, const T &rhs) -> T & { return lhs |= rhs; }), "|=");
}
template<typename T>
void assign_difference(Module &m) {
template<typename T, typename ModuleType>
void assign_difference(ModuleType &m) {
m.add(chaiscript::fun([](T &lhs, const T &rhs) -> T & { return lhs -= rhs; }), "-=");
}
template<typename T>
void assign_left_shift(Module &m) {
template<typename T, typename ModuleType>
void assign_left_shift(ModuleType &m) {
m.add(chaiscript::fun([](T &lhs, const T &rhs) -> T & { return lhs <<= rhs; }), "<<=");
}
template<typename T>
void assign_product(Module &m) {
template<typename T, typename ModuleType>
void assign_product(ModuleType &m) {
m.add(chaiscript::fun([](T &lhs, const T &rhs) -> T & { return lhs <<= rhs; }), "*=");
}
template<typename T>
void assign_quotient(Module &m) {
template<typename T, typename ModuleType>
void assign_quotient(ModuleType &m) {
m.add(chaiscript::fun([](T &lhs, const T &rhs) -> T & { return lhs /= rhs; }), "/=");
}
template<typename T>
void assign_remainder(Module &m) {
template<typename T, typename ModuleType>
void assign_remainder(ModuleType &m) {
m.add(chaiscript::fun([](T &lhs, const T &rhs) -> T & { return lhs %= rhs; }), "%=");
}
template<typename T>
void assign_right_shift(Module &m) {
template<typename T, typename ModuleType>
void assign_right_shift(ModuleType &m) {
m.add(chaiscript::fun([](T &lhs, const T &rhs) -> T & { return lhs >>= rhs; }), ">>=");
}
template<typename T>
void assign_sum(Module &m) {
template<typename T, typename ModuleType>
void assign_sum(ModuleType &m) {
m.add(chaiscript::fun([](T &lhs, const T &rhs) -> T & { return lhs += rhs; }), "+=");
}
template<typename T>
void prefix_decrement(Module &m) {
template<typename T, typename ModuleType>
void prefix_decrement(ModuleType &m) {
m.add(chaiscript::fun([](T &lhs) -> T & { return --lhs; }), "--");
}
template<typename T>
void prefix_increment(Module &m) {
template<typename T, typename ModuleType>
void prefix_increment(ModuleType &m) {
m.add(chaiscript::fun([](T &lhs) -> T & { return ++lhs; }), "++");
}
template<typename T>
void equal(Module &m) {
template<typename T, typename ModuleType>
void equal(ModuleType &m) {
m.add(chaiscript::fun([](const T &lhs, const T &rhs) { return lhs == rhs; }), "==");
}
template<typename T>
void greater_than(Module &m) {
template<typename T, typename ModuleType>
void greater_than(ModuleType &m) {
m.add(chaiscript::fun([](const T &lhs, const T &rhs) { return lhs > rhs; }), ">");
}
template<typename T>
void greater_than_equal(Module &m) {
template<typename T, typename ModuleType>
void greater_than_equal(ModuleType &m) {
m.add(chaiscript::fun([](const T &lhs, const T &rhs) { return lhs >= rhs; }), ">=");
}
template<typename T>
void less_than(Module &m) {
template<typename T, typename ModuleType>
void less_than(ModuleType &m) {
m.add(chaiscript::fun([](const T &lhs, const T &rhs) { return lhs < rhs; }), "<");
}
template<typename T>
void less_than_equal(Module &m) {
template<typename T, typename ModuleType>
void less_than_equal(ModuleType &m) {
m.add(chaiscript::fun([](const T &lhs, const T &rhs) { return lhs <= rhs; }), "<=");
}
template<typename T>
void logical_compliment(Module &m) {
template<typename T, typename ModuleType>
void logical_compliment(ModuleType &m) {
m.add(chaiscript::fun([](const T &lhs) { return !lhs; }), "!");
}
template<typename T>
void not_equal(Module &m) {
template<typename T, typename ModuleType>
void not_equal(ModuleType &m) {
m.add(chaiscript::fun([](const T &lhs, const T &rhs) { return lhs != rhs; }), "!=");
}
template<typename T>
void addition(Module &m) {
template<typename T, typename ModuleType>
void addition(ModuleType &m) {
m.add(chaiscript::fun([](const T &lhs, const T &rhs) { return lhs + rhs; }), "+");
}
template<typename T>
void unary_plus(Module &m) {
template<typename T, typename ModuleType>
void unary_plus(ModuleType &m) {
m.add(chaiscript::fun([](const T &lhs) { return +lhs; }), "+");
}
template<typename T>
void subtraction(Module &m) {
template<typename T, typename ModuleType>
void subtraction(ModuleType &m) {
m.add(chaiscript::fun([](const T &lhs, const T &rhs) { return lhs - rhs; }), "-");
}
template<typename T>
void unary_minus(Module &m) {
template<typename T, typename ModuleType>
void unary_minus(ModuleType &m) {
m.add(chaiscript::fun([](const T &lhs) { return -lhs; }), "-");
}
template<typename T>
void bitwise_and(Module &m) {
template<typename T, typename ModuleType>
void bitwise_and(ModuleType &m) {
m.add(chaiscript::fun([](const T &lhs, const T &rhs) { return lhs & rhs; }), "&");
}
template<typename T>
void bitwise_compliment(Module &m) {
template<typename T, typename ModuleType>
void bitwise_compliment(ModuleType &m) {
m.add(chaiscript::fun([](const T &lhs) { return ~lhs; }), "~");
}
template<typename T>
void bitwise_xor(Module &m) {
template<typename T, typename ModuleType>
void bitwise_xor(ModuleType &m) {
m.add(chaiscript::fun([](const T &lhs, const T &rhs) { return lhs ^ rhs; }), "^");
}
template<typename T>
void bitwise_or(Module &m) {
template<typename T, typename ModuleType>
void bitwise_or(ModuleType &m) {
m.add(chaiscript::fun([](const T &lhs, const T &rhs) { return lhs | rhs; }), "|");
}
template<typename T>
void division(Module &m) {
template<typename T, typename ModuleType>
void division(ModuleType &m) {
m.add(chaiscript::fun([](const T &lhs, const T &rhs) { return lhs / rhs; }), "/");
}
template<typename T>
void left_shift(Module &m) {
template<typename T, typename ModuleType>
void left_shift(ModuleType &m) {
m.add(chaiscript::fun([](const T &lhs, const T &rhs) { return lhs << rhs; }), "<<");
}
template<typename T>
void multiplication(Module &m) {
template<typename T, typename ModuleType>
void multiplication(ModuleType &m) {
m.add(chaiscript::fun([](const T &lhs, const T &rhs) { return lhs * rhs; }), "*");
}
template<typename T>
void remainder(Module &m) {
template<typename T, typename ModuleType>
void remainder(ModuleType &m) {
m.add(chaiscript::fun([](const T &lhs, const T &rhs) { return lhs % rhs; }), "%");
}
template<typename T>
void right_shift(Module &m) {
template<typename T, typename ModuleType>
void right_shift(ModuleType &m) {
m.add(chaiscript::fun([](const T &lhs, const T &rhs) { return lhs >> rhs; }), ">>");
}
} // namespace chaiscript::bootstrap::operators

View File

@ -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<Issue601_EnumClass>(chai,
"Issue601_EnumClass",
{{Issue601_EnumClass::Apple, "Apple"},
{Issue601_EnumClass::Banana, "Banana"},
{Issue601_EnumClass::Pear, "Pear"}});
CHECK(chai.eval<bool>("Apple == Apple"));
CHECK(chai.eval<bool>("Apple != Banana"));
CHECK_NOTHROW(chai.eval("var e = Apple; e = Pear"));
CHECK(chai.eval<Issue601_EnumClass>("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<Issue601_PlainEnum>(chai,
"Issue601_PlainEnum",
{{Issue601_Red, "Red"},
{Issue601_Green, "Green"},
{Issue601_Blue, "Blue"}});
CHECK(chai.eval<bool>("Red == Red"));
CHECK(chai.eval<bool>("Red == 0"));
CHECK(chai.eval<bool>("Red != Green"));
CHECK_NOTHROW(chai.eval("var c = Red; c = Blue"));
}
////// Object copy count test
class Object_Copy_Count_Test {