ChaiScript/unittests/function_introspection.chai
leftibot 46cf4cdef0
Fix #458: Cannot assign string to function that expects std::string_view (#697)
* Fix #458: Support std::string_view interop with ChaiScript strings

Register std::string_view as the "string_view" user type with a built-in
implicit conversion from std::string, so a ChaiScript string can be passed
directly to a C++ function taking std::string_view. Adds the reverse
explicit conversion via string(sv) / to_string(sv), plus basic queries
(size, length, empty, data) and comparison operators on string_view.
String-style methods that take size_t (substr, find, ...) are intentionally
not duplicated on string_view: with the implicit conversion in place they
would create dispatch ambiguity for calls like string.substr(int, int).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* Address review: expand string_view binding with substr/find/[]

Adds substr, find/rfind/find_first_of/find_last_of/find_first_not_of/
find_last_not_of, starts_with/ends_with, and operator[] to string_view_type,
mirroring string_type's search/substring surface so script authors can
traverse a buffer through string_view without allocating.

Sharing method names with string_type creates an ambiguity in
dispatch_with_conversions when the script call needs arithmetic conversion
(e.g. myString.substr(int, int)): both string::substr and string_view::substr
match-except-for-arithmetic, and the existing const/non-const tiebreaker
doesn't apply. Extend dispatch_with_conversions to prefer the candidate
whose first/receiver parameter type exactly matches the actual receiver,
mirroring the deprioritization already in dispatch(). With this in place,
myString.substr(1, 2) still resolves to string::substr while
mySV.substr(1, 2) resolves to string_view::substr (returning a string_view).

Updates the doc comment on string_view_type and adds a compiled test that
locks in both dispatch directions plus the new search/substring methods.

Requested by @lefticus in PR #697 review.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: leftibot <leftibot@users.noreply.github.com>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-02 14:45:29 -06:00

77 lines
2.1 KiB
ChaiScript

#Test Function Description
def test_function(a)
{
return a;
}
// test_function tests
assert_equal(test_function.get_arity(), 1);
assert_equal(test_function.get_contained_functions().size(), 0);
assert_equal(test_function.get_param_types().size(), 2);
assert_equal(test_function, test_function);
assert_not_equal(test_function, `+`);
assert_equal(test_function.call([1]), 1);
// dynamic object function tests
def int::test_fun()
{
return this;
}
assert_equal(test_fun.get_arity(), 1);
assert_equal(test_fun.get_contained_functions.size(), 1);
assert_equal(test_fun.get_param_types().size(), 2);
assert_equal(test_fun, test_fun);
auto test_fun_types = test_fun.get_param_types();
assert_equal(true, test_fun_types[0].bare_equal(Object_type));
assert_equal(true, test_fun_types[1].bare_equal(int_type));
// built-ins tests
assert_equal(2, `==`.get_arity());
// < should be the merging of three functions bool <(PODObject, PODObject),
// bool <(string, string), and bool <(string_view, string_view)
// we want to peel it apart and make sure that's true
auto types = `<`.get_param_types();
assert_equal(3, types.size());
assert_equal(true, types[0].bare_equal(bool_type));
assert_equal(true, types[1].bare_equal(Object_type));
assert_equal(true, types[2].bare_equal(Object_type));
assert_equal(3, `<`.get_contained_functions().size());
// guard existence tests
def with_guard(x) : x > 3 {}
def without_guard(x) {}
def group_guard(x) {}
def group_guard(x) : x > 3 {}
assert_equal(true, with_guard.has_guard());
assert_equal(false, without_guard.has_guard());
assert_equal(2, group_guard.get_contained_functions().size());
auto group = group_guard.get_contained_functions();
assert_equal(true, group[0].has_guard())
assert_equal(false, group[1].has_guard())
assert_throws("Function does not have a guard", fun[group]() { group[1].get_guard(); } );
assert_throws("Function does not have a guard", fun[without_guard]() { without_guard.get_guard(); } );
auto guard = with_guard.get_guard();
assert_equal(false, guard.has_guard());
assert_throws("Function does not have a guard", fun[guard]() { guard.get_guard(); } );