Thread overview
Unable to pass a D function member to a C callback
Nov 02, 2019
Luh
Nov 02, 2019
Stefan Koch
Nov 02, 2019
Luh
Nov 02, 2019
Dennis
Nov 02, 2019
Luh
Nov 02, 2019
Dennis
Nov 02, 2019
Alex
November 02, 2019
Hello,

When trying to pass a D function to the C callback, the compiler says:

'Error: cannot implicitly convert expression &this.onProcessCb of type extern (C) bool delegate(const(short*) a, ulong b, void* c) to extern (C) bool function(const(short*), ulong, void*'

because my function is member of a class (compiles when the function is out of the class).

Is there any way to say to solve this ?
The wiki isn't very clear about the C callbacks:
https://dlang.org/spec/interfaceToC.html#callbacks

C code:
----
typedef bool (*onProcessCallback)(const short*, size_t, void*);
----


D Code:
-----
class Game
{
	onProcessCallback m_onProcessCb;
	
	this()
	{
		m_onProcessCb = &onProcessCb; // Error here
	}
	
	void onProcess()
	{
		// ...
	}

	extern(C) bool onProcessCb(const short* a, size_t b, void* c)
	{
		onProcess();
		return true;
	}
}

private extern(C)
{
	// Should call onProcess() when executed by the C lib
	alias onProcessCallback = bool function(const short*, size_t, void*);
}
-----
November 02, 2019
On Saturday, 2 November 2019 at 17:49:09 UTC, Luh wrote:
> Hello,
>
> When trying to pass a D function to the C callback, the compiler says:
>
> 'Error: cannot implicitly convert expression &this.onProcessCb of type extern (C) bool delegate(const(short*) a, ulong b, void* c) to extern (C) bool function(const(short*), ulong, void*'
>
> because my function is member of a class (compiles when the function is out of the class).
>
> Is there any way to say to solve this ?
> The wiki isn't very clear about the C callbacks:
> https://dlang.org/spec/interfaceToC.html#callbacks
>
> C code:
> ----
> typedef bool (*onProcessCallback)(const short*, size_t, void*);
> ----
>
>
> D Code:
> -----
> class Game
> {
> 	onProcessCallback m_onProcessCb;
> 	
> 	this()
> 	{
> 		m_onProcessCb = &onProcessCb; // Error here
> 	}
> 	
> 	void onProcess()
> 	{
> 		// ...
> 	}
>
> 	extern(C) bool onProcessCb(const short* a, size_t b, void* c)
> 	{
> 		onProcess();
> 		return true;
> 	}
> }
>
> private extern(C)
> {
> 	// Should call onProcess() when executed by the C lib
> 	alias onProcessCallback = bool function(const short*, size_t, void*);
> }
> -----

you are missing a static in your member function's signature.
The callback is not providing a this pointer.
November 02, 2019
On Saturday, 2 November 2019 at 17:49:09 UTC, Luh wrote:
> Hello,
>
> When trying to pass a D function to the C callback, the compiler says:
>
> 'Error: cannot implicitly convert expression &this.onProcessCb of type extern (C) bool delegate(const(short*) a, ulong b, void* c) to extern (C) bool function(const(short*), ulong, void*'
>
> because my function is member of a class (compiles when the function is out of the class).
>
> Is there any way to say to solve this ?
> The wiki isn't very clear about the C callbacks:
> https://dlang.org/spec/interfaceToC.html#callbacks
>
> C code:
> ----
> typedef bool (*onProcessCallback)(const short*, size_t, void*);
> ----
>
>
> D Code:
> -----
> class Game
> {
> 	onProcessCallback m_onProcessCb;
> 	
> 	this()
> 	{
> 		m_onProcessCb = &onProcessCb; // Error here
> 	}
> 	
> 	void onProcess()
> 	{
> 		// ...
> 	}
>
> 	extern(C) bool onProcessCb(const short* a, size_t b, void* c)
> 	{
> 		onProcess();
> 		return true;
> 	}
> }
>
> private extern(C)
> {
> 	// Should call onProcess() when executed by the C lib
> 	alias onProcessCallback = bool function(const short*, size_t, void*);
> }
> -----

