I am using WebKitGTK+ in a larger GTKmm/C++ application. I am using JavaScriptCore to interact with the WebKitWebFrame and JSContext within.
I am stuck now as I need to interact with a GTK GUI component when a javascript function is called. To this end I found the JSObjectMakeFunctionWithCallback function.
JSStringRef str = JSStringCreateWithUTF8CString("ClickCallback");
JSObjectRef func = JSObjectMakeFunctionWithCallback(m_jsRef, str, ClickCallback);
JSObjectSetProperty(m_jsRef, JSContextGetGlobalObject(m_jsRef), str, func, kJSPropertyAttributeNone, NULL);
JSStringRelease(str);
Where the callback must be defined as a static function with def:
static JSValueRef ClickCallback(JSContextRef ctx, JSObjectRef func, JSObjectRef self, size_t argc, const JSValueRef argv[], JSValueRef* exception)
So everything is working except I need my object instance in the callback to get back at the GUI component I need to manipulate.
There are tons of similar questions on SO but most focus on passing the object instance into the callback. I can not see a way of doing that with this API.
Any ideas?
The callback is required to be a pointer to a free function, so no amount of magic can get you to pass a member function directly. One common solution is to make an intermediate global free function that holds the object instance:
JSValueRef ClickCallback(...)
{
Foo & obj = getInstance(); // implement this somehow
return obj.click(...);
}
Alternatively, you can make this a static member function of your GUI class. The main point is that you must obtain the instance reference separately and call the member function yourself if you have to pass a plain function pointer to your API.
Related
I'm working on a project in C++, but at some point in the application it fails and generates a core dump. The application uses a couple of classes, which for the purposes here I'm concentrating on one of the classes, which I'm calling A, and is instantiated as object a. This has a large number of member functions, of which at the moment only a few are being used, but one of these generates a log to produce diagnostics to be used for debugging. I want to use this to find out why the application is failing.
The project is to put together code that invokes the various member functions, and although I have access to the source code and some limited documentation, none of the code can be changed, with all changes being in the code that makes use of the classes and invokes the member functions. The member function in question is:
void enable_log (log_callback callback, void * user_data = nullptr)
where the 1st argument callback contains the message and 2nd argument is optional. For now it can be set to nullptr, so would be invoked as:
a.enable_log(callback, nullptr);
From this documentation it's not at all clear what exactly callback is. However, in looking at the source code this is:
using log_callback = void (*)(const std::string& message, void* user_data);
in a header file, where log_callback is an alias for const std::string& if I understand this correctly.
I already have dummy classes on a platform using Visual Studio 2019 with some test member functions to simulate invoking the member functions on a remote Linux server, but I'm unable to find a way of making use of the member function above. I added the test member function to the dummy class as follows:
void enable_log(const std::string& callback, void* user_data = nullptr) {
callback = "ABCD";
}
which is supposed to generate a test string which is returned, such that in the real application this string will have diagnostic information that will be written to a file. However, the "=" is an error.
The idea is that in the main function an empty string will be declared, then enable_log() should populate this string, which can be printed out.
I've spent some time looking at various resources, including Stackoverflow, but I cannot find a way of returning a string with the information that can be printed out. I need a simple way to simulate this, and as I said above, I must not change the source code of the real member function, so the simulated member function has to produce a string in the same way. How is this done? Some advice would be appreciated.
Callback, in simple words, is some function that will be called later at some point. Example:
void callback_fn(int a);
using callback_t = (void)(*)(int a);
void some_func(callback_t);
You can use some_func() like so:
some_func(callback_fn);
Full example here: https://godbolt.org/z/ET3GhfYrv
For your usecase the parameters of the callback are slightly different. Here's how to read the syntax:
using log_callback = // this just creates an alias for whatever is on the right handside
void // the return type of the "callable" should be void
(*) // this tells us that it is a function pointer
(const std::string& message, void* user_data) // These are the arguments the callable takes. It is a "std::string" and a "void *"
To use this, just create a free function with the same signature:
void callable(const std::string &msg, void *userData = nullptr)
{
// msg is the data sent by the function. use it in whatever way
// you want.
std::cout << msg << '\n';
}
// Pass it to the enable_log
enable_log(callable);
I have working code where I can create as many Point objects as I want, but it re-creates the object template each time the constructor is called, which seems like it's probably wrong.
Local<ObjectTemplate> global_templ = ObjectTemplate::New(isolate);
// make the Point constructor function available to JS
global_templ->Set(v8::String::NewFromUtf8(isolate, "Point"), FunctionTemplate::New(isolate, v8_Point));
and then the constructor itself:
void v8_Point(const v8::FunctionCallbackInfo<v8::Value>& args) {
HandleScope scope(args.GetIsolate());
// this bit should probably be cached somehow
Local<ObjectTemplate> point_template = ObjectTemplate::New(args.GetIsolate());
point_template->SetInternalFieldCount(1);
point_template->SetAccessor(String::NewFromUtf8(args.GetIsolate(), "x"), GetPointX, SetPointX);
point_template->SetAccessor(String::NewFromUtf8(args.GetIsolate(), "y"), GetPointY, SetPointY);
// end section to be cached
Local<Object> obj = point_template->NewInstance();
Point * p = new Point(1,1);
obj->SetInternalField(0, External::New(args.GetIsolate(), p));
args.GetReturnValue().Set(obj);
}
But it seems like I should be able to pass in the point_template object instead of re-creating it each time. I saw that there's a Data() field in args, but that only allows for a Value type and an ObjectTemplate is of type Template, not Value.
Any help on the right way to do this would be greatly appreciated.
I figured it out finally.
In javascript, when you add a function via a FunctionTemplate and then call it as a constructor (e.g. new MyFunction), then in your c++ callback the args.This() will be a new object created by the using the FunctionTemplate's InstanceTemplate object template.
// Everything has to go in a single global template (as I understand)
Local<ObjectTemplate> global_templ = ObjectTemplate::New(isolate);
// create the function template and tell it the callback to use
Local<FunctionTemplate> point_constructor = FunctionTemplate::New(isolate, v8_Point);
// set the internal field count so our actual c++ object can tag along
// with the javascript object so our accessors can use it
point_constructor->InstanceTemplate()->SetInternalFieldCount(1);
// associate getters and setters for the 'x' field on point
point_constructor->InstanceTemplate()->SetAccessor(String::NewFromUtf8(isolate, "x"), GetPointX, SetPointX);
... add any other function and object templates to the global template ...
// add the global template to the context our javascript will run in
Local<Context> x_context = Context::New(isolate, NULL, global_templ);
Then, for the actual function:
void v8_Point(const v8::FunctionCallbackInfo<v8::Value>& args) {
// (just an example of a handy utility function)
// whether or not it was called as "new Point()" or just "Point()"
printf("Is constructor call: %s\n", args.IsConstructCall()?"yes":"no");
// create your c++ object that will follow the javascript object around
// make sure not to make it on the stack or it won't be around later when you need it
Point * p = new Point();
// another handy helper function example
// see how the internal field count is what it was set to earlier
// in the InstanceTemplate
printf("Internal field count: %d\n",args.This()->InternalFieldCount()); // this prints the value '1'
// put the new Point object into the internal field
args.This()->SetInternalField(0, External::New(args.GetIsolate(), p));
// return the new object back to the javascript caller
args.GetReturnValue().Set(args.This());
}
Now, when you write the getter and setter, you have access to your actual c++ object in the body of them:
void GetPointX(Local<String> property,
const PropertyCallbackInfo<Value>& info) {
Local<Object> self = info.Holder();
// This is where we take the actual c++ object that was embedded
// into the javascript object and get it back to a useable c++ object
Local<External> wrap = Local<External>::Cast(self->GetInternalField(0));
void* ptr = wrap->Value();
int value = static_cast<Point*>(ptr)->x_; //x_ is the name of the field in the c++ object
// return the value back to javascript
info.GetReturnValue().Set(value);
}
void SetPointX(Local<String> property, Local<Value> value,
const PropertyCallbackInfo<void>& info) {
Local<Object> self = info.Holder();
// same concept here as in the "getter" above where you get access
// to the actual c++ object and then set the value from javascript
// into the actual c++ object field
Local<External> wrap = Local<External>::Cast(self->GetInternalField(0));
void* ptr = wrap->Value();
static_cast<Point*>(ptr)->x_ = value->Int32Value();
}
Almost all of this came from here: https://developers.google.com/v8/embed?hl=en#accessing-dynamic-variables
except it doesn't talk about the proper way to make your objects in a repeatable fashion.
I figured out how to clean up the c++ object in the internal field, but I don't have time to put the whole answer here. You have to pass in a Global object into your weak callback by creating a hybrid field (a struct works well) on the heap that has both the global object and a pointer to your c++ object. You can then delete your c++ object, call Reset() on your Global and then delete the whole thing. I'll try to add actual code, but may forget.
Here is a good source: https://code.google.com/p/chromium/codesearch#chromium/src/v8/src/d8.cc&l=1064 lines 1400-1441 are what you want. (edit: line numbers seem to be wrong now - maybe the link above has changed?)
Remember, v8 won't garbage collect small amounts of memory, so you may never see it. Also, just because your program ends doesn't mean the GC will run. You can use isolate->AdjustAmountOfExternalAllocatedMemory(length); to tell v8 about the size of the memory you've allocated (it includes this in its calculations about when there's too much memory in use and GC needs to run) and you can use isolate->IdleNotificationDeadline(1); to give the GC a chance to run (though it may choose not to).
is there a possibility or a workaround to pass a member function to the Windows API function QueueUserAPC()?
Okay, I could pass a static member function.
But then I won't have full access to local member variables...
So is there a possibility to combine both, passing as member function and full access to non-static member variables?
I tried to work out a solution related to this but without any success yet.
Hopefully someone got an idea to solve this.
This is a kind of standard pattern to use when having C-style callbacks call your C++ functions.
You create a free function (or static member) that forwards the call ...
VOID CALLBACK ForwardTo_MyClass_func( _In_ ULONG_PTR dwParam )
{
auto* p = (MyClass*)dwParam;
p->func();
}
... and you then set it up by passing the instance pointer as the third parameter to QueueUserAPC:
QueueUserAPC( ForwardToMyClass_func, hThread, (ULONG_PTR)pMyClass );
If you need further arguments, you will have to create some kind of structure to hold both the instance pointer and the arguments.
The answer is no.
Windows API has a C interface, and therefor cannot handle name mangled signatures, such as C++ member functions. The function you pass must be a C style free function.
By the way, nesting it in a namespace is acceptable, if less scalable:
namespace apc_stuff
{
static MyStruct some_static_data;
static void __stdcall MyApcFunc(ULONG_PTR data); // PAPCFUNC
};
using namespace apc_stuff;
MyClass::DoSomething(...)
{
auto my_data = new MyData(...);
auto data = reinterpret_cast<ULONG_PTR>(my_data);
QueueUserAPC(MyApcFunc, hThread, data)
}
/*static*/ void __stdcall apc_stuff::MyApcFunc(ULONG_PTR data)
{
auto my_data = reinterpret_cast<MyData *>(data);
//
// use my_data
// use some_static_data
//
}
I need to register a member function using luabind which is supposed to take a lua-function as parameter. For any normal function I would usually just do this:
int SomeLuaFunction(lua_State *l)
{
luaL_checkfunction(l,1);
int fc = luaL_ref(l,LUA_REGISTRYINDEX);
[...]
}
Luabind however uses the parameter list, so I'm unsure how to tell it I'm expecting a function:
void Lua_ALSound_CallOnStateChanged(lua_State *l,boost::shared_ptr<ALSound> pAl,<function-parameter?>)
{
[...]
}
lua_bind(luabind::class_<ALSound COMMA boost::shared_ptr<ALSound>>("ALSound")
.def("CallOnStateChanged",&Lua_ALSound_CallOnStateChanged)
);
(Only the relevant part of the code is shown here, lua_bind is using luabind::module)
lua-example of what I'm trying to accomplish:
local al = ALSound() -- I'm aware this wouldn't work since I haven't defined a constructor
al:CallOnStateChanged(function()
[...]
end)
Perhaps there is a way to add additional functions to an already registered class without luabind? Any suggestions would be appreciated.
If you want to be able to have a function that takes Lua objects as parameters, you should have the function take a luabind::object as a parameter. Then you can check to see if it's a function and call it if it is.
I'm having some trouble making a callback wrapper class method that needs to be used by a third party library; the JackAudio library.
I have been able to make a wrapper for a JackAudio callback function that needs two arguments.
I'm just having trouble creating a callback function for a particular function that needs a const char * as an argument.
So far I have been able to make the JackAudio library jack_set_sample_rate_callback function use a custom class and can be executed like so:
SoundClass Sound;
SoundClass * SoundPointer = &Sound;
jack_set_sample_rate_callback(
client,
SoundClass::SampleRateCallbackWrapper,
SoundPointer
);
And the class looks something like this:
SoundClass
{
int SampleRateCallback( jack_nframes_t nframes )
{
//executes some code when called.
}
static int SampleRateCallbackWrapper( jack_nframes_t nframes, void * arg )
{
return static_cast < SoundClass* > ( arg )->SampleRateCallback( nframes );
}
};
All of the above works well, with no issues.
The problem I'm having now is with the JackAudio callback function jack_set_error_function
This is what I tried:
static void ErrorCallbackWrapper( const char * arg )
{
return static_cast < SoundClass*>( arg )->SomeErrorFunction();
}
But I get error: invalid static_cast from type ‘const char*’ to type ‘SoundClass*’
I get the gist why this is happening, I just have no idea what to do for a solution.
Thanks in advance for any help guys.
Assuming the Jack API is written for the C language, there is a formal problem already with the working callback that you have. Namely that it then needs to be extern "C", and that as a static member function it cannot be. So formally it needs to be a free-standing function.
The documentation that you link to for the jack_set_error_function gives this signature, presumably expressed in C:
void jack_set_error_function( void(*)(const char *) func);
For C++ the callback must be assumed to be extern "C", so,
extern "C" void MyErrorFunction( char const* errorMessage )
{
// Whatever, e.g. post a message to the GUI event queue, or terminate.
}
If you want this function to in turn call a method on an object, then unless the library provides some special mechanism to help you, you will just have to use one of the following techniques:
a namespace scope variable accessed by the callback, or
a dynamically generated callback.
C++ does not as of yet support the second approach, at all, so the first one is strongly indicated – if you want a callback on a method of an object.
EDIT: Sorry, I forgot to mention,
the function declarations in the API documentation are syntactically invalid.
E.g. the documentation’s signature
void jack_set_info_function( void(*)(const char *) func );
simply won’t compile with a standard-conforming compiler. Not as C, and not as C++. It’s syntactically invalid in both languages.
Instead it should be
void jack_set_info_function( void(*func)(const char *) );
Since the documentation apparently is generated by DOxygen, it stands to reason that it's been generated from source code that compiles. If so then this is a bug in DOxygen, and a problem with the quality assurance of the library provider. However it might be a problem that lies solely with the library provider, or, I might be mistaken in the assumption that this is a C library?