v2.h 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514
  1. #pragma once
  2. #include <library/cpp/pybind/method.h>
  3. #include <library/cpp/pybind/typedesc.h>
  4. #include <library/cpp/pybind/module.h>
  5. #include <util/generic/hash.h>
  6. #include <util/generic/hash_set.h>
  7. #include <util/generic/string.h>
  8. namespace NPyBind {
  9. #define DEFINE_CONVERTERS_IMPL(TClass) \
  10. PyObject* BuildPyObject(typename TClass::TBase&& base) { \
  11. return TClass::BuildPyObject(std::move(base)); \
  12. } \
  13. PyObject* BuildPyObject(const typename TClass::TBase& base) { \
  14. return TClass::BuildPyObject(base); \
  15. }
  16. #define DEFINE_CONVERTERS(function) DEFINE_CONVERTERS_IMPL(TFunctionResult<decltype(function)>)
  17. #define DEFINE_TRANSFORMERS_IMPL(TClass) \
  18. template <> \
  19. bool ::NPyBind::FromPyObject<typename TClass::TBase*>(PyObject * obj, typename TClass::TBase * &res) { \
  20. res = TClass::CastToObject(obj); \
  21. return res != nullptr; \
  22. } \
  23. template <> \
  24. bool ::NPyBind::FromPyObject<typename TClass::TBase const*>(PyObject * obj, typename TClass::TBase const*& res) { \
  25. res = TClass::CastToObject(obj); \
  26. return res != nullptr; \
  27. }
  28. #define DEFINE_TRANSFORMERS(function) DEFINE_TRANSFORMERS_IMPL(TFunctionResult<decltype(function)>)
  29. namespace Detail {
  30. struct IGetContextBase {
  31. virtual ~IGetContextBase() = default;
  32. };
  33. } //Detail
  34. struct TPyModuleDefinition {
  35. static void InitModule(const TString& name);
  36. static TPyModuleDefinition& GetModule();
  37. TString Name;
  38. NPyBind::TPyObjectPtr M;
  39. THashMap<TString, PyTypeObject*> ClassName2Type;
  40. THashMap<TString, Detail::IGetContextBase*> Class2ContextGetter;
  41. };
  42. namespace Detail {
  43. // Manages modules lifecycle
  44. // IMPORTANT!!! Don't use it in PyBind v1 environment, it will lead to inconsistent state of v1 module
  45. // UnnamedModule-> new unnamed module stub, this stub become current module. In this case you can add functions to it
  46. // InitModuleWithName -> convert unnamed module into named one, now you can switch to it in switch, this module remains current
  47. // SwitchToModule switches to the particular module in registry, this module becomes current.
  48. class TPyModuleRegistry {
  49. private:
  50. TPyModuleRegistry();
  51. TPyModuleRegistry(const TPyModuleRegistry&) = delete;
  52. TPyModuleRegistry& operator=(TPyModuleRegistry&) = delete;
  53. public:
  54. static TPyModuleRegistry& Get() {
  55. static TPyModuleRegistry registry;
  56. return registry;
  57. }
  58. TPyModuleDefinition& GetCurrentModule() {
  59. if (!CurrentModule) {
  60. GetUnnamedModule();
  61. }
  62. return *CurrentModule;
  63. }
  64. TPyModuleDefinition& GetUnnamedModule() {
  65. if (!UnnamedModule) {
  66. UnnamedModule = TPyModuleDefinition();
  67. CurrentModule = const_cast<TPyModuleDefinition*>(UnnamedModule.Get());
  68. }
  69. return *UnnamedModule;
  70. }
  71. TPyModuleDefinition& InitModuleWithName(const TString& name) {
  72. if (!UnnamedModule) {
  73. GetUnnamedModule();
  74. }
  75. Name2Def[name] = *UnnamedModule;
  76. UnnamedModule.Clear();
  77. CurrentModule = &Name2Def[name];
  78. return *CurrentModule;
  79. }
  80. TPyModuleDefinition& SwitchToModuleByName(const TString& name) {
  81. Y_ENSURE(Name2Def.contains(name));
  82. Y_ENSURE(UnnamedModule.Empty());
  83. CurrentModule = &Name2Def[name];
  84. return *CurrentModule;
  85. }
  86. private:
  87. TPyModuleDefinition* CurrentModule = nullptr;
  88. TMaybe<TPyModuleDefinition> UnnamedModule;//
  89. THashMap<TString, TPyModuleDefinition> Name2Def;
  90. };
  91. }//Detail
  92. inline void TPyModuleDefinition::InitModule(const TString& name) {
  93. Detail::TPyModuleRegistry::Get().GetUnnamedModule() = TPyModuleDefinition{name, TModuleHolder::Instance().InitModule(name), {}, {}};
  94. Detail::TPyModuleRegistry::Get().InitModuleWithName(name);
  95. }
  96. inline TPyModuleDefinition& TPyModuleDefinition::GetModule() {
  97. return Detail::TPyModuleRegistry::Get().GetCurrentModule();
  98. }
  99. namespace Detail {
  100. template <class TPythonType>
  101. struct TNameCtx {
  102. TString ClassShortName;
  103. static TNameCtx& GetNameCtx() {
  104. static TNameCtx result;
  105. return result;
  106. }
  107. };
  108. template <class TBase>
  109. struct TContextImpl {
  110. PyTypeObject* ParentType = nullptr;
  111. TString ClassShortName;
  112. TString ClassFullName;
  113. TString ClassDescription;
  114. TVector<std::pair<TString, typename TPythonTypeAttributes<TBase>::TCallerPtr>> ListCallers;
  115. TVector<std::pair<TString, typename TPythonTypeAttributes<TBase>::TGetterPtr>> ListGetters;
  116. TVector<std::pair<TString, typename TPythonTypeAttributes<TBase>::TSetterPtr>> ListSetters;
  117. };
  118. template <class TObject>
  119. struct IGetContext: public IGetContextBase {
  120. virtual ~IGetContext() = default;
  121. virtual const TContextImpl<TObject>& GetContext() const = 0;
  122. };
  123. template <typename THolderClass, typename TBaseClass, bool ShouldEnable, typename=std::enable_if_t<!ShouldEnable || !std::is_default_constructible_v<TBaseClass>>>
  124. THolderClass* DoInitPureObject(const TVector<TString>&) {
  125. ythrow yexception() << "Can't create this object in pure mode from python";
  126. }
  127. template <typename THolderClass, typename TBaseClass, bool ShouldEnable, typename=std::enable_if_t<ShouldEnable && std::is_default_constructible_v<TBaseClass>>, typename=void>
  128. THolderClass* DoInitPureObject(const TVector<TString>&) {
  129. return new THolderClass(MakeHolder<TBaseClass>());
  130. }
  131. template <typename T>
  132. PyTypeObject* GetParentType(const TPyModuleDefinition& m) {
  133. auto shortName = Detail::TNameCtx<T>::GetNameCtx().ClassShortName;
  134. auto it = m.ClassName2Type.find(shortName);
  135. return (it == m.ClassName2Type.end()) ? nullptr : it->second;
  136. }
  137. template <>
  138. PyTypeObject* GetParentType<void>(const TPyModuleDefinition&);
  139. template <bool InitEnabled>
  140. void UpdateClassNamesInModule(TPyModuleDefinition& M, const TString& name, PyTypeObject* pythonType);
  141. template <bool InitEnabled>
  142. void UpdateGetContextInModule(TPyModuleDefinition& M, const TString& name, IGetContextBase* base);
  143. }
  144. template <class TParentPyClass_=void>
  145. struct TPyParentClassTraits {
  146. using TParentPyClass = TParentPyClass_;
  147. };
  148. template <bool InitEnabled_, class TParentPyClass_=void>
  149. struct TPyClassConfigTraits: public TPyParentClassTraits<TParentPyClass_> {
  150. constexpr static bool InitEnabled = InitEnabled_;
  151. constexpr static bool RawInit = false;
  152. };
  153. template <class TParentPyClass_=void>
  154. struct TPyClassRawInitConfigTraits: public TPyParentClassTraits<TParentPyClass_> {
  155. constexpr static bool InitEnabled = true;
  156. constexpr static bool RawInit = true;
  157. };
  158. template <typename TBaseClass, typename TPyClassConfigTraits, typename... ConstructorArgs>
  159. class TPyClass {
  160. public:
  161. using TBase = TBaseClass;
  162. private:
  163. using TThisClass = TPyClass<TBaseClass, TPyClassConfigTraits, ConstructorArgs...>;
  164. using TContext = Detail::TContextImpl<TBase>;
  165. struct THolder {
  166. ::THolder<TBase> Holder;
  167. THolder(::THolder<TBase>&& right)
  168. : Holder(std::move(right))
  169. {
  170. }
  171. THolder(TBase&& right)
  172. : Holder(MakeHolder<TBase>(std::move(right)))
  173. {
  174. }
  175. };
  176. class TSelectedTraits: public NPyBind::TPythonType<THolder, TBase, TSelectedTraits> {
  177. private:
  178. using TParent = NPyBind::TPythonType<THolder, TBase, TSelectedTraits>;
  179. friend TParent;
  180. public:
  181. TSelectedTraits()
  182. : TParent(TThisClass::GetContext().ClassFullName.data(), TThisClass::GetContext().ClassDescription.data(), TThisClass::GetContext().ParentType)
  183. {
  184. for (const auto& caller : TThisClass::GetContext().ListCallers) {
  185. TParent::AddCaller(caller.first, caller.second);
  186. }
  187. for (const auto& getter : TThisClass::GetContext().ListGetters) {
  188. TParent::AddGetter(getter.first, getter.second);
  189. }
  190. for (const auto& setter : TThisClass::GetContext().ListSetters) {
  191. TParent::AddSetter(setter.first, setter.second);
  192. }
  193. }
  194. static TBase* GetObject(const THolder& holder) {
  195. return holder.Holder.Get();
  196. }
  197. static THolder* DoInitObject(PyObject* args, PyObject* kwargs) {
  198. if constexpr (TPyClassConfigTraits::InitEnabled) {
  199. if constexpr (TPyClassConfigTraits::RawInit) {
  200. static_assert(sizeof...(ConstructorArgs) == 0, "Do not pass construction args if use RawInit.");
  201. return new THolder(::MakeHolder<TBase>(args, kwargs));
  202. } else {
  203. if (args && (!PyTuple_Check(args) || PyTuple_Size(args) != sizeof...(ConstructorArgs))) {
  204. ythrow yexception() << "Method takes " << sizeof...(ConstructorArgs) << " arguments, " << PyTuple_Size(args) << " provided";
  205. }
  206. ::THolder<TBaseClass> basePtr{Apply([](auto&&... unpackedArgs) {return new TBase(std::forward<decltype(unpackedArgs)>(unpackedArgs)...); }, GetArguments<ConstructorArgs...>(args))};
  207. return new THolder(std::move(basePtr));
  208. }
  209. } else {
  210. ythrow yexception() << "Can't create this object from python";
  211. }
  212. }
  213. static THolder* DoInitPureObject(const TVector<TString>& properties) {
  214. return Detail::DoInitPureObject<THolder, TBase, TPyClassConfigTraits::InitEnabled>(properties);
  215. }
  216. static TBase* CastToObject(PyObject* obj) {
  217. return TParent::CastToObject(obj);
  218. }
  219. static PyTypeObject* GetType() {
  220. return TParent::GetPyTypePtr();
  221. }
  222. };
  223. class TContextHolder: public Detail::IGetContext<TBaseClass> {
  224. public:
  225. static TContextHolder& GetContextHolder() {
  226. static TContextHolder holder;
  227. return holder;
  228. }
  229. TContext& GetContext() {
  230. return Context;
  231. }
  232. const TContext& GetContext() const override {
  233. return Context;
  234. }
  235. private:
  236. TContext Context;
  237. };
  238. template <class TDerivedClass, class TSuperClass>
  239. class TCallerWrapper: public TBaseMethodCaller<TDerivedClass> {
  240. public:
  241. explicit TCallerWrapper(TSimpleSharedPtr<const TBaseMethodCaller<TSuperClass>> baseCaller)
  242. : BaseCaller(baseCaller) {
  243. Y_ENSURE(BaseCaller);
  244. }
  245. bool CallMethod(PyObject* owner, TDerivedClass* self, PyObject* args, PyObject* kwargs, PyObject*& res) const override {
  246. return BaseCaller->CallMethod(owner, static_cast<TSuperClass*>(self), args, kwargs, res);
  247. }
  248. private:
  249. TSimpleSharedPtr<const TBaseMethodCaller<TSuperClass>> BaseCaller;
  250. };
  251. template <class TDerivedClass, class TSuperClass>
  252. class TSetterWrapper: public TBaseAttrSetter<TDerivedClass> {
  253. public:
  254. explicit TSetterWrapper(TSimpleSharedPtr<TBaseAttrSetter<TSuperClass>> baseSetter)
  255. : BaseSetter(baseSetter) {
  256. Y_ENSURE(BaseSetter);
  257. }
  258. bool SetAttr(PyObject* owner, TDerivedClass& self, const TString& attr, PyObject* val) override {
  259. return BaseSetter->SetAttr(owner, static_cast<TSuperClass&>(self), attr, val);
  260. }
  261. private:
  262. TSimpleSharedPtr<TBaseAttrSetter<TSuperClass>> BaseSetter;
  263. };
  264. template <class TDerivedClass, class TSuperClass>
  265. class TGetterWrapper: public TBaseAttrGetter<TDerivedClass> {
  266. public:
  267. explicit TGetterWrapper(TSimpleSharedPtr<const TBaseAttrGetter<TSuperClass>> baseGetter)
  268. : BaseGetter(baseGetter) {
  269. Y_ENSURE(BaseGetter);
  270. }
  271. bool GetAttr(PyObject* owner, const TDerivedClass& self, const TString& attr, PyObject*& res) const override {
  272. return BaseGetter->GetAttr(owner, static_cast<const TSuperClass&>(self), attr, res);
  273. }
  274. private:
  275. TSimpleSharedPtr<const TBaseAttrGetter<TSuperClass>> BaseGetter;
  276. };
  277. template <class TSuperClass, typename=std::enable_if_t<!std::is_same_v<TSuperClass, void>>>
  278. void ReloadAttrsFromBase() {
  279. auto shortName = Detail::TNameCtx<TSuperClass>::GetNameCtx().ClassShortName;
  280. if (!M.Class2ContextGetter.count(shortName)) {
  281. return;
  282. }
  283. auto callerBasePtr = M.Class2ContextGetter[shortName];
  284. if (auto getContextPtr = dynamic_cast<const Detail::IGetContext<TSuperClass>*>(callerBasePtr)) {
  285. auto& ctx = getContextPtr->GetContext();
  286. auto getUniqueNames = [](const auto& collection) {
  287. THashSet<TString> uniqueNames;
  288. for (const auto& elem : collection) {
  289. uniqueNames.insert(elem.first);
  290. }
  291. return uniqueNames;
  292. };
  293. auto uniqueCallerNames = getUniqueNames(GetContext().ListCallers);
  294. using TConcreteCallerWrapper = TCallerWrapper<TBaseClass, TSuperClass>;
  295. for (const auto& caller : ctx.ListCallers) {
  296. if (uniqueCallerNames.contains(caller.first)) {
  297. continue;
  298. }
  299. GetContext().ListCallers.push_back(std::make_pair(caller.first, MakeSimpleShared<TConcreteCallerWrapper>(caller.second)));
  300. }
  301. auto uniqueGettersNames = getUniqueNames(GetContext().ListGetters);
  302. using TConcreteGetterWrapper = TGetterWrapper<TBaseClass, TSuperClass>;
  303. for (const auto& getter : ctx.ListGetters) {
  304. if (uniqueGettersNames.contains(getter.first)) {
  305. continue;
  306. }
  307. GetContext().ListGetters.push_back(std::make_pair(getter.first, MakeSimpleShared<TConcreteGetterWrapper>(getter.second)));
  308. }
  309. auto uniqueSetterNames = getUniqueNames(GetContext().ListSetters);
  310. using TConcreteSetterWrapper = TSetterWrapper<TBaseClass, TSuperClass>;
  311. for (auto& setter : ctx.ListSetters) {
  312. if (uniqueSetterNames.contains(setter.first)) {
  313. continue;
  314. }
  315. GetContext().ListSetters.push_back(std::make_pair(setter.first, MakeSimpleShared<TConcreteSetterWrapper>(setter.second)));
  316. }
  317. }
  318. }
  319. template <class TSuperClass, typename=std::enable_if_t<std::is_same_v<TSuperClass, void>>, typename=void>
  320. void ReloadAttrsFromBase() {
  321. }
  322. void CompleteImpl() {
  323. ReloadAttrsFromBase<typename TPyClassConfigTraits::TParentPyClass>();
  324. TSelectedTraits::Instance().Register(M.M, GetContext().ClassShortName);
  325. }
  326. static TContext& GetContext() {
  327. return TContextHolder::GetContextHolder().GetContext();
  328. }
  329. friend struct Detail::TContextImpl<TBase>;//instead of context
  330. friend struct THolder;
  331. friend class TSelectedTraits;
  332. using TCallerFunc = std::function<bool(PyObject*, TBaseClass*, PyObject*, PyObject*, PyObject*&)>;
  333. class TFuncCallerWrapper: public TBaseMethodCaller<TBaseClass> {
  334. public:
  335. explicit TFuncCallerWrapper(TCallerFunc func)
  336. : Func(func) {
  337. Y_ENSURE(func);
  338. }
  339. bool CallMethod(PyObject* owner, TBaseClass* self, PyObject* args, PyObject* kwargs, PyObject*& res) const override {
  340. return Func(owner, self, args, kwargs, res);
  341. }
  342. private:
  343. mutable TCallerFunc Func;
  344. };
  345. public:
  346. TPyClass(const TString& name, const TString& descr = "")
  347. : M(TPyModuleDefinition::GetModule())
  348. {
  349. Detail::UpdateClassNamesInModule<TPyClassConfigTraits::InitEnabled>(M, name, TSelectedTraits::GetType());
  350. Detail::UpdateGetContextInModule<TPyClassConfigTraits::InitEnabled>(M, name, &TContextHolder::GetContextHolder());
  351. GetContext().ClassFullName = TString::Join(M.Name, ".", name);
  352. GetContext().ClassShortName = name;
  353. GetContext().ClassDescription = descr;
  354. GetContext().ParentType = Detail::GetParentType<typename TPyClassConfigTraits::TParentPyClass>(M);
  355. Detail::TNameCtx<TBaseClass>::GetNameCtx().ClassShortName = name;
  356. }
  357. template <typename TMemberFuction, typename = std::enable_if_t<std::is_member_function_pointer_v<TMemberFuction>>, typename=std::enable_if_t<!TIsPointerToConstMemberFunction<TMemberFuction>::value>>
  358. TThisClass& Def(const TString& name, TMemberFuction t) {
  359. GetContext().ListCallers.push_back(std::make_pair(name, CreateMethodCaller<TBase>(t)));
  360. return *this;
  361. }
  362. template <typename TMemberFuction, typename = std::enable_if_t<std::is_member_function_pointer_v<TMemberFuction>>, typename=std::enable_if_t<TIsPointerToConstMemberFunction<TMemberFuction>::value>, typename=void>
  363. TThisClass& Def(const TString& name, TMemberFuction t) {
  364. GetContext().ListCallers.push_back(std::make_pair(name, CreateConstMethodCaller<TBase>(t)));
  365. return *this;
  366. }
  367. template <typename TMemberObject, typename = std::enable_if_t<std::is_member_object_pointer_v<TMemberObject>>>
  368. TThisClass& Def(const TString& name, TMemberObject t) {
  369. GetContext().ListGetters.push_back(std::make_pair(name, CreateAttrGetter<TBase>(t)));
  370. GetContext().ListSetters.push_back(std::make_pair(name, CreateAttrSetter<TBase>(t)));
  371. return *this;
  372. }
  373. template <typename TResultType, typename... Args>
  374. TThisClass& DefByFunc(const TString& name, std::function<TResultType(TBaseClass&, Args...)> func) {
  375. GetContext().ListCallers.push_back(std::make_pair(name, CreateFunctorCaller<TBase, TResultType, Args...>(func)));
  376. return *this;
  377. }
  378. TThisClass& DefByFunc(const TString& name, TCallerFunc origFunc) {
  379. GetContext().ListCallers.push_back(std::make_pair(name, MakeSimpleShared<TFuncCallerWrapper>(origFunc)));
  380. return *this;
  381. }
  382. template <typename TMemberObject>
  383. TThisClass& DefReadonly(const TString& name, TMemberObject t, std::enable_if_t<std::is_member_object_pointer<TMemberObject>::value>* = nullptr) {
  384. GetContext().ListGetters.push_back(std::make_pair(name, CreateAttrGetter<TBase>(t)));
  385. return *this;
  386. }
  387. template <typename TMethodGetter, typename TMethodSetter, typename=std::enable_if_t<std::is_member_function_pointer_v<TMethodGetter> && std::is_member_function_pointer_v<TMethodSetter>>>
  388. TThisClass& AsProperty(const TString& name, TMethodGetter getter, TMethodSetter setter) {
  389. GetContext().ListGetters.push_back(std::make_pair(name, CreateMethodAttrGetter<TBase>(getter)));
  390. GetContext().ListSetters.push_back(std::make_pair(name, CreateMethodAttrSetter<TBase>(setter)));
  391. return *this;
  392. }
  393. template <typename TMethodGetter, typename TMethodSetter, typename=std::enable_if_t<!std::is_member_function_pointer_v<TMethodGetter> && !std::is_member_function_pointer_v<TMethodSetter>>>
  394. TThisClass& AsPropertyByFunc(const TString& name, TMethodGetter getter, TMethodSetter setter) {
  395. GetContext().ListGetters.push_back(std::make_pair(name, CreateFunctorAttrGetter<TBase>(getter)));
  396. GetContext().ListSetters.push_back(std::make_pair(name, CreateFunctorAttrSetter<TBase>(setter)));
  397. return *this;
  398. }
  399. template <typename TMethodGetter, typename=std::enable_if_t<std::is_member_function_pointer_v<TMethodGetter>>>
  400. TThisClass& AsProperty(const TString& name, TMethodGetter getter) {
  401. GetContext().ListGetters.push_back(std::make_pair(name, CreateMethodAttrGetter<TBase>(getter)));
  402. return *this;
  403. }
  404. template <typename TMethodGetter>
  405. TThisClass& AsPropertyByFunc(const TString& name, TMethodGetter getter) {
  406. GetContext().ListGetters.push_back(std::make_pair(name, CreateFunctorAttrGetter<TBase>(getter)));
  407. return *this;
  408. }
  409. TThisClass& Complete() {
  410. if (!Completed) {
  411. CompleteImpl();
  412. Completed = true;
  413. }
  414. return *this;
  415. }
  416. public:
  417. static PyObject* BuildPyObject(TBase&& base) {
  418. return NPyBind::BuildPyObject(TSelectedTraits::Instance().CreatePyObject(new THolder(std::move(base))));
  419. }
  420. static PyObject* BuildPyObject(const TBase& base) {
  421. return NPyBind::BuildPyObject(TSelectedTraits::Instance().CreatePyObject(new THolder(TBase(base)))); // WARN - copy
  422. }
  423. static TBase* CastToObject(PyObject* obj) {
  424. return TSelectedTraits::CastToObject(obj);
  425. }
  426. private:
  427. TPyModuleDefinition& M;
  428. bool Completed = false;
  429. };
  430. template <typename TFunctionSignature, TFunctionSignature function>
  431. void DefImpl(const TString& name, const TString& descr = "") {
  432. NPyBind::TModuleHolder::Instance().AddModuleMethod<TModuleMethodCaller<TFunctionSignature, function>::Call>(name, descr);
  433. }
  434. #define DefFunc(NAME, FUNC) NPyBind::DefImpl<decltype(FUNC), FUNC>(NAME)
  435. #define DefFuncDescr(NAME, FUNC, DESCR) NPyBind::DefImpl<decltype(FUNC), FUNC>(NAME, DESCR)
  436. };