Skip to content

Instantly share code, notes, and snippets.

@TekuConcept
Last active January 11, 2024 01:17
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save TekuConcept/e92d10d6585c9d1fd3d507d1918cf77b to your computer and use it in GitHub Desktop.
Save TekuConcept/e92d10d6585c9d1fd3d507d1918cf77b to your computer and use it in GitHub Desktop.
SWIG Native C++ / NodeJS Callbacks
// SWIG's %native directive is now supported for JavaScript since SWIG release (4.0.1+)
// This allows us to not only wrap our C/C++ code with ease, but also add our own
// specialized wrapper functionality to our projects - such as callbacks!
//
// Below are three callback implementations shared for convinience (for Node/V8 engines)
// 1. one-off, inline callback
// 2. one-off, async callback
// 3. persistent async callback
%module "native_callbacks"
%native(funcA) void _wrap_funcA();
%native(funcB) void _wrap_funcB();
%native(funcC) void _wrap_funcC();
%header %{
#include <array>
#include <vector>
#include <memory>
#include <uv.h>
// these are used to help demonstrate (3)
#include <future>
std::future<void> thread;
%}
%wrapper %{
// base class, which provides a way to access
// different data types in one vector (Pre-C++17)
class arg_t {
public:
arg_t() : data(NULL) {}
virtual ~arg_t() = default;
bool valid() const { return data != NULL; }
template <typename T>
T& get() const { return *(T*)(data); }
protected:
void* data;
};
// templated parent class to hold the actual data
template <typename T>
class type_arg : public arg_t {
public:
type_arg(T t) : value(t) { arg_t::data = &value; }
private:
T value;
};
// generic worker packet, which saves the context
// for almost all variations of async tasks
typedef struct worker_packet {
bool done; // teardown flag
uv_work_t request; // uv_queue_work
uv_async_t async; // uv_async_init
uv_loop_t* loop; // (save this so it can be freed later)
uv_rwlock_t lock;
// v8::Persistent<v8::Function> do not play nice with std::vector
std::array<v8::Persistent<v8::Function>,3> callbacks;
std::vector<std::shared_ptr<arg_t>> args;
std::shared_ptr<arg_t> return_value;
std::shared_ptr<arg_t> parent; // hold the class object if any
} worker_packet;
// convinience function for converting args to persistent functions
int SWIG_AsVal_function(
v8::Handle<v8::Value> valRef,
v8::Persistent<v8::Function>* val)
{
if (!valRef->IsFunction()) return SWIG_TypeError;
if (val) {
auto iso = v8::Isolate::GetCurrent();
v8::Local<v8::Function> callback = v8::Local<v8::Function>::Cast(valRef);
val->Reset(iso, callback);
}
return SWIG_OK;
}
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - *\
One-Off, Inline Callback
\* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
static SwigV8ReturnValue
_wrap_funcA(const SwigV8Arguments &args)
{
SWIGV8_HANDLESCOPE();
auto iso = v8::Isolate::GetCurrent();
if (args.Length() != 1) SWIG_exception_fail(SWIG_ERROR,
"Illegal number of arguments for _wrap_funcA.");
if (!args[0]->IsFunction()) SWIG_exception_fail(SWIG_ERROR,
"Invalid argument; expecting function for _wrap_funcA.");
{
v8::Local<v8::Function> callback = v8::Local<v8::Function>::Cast(args[0]);
const int argc = 1;
v8::Handle<v8::Value> argv[] = { SWIGV8_STRING_NEW("hello world") };
callback->Call(iso->GetCurrentContext()->Global(), argc, argv);
}
fail:
SWIGV8_RETURN(SWIGV8_UNDEFINED());
}
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - *\
One-Off, Async Callback
\* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
static void
_cb_funcB_async(uv_work_t* req)
{
worker_packet* worker = static_cast<worker_packet*>(req->data);
worker->parent->get<FooClass*>()->bar(worker->args[0]->get<std::string>());
}
static void
_cb_funcB_complete(uv_work_t *req, int status)
{
SWIGV8_HANDLESCOPE();
v8::Isolate* iso = v8::Isolate::GetCurrent();
worker_packet* worker = static_cast<worker_packet*>(req->data);
int argc = 1;
v8::Handle<v8::Value> argv[] =
{ SWIGV8_BOOLEAN_NEW(work->return_value->get<bool>()) };
v8::Local<v8::Function>::New(iso, worker->callbacks[0])
->Call(iso->GetCurrentContext()->Global(), argc, argv);
worker->callback.Reset();
worker->request.data = NULL;
uv_loop_close(worker->loop);
delete work;
}
static SwigV8ReturnValue
_wrap_funcB(const SwigV8Arguments &args)
{
SWIGV8_HANDLESCOPE();
WorkerPacket* worker = new WorkerPacket();
int success = 0 ;
if (args.Length() != 2) SWIG_exception_fail(SWIG_ERROR,
"Illegal number of arguments for _wrap_funcB.");
success = SWIG_ConvertPtr(args[0], &argp1, SWIGTYPE_p_FooClass, 0 | 0 );
if (!SWIG_IsOK(success))
SWIG_exception_fail(SWIG_ArgError(success),
"in method '_wrap_funcB', argument 1 of type 'FooClass*'");
worker->parent = std::make_shared<type_arg< FooClass* >>(
reinterpret_cast< FooClass* >(argp1)
success = SWIG_AsVal_function(args[1], &worker->callbacks[0]);
if (!SWIG_IsOK(success))
SWIG_exception_fail(SWIG_ArgError(success),
"in method '_wrap_funcB', argument 2 of type 'function'");
worker->args.push_back(
std::make_shared<type_arg<std::string>>("hello world"));
worker->return_value = std::make_shared<type_arg<bool>>(false);
worker->request.data = worker;
worker->loop = uv_default_loop();
uv_queue_work(
worker->loop, &worker->request,
_cb_funcB_async, _cb_funcB_complete);
fail:
SWIGV8_RETURN(SWIGV8_UNDEFINED());
}
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - *\
Persistent Async Callback
\* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
void
recuring_work(worker_packet* packet)
{
thread = std::async(std::launch::async, [](worker_packet* worker) {
for (int i = 0; i < 3; i++) {
uv_rwlock_wrlock(&worker->lock);
worker->args.push_back(std::make_shared<type_arg<std::string>>(std::to_string(i)));
uv_rwlock_wrunlock(&worker->lock);
uv_async_send(&worker->async);
std::cout << "native " << i << "\n";
std::this_thread::sleep_for(std::chrono::seconds(1));
}
uv_rwlock_wrlock(&worker->lock);
worker->done = true;
uv_rwlock_wrunlock(&worker->lock);
uv_async_send(&worker->async);
std::cout << "native done\n";
}, packet);
}
void
_cp_funcC_async(uv_async_t* async)
{
SWIGV8_HANDLESCOPE();
v8::Isolate* iso = v8::Isolate::GetCurrent();
worker_packet* worker = static_cast<worker_packet*>(async->data);
uv_rwlock_wrlock(&worker->lock);
auto args = worker->args;
bool done = worker->done;
worker->args.clear();
uv_rwlock_wrunlock(&worker->lock);
for (auto& arg : args) {
const int argc = 1;
v8::Handle<v8::Value> argv[] =
{ SWIGV8_STRING_NEW(arg->get<std::string>().c_str()) };
v8::Local<v8::Function>::New(iso, work->callbacks[0])
->Call(iso->GetCurrentContext()->Global(), argc, argv);
}
if (done) {
uv_close(reinterpret_cast<uv_handle_t*>(&worker->async), nullptr);
uv_loop_close(worker->loop);
uv_rwlock_destroy(&worker->lock);
for (auto& callback : worker->callbacks)
callback.Reset();
delete worker;
}
}
static SwigV8ReturnValue
_wrap_funcC(const SwigV8Arguments &args)
{
SWIGV8_HANDLESCOPE();
worker_packet* worker = new worker_packet();
int success = 0 ;
int status = 0 ;
if(args.Length() != 1) SWIG_exception_fail(SWIG_ERROR,
"Illegal number of arguments for _wrap_funcC.");
{
success = SWIG_AsVal_function(args[0], &worker->callbacks[0]);
if (!SWIG_IsOK(success))
SWIG_exception_fail(SWIG_ArgError(success),
"in method '_wrap_funcC', argument 1 of type 'function'");
}
worker->async.data = worker;
worker->loop = uv_default_loop();
worker->done = false;
uv_rwlock_init(&worker->lock);
uv_async_init(worker->loop, &worker->async, _cb_funcC_async);
recuring_work(worker);
fail:
SWIGV8_RETURN(SWIGV8_UNDEFINED());
}
%}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment