From 0df6f5c7d8f08bdf6c413ae8e1e3ff9c111f35c5 Mon Sep 17 00:00:00 2001 From: Vincent Isambart Date: Thu, 25 Feb 2021 10:30:57 +0900 Subject: [PATCH] Allow to pass callbacks in varargs --- ext/ffi_c/Variadic.c | 15 +++++++++++++-- spec/ffi/callback_spec.rb | 8 +++++++- spec/ffi/fixtures/ClosureTest.c | 12 ++++++++++++ 3 files changed, 32 insertions(+), 3 deletions(-) diff --git a/ext/ffi_c/Variadic.c b/ext/ffi_c/Variadic.c index d9d7fd44f..d18a14338 100644 --- a/ext/ffi_c/Variadic.c +++ b/ext/ffi_c/Variadic.c @@ -168,7 +168,8 @@ variadic_invoke(VALUE self, VALUE parameterTypes, VALUE parameterValues) ffi_type* ffiReturnType; Type** paramTypes; VALUE* argv; - int paramCount = 0, fixedCount = 0, i; + VALUE* callbackParameters = NULL; + int paramCount = 0, fixedCount = 0, callbackCount = 0, i; ffi_status ffiStatus; rbffi_frame_t frame = { 0 }; @@ -211,6 +212,16 @@ variadic_invoke(VALUE self, VALUE parameterTypes, VALUE parameterValues) Data_Get_Struct(rbType, Type, paramTypes[i]); break; + case NATIVE_FUNCTION: + case NATIVE_CALLBACK: + if (!rb_obj_is_kind_of(rbType, rbffi_FunctionTypeClass)) { + VALUE typeName = rb_funcall2(rbType, rb_intern("inspect"), 0, NULL); + rb_raise(rb_eTypeError, "Incorrect parameter type (%s)", RSTRING_PTR(typeName)); + } + REALLOC_N(callbackParameters, VALUE, callbackCount + 1); + callbackParameters[callbackCount++] = rbType; + break; + default: break; } @@ -248,7 +259,7 @@ variadic_invoke(VALUE self, VALUE parameterTypes, VALUE parameterValues) } rbffi_SetupCallParams(paramCount, argv, -1, paramTypes, params, - ffiValues, NULL, 0, invoker->rbEnums); + ffiValues, callbackParameters, callbackCount, invoker->rbEnums); rbffi_frame_push(&frame); diff --git a/spec/ffi/callback_spec.rb b/spec/ffi/callback_spec.rb index 58e5c8156..53ff1e49f 100644 --- a/spec/ffi/callback_spec.rb +++ b/spec/ffi/callback_spec.rb @@ -49,6 +49,7 @@ class S8F32S32 < FFI::Struct callback :cbVrU32, [ ], :uint callback :cbVrL, [ ], :long callback :cbVrUL, [ ], :ulong + callback :cbVrD, [ ], :double callback :cbVrS64, [ ], :long_long callback :cbVrU64, [ ], :ulong_long callback :cbVrP, [], :pointer @@ -86,7 +87,7 @@ class S8F32S32 < FFI::Struct attach_variable :pVrS8, :gvar_pointer, :pointer attach_function :testGVarCallbackVrS8, :testClosureVrB, [ :pointer ], :char attach_function :testOptionalCallbackCrV, :testOptionalClosureBrV, [ :cbCrV, :char ], :void - + attach_function :testCallbackVrDva, :testClosureVrDva, [ :double, :varargs ], :double end it "returning :char (0)" do @@ -261,6 +262,11 @@ class S8F32S32 < FFI::Struct expect(LibTest.testCallbackVrZ { true }).to be true end + it "returning double" do + pr = proc { 42.0 } + expect(LibTest.testCallbackVrDva(3.0, :cbVrD, pr)).to eq(45.0) + end + it "returning :pointer (nil)" do expect(LibTest.testCallbackVrP { nil }).to be_null end diff --git a/spec/ffi/fixtures/ClosureTest.c b/spec/ffi/fixtures/ClosureTest.c index c477be8a7..daabe0e88 100644 --- a/spec/ffi/fixtures/ClosureTest.c +++ b/spec/ffi/fixtures/ClosureTest.c @@ -6,6 +6,7 @@ #include #include +#include #ifndef _WIN32 # include #else @@ -13,6 +14,17 @@ # include #endif +double testClosureVrDva(double d, ...) { + va_list args; + double (*closure)(void); + + va_start(args, d); + closure = va_arg(args, double (*)(void)); + va_end(args); + + return d + closure(); +} + #define R(T, rtype) rtype testClosureVr##T(rtype (*closure)(void)) { \ return closure != NULL ? (*closure)() : (rtype) 0; \ }