wlav / cppyy

Other
391 stars 40 forks source link

can not convert null function pointer #119

Open N-Coder opened 1 year ago

N-Coder commented 1 year ago

Cppyy fails with an exception if one tries to access a function pointer that is null:

>>> cppyy.cppdef("using FT = void(*)(); FT func = nullptr;")
True
>>> cppyy.gbl.FT
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: <namespace cppyy.gbl at 0x556b0ec2a060> has no attribute 'FT'. Full details:
  type object '' has no attribute 'FT'
  'FT' is not a known C++ class
  'FT' is not a known C++ template
  'FT' is not a known C++ enum
>>> cppyy.gbl.func
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: <namespace cppyy.gbl at 0x556b0ec2a060> has no attribute 'func'. Full details:
  type object '' has no attribute 'func'
  'func' is not a known C++ class
  can not convert null function pointer

I would probably expect cppyy.bind_object(cppyy.nullptr, cppyy.gbl.FT) as return value, but it's also not clear to me why the FT typedef cannot be accessed.

wlav commented 1 year ago

Issue is that a function pointer isn't represented as a function pointer b/c Cling doesn't track it as a Type but as a FunctionDecl. I've never dug into it to see whether that's for Clang reasons or just a Cling artifact. The upshot is that the chosen representation of a function pointer is an std::function<> using a JITed helper function, so that the actual function type isn't needed.

That in turn makes the "pointer" an object, which is automatically callable using existing machinery. And yes, I could simply expose that when null, but it wouldn't be cppyy.bind_object(cppyy.nullptr, cppyy.gbl.FT) (for the reason explained above), but rather a callable that evaluates to False and raises std::bad_function_call on use. I'll make that change if it doesn't break anything.

In addition to Cling not giving me the type of FT, exposing FT as a type would require it to be a custom Python type as there's nothing equivalent in Python that would do. Using std::function wouldn't work b/c that doesn't have pointer behavior. The only reason I could see for doing that is for templates (same as was done for enums).

wlav commented 1 year ago

Changed in repo: a nullptr return now gives an std::function object that is uncallable and evaluates to False.