November 08

Here's this precompiler macro from Pipewire, on which many important inline functions depend on, like this one:

/**
 * Invoke method named \a method in the \a callbacks.
 * The \a method_type defines the type of the method struct.
 * Returns true if the method could be called, false otherwise.
 */
#define spa_callbacks_call(callbacks,type,method,vers,...)			\
({										\
	const type *_f = (const type *) (callbacks)->funcs;			\
	bool _res = SPA_CALLBACK_CHECK(_f,method,vers);				\
	if (SPA_LIKELY(_res))							\
		_f->method((callbacks)->data, ## __VA_ARGS__);			\
	_res;									\
})

So far, the only way out I see is to turn it into a string mixin. (SPA_LIKELY is just a needless precompiler macro for labeling things.)

November 09

On Wednesday, 8 November 2023 at 20:43:21 UTC, solidstate1991 wrote:

>

Here's this precompiler macro from Pipewire, on which many important inline functions depend on, like this one:

/**
 * Invoke method named \a method in the \a callbacks.
 * The \a method_type defines the type of the method struct.
 * Returns true if the method could be called, false otherwise.
 */
#define spa_callbacks_call(callbacks,type,method,vers,...)			\
({										\
	const type *_f = (const type *) (callbacks)->funcs;			\
	bool _res = SPA_CALLBACK_CHECK(_f,method,vers);				\
	if (SPA_LIKELY(_res))							\
		_f->method((callbacks)->data, ## __VA_ARGS__);			\
	_res;									\
})

So far, the only way out I see is to turn it into a string mixin. (SPA_LIKELY is just a needless precompiler macro for labeling things.)

Not sure if it will work in real situations, expect memory errors.
Also I used a fixed type, you should use CTFE to cast data to proper function argument types.

import std.stdio;

bool SPA_CALLBACK_CHECK(T)(const (T)* f, string method, uint version_)
{
	// do some checks...
	return true;
}

bool spa_callbacks_call(alias callbacks, alias type, alias method, uint vers, Args...)(Args)
{
	const (type)* _f = cast(type*) callbacks.funcs;
	bool _res = SPA_CALLBACK_CHECK(_f, __traits(identifier, method), vers);
	if (_res)
		__traits(getMember, _f, __traits(identifier, method))(cast(int) callbacks.data, Args); // callback call
	return _res;
}

// your callback, see pipewire docs for real examples
struct Foo
{
	void bar(int x) const { import std.stdio; writeln(x);  }
}

// pipewire internals
struct spa_callback
{
	const(void)* funcs;
	void* data;
}


void main()
{
	Foo foo;
	// instead of this naive approach you should use provided initialization method
	// but roughly first parameter is pointer to struct of function pointers
	spa_callback cb = { cast(void*) &foo, cast(void*) 42 };
	spa_callbacks_call!(cb, Foo, Foo.bar, 0)();
}