tkausl
tkausl

Reputation: 14269

Cast in consteval function fails; works outside

I'm trying to write a thin wrapper layer to interface c++ classes from python.

Python itself uses those three signatures to call a c function from py:

typedef PyObject *(*PyCFunction)(PyObject *, PyObject *);
typedef PyObject *(*PyCFunctionWithKeywords)(PyObject *, PyObject *, PyObject *);
typedef PyObject *(*PyNoArgsFunction)(PyObject *);

Unfortunately, the PyMethodDef struct stores a function as PyCFunction and a flags member to decide at runtime which function is actually stored/called.

I wrote a function template which takes three PyObject*s and returns one PyObject*, essentially a PyCFunctionWithKeywords, calling the classes member function with either zero, one or two arguments (as the first argument is the instance itself).

template<auto Fn>
PyObject* member_wrapper(PyObject* obj, PyObject* args, PyObject* kwargs)

Fn is the member function pointer to wrap. Given a macro:

#define PY_WRAP(fn) (PyCFunction)::py::member_wrapper<fn>

I can successfully set the function pointer:

PyCFunction func = PY_WRAP(&MyClass::SomeFunc);

Above compiles as expected. However I've tried using a consteval function instead of a macro:

template<typename T>
consteval PyCFunction make_wrapper(T fn) {
    return (PyCFunction)::py::member_wrapper<fn>;
}

PyCFunction func = make_wrapper(&MyClass::SomeFunc);

This however fails with:

 error C2440: Cannot convert "PyObject *(__cdecl *)(PyObject *,PyObject *,PyObject *)" to "PyCFunction"

I'm confused why the cast works in the macro but fails in the consteval function.

Upvotes: 0

Views: 84

Answers (1)

Nicol Bolas
Nicol Bolas

Reputation: 473966

consteval is not a macro; it's not just a textual copy-and-paste. It's a way of decorating a function such that using it outside of constant expression evaluation is a compile error. consteval functions therefore must follow the rules of constexpr functions.

Among those rules are that reinterpret_cast, and any C-style cast that would be equivalent to one, is expressly forbidden. Conversion of a function pointer to one signature to a different signature requires a reinterpret_cast, even if you're using a C-style cast. So it's not allowed.

You can use a non-constexpr function call if you want to avoid a macro. But you can't use a compile-time construct for something that cannot be done at compile-time.

Upvotes: 2

Related Questions