Skip to content

Commit 66eb416

Browse files
committed
[CPyCppyy] Sync up with compiler-research forks
1 parent 042f601 commit 66eb416

3 files changed

Lines changed: 149 additions & 12 deletions

File tree

bindings/pyroot/cppyy/CPyCppyy/src/CPPInstance.cxx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -405,7 +405,7 @@ PyCFunction &CPPInstance::ReduceMethod() {
405405
return reducer;
406406
}
407407

408-
PyObject *op_reduce(PyObject *self, PyObject * args)
408+
PyObject *op_reduce(PyObject *self, PyObject *args)
409409
{
410410
auto &reducer = CPPInstance::ReduceMethod();
411411
if (!reducer) {

bindings/pyroot/cppyy/CPyCppyy/src/CPPInstance.h

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@
1616

1717
// Standard
1818
#include <utility>
19-
#include <functional>
2019
#include <vector>
2120

2221

@@ -87,7 +86,7 @@ class CPPInstance {
8786
// implementation of the __reduce__ method: doesn't wrap any function by
8887
// default but can be re-assigned by libraries that add C++ object
8988
// serialization support, like ROOT
90-
static std::function<PyObject *(PyObject *)> &ReduceMethod();
89+
static PyCFunction &ReduceMethod();
9190

9291

9392
private:

bindings/pyroot/cppyy/CPyCppyy/src/Converters.cxx

Lines changed: 147 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,9 @@
2929
#include <sstream>
3030
#include <cstddef>
3131
#include <string_view>
32+
#if __cplusplus >= 202002L
33+
#include <span>
34+
#endif
3235

3336
// codecvt does not exist for gcc4.8.5 and is in principle deprecated; it is
3437
// only used in py2 for char -> wchar_t conversion for std::wstring; if not
@@ -1292,7 +1295,8 @@ bool CPyCppyy::CStringConverter::SetArg(
12921295

12931296
// verify (too long string will cause truncation, no crash)
12941297
if (fMaxSize != std::string::npos && fMaxSize < fBuffer.size())
1295-
PyErr_Warn(PyExc_RuntimeWarning, (char*)"string too long for char array (truncated)");
1298+
if (PyErr_WarnEx(PyExc_RuntimeWarning, (char*)"string too long for char array (truncated)", 1) < 0)
1299+
return false;
12961300

12971301
if (!ctxt->fPyContext) {
12981302
// use internal buffer as workaround
@@ -1337,7 +1341,8 @@ bool CPyCppyy::CStringConverter::ToMemory(PyObject* value, void* address, PyObje
13371341

13381342
// verify (too long string will cause truncation, no crash)
13391343
if (fMaxSize != std::string::npos && fMaxSize < (std::string::size_type)len)
1340-
PyErr_Warn(PyExc_RuntimeWarning, (char*)"string too long for char array (truncated)");
1344+
if (PyErr_WarnEx(PyExc_RuntimeWarning, (char*)"string too long for char array (truncated)", 1) < 0)
1345+
return false;
13411346

13421347
// if address is available, and it wasn't set by this converter, assume a byte-wise copy;
13431348
// otherwise assume a pointer copy (this relies on the converter to be used for properties,
@@ -1414,7 +1419,8 @@ bool CPyCppyy::WCStringConverter::ToMemory(PyObject* value, void* address, PyObj
14141419

14151420
// verify (too long string will cause truncation, no crash)
14161421
if (fMaxSize != std::wstring::npos && fMaxSize < (std::wstring::size_type)len)
1417-
PyErr_Warn(PyExc_RuntimeWarning, (char*)"string too long for wchar_t array (truncated)");
1422+
if (PyErr_WarnEx(PyExc_RuntimeWarning, (char*)"string too long for wchar_t array (truncated)", 1) < 0)
1423+
return false;
14181424

14191425
Py_ssize_t res = -1;
14201426
if (fMaxSize != std::wstring::npos)
@@ -1473,7 +1479,10 @@ bool CPyCppyy::name##Converter::ToMemory(PyObject* value, void* address, PyObjec
14731479
\
14741480
/* verify (too long string will cause truncation, no crash) */ \
14751481
if (fMaxSize != std::wstring::npos && maxbytes < len) { \
1476-
PyErr_Warn(PyExc_RuntimeWarning, (char*)"string too long for "#type" array (truncated)");\
1482+
if (PyErr_WarnEx(PyExc_RuntimeWarning, (char*)"string too long for "#type" array (truncated)", 1) < 0) { \
1483+
Py_DECREF(bstr); \
1484+
return false; \
1485+
} \
14771486
len = maxbytes; \
14781487
} \
14791488
\
@@ -1637,6 +1646,78 @@ bool CPyCppyy::VoidArrayConverter::ToMemory(PyObject* value, void* address, PyOb
16371646
return true;
16381647
}
16391648

1649+
#if __cplusplus >= 202002L
1650+
1651+
namespace CPyCppyy {
1652+
1653+
class StdSpanConverter : public InstanceConverter {
1654+
public:
1655+
StdSpanConverter(std::string const &typeName, Cppyy::TCppType_t klass, bool keepControl = false)
1656+
: InstanceConverter{klass, keepControl}, fTypeName{typeName}
1657+
{
1658+
}
1659+
1660+
~StdSpanConverter()
1661+
{
1662+
if (fHasBuffer) {
1663+
PyBuffer_Release(&fBufinfo);
1664+
}
1665+
}
1666+
1667+
bool SetArg(PyObject *, Parameter &, CallContext * = nullptr) override;
1668+
bool HasState() override { return true; }
1669+
1670+
private:
1671+
std::string fTypeName;
1672+
std::span<std::size_t> fBuffer;
1673+
bool fHasBuffer = false;
1674+
Py_buffer fBufinfo;
1675+
};
1676+
1677+
} // namespace CPyCppyy
1678+
1679+
//----------------------------------------------------------------------------
1680+
bool CPyCppyy::StdSpanConverter::SetArg(PyObject *pyobject, Parameter &para, CallContext *ctxt)
1681+
{
1682+
auto typecodeFound = Utility::TypecodeMap().find(fTypeName);
1683+
1684+
// attempt to get buffer if the C++ type maps to a buffer type
1685+
if (typecodeFound == Utility::TypecodeMap().end() || !PyObject_CheckBuffer(pyobject)) {
1686+
// Fall back to regular InstanceConverter
1687+
return this->InstanceConverter::SetArg(pyobject, para, ctxt);
1688+
}
1689+
1690+
Py_ssize_t buflen = 0;
1691+
char typecode = typecodeFound->second;
1692+
memset(&fBufinfo, 0, sizeof(Py_buffer));
1693+
1694+
if (PyObject_GetBuffer(pyobject, &fBufinfo, PyBUF_FORMAT) == 0) {
1695+
if (!strchr(fBufinfo.format, typecode)) {
1696+
PyErr_Format(PyExc_TypeError,
1697+
"buffer has incompatible type: expected '%c' for C++ type '%s', but got format '%s'", typecode,
1698+
fTypeName.c_str(), fBufinfo.format ? fBufinfo.format : "<null>");
1699+
PyBuffer_Release(&fBufinfo);
1700+
return false;
1701+
}
1702+
buflen = Utility::GetBuffer(pyobject, typecode, 1, para.fValue.fVoidp, false);
1703+
}
1704+
1705+
// ok if buffer exists (can't perform any useful size checks)
1706+
if (para.fValue.fVoidp && buflen != 0) {
1707+
// We assume the layout for any std::span<T> is the same, and just use
1708+
// std::span<std::size_t> as a placeholder. Not elegant, but works.
1709+
fBuffer = std::span<std::size_t>{(std::size_t *)para.fValue.fVoidp, static_cast<std::size_t>(buflen)};
1710+
fHasBuffer = true;
1711+
para.fValue.fVoidp = &fBuffer;
1712+
para.fTypeCode = 'V';
1713+
return true;
1714+
}
1715+
1716+
return false;
1717+
}
1718+
1719+
#endif // __cplusplus >= 202002L
1720+
16401721

16411722
//----------------------------------------------------------------------------
16421723
#define CPPYY_IMPL_ARRAY_CONVERTER(name, ctype, type, code, suffix) \
@@ -3177,11 +3258,25 @@ bool CPyCppyy::InitializerListConverter::SetArg(
31773258
#endif
31783259
}
31793260

3261+
namespace CPyCppyy {
3262+
3263+
// raising converter to take out overloads
3264+
class NotImplementedConverter : public Converter {
3265+
public:
3266+
NotImplementedConverter(PyObject *errorType, std::string const &message) : fErrorType{errorType}, fMessage{message} {}
3267+
bool SetArg(PyObject*, Parameter&, CallContext* = nullptr) override;
3268+
private:
3269+
PyObject *fErrorType;
3270+
std::string fMessage;
3271+
};
3272+
3273+
} // namespace CPyCppyy
3274+
31803275
//----------------------------------------------------------------------------
31813276
bool CPyCppyy::NotImplementedConverter::SetArg(PyObject*, Parameter&, CallContext*)
31823277
{
31833278
// raise a NotImplemented exception to take a method out of overload resolution
3184-
PyErr_SetString(PyExc_NotImplementedError, "this method cannot (yet) be called");
3279+
PyErr_SetString(fErrorType, fMessage.c_str());
31853280
return false;
31863281
}
31873282

@@ -3247,6 +3342,14 @@ CPyCppyy::Converter* CPyCppyy::CreateConverter(const std::string& fullType, cdim
32473342
const std::string& cpd = TypeManip::compound(resolvedType);
32483343
std::string realType = TypeManip::clean_type(resolvedType, false, true);
32493344

3345+
// mutable pointer references (T*&) are incompatible with Python's object model
3346+
if (cpd == "*&") {
3347+
return new NotImplementedConverter{PyExc_TypeError,
3348+
"argument type '" + resolvedType + "' is not supported: non-const references to pointers (T*&) allow a"
3349+
" function to replace the pointer itself. Python cannot represent this safely. Consider changing the"
3350+
" C++ API to return the new pointer or use a wrapper"};
3351+
}
3352+
32503353
// accept unqualified type (as python does not know about qualifiers)
32513354
h = gConvFactories.find((isConst ? "const " : "") + realType + cpd);
32523355
if (h != gConvFactories.end())
@@ -3331,6 +3434,31 @@ CPyCppyy::Converter* CPyCppyy::CreateConverter(const std::string& fullType, cdim
33313434
}
33323435
}
33333436

3437+
// FIXME: Taken from ROOT, update this to use CppInterOp for span check and extracting value type
3438+
#if __cplusplus >= 202002L
3439+
//-- special case: std::span
3440+
pos = resolvedType.find("span<");
3441+
if (pos == 0 /* no std:: */ || pos == 5 /* with std:: */ ||
3442+
pos == 6 /* const no std:: */ || pos == 11 /* const with std:: */ ) {
3443+
3444+
auto pos1 = realType.find('<');
3445+
auto pos21 = realType.find(','); // for the case there are more template args
3446+
auto pos22 = realType.find('>');
3447+
auto len = std::min(pos21 - pos1, pos22 - pos1) - 1;
3448+
std::string value_type = realType.substr(pos1+1, len);
3449+
3450+
// strip leading "const "
3451+
const std::string cprefix = "const ";
3452+
if (value_type.compare(0, cprefix.size(), cprefix) == 0) {
3453+
value_type = value_type.substr(cprefix.size());
3454+
}
3455+
3456+
std::string span_type = "std::span<" + value_type + ">";
3457+
3458+
return new StdSpanConverter{value_type, Cppyy::GetScope(span_type)};
3459+
}
3460+
#endif
3461+
33343462
// converters for known C++ classes and default (void*)
33353463
Converter* result = nullptr;
33363464
if (Cppyy::TCppScope_t klass = Cppyy::GetFullScope(realType)) {
@@ -3372,7 +3500,7 @@ CPyCppyy::Converter* CPyCppyy::CreateConverter(const std::string& fullType, cdim
33723500
if (h != gConvFactories.end())
33733501
return (h->second)(dims);
33743502
// else, unhandled moves
3375-
result = new NotImplementedConverter(failure_msg);
3503+
result = new NotImplementedConverter{PyExc_NotImplementedError, "this method cannot (yet) be called"};
33763504
}
33773505

33783506
if (!result && h != gConvFactories.end())
@@ -3385,7 +3513,8 @@ CPyCppyy::Converter* CPyCppyy::CreateConverter(const std::string& fullType, cdim
33853513
else if (!cpd.empty())
33863514
result = new VoidArrayConverter(/* keepControl= */ true, failure_msg); // "user knows best"
33873515
else
3388-
result = new NotImplementedConverter(failure_msg); // fails on use
3516+
// fails on use
3517+
result = new NotImplementedConverter{PyExc_NotImplementedError, "this method cannot (yet) be called"};
33893518
}
33903519

33913520
return result;
@@ -3429,6 +3558,14 @@ CPyCppyy::Converter* CPyCppyy::CreateConverter(Cppyy::TCppType_t type, cdims_t d
34293558
std::string realTypeStr = Cppyy::GetTypeAsString(realType);
34303559
std::string realUnresolvedTypeStr = TypeManip::clean_type(fullType, false, true);
34313560

3561+
// mutable pointer references (T*&) are incompatible with Python's object model
3562+
if (cpd == "*&") {
3563+
return new NotImplementedConverter{PyExc_TypeError,
3564+
"argument type '" + resolvedTypeStr + "' is not supported: non-const references to pointers (T*&) allow a"
3565+
" function to replace the pointer itself. Python cannot represent this safely. Consider changing the"
3566+
" C++ API to return the new pointer or use a wrapper"};
3567+
}
3568+
34323569
// accept unqualified type (as python does not know about qualifiers)
34333570
h = gConvFactories.find((isConst ? "const " : "") + realTypeStr + cpd);
34343571
if (h != gConvFactories.end()) {
@@ -3579,7 +3716,7 @@ CPyCppyy::Converter* CPyCppyy::CreateConverter(Cppyy::TCppType_t type, cdims_t d
35793716
if (h != gConvFactories.end())
35803717
return (h->second)(dims);
35813718
// else, unhandled moves
3582-
result = new NotImplementedConverter(failure_msg);
3719+
result = new NotImplementedConverter{PyExc_NotImplementedError, "this method cannot (yet) be called"};
35833720
}
35843721

35853722
if (!result && h != gConvFactories.end()) {
@@ -3592,7 +3729,8 @@ CPyCppyy::Converter* CPyCppyy::CreateConverter(Cppyy::TCppType_t type, cdims_t d
35923729
} else if (!cpd.empty()) {
35933730
result = new VoidArrayConverter(/* keepControl= */ true, failure_msg); // "user knows best"
35943731
} else {
3595-
result = new NotImplementedConverter(failure_msg); // fails on use
3732+
// fails on use
3733+
result = new NotImplementedConverter{PyExc_NotImplementedError, "this method cannot (yet) be called"};
35963734
}
35973735
}
35983736

0 commit comments

Comments
 (0)