mirror of
https://github.com/ChaiScript/ChaiScript.git
synced 2026-05-01 03:19:28 +08:00
Address review: conditionally register operators based on underlying type support
Only register strong typedef operators that actually exist for the underlying type. Previously all operators were added unconditionally, causing confusing reflection entries (e.g. * for StrongString) that would fail at runtime. Now each operator is probed via call_match against default-constructed base type values before registration. Also adds bitwise/shift operators (&, |, ^, <<, >>) for types that support them, and expands test coverage for unsupported operator rejection. Requested by @lefticus in PR #680 review. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
b02380a1b3
commit
66f06df2a5
@ -1034,6 +1034,11 @@ namespace chaiscript {
|
||||
{"*", Operators::Opers::product, true},
|
||||
{"/", Operators::Opers::quotient, true},
|
||||
{"%", Operators::Opers::remainder, true},
|
||||
{"<<", Operators::Opers::shift_left, true},
|
||||
{">>", Operators::Opers::shift_right, true},
|
||||
{"&", Operators::Opers::bitwise_and, true},
|
||||
{"|", Operators::Opers::bitwise_or, true},
|
||||
{"^", Operators::Opers::bitwise_xor, true},
|
||||
{"<", Operators::Opers::less_than, false},
|
||||
{">", Operators::Opers::greater_than, false},
|
||||
{"<=", Operators::Opers::less_than_equal, false},
|
||||
@ -1042,11 +1047,40 @@ namespace chaiscript {
|
||||
{"!=", Operators::Opers::not_equal, false},
|
||||
};
|
||||
|
||||
const auto op_exists_for_base_type = [&t_ss, &base_type_name](const char *op_name) {
|
||||
std::atomic_uint_fast32_t loc{0};
|
||||
const auto [func_loc, funcs] = t_ss->get_function(op_name, loc);
|
||||
if (!funcs || funcs->empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::atomic_uint_fast32_t ctor_loc{0};
|
||||
Boxed_Value test_val;
|
||||
try {
|
||||
const std::array<Boxed_Value, 0> empty_params{};
|
||||
test_val = t_ss->call_function(base_type_name, ctor_loc,
|
||||
Function_Params(empty_params), t_ss.conversions());
|
||||
} catch (...) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const std::array<Boxed_Value, 2> test_params{test_val, test_val};
|
||||
const Function_Params fp(test_params);
|
||||
for (const auto &func : *funcs) {
|
||||
if (func->call_match(fp, t_ss.conversions())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
for (const auto &op : ops) {
|
||||
t_ss->add(
|
||||
chaiscript::make_shared<dispatch::Proxy_Function_Base, detail::Strong_Typedef_Binary_Op>(
|
||||
new_type_name, std::string(op.name), op.oper, op.rewrap, engine),
|
||||
op.name);
|
||||
if (op_exists_for_base_type(op.name)) {
|
||||
t_ss->add(
|
||||
chaiscript::make_shared<dispatch::Proxy_Function_Base, detail::Strong_Typedef_Binary_Op>(
|
||||
new_type_name, std::string(op.name), op.oper, op.rewrap, engine),
|
||||
op.name);
|
||||
}
|
||||
}
|
||||
|
||||
return void_var();
|
||||
|
||||
@ -89,6 +89,21 @@ assert_equal(Meters(5) <= Meters(5), true)
|
||||
assert_equal(Meters(3) >= Meters(3), true)
|
||||
assert_equal(Meters(3) >= Meters(5), false)
|
||||
|
||||
// --- Bitwise and shift operators ---
|
||||
assert_equal(to_underlying(Meters(6) & Meters(3)), 2)
|
||||
assert_equal(to_underlying(Meters(6) | Meters(3)), 7)
|
||||
assert_equal(to_underlying(Meters(6) ^ Meters(3)), 5)
|
||||
assert_equal(to_underlying(Meters(5) << Meters(2)), 20)
|
||||
assert_equal(to_underlying(Meters(12) >> Meters(1)), 6)
|
||||
|
||||
// Bitwise results are strongly typed
|
||||
try {
|
||||
takes_int(Meters(6) & Meters(3))
|
||||
assert_equal(true, false)
|
||||
} catch(e) {
|
||||
// Expected: result is Meters, not int
|
||||
}
|
||||
|
||||
// --- Strong typedef over string ---
|
||||
using StrongString = string
|
||||
|
||||
@ -103,13 +118,31 @@ def takes_strong_string(StrongString ss) {
|
||||
}
|
||||
takes_strong_string(ss_cat)
|
||||
|
||||
// StrongString * StrongString -> error (no * for strings)
|
||||
// Operators not supported by the underlying type are not registered
|
||||
try {
|
||||
var bad = ss1 * ss2
|
||||
assert_equal(true, false)
|
||||
} catch(e) {
|
||||
// Expected: no * operator for strings
|
||||
}
|
||||
try {
|
||||
var bad = ss1 - ss2
|
||||
assert_equal(true, false)
|
||||
} catch(e) {
|
||||
// Expected: no - operator for strings
|
||||
}
|
||||
try {
|
||||
var bad = ss1 / ss2
|
||||
assert_equal(true, false)
|
||||
} catch(e) {
|
||||
// Expected: no / operator for strings
|
||||
}
|
||||
try {
|
||||
var bad = ss1 % ss2
|
||||
assert_equal(true, false)
|
||||
} catch(e) {
|
||||
// Expected: no % operator for strings
|
||||
}
|
||||
|
||||
// Comparison on StrongString
|
||||
assert_equal(StrongString("abc") < StrongString("def"), true)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user