This is because onProcessCb is a member of an object. Therefore, it carries also the information about the context, which includes e.g. all members of the class.

Due to this onProcessCb is a delegate, which is something different from a function, cf.
https://dlang.org/spec/function.html#closures

So, there is a type mismatch.

November 02, 2019
On Saturday, 2 November 2019 at 18:31:28 UTC, Stefan Koch wrote:
> On Saturday, 2 November 2019 at 17:49:09 UTC, Luh wrote:
>> Hello,
>>
>> When trying to pass a D function to the C callback, the compiler says:
>>
>> 'Error: cannot implicitly convert expression &this.onProcessCb of type extern (C) bool delegate(const(short*) a, ulong b, void* c) to extern (C) bool function(const(short*), ulong, void*'
>>
>> because my function is member of a class (compiles when the function is out of the class).
>>
>> Is there any way to say to solve this ?
>> The wiki isn't very clear about the C callbacks:
>> https://dlang.org/spec/interfaceToC.html#callbacks
>>
>> C code:
>> ----
>> typedef bool (*onProcessCallback)(const short*, size_t, void*);
>> ----
>>
>>
>> D Code:
>> -----
>> class Game
>> {
>> 	onProcessCallback m_onProcessCb;
>> 	
>> 	this()
>> 	{
>> 		m_onProcessCb = &onProcessCb; // Error here
>> 	}
>> 	
>> 	void onProcess()
>> 	{
>> 		// ...
>> 	}
>>
>> 	extern(C) bool onProcessCb(const short* a, size_t b, void* c)
>> 	{
>> 		onProcess();
>> 		return true;
>> 	}
>> }
>>
>> private extern(C)
>> {
>> 	// Should call onProcess() when executed by the C lib
>> 	alias onProcessCallback = bool function(const short*, size_t, void*);
>> }
>> -----
>
> you are missing a static in your member function's signature.
> The callback is not providing a this pointer.

My problem is, that the onProcess function should not be static because it uses a variable inside the class.

like:
-----
class Game
{
	private short[] m_a;
	
	void onProcess(short[] a)
	{
		m_a ~= a;
		// ...
	}

	extern(C) bool onProcessCb(const short* a, size_t b, void* c)
	{
		onProcess();
		return true;
	}
}
-----

So I think I just can't. :(
November 02, 2019
On Saturday, 2 November 2019 at 19:42:54 UTC, Luh wrote:
> So I think I just can't. :(

Is that `void* c` in the callback a context pointer by any chance?
That's a common thing in C callbacks precisely for purposes like this.
You can cast your class to a void* when you register the callback and in the callback function cast it back to a class and call process on that.

I don't know what C library you're working with so can't give you specifics.
November 02, 2019
On Saturday, 2 November 2019 at 19:55:58 UTC, Dennis wrote:
> On Saturday, 2 November 2019 at 19:42:54 UTC, Luh wrote:
>> So I think I just can't. :(
>
> Is that `void* c` in the callback a context pointer by any chance?
> That's a common thing in C callbacks precisely for purposes like this.
> You can cast your class to a void* when you register the callback and in the callback function cast it back to a class and call process on that.
>
> I don't know what C library you're working with so can't give you specifics.

Yup that's it !
Many thanks !
November 02, 2019
On Saturday, 2 November 2019 at 20:42:29 UTC, Luh wrote:
> Yup that's it !
> Many thanks !

One word of warning: ensure the C library does not have the only reference to your Game class instance, or the garbage collector might deallocate it since it does not scan threads created by C libraries.

See:
https://dlang.org/spec/garbage.html#gc_foreign_obj