I have the following code: https://godbolt.org/z/e31EcTbeb
template<PyObject* (*fn)(PyObject*, PyObject*[], Py_ssize_t)>
PyObject* python_fastcall(PyObject* self, PyObject* args)
{
typedef struct
{
PyObject_VAR_HEAD
PyObject *ob_item[1];
} PyTupleObject;
return fn(self, reinterpret_cast<PyTupleObject*>(args)->ob_item, PyTuple_Size(args));
}
and a macro to call it:
using PyCFunction = PyObject* (*)(PyObject*, PyObject*[])
#define PYTHON_FASTCALL(f) (PyCFunction)python_fastcall<f>
Finally I have a few functions like:
PyObject* foo(PyObject* self, PyObject* args[], Py_ssize_t args_length);
PyObject* bar(PySomeObject* self, PyObject* args[], Py_ssize_t args_length);
PyObject* meh(PyDifferentObject* self, PyObject* args[], Py_ssize_t args_length);
and I want to be able to do:
PYTHON_FASTCALL(foo)
PYTHON_FASTCALL(bar)
PYTHON_FASTCALL(meh)
But I get the errors:
error: address of overloaded function 'python_fastcall' does not match required type 'PyObject* (PyObject*, PyObject*)' -> PYTHON_FASTCALL(bar)
error: address of overloaded function 'python_fastcall' does not match required type 'PyObject* (PyObject*, PyObject*)' -> PYTHON_FASTCALL(meh)
No error for foo
as the signature matches of course.
Is there a way to make the function accept such function pointers where only the FIRST
parameter differs?
I tried something like:
template<typename T, typename... Ts>
struct is_any_of : std::bool_constant<(std::is_same<T, Ts || ...)> { };
template<typename fn>
typename std::enable_if<
is_any_of<fn,
PyObject* (*)(PyObject*, PyObject*[], Py_ssize_t),
PyObject* (*)(PySomeObject*, PyObject*[], Py_ssize_t),
PyObject* (*)(PyDifferentObject*, PyObject*[], Py_ssize_t)
>::value,
PyObject*
>::type
python_fastcall(PyObject* self, PyObject* args)
{
return fn(self, args, args_size(args));
}
But it doesn’t work because fn
becomes a type instead of a function pointer.
Any ideas how I can achieve this?
… because fn becomes a type …
Its a type because you declared it to be a type here template<typename fn>
. You can use a non-type template parameter just like you did before.
Without restricting the template parameter you can do this:
struct A {};
struct B : A {};
struct C : A {};
template <auto f>
void call(auto* a, int x) {
f(a,x);
}
void foo(A*,int){}
void bar(B*,int) {}
void moo(C*,int) {}
int main() {
A* a;
B* b;
C* c;
call<foo>(a,42);
call<bar>(b,42);
call<moo>(c,42);
}
If you want to restrict the parameter to a function pointer with one of the three signatures you can still use the auto
template argument and then SFINAE on the decltype
of it. I also fixed the first arg of call
to the first arg of the function pointer:
#include <type_traits>
struct A {};
struct B : A {};
struct C : A {};
void foo(A*,int){}
void bar(B*,int) {}
void moo(C*,int) {}
template<typename T, typename U, typename... Ts>
struct is_any_of : is_any_of<T,Ts...> { };
template<typename T, typename... Ts>
struct is_any_of<T,T, Ts...> : std::true_type { };
template<typename T, typename U>
struct is_any_of<T,U> : std::false_type { };
template <typename F> struct first_arg;
template <typename R,typename T,typename ...More> struct first_arg<R(*)(T,More...)> {
using type = T;
};
template <auto f>
std::enable_if_t<
is_any_of<decltype(f),decltype(&foo),decltype(&bar),decltype(&moo),void>::value
> call(typename first_arg<decltype(f)>::type a, int x) {
f(a,x);
}
int main() {
A* a;
B* b;
C* c;
call<foo>(a,42);
call<bar>(b,42);
call<moo>(c,42);
}
I didn’t get your is_any_of
to compile, so I used the one from here. It has an issue when the last argument matches, here it can be fixed by using void
as last argument (decltype(fn)
cannot be void
).
args
isPyObject*
butfn
takes aPyObject**
as 2nd parameterPyObject*[] args
is also incorrect syntax. godbolt.org/z/q3csvcMje. Please post a minimal reproducible exampleFixed the example. I just didn’t want to bombard the post with huge functions. Args is actually a tuple that gets converted to an array.
you should not bombard the question with huge functions. You should prepare a minimal reproducible example
PyObject* foo(PyObject* self, PyObject*[] args, Py_ssize_t args_length);
is still bogusShow 3 more comments