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.size() > 1);
|
||||||
// assert(types[1].bare_equal(user_type<Boxed_Value>()));
|
// 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;
|
return types;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -380,7 +380,15 @@ namespace chaiscript {
|
|||||||
|
|
||||||
for (const auto &t : t_types.types()) {
|
for (const auto &t : t_types.types()) {
|
||||||
if (t.second.is_undef()) {
|
if (t.second.is_undef()) {
|
||||||
types.push_back(chaiscript::detail::Get_Type_Info<Boxed_Value>::get());
|
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 {
|
} else {
|
||||||
types.push_back(t.second);
|
types.push_back(t.second);
|
||||||
}
|
}
|
||||||
@ -806,6 +814,30 @@ namespace chaiscript {
|
|||||||
numdiffs = plist.size();
|
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());
|
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