12#ifndef CLASSDESC_PYTHON_BUFFER_H
13#define CLASSDESC_PYTHON_BUFFER_H
15#include <json_pack_base.h>
16#include <RESTProcess_base.h>
21#include "pythonCAPI.h"
26#define CLASSDESC_PY_EXCEPTION_ABSORB(ret) \
27 catch (const std::exception& ex) \
29 PyErr_SetString(PyExc_RuntimeError, ex.what()); \
34 PyErr_SetString(PyExc_RuntimeError, "Unknown exception"); \
41 inline std::map<std::string, RESTProcess_t*>& registries()
43 static std::map<std::string, RESTProcess_t*> registries;
51 PyObject* pythonModule=
nullptr;
56 template <
class T>
inline typename enable_if<And<is_integral<T>, Not<is_same<T,bool>>>,
PyObject*>::T
57 newPyObject(
const T& x) {
return PyLong_FromLong(x);}
60 inline PyObject*
newPyObject(
const string& x) {
return PyUnicode_FromString(x.c_str());}
61 inline PyObject*
newPyObject(
const char* x) {
return PyUnicode_FromString(x);}
64 inline PyObject* newPyObjectJson(
const json5_parser::mValue& j) {
67 case json5_parser::obj_type:
69 auto pyObject=PyDict_New();
70 for (
auto& i: j.get_obj())
71 PyDict_SetItemString(pyObject, i.first.c_str(), newPyObjectJson(i.second));
74 case json5_parser::array_type:
76 auto arr=j.get_array();
77 auto pyObject=PyList_New(arr.size());
78 for (
size_t i=0; i<arr.size(); ++i)
79 PyList_SetItem(pyObject, i, newPyObjectJson(arr[i]));
82 case json5_parser::str_type:
84 case json5_parser::bool_type:
86 case json5_parser::int_type:
88 case json5_parser::real_type:
90 case json5_parser::null_type:
102 auto pyObject=PyList_New(x.size());
103 for (
size_t i=0; i<x.size(); ++i)
111 template <
class T>
inline typename enable_if<
113 Not<is_arithmetic<T>>,
119 return newPyObjectJson(tmp);
128 PyObjectRef()=
default;
129 PyObjectRef(
PyObject* ref): ref(ref) {
130 assert(!ref || Py_REFCNT(ref));
132 ~PyObjectRef() {Py_XDECREF(ref);}
133 PyObjectRef(
const PyObjectRef& x)=
delete;
134 PyObjectRef& operator=(
const PyObjectRef& x)=
delete;
135 PyObjectRef(PyObjectRef&& x): ref(x.ref) {
137 assert(!ref || Py_REFCNT(ref));
139 PyObjectRef& operator=(PyObjectRef&& x) {
142 assert(!ref || Py_REFCNT(ref));
146 operator PyObject*()
const {
return ref;}
147 operator bool()
const {
return ref;}
148 PyObject* release() {
auto tmp=ref; ref=
nullptr;
return tmp;}
154 using Array=std::deque<PythonBuffer>;
155 PythonBuffer() {Py_INCREF(pyObject);}
157 explicit PythonBuffer(RESTProcessType::Type type) {
160 case RESTProcessType::null: pyObject=Py_None; Py_INCREF(pyObject);
return;
161 case RESTProcessType::boolean: pyObject=Py_False; Py_INCREF(pyObject);
return;
162 case RESTProcessType::int_number: pyObject=PyLong_FromLong(0);
return;
163 case RESTProcessType::float_number: pyObject=PyFloat_FromDouble(0.0);
return;
164 case RESTProcessType::string: pyObject=PyUnicode_FromString(
"");
return;
165 case RESTProcessType::object: pyObject=PyDict_New();
return;
166 case RESTProcessType::array: pyObject=PyList_New(0);
return;
170 explicit PythonBuffer(
const T& x): pyObject(
newPyObject(x)) {}
171 explicit PythonBuffer(
const json_pack_t& x): pyObject(newPyObjectJson(x)) {}
172 explicit PythonBuffer(
const json5_parser::mArray& x): pyObject(newPyObjectJson(
json_pack_t(x))) {}
173 explicit PythonBuffer(
PyObject* x) {
181 ~PythonBuffer() {Py_DECREF(pyObject);}
183 PythonBuffer(
const PythonBuffer& x): pyObject(x.pyObject) {Py_INCREF(pyObject);}
184 PythonBuffer& operator=(
const PythonBuffer& x)
185 {pyObject=x.pyObject; Py_INCREF(pyObject);
return *
this;}
190 if (PySequence_Check(pyObject))
191 for (Py_ssize_t i=0; i<PySequence_Size(pyObject); ++i)
192 r.push_back(PythonBuffer(PySequence_GetItem(pyObject,i)));
197 RESTProcessType::Type type()
const {
198 if (pyObject==Py_None)
return RESTProcessType::null;
199 if (PyBool_Check(pyObject))
return RESTProcessType::boolean;
200 if (PyLong_Check(pyObject))
return RESTProcessType::int_number;
201 if (PyFloat_Check(pyObject))
return RESTProcessType::float_number;
202 if (PyUnicode_Check(pyObject))
return RESTProcessType::string;
203 if (PySequence_Check(pyObject))
return RESTProcessType::array;
204 return RESTProcessType::object;
208 template <
class T>
typename enable_if<is_integral<T>, T>::T
209 get()
const {
return PyLong_AsLongLong(pyObject);}
210 template <
class T>
typename enable_if<is_floating_point<T>,T>::T
211 get()
const {
return PyFloat_AsDouble(pyObject);}
213 template <
class T>
typename enable_if<is_string<T>, T>::T
214 get()
const {
return PyUnicode_AsUTF8AndSize(pyObject,
nullptr);}
216 template <
class T>
typename enable_if<is_same<T,json_pack_t>, T>::T
220 case RESTProcessType::null:
221 return json_pack_t(json5_parser::mValue::null);
222 case RESTProcessType::boolean:
223 return json_pack_t(get<bool>());
224 case RESTProcessType::int_number:
225 return json_pack_t(get<int64_t>());
226 case RESTProcessType::float_number:
227 return json_pack_t(get<double>());
228 case RESTProcessType::string:
229 return json_pack_t(get<string>());
230 case RESTProcessType::array:
232 json_pack_t::Array arr;
233 for (
auto& i:
array())
234 arr.push_back(i.get<json_pack_t>());
235 return json_pack_t(arr);
237 case RESTProcessType::object:
239 json_pack_t::Object obj;
240 if (PyMapping_Check(pyObject))
242 PyObjectRef keyValues(PyMapping_Items(pyObject));
243 for (Py_ssize_t i=0; i<PySequence_Size(keyValues); ++i)
245 PyObjectRef keyValue=PySequence_GetItem(keyValues, i);
246 PyObjectRef keyRef(PyObject_Str(PySequence_GetItem(keyValue,0)));
247 string keyStr=PyUnicode_AsUTF8AndSize(keyRef,
nullptr);
248 obj[keyStr]=PythonBuffer(PySequence_GetItem(keyValue,1)).get<json_pack_t>();
253 auto dir(PyObject_Dir(pyObject));
254 for (Py_ssize_t i=0; dir && i<PySequence_Size(dir); ++i)
256 auto key=PySequence_GetItem(dir, i);
257 PyObjectRef keyRef(PyObject_Str(key));
258 string keyStr=PyUnicode_AsUTF8AndSize(keyRef,
nullptr);
259 obj[keyStr]=PythonBuffer(PyObject_GetAttr(pyObject, key)).get<json_pack_t>();
262 return json_pack_t(obj);
266 return json_pack_t();
269 template <
class T>
void push_back(
const T& x) {
270 if (PyList_Check(pyObject))
273 if (PyList_Append(pyObject, pb.pyObject))
274 Py_INCREF(pb.pyObject);
281 std::string str()
const {
283 return PyUnicode_AsUTF8AndSize(pyStr,
nullptr);
294 {x=b.get<
string>()[0];
return b;}
305 {x=b.get<
T>();
return b;}
317 {x=b.get<
string>();
return b;}
328 {
auto tmp=b.get<
string>(); x=enum_keys<T>()(tmp);
return b;}
341 if (b.type()!=RESTProcessType::array)
return b;
345 typename T::value_type e;
373 throw std::runtime_error(
"cannot unpack to char*, please use string instead");
411 CppPyObject() {memset(
this,0,
sizeof(
PyObject));}
418 struct CppWrapper:
public CppPyObject
422 std::map<string, PyObjectRef> methods;
423 static CppWrapper* create(
const RPPtr& command,
bool special) {
return new CppWrapper(command,special);}
424 CppWrapper(CppWrapper&&)=
default;
426 static PyObject* returnResult(
const RPPtr& result)
430 switch (resultBuffer.type())
432 case RESTProcessType::object:
433 case RESTProcessType::array:
435 auto r=CppWrapper::create(result,
false);
437 attachRegistryObjects(result->list(),*r,
".");
438 return ref.release();
442 if (PyErr_Occurred())
444 return pyResult.release();
450 auto methods=self->command->list();
451 std::vector<string> methodList;
452 for (
auto& i: methods)
453 if (!i.first.empty())
454 methodList.push_back(i.first);
457 CLASSDESC_PY_EXCEPTION_ABSORB(
nullptr);
464 CLASSDESC_PY_EXCEPTION_ABSORB(
nullptr);
471 CLASSDESC_PY_EXCEPTION_ABSORB(
nullptr);
478 CLASSDESC_PY_EXCEPTION_ABSORB(
nullptr);
483 self->command->insert(
PythonBuffer(value).get<json_pack_t>());
486 CLASSDESC_PY_EXCEPTION_ABSORB(
nullptr);
491 self->command->erase(
PythonBuffer(key).get<json_pack_t>());
494 CLASSDESC_PY_EXCEPTION_ABSORB(
nullptr);
502 CLASSDESC_PY_EXCEPTION_ABSORB(
nullptr);
509 CLASSDESC_PY_EXCEPTION_ABSORB(
nullptr);
512 CppWrapper(
const RPPtr& command,
bool special);
513 CppWrapper(
const CppWrapper&)=
delete;
514 void operator=(
const CppWrapper&)=
delete;
518 {
"_list",(PyCFunction)CppWrapper::list,METH_NOARGS,
"List of members/methods of this object"},
519 {
"_properties",(PyCFunction)CppWrapper::properties,METH_NOARGS,
"Return current state of this object"},
520 {
"_signature",(PyCFunction)CppWrapper::signature,METH_NOARGS,
"Signatures of all method overloads"},
521 {
"_type",(PyCFunction)CppWrapper::type,METH_NOARGS,
"Object type"},
522 {
"insert",(PyCFunction)CppWrapper::insert,METH_O,
"Insert element into container"},
523 {
"erase",(PyCFunction)CppWrapper::erase,METH_O,
"Remove element from container"},
524 {
"keys",(PyCFunction)CppWrapper::keys,METH_NOARGS,
"List keys of a map/set"},
525 {
"contains",(PyCFunction)CppWrapper::contains,METH_O,
"Retrun true/false whether map/set contains a key"},
526 {
nullptr,
nullptr, 0,
nullptr}
532 static bool containerSpecialCommand(
const std::string& command)
534 static const std::set<std::string> specialCommands{
".@elem",
".@elemNoThrow"};
535 auto n=command.rfind(
'.');
536 return n!=string::npos && specialCommands.count(command.substr(n));
541 auto cppWrapper=
static_cast<CppWrapper*
>(self);
542 auto command=cppWrapper->command;
543 if (command->isObject() && PySequence_Size(args)==0)
544 return CppWrapper::properties(cppWrapper,
nullptr);
545 PythonBuffer arguments(PySequence_Size(args)? RESTProcessType::array: RESTProcessType::null);
546 std::string remainder;
547 if (cppWrapper->special && PySequence_Size(args))
549 remainder=write(
PythonBuffer(PySequence_GetItem(args,0)).get<json_pack_t>());
551 for (Py_ssize_t i=0; i<PySequence_Size(args); ++i)
552 arguments.push_back(PySequence_GetItem(args,i));
553 if (PyErr_Occurred())
557 return CppWrapper::returnResult(command->process(remainder,arguments.get<
json_pack_t>()));
559 CLASSDESC_PY_EXCEPTION_ABSORB(
nullptr);
562 static void deleteCppWrapper(
PyObject* x) {
569 auto cppWrapper=
static_cast<CppWrapper*
>(self);
570 auto i=cppWrapper->methods.find(PyUnicode_AsUTF8AndSize(attr,
nullptr));
571 if (i!=cppWrapper->methods.end())
573 Py_INCREF(i->second);
578 auto methods=cppWrapper->command->list();
579 auto attribute=methods.find(
string(
".")+PyUnicode_AsUTF8AndSize(attr,
nullptr));
580 if (attribute!=methods.end())
581 return CppWrapper::create(attribute->second,
false);
583 return PyObject_GenericGetAttr(self,attr);
585 CLASSDESC_PY_EXCEPTION_ABSORB(
nullptr);
589 auto cppWrapper=
static_cast<CppWrapper*
>(self);
590 auto key=PyUnicode_AsUTF8AndSize(name,
nullptr);
594 assert(Py_REFCNT(cppWrapper->methods[key]));
597 cppWrapper->methods.erase(key);
603 static Py_ssize_t size(
PyObject* self)
607 auto cppWrapper=
static_cast<CppWrapper*
>(self);
608 return cppWrapper->command->size();
610 CLASSDESC_PY_EXCEPTION_ABSORB(0);
615 auto cppWrapper=
static_cast<CppWrapper*
>(self);
618 return CppWrapper::returnResult(cppWrapper->command->getElem(
PythonBuffer(key).get<json_pack_t>()));
620 CLASSDESC_PY_EXCEPTION_ABSORB(
nullptr);
625 auto cppWrapper=
static_cast<CppWrapper*
>(self);
628 cppWrapper->command->setElem(
PythonBuffer(key).get<json_pack_t>(),
632 catch (
const std::exception& ex)
634 PyErr_SetString(PyExc_RuntimeError, ex.what());
641 mp_subscript=getElem;
642 mp_ass_subscript=setElem;
650 tp_name=
"CppWrapperType";
651 tp_methods=cppWrapperMethods;
654 tp_dealloc=deleteCppWrapper;
655 tp_getattro=getAttro;
656 tp_setattro=setAttro;
657 tp_as_mapping=&mappingMethods;
662 inline CppWrapperType& CppWrapperTypeSingleton() {
663 static CppWrapperType cppWrapperType;
664 return cppWrapperType;
667 inline CppWrapper::CppWrapper(
const RPPtr& command,
bool special): special(special), command(command) {
669 ob_type=&CppWrapperTypeSingleton();
672 inline void attachRegistryObjects(
const RESTProcess_t& registry, CppWrapper&
object,
const std::string& prefix)
674 for (
auto& i: registry)
676 std::string_view key(i.first);
677 if (i.second && key.substr(0,prefix.length())==prefix)
679 std::string method(key.substr(prefix.length()));
680 if (method.find(
'.')!=std::string::npos)
continue;
681 bool special=method[0]==
'@';
682 auto methodObject=CppWrapper::create(i.second, special);
683 attachRegistryObjects(registry, *methodObject, i.first+
".");
684 if (special) method[0]=
'_';
685 PyObject_SetAttrString(&
object,method.c_str(),methodObject);
691 struct RESTProcessFactory:
public RESTProcessFunction<F>
693 RESTProcessFactory(F f): RESTProcessFunction<F>(f) {}
694 RPPtr
process(
const string& remainder,
const REST_PROCESS_BUFFER& arguments)
override
698 return makeRESTProcessHeapObject
699 (std::move(functional::callOnBuffer(argBuf,RESTProcessFunction<F>::f)));
704 template <
class T,
class...
Args>
struct DeclareType
706 static std::unique_ptr<T> factory(
Args... args)
707 {
return std::make_unique<T>(std::forward<Args>(args)...);}
708 DeclareType(
const string& typeName) {
722 assert(pythonModule);
723 for (
auto& i: registry)
725 if (!i.second || i.first.empty() || i.first.find(
'.')!=std::string::npos || i.first[0]==
'@')
continue;
726 auto pyObject=CppWrapper::create(i.second,
false);
727 attachRegistryObjects(registry, *pyObject, i.first+
".");
728 PyModule_AddObject(pythonModule, i.first.c_str(), pyObject);
732 PyObjectRef enummer=PyDict_New();
733 auto enumList=registry.process(
"@enum.@list",{})->asBuffer();
734 for (
auto& i: enumList.array())
736 string name=i.get_str();
737 PyDict_SetItemString(enummer, name.c_str(),
738 newPyObjectJson(registry.process(
"@enum."+name,{})->asBuffer()));
740 PyModule_AddObject(pythonModule,
"enum", enummer.release());
743 inline PyObject* registerObject(PyObject* self, PyObject* args) {
744 auto modName=PyModule_GetName(self);
745 if (!modName || !registries().count(modName)) {
746 PyErr_SetString(PyExc_RuntimeError,
"Invalid module name");
749 if (PySequence_Size(args)<2) {
750 PyErr_SetString(PyExc_RuntimeError,
"Missing arguments");
753 auto object=PySequence_GetItem(args,0);
754 if (!
object || Py_TYPE(
object)!=&CppWrapperTypeSingleton()) {
755 PyErr_SetString(PyExc_RuntimeError,
"First argument not a CppWrapper");
758 auto name=PyUnicode_AsUTF8AndSize(PySequence_GetItem(args,1),
nullptr);
760 PyErr_SetString(PyExc_RuntimeError,
"Second argument not a string");
763 (*registries()[modName])[name]=
static_cast<CppWrapper*
>(
object)->command;
764 if (PyModule_AddObject(self,name,
object)==0)
771namespace classdesc_access
784 {
"register",classdesc::registerObject,METH_VARARGS,
"Register a C++ wrapped object with the registry"},
785 {
nullptr,
nullptr,0,
nullptr}
792#define CLASSDESC_PYTHON_MODULE(name) \
793 PyMODINIT_FUNC PyInit_##name() \
795 static PyModuleDef module_##name = { \
796 PyModuleDef_HEAD_INIT, \
798 "Python interface to C++ code: "#name, \
806 using namespace classdesc; \
807 registries()[#name]=®istry; \
808 pythonModule=PyModule_Create(&module_##name); \
809 if (PyErr_Occurred()) PyErr_Print(); \
810 if (pythonModule) initModule(pythonModule,registry); \
811 return pythonModule; \
817#define CLASSDESC_ADD_GLOBAL(object) \
818 static int add_global_##object=(classdesc::RESTProcess(classdesc::registry,#object,object), 0);
820#define CLASSDESC_ADD_FUNCTION(object) \
821 static int add_global_##object=(classdesc::RESTProcess(classdesc::registry,#object,&object), 0);
829#define CLASSDESC_DECLARE_TYPE(type,...) \
830 static classdesc::DeclareType<type __VA_OPT__(,) __VA_ARGS__> declareType_##type(#type);
Definition RESTProcess_base.h:1127
Definition pythonBuffer.h:125
Definition pythonBuffer.h:152
PyObjectRef getPyObject() const
returns a copy of the controlled object. Reference counter is incremented.
Definition pythonBuffer.h:279
Array array() const
return type conforms to the sequence concept
Definition pythonBuffer.h:188
Definition json_pack_base.h:99
Contains definitions related to classdesc functionality.
PyObject * newPyObject(const bool &x)
utility python object constructors
Definition pythonBuffer.h:55
Definition pythonCAPI.h:233
Definition pythonCAPI.h:225
Definition pythonCAPI.h:97
Definition pythonCAPI.h:143
Definition classdesc.h:420
Definition RESTProcess_epilogue.h:159
Definition pythonBuffer.h:419
Definition classdesc.h:405
Definition pythonBuffer.h:692
RPPtr process(const string &remainder, const REST_PROCESS_BUFFER &arguments) override
Definition pythonBuffer.h:694
REST processor registry.
Definition RESTProcess_base.h:351
controlled template specialisation: stolen from boost::enable_if.
Definition classdesc.h:282
Definition json_pack_epilogue.h:92