diff --git a/xo-pyunit/.gitignore b/xo-pyunit/.gitignore new file mode 100644 index 00000000..13c0afb7 --- /dev/null +++ b/xo-pyunit/.gitignore @@ -0,0 +1,6 @@ +# 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/xo-pyunit/CMakeLists.txt b/xo-pyunit/CMakeLists.txt new file mode 100644 index 00000000..7ac955b9 --- /dev/null +++ b/xo-pyunit/CMakeLists.txt @@ -0,0 +1,36 @@ +# xo-pyunit/CMakeLists.txt + +cmake_minimum_required(VERSION 3.10) + +project(xo_pyunit VERSION 0.1) + +include(GNUInstallDirs) +include(cmake/xo-bootstrap-macros.cmake) + +xo_cxx_toplevel_options2() + +# ---------------------------------------------------------------- +# cmake -DCMAKE_BUILD_TYPE=coverage +xo_toplevel_coverage_config2() + +# ---------------------------------------------------------------- +# cmake -DCMAKE_BUILD_TYPE=debug +xo_toplevel_debug_config2() + +# ---------------------------------------------------------------- +# c++ settings (usually temporary) + +set(PROJECT_CXX_FLAGS "") +add_definitions(${PROJECT_CXX_FLAGS}) + +# ---------------------------------------------------------------- + +add_subdirectory(src/pyunit) +#add_subdirectory(utest) + +# ---------------------------------------------------------------- +# provide find_package() support + +xo_export_cmake_config(${PROJECT_NAME} ${PROJECT_VERSION} ${PROJECT_NAME}Targets) + +# end CMakeLists.txt diff --git a/xo-pyunit/cmake/xo-bootstrap-macros.cmake b/xo-pyunit/cmake/xo-bootstrap-macros.cmake new file mode 100644 index 00000000..aba31169 --- /dev/null +++ b/xo-pyunit/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() diff --git a/xo-pyunit/cmake/xo_pyunitConfig.cmake.in b/xo-pyunit/cmake/xo_pyunitConfig.cmake.in new file mode 100644 index 00000000..18187c96 --- /dev/null +++ b/xo-pyunit/cmake/xo_pyunitConfig.cmake.in @@ -0,0 +1,11 @@ +@PACKAGE_INIT@ + +include(CMakeFindDependencyMacro) + +find_dependency(xo_pyutil) +find_dependency(xo_unit) +find_dependency(refcnt) +find_dependency(indentlog) + +include("${CMAKE_CURRENT_LIST_DIR}/@PROJECT_NAME@Targets.cmake") +check_required_components("@PROJECT_NAME@") diff --git a/xo-pyunit/src/pyunit/CMakeLists.txt b/xo-pyunit/src/pyunit/CMakeLists.txt new file mode 100644 index 00000000..393a1387 --- /dev/null +++ b/xo-pyunit/src/pyunit/CMakeLists.txt @@ -0,0 +1,14 @@ +# xo_pyunit/src/pyunit/CMakeLists.txt + +set(SELF_LIB pyunit) +set(SELF_SRCS pyunit.cpp) + +# ---------------------------------------------------------------- + +xo_pybind11_library(${SELF_LIB} ${PROJECT_NAME}Targets ${SELF_SRCS}) +xo_pybind11_dependency(${SELF_LIB} xo_pyutil) +xo_pybind11_dependency(${SELF_LIB} xo_unit) +xo_pybind11_dependency(${SELF_LIB} refcnt) +xo_pybind11_dependency(${SELF_LIB} indentlog) + +# CMakeLists.txt diff --git a/xo-pyunit/src/pyunit/pyunit.cpp b/xo-pyunit/src/pyunit/pyunit.cpp new file mode 100644 index 00000000..7dd9992b --- /dev/null +++ b/xo-pyunit/src/pyunit/pyunit.cpp @@ -0,0 +1,183 @@ +/* @file pyunit.cpp */ + +// note: need pyreflect/ here bc pyreflect.hpp is generated, located in build directory +#include "pyunit.hpp" +#include "xo/unit/xquantity.hpp" +#include "xo/unit/xquantity_iostream.hpp" +#include "xo/pyutil/pyutil.hpp" +//#include +//#include +//#include +//#include + +namespace xo { + namespace py = pybind11; + using Unit = xo::qty::natural_unit; + using XoQuantity = xo::qty::xquantity; + + namespace qty { + PYBIND11_MODULE(PYUNIT_MODULE_NAME(), m) { + + m.doc() = "pybind11 plugin for xo.unit"; + + py::class_(m, "Unit") + .def("__repr__", + [](Unit & x) + { + /* e.g. "" for xo::qty::nu::gram */ + return tostr(""); + }) + ; + + py::module unit = m.def_submodule("unit"); + + unit.attr("picogram") = &xo::qty::nu::picogram; + unit.attr("nanoogram") = &xo::qty::nu::nanogram; + unit.attr("microgram") = &xo::qty::nu::microgram; + unit.attr("milligram") = &xo::qty::nu::milligram; + unit.attr("gram") = &xo::qty::nu::gram; + unit.attr("kilogram") = &xo::qty::nu::kilogram; + unit.attr("tonne") = &xo::qty::nu::tonne; + unit.attr("kilotonne") = &xo::qty::nu::kilotonne; + unit.attr("megatonne") = &xo::qty::nu::megatonne; + + unit.attr("picometer") = &xo::qty::nu::picometer; + unit.attr("nanometer") = &xo::qty::nu::nanometer; + unit.attr("micrometer") = &xo::qty::nu::micrometer; + unit.attr("millimeter") = &xo::qty::nu::millimeter; + unit.attr("meter") = &xo::qty::nu::meter; + unit.attr("kilometer") = &xo::qty::nu::kilometer; + unit.attr("megameter") = &xo::qty::nu::megameter; + + py::module qty = m.def_submodule("qty"); + + qty.def("picograms", [](double x) { return XoQuantity(x, nu::picogram); }); + qty.def("nanograms", [](double x) { return XoQuantity(x, nu::nanogram); }); + qty.def("micrograms", [](double x) { return XoQuantity(x, nu::microgram); }); + qty.def("milligrams", [](double x) { return XoQuantity(x, nu::milligram); }); + qty.def("grams", [](double x) { return XoQuantity(x, nu::gram); }); + qty.def("kilograms", [](double x) { return XoQuantity(x, nu::kilogram); }); + qty.def("tonnes", [](double x) { return XoQuantity(x, nu::tonne); }); + qty.def("kilotonnes", [](double x) { return XoQuantity(x, nu::kilotonne); }); + qty.def("megaonnes", [](double x) { return XoQuantity(x, nu::megatonne); }); + + qty.def("picometers", [](double x) { return XoQuantity(x, nu::picometer); }); + qty.def("nanometers", [](double x) { return XoQuantity(x, nu::nanometer); }); + qty.def("micrometers", [](double x) { return XoQuantity(x, nu::micrometer); }); + qty.def("millimeters", [](double x) { return XoQuantity(x, nu::millimeter); }); + qty.def("meters", [](double x) { return XoQuantity(x, nu::meter); }); + qty.def("kilometers", [](double x) { return XoQuantity(x, nu::kilometer); }); + qty.def("megameters", [](double x) { return XoQuantity(x, nu::megameter); }); + + py::class_(m, "Quantity") + .def(py::init(), + py::arg("scale"), py::arg("unit")) + + .def("scale", &XoQuantity::scale) + .def("unit", &XoQuantity::unit) + .def("is_dimensionless", &XoQuantity::is_dimensionless) + .def("unit_qty", &XoQuantity::unit_qty) + .def("zero_qty", &XoQuantity::zero_qty) + .def("reciprocal", &XoQuantity::reciprocal) + .def("rescale", &XoQuantity::rescale, + py::arg("unit")) + + .def("__add__", + [](const XoQuantity & x, const XoQuantity & y) + { + return x + y; + }) + + .def("__add__", + [](const XoQuantity & x, double y) + { + return x + y; + }) + + .def("__radd__", + [](double y, const XoQuantity & x) + { + return x + y; + }) + + .def("__sub__", + [](const XoQuantity & x, const XoQuantity & y) + { + return x - y; + }) + + .def("__sub__", + [](const XoQuantity & x, double y) + { + return x - y; + }) + + .def("__rsub__", + [](double y, const XoQuantity & x) + { + return x - y; + }) + + .def("__mul__", + [](const XoQuantity & x, const XoQuantity & y) + { + return x * y; + }) + .def("__mul__", + [](const XoQuantity & x, double y) + { + return x * y; + }) + .def("__rmul__", + [](const XoQuantity & y, double x) + { + return x * y; + }) + + .def("__truediv__", + [](const XoQuantity & x, const XoQuantity & y) + { + return x / y; + }) + .def("__truediv__", + [](const XoQuantity & x, double y) + { + return x / y; + }) + .def("__rtruediv__", + [](const XoQuantity & y, double x) + { + return x / y; + }) + + .def("__eq__", + [](const XoQuantity & x, const XoQuantity & y) + { + return x == y; + }) + + .def("__eq__", + [](const XoQuantity & x, double y) + { + return x == y; + }) + + .def("__req__", + [](const XoQuantity & y, double x) + { + return x == y; + }) + + .def("__repr__", + [](const XoQuantity & x) + { + return tostr(x); + }) + .def("abbrev", + [](const XoQuantity & x) { + return std::string(x.abbrev().c_str()); + }) + ; + } + } +} /*namespace xo*/ diff --git a/xo-pyunit/src/pyunit/pyunit.hpp.in b/xo-pyunit/src/pyunit/pyunit.hpp.in new file mode 100644 index 00000000..d2008d8a --- /dev/null +++ b/xo-pyunit/src/pyunit/pyunit.hpp.in @@ -0,0 +1,25 @@ +/* @file pyunit.hpp + * + * automatically generated from src/xo_pyunit/pyunit.hpp.in + * see src/xo_pyunit/CMakeLists.txt + */ + +/* python requires module name = library name + * example: + * PYBIND11_MODULE(PYUNIT_MODULE_NAME(), m) { ... } + */ +#define PYUNIT_MODULE_NAME() @SELF_LIB@ + +/* example: + * py::module_::import(PYUNIT_MODULE_NAME_STR) + */ +#define PYUNIT_MODULE_NAME_STR "@SELF_LIB@" + +/* example: + * PYUNIT_IMPORT_MODULE() + * replaces + * py::module_::import("pyunit") + */ +#define PYUNIT_IMPORT_MODULE() py::module_::import("@SELF_LIB@") + +/* end pyunit.hpp */