Skip to content

Commit 603fefd

Browse files
wlavaaronj0
authored andcommitted
add support for ctypes' function pointers
Co-authored-by: Aaron Jomy <[email protected]>
1 parent 9f00529 commit 603fefd

File tree

4 files changed

+91
-38
lines changed

4 files changed

+91
-38
lines changed

src/Converters.cxx

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -123,13 +123,15 @@ struct CPyCppyy_tagPyCArgObject { // not public (but stable; note that olde
123123
#define ct_c_fcomplex 21
124124
#define ct_c_complex 22
125125
#define ct_c_pointer 23
126-
#define NTYPES 24
126+
#define ct_c_funcptr 24
127+
#define NTYPES 25
127128

128129
static std::array<const char*, NTYPES> gCTypesNames = {
129130
"c_bool", "c_char", "c_wchar", "c_byte", "c_ubyte", "c_short", "c_ushort", "c_uint16",
130131
"c_int", "c_uint", "c_uint32", "c_long", "c_ulong", "c_longlong", "c_ulonglong",
131132
"c_float", "c_double", "c_longdouble",
132-
"c_char_p", "c_wchar_p", "c_void_p", "c_fcomplex", "c_complex", "_Pointer" };
133+
"c_char_p", "c_wchar_p", "c_void_p", "c_fcomplex", "c_complex",
134+
"_Pointer", "_CFuncPtr" };
133135
static std::array<PyTypeObject*, NTYPES> gCTypesTypes;
134136
static std::array<PyTypeObject*, NTYPES> gCTypesPtrTypes;
135137

@@ -2655,6 +2657,12 @@ static void* PyFunction_AsCPointer(PyObject* pyobject,
26552657
// fall-through, with calling through Python
26562658
}
26572659

