diff --git a/examples/custom_numeric_type/.gitignore b/examples/custom_numeric_type/.gitignore new file mode 100644 index 000000000..68e319c8b --- /dev/null +++ b/examples/custom_numeric_type/.gitignore @@ -0,0 +1,3 @@ +include/eigenpy/example/custom/numeric/type/config.hpp +include/eigenpy/example/custom/numeric/type/deprecated.hpp +include/eigenpy/example/custom/numeric/type/warning.hpp \ No newline at end of file diff --git a/examples/custom_numeric_type/CMakeLists.txt b/examples/custom_numeric_type/CMakeLists.txt new file mode 100644 index 000000000..be6dba62e --- /dev/null +++ b/examples/custom_numeric_type/CMakeLists.txt @@ -0,0 +1,205 @@ +# derived from eigenpy/CMakeLists.txt for this example + +cmake_minimum_required(VERSION 3.10) + +set(PROJECT_NAME eigenpy_example_custom_numeric_type) +set(PROJECT_DESCRIPTION + "An example of using eigenpy with a custom numeric type: Boost.Multiprecision complex numbers" +) +set(PROJECT_URL + "http://github.com/stack-of-tasks/eigenpy/examples/custom_numeric_type") +set(PROJECT_USE_CMAKE_EXPORT TRUE) +set(PROJECT_USE_KEYWORD_LINK_LIBRARIES TRUE) +set(PROJECT_CUSTOM_HEADER_EXTENSION "hpp") +set(PROJECT_COMPATIBILITY_VERSION AnyNewerVersion) + +# Check if the submodule cmake have been initialized +set(JRL_CMAKE_MODULES "${CMAKE_CURRENT_LIST_DIR}/cmake") +if(NOT EXISTS "${CMAKE_SOURCE_DIR}/cmake/base.cmake") + if(${CMAKE_VERSION} VERSION_LESS "3.14.0") + message( + FATAL_ERROR + "\nPlease run the following command first:\ngit submodule update --init\n" + ) + else() + message(STATUS "JRL cmakemodules not found. Let's fetch it.") + include(FetchContent) + FetchContent_Declare( + "jrl-cmakemodules" + GIT_REPOSITORY "https://github.com/jrl-umi3218/jrl-cmakemodules.git") + FetchContent_MakeAvailable("jrl-cmakemodules") + FetchContent_GetProperties("jrl-cmakemodules" SOURCE_DIR JRL_CMAKE_MODULES) + endif() +endif() + +# Disable -Werror on Unix for now. +set(CXX_DISABLE_WERROR True) +set(CMAKE_VERBOSE_MAKEFILE True) + +option(INSTALL_DOCUMENTATION "Generate and install the documentation" OFF) +option(SUFFIX_SO_VERSION "Suffix library name with its version" OFF) + +if(DEFINED BUILD_UNIT_TESTS) + message( + AUTHOR_WARNING "BUILD_UNIT_TESTS is deprecated. Use BUILD_TESTING instead.") + set(BUILD_TESTING ${BUILD_UNIT_TESTS}) +endif(DEFINED BUILD_UNIT_TESTS) + +include("${JRL_CMAKE_MODULES}/base.cmake") +compute_project_args(PROJECT_ARGS LANGUAGES CXX) +project(${PROJECT_NAME} ${PROJECT_ARGS}) + +include("${JRL_CMAKE_MODULES}/boost.cmake") +include("${JRL_CMAKE_MODULES}/python.cmake") +include("${JRL_CMAKE_MODULES}/ide.cmake") +include("${JRL_CMAKE_MODULES}/apple.cmake") + +option(GENERATE_PYTHON_STUBS + "Generate the Python stubs associated to the Python library" OFF) + +string(REPLACE "-pedantic" "" CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS}) + +# If needed, fix CMake policy for APPLE systems +apply_default_apple_configuration() +check_minimal_cxx_standard(11 ENFORCE) + +if(WIN32) + set(LINK copy_if_different) +else(WIN32) + set(LINK create_symlink) +endif(WIN32) + +if(CMAKE_CROSSCOMPILING) + set(PYTHON_COMPONENTS Interpreter NumPy) +else() + set(PYTHON_COMPONENTS Interpreter Development.Module NumPy) +endif() +set(PYTHON_EXPORT_DEPENDENCY ON) +findpython(REQUIRED) + +if(${NUMPY_VERSION} VERSION_LESS "1.16.0") + set(NUMPY_WITH_BROKEN_UFUNC_SUPPORT TRUE) +endif() + +if(WIN32) + link_directories(${PYTHON_LIBRARY_DIRS}) + # # Set default Windows build paths SET(CMAKE_LIBRARY_OUTPUT_DIRECTORY + # ${PROJECT_BINARY_DIR}/Bin CACHE PATH "Single directory for all libraries") + # SET(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/Bin CACHE PATH + # "Single directory for all executables") SET(CMAKE_ARCHIVE_OUTPUT_DIRECTORY + # ${PROJECT_BINARY_DIR}/Bin CACHE PATH "Sing$le directory for all archives") +endif(WIN32) + +# ---------------------------------------------------- +# --- DEPENDENCIES ----------------------------------- +# ---------------------------------------------------- + +add_project_dependency(Eigen3 REQUIRED PKG_CONFIG_REQUIRES "eigen3 >= 3.0.5") +add_project_dependency(eigenpy REQUIRED PKG_CONFIG_REQUIRES "eigenpy >= 3.0.0") + +set_boost_default_options() +export_boost_default_options() +find_package(Boost REQUIRED) +search_for_boost_python(REQUIRED) + +# ---------------------------------------------------- +# --- INCLUDE ---------------------------------------- +# ---------------------------------------------------- +set(${PROJECT_NAME}_HEADERS include/header.hpp) + +set(${PROJECT_NAME}_SOURCES src/src.cpp) + +add_library(${PROJECT_NAME} SHARED ${${PROJECT_NAME}_SOURCES} + ${${PROJECT_NAME}_HEADERS}) + +target_include_directories( + ${PROJECT_NAME} SYSTEM + PUBLIC $ + $) + +target_link_directories(${PROJECT_NAME} PUBLIC ${EIGENPY_LIB_DIR}) + +modernize_target_link_libraries( + ${PROJECT_NAME} + SCOPE + PUBLIC + TARGETS + Python${PYTHON_VERSION_MAJOR}::NumPy + Eigen3::Eigen + eigenpy + INCLUDE_DIRS + ${EIGEN3_INCLUDE_DIR} + ${EIGENPY_INCLUDE_DIR} + ${NUMPY_INCLUDE_DIRS} + ${PYTHON_INCLUDE_DIR}) + +target_link_libraries(${PROJECT_NAME} PUBLIC eigenpy::eigenpy mpc mpfr) + +if(SUFFIX_SO_VERSION) + set_target_properties(${PROJECT_NAME} PROPERTIES SOVERSION ${PROJECT_VERSION}) +endif(SUFFIX_SO_VERSION) + +# silviana did this +set_target_properties(${PROJECT_NAME} PROPERTIES PREFIX "") +set_target_properties(${PROJECT_NAME} PROPERTIES SUFFIX ".so") + +if(NOT WIN32) + target_compile_options( + ${PROJECT_NAME} PRIVATE $<$:-bigobj> + "-Wno-conversion") +else() + target_compile_options(${PROJECT_NAME} + PRIVATE $<$:-bigobj>) + target_compile_definitions(${PROJECT_NAME} PUBLIC "HAVE_SNPRINTF") +endif() + +target_link_boost_python(${PROJECT_NAME} PUBLIC) + +install( + TARGETS ${PROJECT_NAME} + EXPORT ${TARGETS_EXPORT_NAME} + PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} + INCLUDES + DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) + +add_header_group(${PROJECT_NAME}_HEADERS) +add_source_group(${PROJECT_NAME}_SOURCES) + +# Install package for ROS +install(FILES package.xml DESTINATION share/example_) +# Allows Colcon to find non-Ament packages when using workspace underlays +file( + WRITE + ${CMAKE_CURRENT_BINARY_DIR}/share/ament_index/resource_index/packages/${PROJECT_NAME} + "") +install( + FILES + ${CMAKE_CURRENT_BINARY_DIR}/share/ament_index/resource_index/packages/${PROJECT_NAME} + DESTINATION share/ament_index/resource_index/packages) +file( + WRITE + ${CMAKE_CURRENT_BINARY_DIR}/share/${PROJECT_NAME}/hook/ament_prefix_path.dsv + "prepend-non-duplicate;AMENT_PREFIX_PATH;") +install( + FILES + ${CMAKE_CURRENT_BINARY_DIR}/share/${PROJECT_NAME}/hook/ament_prefix_path.dsv + DESTINATION share/${PROJECT_NAME}/hook) +file(WRITE + ${CMAKE_CURRENT_BINARY_DIR}/share/${PROJECT_NAME}/hook/python_path.dsv + "prepend-non-duplicate;PYTHONPATH;${PYTHON_SITELIB}") +install( + FILES ${CMAKE_CURRENT_BINARY_DIR}/share/${PROJECT_NAME}/hook/python_path.dsv + DESTINATION share/${PROJECT_NAME}/hook) + +# ---------------------------------------------------- +# --- PYTHON LIBRARY --------------------------------- +# ---------------------------------------------------- +# add_subdirectory(python) + +pkg_config_append_libs(${PROJECT_NAME}) +pkg_config_append_cflags("-I${PYTHON_INCLUDE_DIRS}") +pkg_config_append_cflags("-I${NUMPY_INCLUDE_DIRS}") +pkg_config_append_boost_libs(${BOOST_COMPONENTS}) diff --git a/examples/custom_numeric_type/examplescript.py b/examples/custom_numeric_type/examplescript.py new file mode 100644 index 000000000..e566d36be --- /dev/null +++ b/examples/custom_numeric_type/examplescript.py @@ -0,0 +1,3 @@ +import eigenpy_example_custom_numeric_type as example + +example.MpfrComplex(2) # the number 2, in variable precision as a complex number diff --git a/examples/custom_numeric_type/include/header.hpp b/examples/custom_numeric_type/include/header.hpp new file mode 100644 index 000000000..d8289aaef --- /dev/null +++ b/examples/custom_numeric_type/include/header.hpp @@ -0,0 +1,385 @@ + + +#pragma once + +#ifndef EXAMPLE_CUSTOM_NUMERIC_TYPE +#define EXAMPLE_CUSTOM_NUMERIC_TYPE + +#include +#include +#include + +#include + +#include + +namespace bmp = boost::multiprecision; + +using mpfr_float = + boost::multiprecision::number, + boost::multiprecision::et_off>; + +using bmp::backends::mpc_complex_backend; +using mpfr_complex = + bmp::number, + bmp::et_off>; // T is a variable-precision complex number with + // expression templates turned on. + +void ExposeAll(); +void ExposeReal(); +void ExposeComplex(); + +// this code derived from +// https://github.com/stack-of-tasks/eigenpy/issues/365 +// where I asked about using custom types, and @jcarpent responded with a +// discussion of an application of this in Pinnochio, a library for rigid body +// dynamics. +namespace eigenpy { +namespace internal { + +// a template specialization for complex numbers +// derived directly from the example for Pinnochio +template <> +struct getitem { + static PyObject *run(void *data, void * /* arr */) { + mpfr_float &mpfr_scalar = *static_cast(data); + auto &backend = mpfr_scalar.backend(); + + if (backend.data()[0]._mpfr_d == + 0) // If the mpfr_scalar is not initialized, we have to init it. + { + mpfr_scalar = mpfr_float(0); + } + boost::python::object m(boost::ref(mpfr_scalar)); + Py_INCREF(m.ptr()); + return m.ptr(); + } +}; + +// a template specialization for complex numbers +// derived directly from the example for Pinnochio +template <> +struct getitem { + static PyObject *run(void *data, void * /* arr */) { + mpfr_complex &mpfr_scalar = *static_cast(data); + auto &backend = mpfr_scalar.backend(); + + if (backend.data()[0].re->_mpfr_d == 0 || + backend.data()[0].im->_mpfr_d == + 0) // If the mpfr_scalar is not initialized, we have to init it. + { + mpfr_scalar = mpfr_complex(0); + } + boost::python::object m(boost::ref(mpfr_scalar)); + Py_INCREF(m.ptr()); + return m.ptr(); + } +}; + +} // namespace internal + +// i lifted this from EigenPy and adapted it, basically removing the calls for +// the comparitors. +template +void registerUfunct_without_comparitors() { + const int type_code = Register::getTypeCode(); + + PyObject *numpy_str; +#if PY_MAJOR_VERSION >= 3 + numpy_str = PyUnicode_FromString("numpy"); +#else + numpy_str = PyString_FromString("numpy"); +#endif + PyObject *numpy; + numpy = PyImport_Import(numpy_str); + Py_DECREF(numpy_str); + + import_ufunc(); + + // Matrix multiply + { + int types[3] = {type_code, type_code, type_code}; + + std::stringstream ss; + ss << "return result of multiplying two matrices of "; + ss << bp::type_info(typeid(Scalar)).name(); + PyUFuncObject *ufunc = + (PyUFuncObject *)PyObject_GetAttrString(numpy, "matmul"); + if (!ufunc) { + std::stringstream ss; + ss << "Impossible to define matrix_multiply for given type " + << bp::type_info(typeid(Scalar)).name() << std::endl; + eigenpy::Exception(ss.str()); + } + if (PyUFunc_RegisterLoopForType((PyUFuncObject *)ufunc, type_code, + &internal::gufunc_matrix_multiply, + types, 0) < 0) { + std::stringstream ss; + ss << "Impossible to register matrix_multiply for given type " + << bp::type_info(typeid(Scalar)).name() << std::endl; + eigenpy::Exception(ss.str()); + } + + Py_DECREF(ufunc); + } + + // Binary operators + EIGENPY_REGISTER_BINARY_UFUNC(add, type_code, Scalar, Scalar, Scalar); + EIGENPY_REGISTER_BINARY_UFUNC(subtract, type_code, Scalar, Scalar, Scalar); + EIGENPY_REGISTER_BINARY_UFUNC(multiply, type_code, Scalar, Scalar, Scalar); + EIGENPY_REGISTER_BINARY_UFUNC(divide, type_code, Scalar, Scalar, Scalar); + + // Comparison operators + EIGENPY_REGISTER_BINARY_UFUNC(equal, type_code, Scalar, Scalar, bool); + EIGENPY_REGISTER_BINARY_UFUNC(not_equal, type_code, Scalar, Scalar, bool); + + // these are commented out because the comparisons are NOT defined for complex + // types!! + // EIGENPY_REGISTER_BINARY_UFUNC(greater, type_code, Scalar, Scalar, bool); + // EIGENPY_REGISTER_BINARY_UFUNC(less, type_code, Scalar, Scalar, bool); + // EIGENPY_REGISTER_BINARY_UFUNC(greater_equal, type_code, Scalar, Scalar, + // bool); EIGENPY_REGISTER_BINARY_UFUNC(less_equal, type_code, Scalar, + // Scalar, bool); + + // Unary operators + EIGENPY_REGISTER_UNARY_UFUNC(negative, type_code, Scalar, Scalar); + + Py_DECREF(numpy); +} + +} // namespace eigenpy + +namespace bp = boost::python; + +template +struct BoostNumberPythonVisitor : public boost::python::def_visitor< + BoostNumberPythonVisitor > { + public: + template + void visit(PyClass &cl) const { + cl.def(bp::init<>("Default constructor.", bp::arg("self"))) + .def(bp::init("Copy constructor.", + bp::args("self", "value"))) + // .def(bp::init("Copy + // constructor.",bp::args("self","value"))) + // .def(bp::init("Copy + // constructor.",bp::args("self","value"))) + // .def(bp::init("Copy + // constructor.",bp::args("self","value"))) + // .def(bp::init("Copy + // constructor.",bp::args("self","value"))) .def(bp::init("Copy constructor.",bp::args("self","value"))) + // .def(bp::init("Copy + // constructor.",bp::args("self","value"))) + // .def(bp::init("Copy + // constructor.",bp::args("self","value"))) + .def(bp::init("Constructor from a string.", + bp::args("self", "str_value"))) + + .def(bp::self + bp::self) + .def(bp::self += bp::self) + .def(bp::self - bp::self) + .def(bp::self -= bp::self) + .def(bp::self * bp::self) + .def(bp::self *= bp::self) + .def(bp::self / bp::self) + .def(bp::self /= bp::self) + + .def(bp::self < bp::self) + .def(bp::self <= bp::self) + .def(bp::self > bp::self) + .def(bp::self >= bp::self) + .def(bp::self == bp::self) + .def(bp::self != bp::self) + .def(bp::self_ns::pow(bp::self_ns::self, long())) + + .def("str", &BoostNumber::str, + bp::args("self", "precision", "scientific")) + + .def("default_precision", + static_cast(BoostNumber::default_precision), + "Get the default precision of the class.") + .def("default_precision", + static_cast(BoostNumber::default_precision), + bp::arg("digits10"), "Set the default precision of the class.") + .staticmethod("default_precision") + + .def("precision", + static_cast( + &BoostNumber::precision), + bp::arg("self"), "Get the precision of this.") + .def("precision", + static_cast( + &BoostNumber::precision), + bp::args("self", "digits10"), "Set the precision of this.") + + .def("__float__", &cast, bp::arg("self"), "Cast to float.") + .def("__int__", &cast, bp::arg("self"), "Cast to int.") + + .def("__str__", &print, bp::arg("self")) + .def("__repr__", &print, bp::arg("self")) + + .def("set_display_precision", &set_display_precision, bp::arg("digit"), + "Set the precision when printing values.") + .staticmethod("set_display_precision") + + .def("get_display_precision", &get_display_precision, + "Get the precision when printing values.", + bp::return_value_policy()) + .staticmethod("get_display_precision") + + // #ifndef PINOCCHIO_PYTHON_NO_SERIALIZATION + // .def_pickle(Pickle()) + // #endif + ; + } + + static void expose(const std::string &type_name) { + bp::class_(type_name.c_str(), "", bp::no_init) + .def(BoostNumberPythonVisitor()); + + eigenpy::registerNewType(); + eigenpy::registerCommonUfunc(); + +#define IMPLICITLY_CONVERTIBLE(T1, T2) bp::implicitly_convertible(); + // bp::implicitly_convertible(); + + IMPLICITLY_CONVERTIBLE(double, BoostNumber); + IMPLICITLY_CONVERTIBLE(float, BoostNumber); + IMPLICITLY_CONVERTIBLE(long int, BoostNumber); + IMPLICITLY_CONVERTIBLE(int, BoostNumber); + IMPLICITLY_CONVERTIBLE(long, BoostNumber); + IMPLICITLY_CONVERTIBLE(unsigned int, BoostNumber); + IMPLICITLY_CONVERTIBLE(unsigned long int, BoostNumber); + IMPLICITLY_CONVERTIBLE(bool, BoostNumber); + +#undef IMPLICITLY_CONVERTIBLE + + eigenpy::registerCast(false); + eigenpy::registerCast(true); + eigenpy::registerCast(false); + eigenpy::registerCast(true); + eigenpy::registerCast(false); + eigenpy::registerCast(true); + eigenpy::registerCast(false); + eigenpy::registerCast(true); + ; + eigenpy::registerCast(false); + eigenpy::registerCast(true); + } + + private: + template + static T cast(const BoostNumber &self) { + return static_cast(self); + } + + static std::string print(const BoostNumber &self) { + return self.str(get_display_precision(), std::ios_base::dec); + } + + static void set_display_precision(const int digit) { + get_display_precision() = digit; + } + + static int &get_display_precision() { + static int precision = BoostNumber::default_precision(); + return precision; + } +}; + +// this derived directly from the code at +// https://github.com/stack-of-tasks/eigenpy/issues/365, in which this example +// was requested + +template +struct BoostComplexPythonVisitor + : public boost::python::def_visitor< + BoostComplexPythonVisitor > { + public: + template + void visit(PyClass &cl) const { + cl.def(bp::init<>("Default constructor.", bp::arg("self"))) + .def(bp::init("Copy constructor.", + bp::args("self", "value"))) + .def(bp::init("Constructor from a string.", + bp::args("self", "str_value"))) + .def(bp::init( + "Constructor from a pair of strings.", + bp::args("self", "real", "imag"))) + + .def(bp::init("Constructor from a pair of integers.", + bp::args("self", "real", "imag"))) + + .def(bp::self + bp::self) + .def(bp::self += bp::self) + .def(bp::self - bp::self) + .def(bp::self -= bp::self) + .def(bp::self * bp::self) + .def(bp::self *= bp::self) + .def(bp::self / bp::self) + .def(bp::self /= bp::self) + + .def(bp::self == bp::self) + .def(bp::self != bp::self) + .def(bp::self_ns::pow(bp::self_ns::self, long())) + + .def("str", &BoostNumber::str, + bp::args("self", "precision", "scientific")) + + .def("default_precision", + static_cast(BoostNumber::default_precision), + "Get the default precision of the class.") + .def("default_precision", + static_cast(BoostNumber::default_precision), + bp::arg("digits10"), "Set the default precision of the class.") + .staticmethod("default_precision") + + .add_property("real", &get_real, &set_real) + .add_property("imag", &get_imag, &set_imag) + + .def("precision", + static_cast( + &BoostNumber::precision), + bp::arg("self"), "Get the precision of this.") + .def("precision", + static_cast( + &BoostNumber::precision), + bp::args("self", "digits10"), "Set the precision of this.") + + .def("__str__", &print, bp::arg("self")) + .def("__repr__", &print, bp::arg("self")) + + .def("set_display_precision", &set_display_precision, bp::arg("digit"), + "Set the precision when printing values.") + .staticmethod("set_display_precision") + + .def("get_display_precision", &get_display_precision, + "Get the precision when printing values.", + bp::return_value_policy()) + .staticmethod("get_display_precision") + + ; + } + + static void set_real(BoostNumber &c, mpfr_float const &r) { c.real(r); } + static mpfr_float get_real(BoostNumber const &c) { return c.real(); } + + static void set_imag(BoostNumber &c, mpfr_float const &r) { c.imag(r); } + static mpfr_float get_imag(BoostNumber const &c) { return c.imag(); } + + static std::string print(const BoostNumber &self) { + return self.str(get_display_precision(), std::ios_base::dec); + } + + static void set_display_precision(const int digit) { + get_display_precision() = digit; + } + + static int &get_display_precision() { + static int precision = BoostNumber::default_precision(); + return precision; + } +}; + +#endif diff --git a/examples/custom_numeric_type/package.xml b/examples/custom_numeric_type/package.xml new file mode 100644 index 000000000..ea4f32fb4 --- /dev/null +++ b/examples/custom_numeric_type/package.xml @@ -0,0 +1,32 @@ + + + eigenpy_example_custom_numeric_type + 3.0.0 + Example of using EigenPy to enable custom numeric types with Eigen + Justin Carpentier + Wolfgang Merkt + Justin Carpentier + Nicolas Mansard + BSD + + https://github.com/stack-of-tasks/eigenpy/examples/custom_numeric_type + + git + doxygen + + + catkin + + python + python3 + python-numpy + python3-numpy + eigen + boost + eigenpy + + cmake + + cmake + + diff --git a/examples/custom_numeric_type/readme.md b/examples/custom_numeric_type/readme.md new file mode 100644 index 000000000..dd933cb61 --- /dev/null +++ b/examples/custom_numeric_type/readme.md @@ -0,0 +1,10 @@ +# An example of using eigenpy to extend Python using Eigen linear algebra package with custom numeric types + +## Building + +1. Make a build directory. Move into it +2. `cmake ../` +3. `make` +4. `make install` +5. Move back up a directory +6. `python3 examplescript.py` \ No newline at end of file diff --git a/examples/custom_numeric_type/src/src.cpp b/examples/custom_numeric_type/src/src.cpp new file mode 100644 index 000000000..14b055cb2 --- /dev/null +++ b/examples/custom_numeric_type/src/src.cpp @@ -0,0 +1,62 @@ +#include "header.hpp" + +BOOST_PYTHON_MODULE( + eigenpy_example_custom_numeric_type) // this name must match the name of + // the generated .so file. +{ + // see + // https://stackoverflow.com/questions/6114462/how-to-override-the-automatically-created-docstring-data-for-boostpython + // docstring_options d(true, true, false); // local_ + boost::python::docstring_options docopt; + docopt.enable_all(); + docopt.disable_cpp_signatures(); + + boost::python::object package = boost::python::scope(); + package.attr("__path__") = "eigenpy_example_custom_numeric_type"; + + ExposeAll(); +} + +#define IMPLICITLY_CONVERTIBLE(T1, T2) \ + boost::python::implicitly_convertible(); + +void ExposeAll() { + eigenpy::enableEigenPy(); + + ExposeReal(); + ExposeComplex(); +} + +void ExposeReal() { + BoostNumberPythonVisitor::expose("MpfrFloat"); + + using VecX = Eigen::Matrix; + using MatXX = Eigen::Matrix; + + eigenpy::enableEigenPySpecific(); + eigenpy::enableEigenPySpecific(); +} + +void ExposeComplex() { + boost::python::class_("MpfrComplex", "", bp::no_init) + .def(BoostComplexPythonVisitor()); + + eigenpy::registerNewType(); + eigenpy::registerUfunct_without_comparitors(); + + eigenpy::registerCast(true); + eigenpy::registerCast(true); + eigenpy::registerCast(true); + + IMPLICITLY_CONVERTIBLE(int, mpfr_complex); + IMPLICITLY_CONVERTIBLE(long, mpfr_complex); + IMPLICITLY_CONVERTIBLE(int64_t, mpfr_complex); + + using VecX = Eigen::Matrix; + using MatXX = Eigen::Matrix; + + eigenpy::enableEigenPySpecific(); + eigenpy::enableEigenPySpecific(); +} + +#undef IMPLICITLY_CONVERTIBLE