From 595b351edc2aeab9767d653af854a97129ad1edf Mon Sep 17 00:00:00 2001 From: Charlie Savage Date: Thu, 4 Mar 2021 00:18:54 -0800 Subject: [PATCH] Fix conversion from Ruby to char*. See #149. --- include/rice/rice.hpp | 27 ++++++++++++++++++++++++--- rice/detail/NativeArg.hpp | 16 +++++++++++++++- rice/detail/from_ruby.ipp | 11 +++++++++-- test/test_Data_Type.cpp | 16 ++++++++-------- 4 files changed, 56 insertions(+), 14 deletions(-) diff --git a/include/rice/rice.hpp b/include/rice/rice.hpp index f997ec572..2dbd6945e 100644 --- a/include/rice/rice.hpp +++ b/include/rice/rice.hpp @@ -962,7 +962,7 @@ namespace Rice template<> struct From_Ruby { - static char const* convert(VALUE x) + static char* convert(VALUE x) { if (x == Qnil) { @@ -980,7 +980,14 @@ namespace Rice { static char const* convert(VALUE x) { - return RSTRING_PTR(x); + if (x == Qnil) + { + return nullptr; + } + else + { + return RSTRING_PTR(x); + } } }; @@ -1302,7 +1309,8 @@ namespace Rice // converted value so that a reference or a pointer to the value can be passed to // the native function. template - class NativeArg>> + class NativeArg && + !(std::is_same_v> && std::is_pointer_v)>> { public: using Intrinsic_T = std::decay_t>; @@ -1325,6 +1333,19 @@ namespace Rice Intrinsic_T native_; }; + // Special case char which is a native type but if we have a pointer we + // want to pass through the underlying Ruby pointer + template + class NativeArg> && + std::is_pointer_v>> + { + public: + T nativeValue(VALUE value) + { + return From_Ruby::convert(value); + } + }; + // NativeArg implementation that works on all other types. The primary use is for // pointers wrapped by Data_Object where there is no reason to store a local copy. // It is also used for converting to various Rice C++ wrappers such as Rice::Hash, diff --git a/rice/detail/NativeArg.hpp b/rice/detail/NativeArg.hpp index 9dcd38674..8ac1392de 100644 --- a/rice/detail/NativeArg.hpp +++ b/rice/detail/NativeArg.hpp @@ -17,7 +17,8 @@ namespace Rice // converted value so that a reference or a pointer to the value can be passed to // the native function. template - class NativeArg>> + class NativeArg && + !(std::is_same_v> && std::is_pointer_v)>> { public: using Intrinsic_T = std::decay_t>; @@ -40,6 +41,19 @@ namespace Rice Intrinsic_T native_; }; + // Special case char which is a native type but if we have a pointer we + // want to pass through the underlying Ruby pointer + template + class NativeArg> && + std::is_pointer_v>> + { + public: + T nativeValue(VALUE value) + { + return From_Ruby::convert(value); + } + }; + // NativeArg implementation that works on all other types. The primary use is for // pointers wrapped by Data_Object where there is no reason to store a local copy. // It is also used for converting to various Rice C++ wrappers such as Rice::Hash, diff --git a/rice/detail/from_ruby.ipp b/rice/detail/from_ruby.ipp index 34fd69e15..176867620 100644 --- a/rice/detail/from_ruby.ipp +++ b/rice/detail/from_ruby.ipp @@ -125,7 +125,7 @@ namespace Rice template<> struct From_Ruby { - static char const* convert(VALUE x) + static char* convert(VALUE x) { if (x == Qnil) { @@ -143,7 +143,14 @@ namespace Rice { static char const* convert(VALUE x) { - return RSTRING_PTR(x); + if (x == Qnil) + { + return nullptr; + } + else + { + return RSTRING_PTR(x); + } } }; diff --git a/test/test_Data_Type.cpp b/test/test_Data_Type.cpp index 7ba5ee463..259cbd8ed 100644 --- a/test/test_Data_Type.cpp +++ b/test/test_Data_Type.cpp @@ -71,11 +71,11 @@ namespace return i; } - std::string multiple_args(int i, bool b, float f, std::string s) + std::string multiple_args(int i, bool b, float f, std::string s, char* c) { multiple_args_called = true; return "multiple_args(" + std::to_string(i) + ", " + std::to_string(b) + ", " + - std::to_string(f) + ", " + s + ")"; + std::to_string(f) + ", " + s + ", " + std::string(c) + ")"; } public: @@ -109,9 +109,9 @@ TESTCASE(methods_with_member_pointers) ASSERT(MyClass::int_arg_called); ASSERT_EQUAL(42, detail::From_Ruby::convert(result.value())); - result = o.call("multiple_args", 81, true, 7.0, "a string"); + result = o.call("multiple_args", 81, true, 7.0, "a string", "a char"); ASSERT(MyClass::multiple_args_called); - ASSERT_EQUAL("multiple_args(81, 1, 7.000000, a string)", detail::From_Ruby::convert(result.value())); + ASSERT_EQUAL("multiple_args(81, 1, 7.000000, a string, a char)", detail::From_Ruby::convert(result.value())); } TESTCASE(incorrect_number_of_args) @@ -166,9 +166,9 @@ TESTCASE(methods_with_lambdas) return instance.int_arg(anInt); }) .define_method("multiple_args", - [](MyClass& instance, int anInt, bool aBool, float aFloat, std::string aString) + [](MyClass& instance, int anInt, bool aBool, float aFloat, std::string aString, char* aChar) { - return instance.multiple_args(anInt, aBool, aFloat, aString); + return instance.multiple_args(anInt, aBool, aFloat, aString, aChar); }); MyClass::reset(); @@ -186,9 +186,9 @@ TESTCASE(methods_with_lambdas) ASSERT(MyClass::int_arg_called); ASSERT_EQUAL(42, detail::From_Ruby::convert(result.value())); - result = o.call("multiple_args", 81, true, 7.0, "a string"); + result = o.call("multiple_args", 81, true, 7.0, "a string", "a char"); ASSERT(MyClass::multiple_args_called); - ASSERT_EQUAL("multiple_args(81, 1, 7.000000, a string)", detail::From_Ruby::convert(result.value())); + ASSERT_EQUAL("multiple_args(81, 1, 7.000000, a string, a char)", detail::From_Ruby::convert(result.value())); } TESTCASE(static_singleton_method)