From f25389cda2da75ba225d1a663d30dea9660af31b Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Sat, 7 Oct 2023 00:02:05 -0400 Subject: [PATCH 01/17] initial commit --- CMakeLists.txt | 67 ++++++++++++++++++++++++++++++++++ README.md | 13 +++++++ cmake/xo_pyutilConfig.cmake.in | 4 ++ include/xo/pyutil/pyutil.hpp | 30 +++++++++++++++ 4 files changed, 114 insertions(+) create mode 100644 CMakeLists.txt create mode 100644 README.md create mode 100644 cmake/xo_pyutilConfig.cmake.in create mode 100644 include/xo/pyutil/pyutil.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 00000000..ea3f4237 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,67 @@ +# pyutil/CMakeLists.txt + +cmake_minimum_required(VERSION 3.10) + +project(xo_pyutil VERSION 0.1) +enable_language(CXX) + +# common XO cmake macros (see github:Rconybea/xo-cmake) +include(xo_macros/xo_cxx) +#include(xo_macros/code-coverage) # very little to unit test here + +# ---------------------------------------------------------------- +# unit test setup + +#enable_testing() +## enable code coverage for all executables+libraries +## (when configured with -DCODE_COVERAGE=ON) +## +#add_code_coverage() +#add_code_coverage_all_targets( +# EXCLUDE +# /nix/store/* +# ${PROJECT_SOURCE_DIR}/utest/*) + +# ---------------------------------------------------------------- +# bespoke (usually temporary) c++ settings + +set(PROJECT_CXX_FLAGS "") +#set(PROJECT_CXX_FLAGS "-fconcepts-diagnostics-depth=2") +add_definitions(${PROJECT_CXX_FLAGS}) + +# ---------------------------------------------------------------- +# c++ settings + +xo_toplevel_compile_options() + +# ---------------------------------------------------------------- +# external dependencies +# +# set CMAKE_INSTALL_PREFIX to analog of /usr +# to use .cmake assistants from /usr/lib/cmake/indentlog +# +# xo_dependency(..) + +# ---------------------------------------------------------------- + +#add_subdirectory(example) +#add_subdirectory(utest) + +# ---------------------------------------------------------------- +# output targets + +set(SELF_LIB xo_pyutil) +add_library(${SELF_LIB} INTERFACE) +xo_include_headeronly_options2(${SELF_LIB}) + +# ---------------------------------------------------------------- +# standard install + provide find_package() support + +xo_install_library2(${SELF_LIB}) +xo_install_include_tree() +xo_export_cmake_config(${PROJECT_NAME} ${PROJECT_VERSION} ${PROJECT_NAME}Targets) + +# ---------------------------------------------------------------- +# install any additional components + +#install(TARGETS ex1 DESTINATION bin/${PROJECT_NAME}/example) diff --git a/README.md b/README.md new file mode 100644 index 00000000..d9bbae39 --- /dev/null +++ b/README.md @@ -0,0 +1,13 @@ +# pybind11 utilities for XO projects + +# to build + install locally + +``` +$ cd xo-pyutil +$ mkdir build +$ cd build +$ PREFIX=/usr/local # for example +$ cmake -DCMAKE_MODULE_PATH=${PREFIX}/share/cmake -DCMAKE_PREFIX_PATH=$(PREFIX) -DCMAKE_INSTALL_PREFIX=${PREFIX} .. +$ make +$ make install +``` diff --git a/cmake/xo_pyutilConfig.cmake.in b/cmake/xo_pyutilConfig.cmake.in new file mode 100644 index 00000000..9c15f36a --- /dev/null +++ b/cmake/xo_pyutilConfig.cmake.in @@ -0,0 +1,4 @@ +@PACKAGE_INIT@ + +include("${CMAKE_CURRENT_LIST_DIR}/@PROJECT_NAME@Targets.cmake") +check_required_components("@PROJECT_NAME@") diff --git a/include/xo/pyutil/pyutil.hpp b/include/xo/pyutil/pyutil.hpp new file mode 100644 index 00000000..a450f5d2 --- /dev/null +++ b/include/xo/pyutil/pyutil.hpp @@ -0,0 +1,30 @@ +/* @file pyutil.hpp + * + * utility stuff to be used across multiple pybind11 .cpp files + */ + +#pragma once + +#include "xo/refcnt/Refcounted.hpp" +#include "xo/refcnt/Unowned.hpp" +#include + +/* xo::ref::intrusive_ptr is an intrusively-reference-counted pointer. + * always safe to create one from a T* p + * (since refcount is directly accessible from p) + * + * Need declaration like this before any pybind11 bindings + * that expose an object of types like + * (a) intrusive_ptr or + * (b) T * / T const * / T & / T const & to python. + * If this were not done, pybind11 would by default use unique_ptr> + * (ok but inefficient) or unique_ptr (fatal!) + */ +PYBIND11_DECLARE_HOLDER_TYPE(T, xo::ref::intrusive_ptr, true); + +/* xo::ref::unowned_ptr is an unmanaged pointer. + * use this for immortal objects that pybind11 must not delete. + */ +PYBIND11_DECLARE_HOLDER_TYPE(T, xo::ref::unowned_ptr, true); + +/* end pyutil.hpp */ From 19fe68dc0e873b0e6a5eb8e757fcca2b5d0becfe Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Sun, 8 Oct 2023 13:54:55 -0400 Subject: [PATCH 02/17] + .gitignore --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..49f711e2 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +# typical build directories +build +ccov From 4daef0555000f41b8485a765ac85adfe216c9714 Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Sun, 8 Oct 2023 14:21:40 -0400 Subject: [PATCH 03/17] build: + testing (even though no tests) for consistency --- CMakeLists.txt | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index ea3f4237..c7e32668 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -7,20 +7,20 @@ enable_language(CXX) # common XO cmake macros (see github:Rconybea/xo-cmake) include(xo_macros/xo_cxx) -#include(xo_macros/code-coverage) # very little to unit test here +include(xo_macros/code-coverage) # very little to unit test here # ---------------------------------------------------------------- # unit test setup -#enable_testing() +enable_testing() ## enable code coverage for all executables+libraries ## (when configured with -DCODE_COVERAGE=ON) ## -#add_code_coverage() -#add_code_coverage_all_targets( -# EXCLUDE -# /nix/store/* -# ${PROJECT_SOURCE_DIR}/utest/*) +add_code_coverage() +add_code_coverage_all_targets( + EXCLUDE + /nix/store/* + ${PROJECT_SOURCE_DIR}/utest/*) # ---------------------------------------------------------------- # bespoke (usually temporary) c++ settings From 4468aa0ee173b482c48b0bac3656cc49ef14ff9c Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Tue, 17 Oct 2023 15:06:30 -0400 Subject: [PATCH 04/17] doc: README improvements --- README.md | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index d9bbae39..3aa5ee81 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,12 @@ # pybind11 utilities for XO projects -# to build + install locally +## Getting Started + +### build + install dependencies + +- see [github/Rconybea/xo-cmake](https://github.com/Rconybea/xo-cmake) + +### to build + install locally ``` $ cd xo-pyutil @@ -11,3 +17,9 @@ $ cmake -DCMAKE_MODULE_PATH=${PREFIX}/share/cmake -DCMAKE_PREFIX_PATH=$(PREFIX) $ make $ make install ``` + +### LSP support +``` +$ cd xo-pyutil +$ ln -s build/compile_commands.json # lsp will look for compile_commands.json in the root of the source tree +``` From 5b3f712e0fb01151c7b36038faee1a94652ef672 Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Thu, 19 Oct 2023 16:52:29 -0400 Subject: [PATCH 05/17] build: + symlink-centric install --- CMakeLists.txt | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index c7e32668..b5894f9a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -50,15 +50,16 @@ xo_toplevel_compile_options() # ---------------------------------------------------------------- # output targets -set(SELF_LIB xo_pyutil) -add_library(${SELF_LIB} INTERFACE) -xo_include_headeronly_options2(${SELF_LIB}) +set(SELF_SHORTNAME pyutil) +set(SELF_LIB xo_${SELF_SHORTNAME}) +xo_add_headeronly_library(${SELF_LIB}) +#xo_include_headeronly_options2(${SELF_LIB}) # ---------------------------------------------------------------- # standard install + provide find_package() support -xo_install_library2(${SELF_LIB}) -xo_install_include_tree() +xo_install_library5(${SELF_LIB} ${SELF_SHORTNAME} ${PROJECT_NAME}Targets) +#xo_install_include_tree() xo_export_cmake_config(${PROJECT_NAME} ${PROJECT_VERSION} ${PROJECT_NAME}Targets) # ---------------------------------------------------------------- From e548a3e8ed063767d36d095a527620082421e94b Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Sun, 22 Oct 2023 14:56:52 -0400 Subject: [PATCH 06/17] build: handle cmake target called xo_pyutil instead of pyutil --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index b5894f9a..f85dd952 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -52,7 +52,7 @@ xo_toplevel_compile_options() set(SELF_SHORTNAME pyutil) set(SELF_LIB xo_${SELF_SHORTNAME}) -xo_add_headeronly_library(${SELF_LIB}) +xo_add_headeronly_library5(${SELF_LIB} ${SELF_SHORTNAME}) #xo_include_headeronly_options2(${SELF_LIB}) # ---------------------------------------------------------------- From 6f1b0aca760a719a173fe4a60ba97c8c798cd8e5 Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Sun, 22 Oct 2023 17:27:05 -0400 Subject: [PATCH 07/17] build: use streamlined xo-cmake api --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index f85dd952..b5894f9a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -52,7 +52,7 @@ xo_toplevel_compile_options() set(SELF_SHORTNAME pyutil) set(SELF_LIB xo_${SELF_SHORTNAME}) -xo_add_headeronly_library5(${SELF_LIB} ${SELF_SHORTNAME}) +xo_add_headeronly_library(${SELF_LIB}) #xo_include_headeronly_options2(${SELF_LIB}) # ---------------------------------------------------------------- From f880bc66598db2b9fcb57cd6ca3c0b67edd561a6 Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Sun, 22 Oct 2023 17:37:10 -0400 Subject: [PATCH 08/17] build: simplify cmake macro api --- CMakeLists.txt | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index b5894f9a..8b8e6720 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -50,15 +50,14 @@ xo_toplevel_compile_options() # ---------------------------------------------------------------- # output targets -set(SELF_SHORTNAME pyutil) -set(SELF_LIB xo_${SELF_SHORTNAME}) +set(SELF_LIB xo_pyutil) xo_add_headeronly_library(${SELF_LIB}) #xo_include_headeronly_options2(${SELF_LIB}) # ---------------------------------------------------------------- # standard install + provide find_package() support -xo_install_library5(${SELF_LIB} ${SELF_SHORTNAME} ${PROJECT_NAME}Targets) +xo_install_library4(${SELF_LIB} ${PROJECT_NAME}Targets) #xo_install_include_tree() xo_export_cmake_config(${PROJECT_NAME} ${PROJECT_VERSION} ${PROJECT_NAME}Targets) From f81b6abda80a87693494378c4936a8bc977c7b68 Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Fri, 15 Mar 2024 19:29:24 -0400 Subject: [PATCH 09/17] build: streamline CMAKE_MODULE_PATH interaction --- .gitignore | 9 ++++++--- CMakeLists.txt | 3 +-- cmake/xo-bootstrap-macros.cmake | 12 ++++++++++++ 3 files changed, 19 insertions(+), 5 deletions(-) create mode 100644 cmake/xo-bootstrap-macros.cmake diff --git a/.gitignore b/.gitignore index 49f711e2..13c0afb7 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,6 @@ -# typical build directories -build -ccov +# clangd working space (see emacs+lsp) +.cache +# typical cmake build directory (source-tree-nephew) +.build* +# symlink to builddir/compile_commands.json; should be set manually in dev sandbox +compile_commands.json diff --git a/CMakeLists.txt b/CMakeLists.txt index 8b8e6720..e412e4a9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,8 +6,7 @@ project(xo_pyutil VERSION 0.1) enable_language(CXX) # common XO cmake macros (see github:Rconybea/xo-cmake) -include(xo_macros/xo_cxx) -include(xo_macros/code-coverage) # very little to unit test here +include(cmake/xo-bootstrap-macros.cmake) # ---------------------------------------------------------------- # unit test setup diff --git a/cmake/xo-bootstrap-macros.cmake b/cmake/xo-bootstrap-macros.cmake new file mode 100644 index 00000000..16644435 --- /dev/null +++ b/cmake/xo-bootstrap-macros.cmake @@ -0,0 +1,12 @@ +if (("${CMAKE_MODULE_PATH}" STREQUAL "") OR ("${CMAKE_MODULE_PATH}" STREQUAL "prefix")) + # default to typical install location for xo-project-macros + set(CMAKE_MODULE_PATH ${CMAKE_INSTALL_PREFIX}/share/cmake) +endif() + +message("-- CMAKE_INSTALL_PREFIX=${CMAKE_INSTALL_PREFIX}") +message("-- CMAKE_MODULE_PATH=${CMAKE_MODULE_PATH}") + +# needs to have been installed somewhere on CMAKE_MODULE_PATH, +# (e.g. from xo-cmake with the same value for CMAKE_INSTALL_PREFIX) +# +include(xo_macros/xo-project-macros) From c8947337066160bb5be3544f995e9be6395cf496 Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Fri, 29 Mar 2024 14:33:41 -0400 Subject: [PATCH 10/17] build: suppress bootstrap message in submodule build --- cmake/xo-bootstrap-macros.cmake | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/cmake/xo-bootstrap-macros.cmake b/cmake/xo-bootstrap-macros.cmake index 16644435..96592216 100644 --- a/cmake/xo-bootstrap-macros.cmake +++ b/cmake/xo-bootstrap-macros.cmake @@ -3,8 +3,10 @@ if (("${CMAKE_MODULE_PATH}" STREQUAL "") OR ("${CMAKE_MODULE_PATH}" STREQUAL "pr set(CMAKE_MODULE_PATH ${CMAKE_INSTALL_PREFIX}/share/cmake) endif() -message("-- CMAKE_INSTALL_PREFIX=${CMAKE_INSTALL_PREFIX}") -message("-- CMAKE_MODULE_PATH=${CMAKE_MODULE_PATH}") +if (NOT XO_SUBMODULE_BUILD) + message("-- CMAKE_INSTALL_PREFIX=${CMAKE_INSTALL_PREFIX}") + message("-- CMAKE_MODULE_PATH=${CMAKE_MODULE_PATH}") +endif() # needs to have been installed somewhere on CMAKE_MODULE_PATH, # (e.g. from xo-cmake with the same value for CMAKE_INSTALL_PREFIX) From fd5ebbfd2289f7bef3aa08372aeff35e38e8d4c2 Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Wed, 1 May 2024 14:39:09 -0400 Subject: [PATCH 11/17] xo-pyutil: build: streamline using recent xo-cmake improvements --- CMakeLists.txt | 43 ++++++++------------------------- cmake/xo-bootstrap-macros.cmake | 35 +++++++++++++++++++++++++++ 2 files changed, 45 insertions(+), 33 deletions(-) create mode 100644 cmake/xo-bootstrap-macros.cmake diff --git a/CMakeLists.txt b/CMakeLists.txt index c7e32668..a20befe5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,24 +3,19 @@ cmake_minimum_required(VERSION 3.10) project(xo_pyutil VERSION 0.1) -enable_language(CXX) -# common XO cmake macros (see github:Rconybea/xo-cmake) -include(xo_macros/xo_cxx) -include(xo_macros/code-coverage) # very little to unit test here +include(GNUInstallDirs) +include(cmake/xo-bootstrap-macros.cmake) # very little to unit test here + +xo_cxx_toplevel_options2() # ---------------------------------------------------------------- -# unit test setup +# cmake -DCMAKE_BUILD_TYPE=coverage +xo_toplevel_coverage_config2() -enable_testing() -## enable code coverage for all executables+libraries -## (when configured with -DCODE_COVERAGE=ON) -## -add_code_coverage() -add_code_coverage_all_targets( - EXCLUDE - /nix/store/* - ${PROJECT_SOURCE_DIR}/utest/*) +# ---------------------------------------------------------------- +# cmake -DCMAKE_BUILD_TYPE=debug +xo_toplevel_debug_config2() # ---------------------------------------------------------------- # bespoke (usually temporary) c++ settings @@ -29,36 +24,18 @@ set(PROJECT_CXX_FLAGS "") #set(PROJECT_CXX_FLAGS "-fconcepts-diagnostics-depth=2") add_definitions(${PROJECT_CXX_FLAGS}) -# ---------------------------------------------------------------- -# c++ settings - -xo_toplevel_compile_options() - -# ---------------------------------------------------------------- -# external dependencies -# -# set CMAKE_INSTALL_PREFIX to analog of /usr -# to use .cmake assistants from /usr/lib/cmake/indentlog -# -# xo_dependency(..) - # ---------------------------------------------------------------- #add_subdirectory(example) #add_subdirectory(utest) # ---------------------------------------------------------------- -# output targets set(SELF_LIB xo_pyutil) add_library(${SELF_LIB} INTERFACE) xo_include_headeronly_options2(${SELF_LIB}) - -# ---------------------------------------------------------------- -# standard install + provide find_package() support - xo_install_library2(${SELF_LIB}) -xo_install_include_tree() +xo_install_include_tree3(include/xo/pyutil) xo_export_cmake_config(${PROJECT_NAME} ${PROJECT_VERSION} ${PROJECT_NAME}Targets) # ---------------------------------------------------------------- diff --git a/cmake/xo-bootstrap-macros.cmake b/cmake/xo-bootstrap-macros.cmake new file mode 100644 index 00000000..aba31169 --- /dev/null +++ b/cmake/xo-bootstrap-macros.cmake @@ -0,0 +1,35 @@ +# ---------------------------------------------------------------- +# for example: +# $ PREFIX=/usr/local # for example +# $ cmake -DCMAKE_MODULE_PATH=prefix -DCMAKE_INSTALL_PREFIX=$PREFIX -B .build +# +# will get +# CMAKE_MODULE_PATH +# from xo-cmake-config --cmake-module-path +# +# and expect .cmake macros in +# CMAKE_MODULE_PATH/xo_macros/xo_cxx.cmake +# ---------------------------------------------------------------- + +find_program(XO_CMAKE_CONFIG_EXECUTABLE NAMES xo-cmake-config REQUIRED) + +if ("${XO_CMAKE_CONFIG_EXECUTABLE}" STREQUAL "XO_CMAKE_CONFIG_EXECUTABLE-NOT_FOUND") + message(FATAL "could not find xo-cmake-config executable") +endif() + +message(STATUS "XO_CMAKE_CONFIG_EXECUTABLE=${XO_CMAKE_CONFIG_EXECUTABLE}") + +if (NOT XO_SUBMODULE_BUILD) + if (("${CMAKE_MODULE_PATH}" STREQUAL "") OR ("${CMAKE_MODULE_PATH}" STREQUAL prefix)) + # default to typical install location for xo-project-macros + execute_process(COMMAND ${XO_CMAKE_CONFIG_EXECUTABLE} --cmake-module-path OUTPUT_VARIABLE CMAKE_MODULE_PATH) + message(STATUS "CMAKE_MODULE_PATH=${CMAKE_MODULE_PATH}") + endif() +endif() + +# needs to have been installed somewhere on CMAKE_MODULE_PATH, +# (e.g. from xo-cmake with the same value for CMAKE_INSTALL_PREFIX) +# +include(xo_macros/xo_cxx) + +xo_cxx_bootstrap_message() From ecd315c0f2c93cfd699e881931e134cd697cc316 Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Mon, 17 Jun 2024 16:53:07 -0400 Subject: [PATCH 12/17] xo-pyuitl: ++ .projectile in .gitignore --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index 13c0afb7..bc625ff8 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ +# emacs project config +.projectile # clangd working space (see emacs+lsp) .cache # typical cmake build directory (source-tree-nephew) From cd687d2ac40f92370693e6d1e8698e637eec8a30 Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Mon, 17 Jun 2024 16:53:23 -0400 Subject: [PATCH 13/17] xo-pyutil: + pycaller assistant for registering function pointers --- include/xo/pyutil/pycaller.hpp | 132 +++++++++++++++++++++++++++++++++ 1 file changed, 132 insertions(+) create mode 100644 include/xo/pyutil/pycaller.hpp diff --git a/include/xo/pyutil/pycaller.hpp b/include/xo/pyutil/pycaller.hpp new file mode 100644 index 00000000..85bf6652 --- /dev/null +++ b/include/xo/pyutil/pycaller.hpp @@ -0,0 +1,132 @@ +/** @file pycaller.hpp + * + * Author: Roland Conybeare + **/ + +#pragma once + +#include + +//#include + +namespace xo { + namespace pyutil { + struct pycaller_base { + virtual ~pycaller_base() = default; +g + static pybind11::module & declare_once(pybind11::module & m) { + static bool s_once = false; + if (!s_once) { + s_once = true; + pybind11::class_(m, "pycaller_base"); + } + return m; + } + }; + + /** Invoke function pointer of type Retval(*)(Args...), + * with arguments converted from py::object, and return type converted back to py::object. + * + * Each distinct combination of {Retval,Args...} needs to be established at compile time + * (since we need PyCall to be instantiated for particular types) + * + * Use when we don't know function pointer until *runtime*, + * for example getting function pointer from just-compiled code using xo-pyjit + **/ + template + struct pycaller; + + template + struct pycaller : public pycaller_base { + using self_type = pycaller; + using function_type = Retval (*)(); + + pycaller(function_type addr) : fptr_{reinterpret_cast(addr)} {} + + static pybind11::module & declare_once(pybind11::module & m) { + static bool s_once = false; + if (!s_once) { + s_once = true; + pycaller_base::declare_once(m); + pybind11::class_(m, "pycaller0") + .def("__call__", + [](self_type & self) + { + return pybind11::cast((*self.fptr_)()); + }); + } + return m; + } + + pybind11::object operator()() { return pybind11::cast((*fptr_)()); } + + private: + function_type fptr_; + }; + + template + struct pycaller : public pycaller_base { + using self_type = pycaller; + using function_type = Retval (*)(Arg1); + + pycaller(function_type addr) : fptr_{reinterpret_cast(addr)} {} + + static pybind11::module & declare_once(pybind11::module & m) { + static bool s_once = false; + if (!s_once) { + s_once = true; + pycaller_base::declare_once(m); + pybind11::class_(m, "pycaller1") + .def("__call__", + [](self_type & self, Arg1 arg1) + { + return pybind11::cast((*self.fptr_)(arg1)); + }) + ; + } + return m; + } + + pybind11::object operator()(pybind11::object arg1) { + return pybind11::cast((*fptr_)(pybind11::cast(arg1))); + } + + private: + function_type fptr_; + }; + + template + struct pycaller : public pycaller_base { + using self_type = pycaller; + using function_type = Retval (*)(Arg1, Arg2); + + pycaller(function_type addr) : fptr_{reinterpret_cast(addr)} {} + + static pybind11::module & declare_once(pybind11::module & m) { + static bool s_once = false; + if (!s_once) { + s_once = true; + pycaller_base::declare_once(m); + pybind11::class_(m, "pycaller2") + .def("__call__", + [](self_type & self, Arg1 arg1, Arg2 arg2) + { + return pybind11::cast((*self.fptr_)(arg1, arg2)); + }) + ; + } + return m; + } + + pybind11::object operator()(pybind11::object arg1, pybind11::object arg2) { + return pybind11::cast((*fptr_)(pybind11::cast(arg1), + pybind11::cast(arg2))); + } + + private: + function_type fptr_; + }; + } /*namespace pyutil*/ +} + +/** end pycaller.hpp **/ From bc601c472c484a6e8a1bd7ede2a49948c7b93f28 Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Mon, 17 Jun 2024 16:54:24 -0400 Subject: [PATCH 14/17] xo-pyutil: remove stray character --- include/xo/pyutil/pycaller.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/xo/pyutil/pycaller.hpp b/include/xo/pyutil/pycaller.hpp index 85bf6652..8368c060 100644 --- a/include/xo/pyutil/pycaller.hpp +++ b/include/xo/pyutil/pycaller.hpp @@ -13,7 +13,7 @@ namespace xo { namespace pyutil { struct pycaller_base { virtual ~pycaller_base() = default; -g + static pybind11::module & declare_once(pybind11::module & m) { static bool s_once = false; if (!s_once) { From e169a0d276765f7daa196dd91d60ff105654bc02 Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Mon, 17 Jun 2024 17:41:50 -0400 Subject: [PATCH 15/17] xo-pyutil: + example (mostly to make LSP happy) --- CMakeLists.txt | 12 ++++-------- example/CMakeLists.txt | 1 + example/ex1/CMakeLists.txt | 11 +++++++++++ example/ex1/pyex1.cpp | 14 ++++++++++++++ example/ex1/pyutilexample.hpp.in | 21 +++++++++++++++++++++ 5 files changed, 51 insertions(+), 8 deletions(-) create mode 100644 example/CMakeLists.txt create mode 100644 example/ex1/CMakeLists.txt create mode 100644 example/ex1/pyex1.cpp create mode 100644 example/ex1/pyutilexample.hpp.in diff --git a/CMakeLists.txt b/CMakeLists.txt index 0143a02d..596c509c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -26,22 +26,18 @@ add_definitions(${PROJECT_CXX_FLAGS}) # ---------------------------------------------------------------- -#add_subdirectory(example) -#add_subdirectory(utest) - # ---------------------------------------------------------------- set(SELF_LIB xo_pyutil) xo_add_headeronly_library(${SELF_LIB}) -#xo_include_headeronly_options2(${SELF_LIB}) +xo_install_library4(${SELF_LIB} ${PROJECT_NAME}Targets) +xo_export_cmake_config(${PROJECT_NAME} ${PROJECT_VERSION} ${PROJECT_NAME}Targets) # ---------------------------------------------------------------- -# standard install + provide find_package() support -xo_install_library4(${SELF_LIB} ${PROJECT_NAME}Targets) - -xo_export_cmake_config(${PROJECT_NAME} ${PROJECT_VERSION} ${PROJECT_NAME}Targets) +add_subdirectory(example) +#add_subdirectory(utest) # ---------------------------------------------------------------- # install any additional components diff --git a/example/CMakeLists.txt b/example/CMakeLists.txt new file mode 100644 index 00000000..4151ec21 --- /dev/null +++ b/example/CMakeLists.txt @@ -0,0 +1 @@ +add_subdirectory(ex1) diff --git a/example/ex1/CMakeLists.txt b/example/ex1/CMakeLists.txt new file mode 100644 index 00000000..d27232e4 --- /dev/null +++ b/example/ex1/CMakeLists.txt @@ -0,0 +1,11 @@ +# xo-pyutil/example/ex1/CMakeLists.txt + +set(SELF_LIB xo_pyutilexample) +set(SELF_SRCS pyex1.cpp) + +if (XO_ENABLE_EXAMPLES) + xo_pybind11_library(${SELF_LIB} ${PROJECT_NAME}Targets ${SELF_SRCS}) + xo_pybind11_dependency(${SELF_LIB} refcnt) +endif() + +# end CMakeLists.txt diff --git a/example/ex1/pyex1.cpp b/example/ex1/pyex1.cpp new file mode 100644 index 00000000..385a5b70 --- /dev/null +++ b/example/ex1/pyex1.cpp @@ -0,0 +1,14 @@ +/* @file pyex1.cpp */ + +#include "pyutilexample.hpp" +#include "xo/pyutil/pyutil.hpp" +#include + +namespace xo { + + PYBIND11_MODULE(XO_PYUTILEXAMPLE_MODULE_NAME(), m) { + m.def("sqrt", [](double x) { return ::sqrt(x); }); + } +} + +/* end pyex1.cpp */ diff --git a/example/ex1/pyutilexample.hpp.in b/example/ex1/pyutilexample.hpp.in new file mode 100644 index 00000000..a094415a --- /dev/null +++ b/example/ex1/pyutilexample.hpp.in @@ -0,0 +1,21 @@ +/* @file pyutilexample.hpp */ + +/* python requires module name = library name + * example: + * PYBIND11_MODULE(XO_PYUTILEXAMPLE_MODULE_NAME(), m) { ... } + */ +#define XO_PYUTILEXAMPLE_MODULE_NAME() @SELF_LIB@ + +/* example: + * py::module_::import(XO_PYUTILEXAMPLE_MODULE_NAME_STR) + */ +#define XO_PYUTILEXAMPLE_MODULE_NAME_STR "@SELF_LIB@" + +/* example: + * XO_PYUTILEXAMPLE_IMPORT_MODULE() + * replaces + * py::module_::import("xo_pyexpression") + */ +#define XO_PYUTILEXAMPLE_IMPORT_MODULE() py::module_::import("@SELF_LIB@") + +/* end pyutilexample.hpp */ From debc2db34c701262db2f095924770fdfe95bbe3c Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Mon, 17 Jun 2024 19:33:44 -0400 Subject: [PATCH 16/17] xo-pyutil: pycaller: provide type-erased factory functions --- include/xo/pyutil/pycaller.hpp | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/include/xo/pyutil/pycaller.hpp b/include/xo/pyutil/pycaller.hpp index 8368c060..20ecc92c 100644 --- a/include/xo/pyutil/pycaller.hpp +++ b/include/xo/pyutil/pycaller.hpp @@ -12,6 +12,9 @@ namespace xo { namespace pyutil { struct pycaller_base { + using void_function_type = void (*)(); + using factory_function_type = pycaller_base*(*)(void_function_type); + virtual ~pycaller_base() = default; static pybind11::module & declare_once(pybind11::module & m) { @@ -40,8 +43,11 @@ namespace xo { struct pycaller : public pycaller_base { using self_type = pycaller; using function_type = Retval (*)(); + using void_function_type = void (*)(); - pycaller(function_type addr) : fptr_{reinterpret_cast(addr)} {} + pycaller(void_function_type addr) : fptr_{reinterpret_cast(addr)} {} + + static pycaller_base * make(void_function_type addr) { return new pycaller(addr); } static pybind11::module & declare_once(pybind11::module & m) { static bool s_once = false; @@ -68,8 +74,11 @@ namespace xo { struct pycaller : public pycaller_base { using self_type = pycaller; using function_type = Retval (*)(Arg1); + using void_function_type = void (*)(); - pycaller(function_type addr) : fptr_{reinterpret_cast(addr)} {} + pycaller(void_function_type addr) : fptr_{reinterpret_cast(addr)} {} + + static pycaller_base * make(void_function_type addr) { return new pycaller(addr); } static pybind11::module & declare_once(pybind11::module & m) { static bool s_once = false; @@ -99,8 +108,11 @@ namespace xo { struct pycaller : public pycaller_base { using self_type = pycaller; using function_type = Retval (*)(Arg1, Arg2); + using void_function_type = void (*)(); - pycaller(function_type addr) : fptr_{reinterpret_cast(addr)} {} + pycaller(void_function_type addr) : fptr_{reinterpret_cast(addr)} {} + + static pycaller_base * make(void_function_type addr) { return new pycaller(addr); } static pybind11::module & declare_once(pybind11::module & m) { static bool s_once = false; From a2cb8ae60fea391259d0b9530ddd3df658958c84 Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Wed, 19 Jun 2024 18:30:40 -0400 Subject: [PATCH 17/17] xo-pyutil: bugfix: + prototype_str for unique declare_once() syms --- include/xo/pyutil/pycaller.hpp | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/include/xo/pyutil/pycaller.hpp b/include/xo/pyutil/pycaller.hpp index 20ecc92c..a9715d2e 100644 --- a/include/xo/pyutil/pycaller.hpp +++ b/include/xo/pyutil/pycaller.hpp @@ -17,6 +17,7 @@ namespace xo { virtual ~pycaller_base() = default; + /* note: need inherited class pycaller_base revealed to pybind11 too */ static pybind11::module & declare_once(pybind11::module & m) { static bool s_once = false; if (!s_once) { @@ -27,8 +28,10 @@ namespace xo { } }; - /** Invoke function pointer of type Retval(*)(Args...), - * with arguments converted from py::object, and return type converted back to py::object. + /** @class pycaller + * @brief Invoke function pointer of type Retval(*)(Args...) from py::object + * + * Arguments converted from py::object, and return type converted back to py::object. * * Each distinct combination of {Retval,Args...} needs to be established at compile time * (since we need PyCall to be instantiated for particular types) @@ -49,12 +52,14 @@ namespace xo { static pycaller_base * make(void_function_type addr) { return new pycaller(addr); } - static pybind11::module & declare_once(pybind11::module & m) { + /* note: prototype_str must be [const char *], pybind11 requirement */ + static pybind11::module & declare_once(pybind11::module & m, + const char * prototype_str) { static bool s_once = false; if (!s_once) { s_once = true; pycaller_base::declare_once(m); - pybind11::class_(m, "pycaller0") + pybind11::class_(m, prototype_str) .def("__call__", [](self_type & self) { @@ -80,12 +85,14 @@ namespace xo { static pycaller_base * make(void_function_type addr) { return new pycaller(addr); } - static pybind11::module & declare_once(pybind11::module & m) { + /* note: prototype_str must be [const char *], pybind11 requirement */ + static pybind11::module & declare_once(pybind11::module & m, + const char * prototype_str) { static bool s_once = false; if (!s_once) { s_once = true; pycaller_base::declare_once(m); - pybind11::class_(m, "pycaller1") + pybind11::class_(m, prototype_str) .def("__call__", [](self_type & self, Arg1 arg1) { @@ -114,12 +121,14 @@ namespace xo { static pycaller_base * make(void_function_type addr) { return new pycaller(addr); } - static pybind11::module & declare_once(pybind11::module & m) { + /* note: prototype_str must be [const char *], pybind11 requirement */ + static pybind11::module & declare_once(pybind11::module & m, + const char * prototype_str) { static bool s_once = false; if (!s_once) { s_once = true; pycaller_base::declare_once(m); - pybind11::class_(m, "pycaller2") + pybind11::class_(m, prototype_str) .def("__call__", [](self_type & self, Arg1 arg1, Arg2 arg2) {