diff --git a/include/chaiscript/chaiscript_stdlib.hpp b/include/chaiscript/chaiscript_stdlib.hpp index 5c1e7a48..f2034b6c 100644 --- a/include/chaiscript/chaiscript_stdlib.hpp +++ b/include/chaiscript/chaiscript_stdlib.hpp @@ -10,6 +10,7 @@ #include #include #include +#include #include #include @@ -52,9 +53,14 @@ namespace chaiscript { bootstrap::standard_library::vector_type>("Vector", *lib); bootstrap::standard_library::string_type("string", *lib); + bootstrap::standard_library::string_view_type("string_view", *lib); bootstrap::standard_library::map_type>("Map", *lib); bootstrap::standard_library::pair_type>("Pair", *lib); + // Allow explicit conversion from std::string_view back to std::string, + // e.g. `string(sv)` in ChaiScript. + lib->add(fun([](const std::string_view sv) { return std::string{sv}; }), "string"); + #ifndef CHAISCRIPT_NO_THREADS bootstrap::standard_library::future_type>("future", *lib); // Note: async() is registered in ChaiScript_Basic::build_eval_system() diff --git a/include/chaiscript/dispatchkit/bootstrap_stl.hpp b/include/chaiscript/dispatchkit/bootstrap_stl.hpp index 967344d7..088a9122 100644 --- a/include/chaiscript/dispatchkit/bootstrap_stl.hpp +++ b/include/chaiscript/dispatchkit/bootstrap_stl.hpp @@ -539,6 +539,40 @@ namespace chaiscript::bootstrap::standard_library { m.add(fun([](const String *s, size_t pos, size_t len) { return s->substr(pos, len); }), "substr"); } + /// Add a String_View type (e.g. std::string_view), with conversions to and from + /// the matching owning String type (e.g. std::string). + /// + /// Only registers operations that don't share names with the owning String type's + /// methods that take arithmetic arguments (e.g. substr/find). Those would create + /// dispatch ambiguity once the implicit String -> StringView conversion is in + /// play, because neither overload would exactly match a (String, int, int) call. + /// + /// \note A String_View is a non-owning reference. Constructing one from a + /// temporary owning String yields a dangling reference once that temporary + /// is destroyed; the same lifetime caveats as in C++ apply here. + template + void string_view_type(const std::string &type, Module &m) { + m.add(user_type(), type); + m.add(constructor(), type); + m.add(constructor(), type); + m.add(fun([](const String &s) { return StringView{s}; }), type); + + opers_comparison(m); + + m.add(fun([](const StringView *s) { return s->size(); }), "size"); + m.add(fun([](const StringView *s) { return s->length(); }), "length"); + m.add(fun([](const StringView *s) { return s->empty(); }), "empty"); + m.add(fun([](const StringView *s) { return s->data(); }), "data"); + + // Built-in implicit conversion from owning String to non-owning StringView. + m.add(type_conversion()); + + // Explicit conversion from StringView back to owning String, registered as + // to_string(sv); the call site can also register it under the owning type's + // name (e.g. string(sv)) when desired. + m.add(fun([](const StringView sv) { return String{sv}; }), "to_string"); + } + /// Add a MapType container /// http://www.sgi.com/tech/stl/Map.html template diff --git a/unittests/compiled_tests.cpp b/unittests/compiled_tests.cpp index 844be6e9..4738285d 100644 --- a/unittests/compiled_tests.cpp +++ b/unittests/compiled_tests.cpp @@ -2204,3 +2204,19 @@ TEST_CASE("Exception from C++ [] operator is catchable in ChaiScript") { caught )") == true); } + +// Issue #458: ChaiScript strings should be passable to C++ functions that +// take std::string_view, std::string_view should be a known type, and +// explicit conversion from std::string_view to std::string should work. +TEST_CASE("Issue #458: std::string_view interop with ChaiScript strings") { + chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(), create_chaiscript_parser()); + + chai.add(chaiscript::fun([](const std::string_view sv) { return std::string(sv); }), "consume_string_view"); + + CHECK(chai.eval(R"(consume_string_view("Hi there"))") == "Hi there"); + CHECK(chai.eval(R"(var s = "from variable"; consume_string_view(s))") == "from variable"); + + CHECK(chai.eval(R"(type_name(string_view("hello")) == "string_view")")); + + CHECK(chai.eval(R"(string(string_view("round trip")))") == "round trip"); +} diff --git a/unittests/function_introspection.chai b/unittests/function_introspection.chai index 2c511a73..d040ff6a 100644 --- a/unittests/function_introspection.chai +++ b/unittests/function_introspection.chai @@ -39,14 +39,15 @@ assert_equal(true, test_fun_types[1].bare_equal(int_type)); assert_equal(2, `==`.get_arity()); -// < should be the merging of two functions bool <(PODObject, PODObject) and bool <(string, string) +// < 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(2, `<`.get_contained_functions().size()); +assert_equal(3, `<`.get_contained_functions().size()); // guard existence tests