2660+
if (PyObject_IsInstance(pyobject, (PyObject*)GetCTypesType(ct_c_funcptr))) {
2661+
// ctypes function pointer
2662+
void* fptr = *(void**)((CPyCppyy_tagCDataObject*)pyobject)->b_ptr;
2663+
return fptr;
2664+
}
2665+
26582666
if (PyCallable_Check(pyobject)) {
26592667
// generic python callable: create a C++ wrapper function
26602668
void* wpraddress = nullptr;

src/TemplateProxy.cxx

Lines changed: 4 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -16,39 +16,6 @@
1616

1717
namespace CPyCppyy {
1818

19-
//- helper for ctypes conversions --------------------------------------------
20-
static PyObject* TC2CppName(PyObject* pytc, const char* cpd, bool allow_voidp)
21-
{
22-
const char* name = nullptr;
23-
if (CPyCppyy_PyText_Check(pytc)) {
24-
char tc = ((char*)CPyCppyy_PyText_AsString(pytc))[0];
25-
switch (tc) {
26-
case '?': name = "bool"; break;
27-
case 'c': name = "char"; break;
28-
case 'b': name = "char"; break;
29-
case 'B': name = "unsigned char"; break;
30-
case 'h': name = "short"; break;
31-
case 'H': name = "unsigned short"; break;
32-
case 'i': name = "int"; break;
33-
case 'I': name = "unsigned int"; break;
34-
case 'l': name = "long"; break;
35-
case 'L': name = "unsigned long"; break;
36-
case 'q': name = "long long"; break;
37-
case 'Q': name = "unsigned long long"; break;
38-
case 'f': name = "float"; break;
39-
case 'd': name = "double"; break;
40-
case 'g': name = "long double"; break;
41-
case 'z': // special case for C strings, ignore cpd
42-
return CPyCppyy_PyText_FromString(std::string{"const char*"}.c_str());
43-
default: name = (allow_voidp ? "void*" : nullptr); break;
44-
}
45-
}
46-
47-
if (name)
48-
return CPyCppyy_PyText_FromString((std::string{name}+cpd).c_str());
49-
return nullptr;
50-
}
51-
5219
//----------------------------------------------------------------------------
5320
TemplateInfo::TemplateInfo() : fPyClass(nullptr), fNonTemplated(nullptr),
5421
fTemplated(nullptr), fLowPriority(nullptr), fDoc(nullptr)
@@ -109,6 +76,7 @@ PyObject* TemplateProxy::Instantiate(const std::string& fname,
10976
std::string proto = "";
11077

11178
#if PY_VERSION_HEX >= 0x03080000
79+
// adjust arguments for self if this is a rebound global function
11280
bool isNS = (((CPPScope*)fTI->fPyClass)->fFlags & CPPScope::kIsNamespace);
11381
if (!isNS && !fSelf && CPyCppyy_PyArgs_GET_SIZE(args, nargsf)) {
11482
args += 1;
@@ -138,7 +106,7 @@ PyObject* TemplateProxy::Instantiate(const std::string& fname,
138106
PyErr_Clear();
139107
}
140108

141-
PyObject* pyptrname = TC2CppName(pytc, ptrdef.c_str(), true);
109+
PyObject* pyptrname = Utility::CT2CppName(pytc, ptrdef.c_str(), true);
142110
if (pyptrname) {
143111
PyTuple_SET_ITEM(tpArgs, i, pyptrname);
144112
bArgSet = true;
@@ -152,14 +120,14 @@ PyObject* TemplateProxy::Instantiate(const std::string& fname,
152120
if (!bArgSet) pytc = PyObject_GetAttr(itemi, PyStrings::gCTypesType);
153121

154122
if (!bArgSet && pytc) {
155-
PyObject* pyactname = TC2CppName(pytc, "&", false);
123+
PyObject* pyactname = Utility::CT2CppName(pytc, "&", false);
156124
if (!pyactname) {
157125
// _type_ of a pointer to c_type is that type, which will have a type
158126
PyObject* newpytc = PyObject_GetAttr(pytc, PyStrings::gCTypesType);
159127
Py_DECREF(pytc);
160128
pytc = newpytc;
161129
if (pytc) {
162-
pyactname = TC2CppName(pytc, "*", false);
130+
pyactname = Utility::CT2CppName(pytc, "*", false);
163131
} else
164132
PyErr_Clear();
165133
}

src/Utility.cxx

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -522,6 +522,7 @@ static bool AddTypeName(std::string& tmpl_name, PyObject* tn, PyObject* arg,
522522
}
523523

524524
if (arg && PyCallable_Check(arg)) {
525+
// annotated/typed Python function
525526
PyObject* annot = PyObject_GetAttr(arg, PyStrings::gAnnotations);
526527
if (annot) {
527528
if (PyDict_Check(annot) && 1 < PyDict_Size(annot)) {
@@ -543,6 +544,7 @@ static bool AddTypeName(std::string& tmpl_name, PyObject* tn, PyObject* arg,
543544
tpn << ')';
544545
tmpl_name.append(tpn.str());
545546

547+
Py_DECREF(annot);
546548
return true;
547549

548550
} else
@@ -552,6 +554,40 @@ static bool AddTypeName(std::string& tmpl_name, PyObject* tn, PyObject* arg,
552554
} else
553555
PyErr_Clear();
554556

557+
// ctypes function pointer
558+
PyObject* argtypes = PyObject_GetAttrString(arg, "argtypes");
559+
PyObject* ret = PyObject_GetAttrString(arg, "restype");
560+
if (argtypes && ret) {
561+
std::ostringstream tpn;
562+
PyObject* pytc = PyObject_GetAttr(ret, PyStrings::gCTypesType);
563+
tpn << CT2CppNameS(pytc, false)
564+
<< " (*)(";
565+
Py_DECREF(pytc);
566+
567+
for (Py_ssize_t i = 0; i < PySequence_Length(argtypes); ++i) {
568+
if (i) tpn << ", ";
569+
PyObject* item = PySequence_GetItem(argtypes, i);
570+
pytc = PyObject_GetAttr(item, PyStrings::gCTypesType);
571+
tpn << CT2CppNameS(pytc, false);
572+
Py_DECREF(pytc);
573+
Py_DECREF(item);
574+
}
575+
576+
tpn << ')';
577+
tmpl_name.append(tpn.str());
578+
579+
Py_DECREF(ret);
580+
Py_DECREF(argtypes);
581+
582+
return true;
583+
584+
} else {
585+
PyErr_Clear();
586+
Py_XDECREF(ret);
587+
Py_XDECREF(argtypes);
588+
}
589+
590+
// callable C++ type (e.g. std::function)
555591
PyObject* tpName = PyObject_GetAttr(arg, PyStrings::gCppName);
556592
if (tpName) {
557593
const char* cname = CPyCppyy_PyText_AsString(tpName);
@@ -858,6 +894,36 @@ std::vector<Cpp::TemplateArgInfo> CPyCppyy::Utility::GetTemplateArgsTypes(
858894
return types;
859895
}
860896

897+
std::string CPyCppyy::Utility::CT2CppNameS(PyObject* pytc, bool allow_voidp)
898+
{
899+
// helper to convert ctypes' `_type_` info to the equivalent C++ name
900+
const char* name = "";
901+
if (CPyCppyy_PyText_Check(pytc)) {
902+
char tc = ((char*)CPyCppyy_PyText_AsString(pytc))[0];
903+
switch (tc) {
904+
case '?': name = "bool"; break;
905+
case 'c': name = "char"; break;
906+
case 'b': name = "char"; break;
907+
case 'B': name = "unsigned char"; break;
908+
case 'h': name = "short"; break;
909+
case 'H': name = "unsigned short"; break;
910+
case 'i': name = "int"; break;
911+
case 'I': name = "unsigned int"; break;
912+
case 'l': name = "long"; break;
913+
case 'L': name = "unsigned long"; break;
914+
case 'q': name = "long long"; break;
915+
case 'Q': name = "unsigned long long"; break;
916+
case 'f': name = "float"; break;
917+
case 'd': name = "double"; break;
918+
case 'g': name = "long double"; break;
919+
case 'z': name = "const char*"; break;
920+
default: name = (allow_voidp ? "void*" : nullptr); break;
921+
}
922+
}
923+
924+
return name;
925+
}
926+
861927
//----------------------------------------------------------------------------
862928
static inline bool check_scope(const std::string& name)
863929
{

src/Utility.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,17 @@ std::string ConstructTemplateArgs(
4242
std::vector<Cpp::TemplateArgInfo> GetTemplateArgsTypes(
4343
PyObject* scope, PyObject* tpArgs, PyObject* args = nullptr, ArgPreference = kNone, int argoff = 0, int* pcnt = nullptr);
4444

45+
std::string CT2CppNameS(PyObject* pytc, bool allow_voidp);
46+
inline PyObject* CT2CppName(PyObject* pytc, const char* cpd, bool allow_voidp)
47+
{
48+
const std::string& name = CT2CppNameS(pytc, allow_voidp);
49+
if (!name.empty()) {
50+
if (name == "const char*") cpd = "";
51+
return CPyCppyy_PyText_FromString((std::string{name}+cpd).c_str());
52+
}
53+
return nullptr;
54+
}
55+
4556
// helper for generating callbacks
4657
void ConstructCallbackPreamble(const std::string& retType,
4758
const std::vector<std::string>& argtypes, std::ostringstream& code);

0 commit comments

Comments
 (0)