Skip to content

Support std::function as function parameter #142

@ivalylo

Description

@ivalylo

Hello,

Seems this is not supported currently, but it's quite handy to have (for example callbacks). I tried implementing it, but I'm very new to both v8 and v8pp. Also not that good with template programming either. But here is something I got working, and maybe can be used to build somewhat better implementation.

template<typename T, typename ...Args>
struct V8CallFunc
{
   V8CallFunc(v8::Isolate* isolate, v8::Local<v8::Function> func)
      : isolate(isolate), func(isolate, func)
   {
   }

   T operator()(Args&&... args) const
   {
      v8::EscapableHandleScope scope(isolate);

      int const arg_count = sizeof...(Args);
      // +1 to allocate array for arg_count == 0
      v8::Local<v8::Value> v8_args[arg_count + 1] =
      {
         to_v8(isolate, std::forward<Args>(args))...
      };

      v8::TryCatch try_catch(isolate);
      v8::Local<v8::Value> result;
      v8::Local<v8::Function> funcLocal = func.Get(isolate);
      bool const not_empty = funcLocal->Call(isolate->GetCurrentContext(), funcLocal, arg_count, v8_args).ToLocal(&result);

      if (try_catch.HasCaught())
      {
         std::string const msg = v8pp::from_v8<std::string>(isolate,
            try_catch.Exception()->ToString(isolate->GetCurrentContext()).ToLocalChecked());
		 THROW(std::runtime_error, msg);
      }

      assert(not_empty);
      return from_v8<T>(isolate, scope.Escape(result));
   }

   v8::Isolate* isolate;
   v8::Eternal<v8::Function> func;
};

template<typename ...Args>
struct V8CallFunc<void, Args...>
{
   V8CallFunc(v8::Isolate* isolate, v8::Local<v8::Function> func)
      : isolate(isolate), func(isolate, func)
   {
   }

   void operator()(Args&&... args) const
   {
      v8::EscapableHandleScope scope(isolate);

      int const arg_count = sizeof...(Args);
      // +1 to allocate array for arg_count == 0
      v8::Local<v8::Value> v8_args[arg_count + 1] =
      {
         to_v8(isolate, std::forward<Args>(args))...
      };

      v8::TryCatch try_catch(isolate);
      v8::Local<v8::Function> funcLocal = func.Get(isolate);
      funcLocal->Call(isolate->GetCurrentContext(), funcLocal, arg_count, v8_args);

      if (try_catch.HasCaught())
      {
         std::string const msg = v8pp::from_v8<std::string>(isolate,
            try_catch.Exception()->ToString(isolate->GetCurrentContext()).ToLocalChecked());
         THROW(std::runtime_error, msg);
      }
   }

   v8::Isolate* isolate;
   v8::Eternal<v8::Function> func;
};

template <typename Ret, typename... Args> __forceinline
std::function<Ret(Args...)> createV8CallFunc(std::function<Ret(Args...)> f, v8::Isolate* isolate, v8::Local<v8::Function> func)
{
    return V8CallFunc<Ret, Args...>(isolate, func);
}

template<typename T>
struct convert<std::function<T>>
{
	using from_type = std::function<T>;
	using to_type = v8::Local<v8::Function>;

	static bool is_valid(v8::Isolate* isolate, v8::Local<v8::Value> value)
	{
		return !value.IsEmpty() && value->IsFunction();
	}

	static from_type from_v8(v8::Isolate* isolate, v8::Local<v8::Value> value)
	{
		if (!is_valid(isolate, value))
		{
			THROW(invalid_argument, isolate, value, "Function");
		}
		auto func = value.As<v8::Function>();
		return createV8CallFunc(from_type(), isolate, func);
	}

	static to_type to_v8(v8::Isolate* isolate, const from_type& func)
	{
		using T_type = typename std::decay<from_type>::type;
		T_type func_copy = func;

		v8::Local<v8::Function> fn = v8::Function::New(isolate->GetCurrentContext(),
			&detail::forward_function<raw_ptr_traits, T_type>,
			detail::set_external_data(isolate, std::forward<T_type>(func_copy))).ToLocalChecked();
		return fn;
	}
};

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions