mirror of
https://github.com/ChaiScript/ChaiScript.git
synced 2026-04-30 19:09:26 +08:00
The built-in Dynamic_Object::get_attr was always winning dispatch over user-defined [] overrides when a string index was used, because its C++ type signature (Dynamic_Object, const string&) scored numdiffs=0 while ChaiScript-defined functions had higher numdiffs due to undefined type info. The fix deprioritizes generic C++ Dynamic_Object functions when the actual first argument is a specific subtype, and maps named ChaiScript type parameters to Dynamic_Object type_info for correct dispatch scoring. 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:
parent
bcd07e05cd
commit
726169acc8
@ -103,7 +103,9 @@ namespace chaiscript {
|
||||
|
||||
assert(types.size() > 1);
|
||||
// assert(types[1].bare_equal(user_type<Boxed_Value>()));
|
||||
types[1] = t_objectti;
|
||||
// When the object type_info is undefined (ChaiScript-defined class), use
|
||||
// Dynamic_Object so that dispatch priority scoring works correctly.
|
||||
types[1] = t_objectti.is_undef() ? user_type<Dynamic_Object>() : t_objectti;
|
||||
return types;
|
||||
}
|
||||
|
||||
|
||||
@ -380,7 +380,15 @@ namespace chaiscript {
|
||||
|
||||
for (const auto &t : t_types.types()) {
|
||||
if (t.second.is_undef()) {
|
||||
if (!t.first.empty()) {
|
||||
// Named type without C++ type_info — assumed to be a Dynamic_Object subtype.
|
||||
// Using Dynamic_Object type_info ensures correct dispatch priority so that
|
||||
// user-defined overrides on specific subtypes are tried before generic
|
||||
// Dynamic_Object built-in functions.
|
||||
types.push_back(user_type<Dynamic_Object>());
|
||||
} else {
|
||||
types.push_back(chaiscript::detail::Get_Type_Info<Boxed_Value>::get());
|
||||
}
|
||||
} else {
|
||||
types.push_back(t.second);
|
||||
}
|
||||
@ -806,6 +814,30 @@ namespace chaiscript {
|
||||
numdiffs = plist.size();
|
||||
}
|
||||
|
||||
// Deprioritize C++ registered generic Dynamic_Object functions when the
|
||||
// actual first argument is a specific Dynamic_Object subtype. This allows
|
||||
// user-defined operator overrides (e.g. `[]`) on a subtype to take precedence
|
||||
// over the built-in Dynamic_Object operations such as get_attr.
|
||||
// Only deprioritize non-dynamic (C++ registered) functions — ChaiScript-defined
|
||||
// functions (Dynamic_Proxy_Function) have their own type matching via Param_Types.
|
||||
if (!plist.empty()
|
||||
&& dynamic_cast<const Dynamic_Proxy_Function *>(func.get()) == nullptr) {
|
||||
static const auto dynamic_object_ti = user_type<Dynamic_Object>();
|
||||
if (func->get_param_types().size() > 1
|
||||
&& func->get_param_types()[1].bare_equal(dynamic_object_ti)
|
||||
&& plist[0].get_type_info().bare_equal(dynamic_object_ti)
|
||||
&& func->dynamic_object_type_name().empty()) {
|
||||
try {
|
||||
const auto &d = boxed_cast<const Dynamic_Object &>(plist[0], &t_conversions);
|
||||
if (d.get_type_name() != "Dynamic_Object") {
|
||||
numdiffs = plist.size();
|
||||
}
|
||||
} catch (const std::bad_cast &) {
|
||||
// not a Dynamic_Object, ignore
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ordered_funcs.emplace_back(numdiffs, func.get());
|
||||
}
|
||||
}
|
||||
|
||||
45
unittests/index_operator_override.chai
Normal file
45
unittests/index_operator_override.chai
Normal file
@ -0,0 +1,45 @@
|
||||
|
||||
// Test overriding [] operator with various index types (issue #398)
|
||||
|
||||
class my_class
|
||||
{
|
||||
def my_class()
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
def `[]`(my_class o, idx)
|
||||
{
|
||||
return "Hello World!";
|
||||
}
|
||||
|
||||
var o = my_class();
|
||||
|
||||
// Integer index should work
|
||||
assert_equal("Hello World!", o[3]);
|
||||
|
||||
// String index should work
|
||||
assert_equal("Hello World!", o["3"]);
|
||||
|
||||
// String variable as index should work
|
||||
var s = "abc";
|
||||
assert_equal("Hello World!", o[s]);
|
||||
|
||||
// Typed string parameter override
|
||||
class my_class2
|
||||
{
|
||||
def my_class2()
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
def `[]`(my_class2 o, string key)
|
||||
{
|
||||
return "key=" + key;
|
||||
}
|
||||
|
||||
var o2 = my_class2();
|
||||
assert_equal("key=foo", o2["foo"]);
|
||||
|
||||
var key = "bar";
|
||||
assert_equal("key=bar", o2[key]);
|
||||
Loading…
x
Reference in New Issue
Block a user