From 0e5bf542f2950638e154050f98ac5418302b462a Mon Sep 17 00:00:00 2001 From: Justin Carpentier Date: Tue, 3 Sep 2024 14:01:57 +0200 Subject: [PATCH 1/8] utils: add type_info helpers --- CMakeLists.txt | 2 + include/eigenpy/type_info.hpp | 77 +++++++++++++++++++++++++++++++++++ src/eigenpy.cpp | 2 + src/type_info.cpp | 68 +++++++++++++++++++++++++++++++ 4 files changed, 149 insertions(+) create mode 100644 include/eigenpy/type_info.hpp create mode 100644 src/type_info.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 56864d6b6..ed103c432 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -275,6 +275,7 @@ set(${PROJECT_NAME}_HEADERS include/eigenpy/variant.hpp include/eigenpy/std-unique-ptr.hpp include/eigenpy/swig.hpp + include/eigenpy/type_info.hpp include/eigenpy/version.hpp) list( @@ -349,6 +350,7 @@ set(${PROJECT_NAME}_SOURCES src/scipy-type.cpp src/std-vector.cpp src/optional.cpp + src/type_info.cpp src/version.cpp) add_library(${PROJECT_NAME} SHARED ${${PROJECT_NAME}_SOURCES} diff --git a/include/eigenpy/type_info.hpp b/include/eigenpy/type_info.hpp new file mode 100644 index 000000000..7344bf423 --- /dev/null +++ b/include/eigenpy/type_info.hpp @@ -0,0 +1,77 @@ +/// +/// Copyright (c) 2024 INRIA +/// + +#ifndef __eigenpy_type_info_hpp__ +#define __eigenpy_type_info_hpp__ + +#include "eigenpy/fwd.hpp" + +#include +#include +#include + +namespace eigenpy { + +template +boost::typeindex::type_index type_info(const T& value) { + return boost::typeindex::type_id_runtime(value); +} + +template +void expose_boost_type_info() { + boost::python::def( + "type_info", + +[](const T& value) -> boost::typeindex::type_index { + return boost::typeindex::type_id_runtime(value); + }, + bp::arg("value"), + "Returns information of the type of value as a " + "boost::typeindex::type_index (can work without RTTI)."); + boost::python::def( + "boost_type_info", + +[](const T& value) -> boost::typeindex::type_index { + return boost::typeindex::type_id_runtime(value); + }, + bp::arg("value"), + "Returns information of the type of value as a " + "boost::typeindex::type_index (can work without RTTI)."); +} + +template +void expose_std_type_info() { + boost::python::def( + "std_type_info", + +[](const T& value) -> std::type_index { return typeid(value); }, + bp::arg("value"), + "Returns information of the type of value as a std::type_index."); +} + +/// +/// \brief Add the Python method type_info to query information of a type. +/// +template +struct TypeInfoVisitor : public bp::def_visitor > { + template + void visit(PyClass& cl) const { + cl.def("type_info", &boost_type_info, bp::arg("self"), + "Queries information of the type of *this as a " + "boost::typeindex::type_index (can work without RTTI)."); + cl.def("boost_type_info", &boost_type_info, bp::arg("self"), + "Queries information of the type of *this as a " + "boost::typeindex::type_index (can work without RTTI)."); + cl.def("std_type_info", &std_type_info, bp::arg("self"), + "Queries information of the type of *this as a std::type_index."); + } + + private: + static boost::typeindex::type_index boost_type_info(const C& self) { + return boost::typeindex::type_id(); + } + + static std::type_index std_type_info(const C& self) { return typeid(C); } +}; + +} // namespace eigenpy + +#endif // __eigenpy_type_info_hpp__ diff --git a/src/eigenpy.cpp b/src/eigenpy.cpp index cc2dcefbc..93a4b9eb0 100644 --- a/src/eigenpy.cpp +++ b/src/eigenpy.cpp @@ -36,6 +36,7 @@ void exposeMatrixComplexDouble(); void exposeMatrixComplexLongDouble(); void exposeNoneType(); +void exposeTypeInfo(); /* Enable Eigen-Numpy serialization for a set of standard MatrixBase instances. */ @@ -84,6 +85,7 @@ void enableEigenPy() { exposeMatrixComplexLongDouble(); exposeNoneType(); + exposeTypeInfo(); } bool withTensorSupport() { diff --git a/src/type_info.cpp b/src/type_info.cpp new file mode 100644 index 000000000..a3203cce5 --- /dev/null +++ b/src/type_info.cpp @@ -0,0 +1,68 @@ +/// +/// Copyright 2024 INRIA +/// + +#include +#include + +#include +#include + +namespace bp = boost::python; + +void exposeStdTypeIndex() { + typedef std::type_index Self; + bp::class_( + "std_type_index", + "The class type_index holds implementation-specific information about a " + "type, including the name of the type and means to compare two types for " + "equality or collating order.", + bp::no_init) + .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("hash_code", &Self::hash_code, bp::arg("self"), + "Returns an unspecified value (here denoted by hash code) such that " + "for all std::type_info objects referring to the same type, their " + "hash code is the same.") + .def("name", &Self::name, bp::arg("self"), + "Returns an implementation defined null-terminated character string " + "containing the name of the type. No guarantees are given; in " + "particular, the returned string can be identical for several types " + "and change between invocations of the same program."); +} + +void exposeBoostTypeIndex() { + typedef boost::typeindex::type_index Self; + bp::class_( + "boost_type_index", + "The class type_index holds implementation-specific information about a " + "type, including the name of the type and means to compare two types for " + "equality or collating order.", + bp::no_init) + .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("hash_code", &Self::hash_code, bp::arg("self"), + "Returns an unspecified value (here denoted by hash code) such that " + "for all std::type_info objects referring to the same type, their " + "hash code is the same.") + .def("name", &Self::name, bp::arg("self"), + "Returns an implementation defined null-terminated character string " + "containing the name of the type. No guarantees are given; in " + "particular, the returned string can be identical for several types " + "and change between invocations of the same program.") + .def("pretty_name", &Self::pretty_name, bp::arg("self"), + "Human readible name."); +} + +namespace eigenpy { +void exposeTypeInfo() { + exposeStdTypeIndex(); + exposeBoostTypeIndex(); +} +} // namespace eigenpy From 048784ca64084979bc7eba12f2127ded87eb4f8f Mon Sep 17 00:00:00 2001 From: Justin Carpentier Date: Tue, 3 Sep 2024 14:02:22 +0200 Subject: [PATCH 2/8] test: add tests for type_info --- unittest/CMakeLists.txt | 2 ++ unittest/python/test_type_info.py | 10 ++++++++++ unittest/type_info.cpp | 21 +++++++++++++++++++++ 3 files changed, 33 insertions(+) create mode 100644 unittest/python/test_type_info.py create mode 100644 unittest/type_info.cpp diff --git a/unittest/CMakeLists.txt b/unittest/CMakeLists.txt index 2c1eec31b..98485fbed 100644 --- a/unittest/CMakeLists.txt +++ b/unittest/CMakeLists.txt @@ -35,6 +35,7 @@ endfunction() add_dependencies(build_tests ${PYWRAP}) add_lib_unit_test(matrix) +add_lib_unit_test(type_info) add_lib_unit_test(multiple_registration) if(BUILD_TESTING_SCIPY) find_scipy() @@ -111,6 +112,7 @@ endif() add_lib_unit_test(bind_virtual_factory) add_python_lib_unit_test("py-matrix" "unittest/python/test_matrix.py") +add_python_lib_unit_test("py-type-info" "unittest/python/test_type_info.py") add_python_lib_unit_test("py-multiple-registration" "unittest/python/test_multiple_registration.py") diff --git a/unittest/python/test_type_info.py b/unittest/python/test_type_info.py new file mode 100644 index 000000000..c95737451 --- /dev/null +++ b/unittest/python/test_type_info.py @@ -0,0 +1,10 @@ +import type_info + +d = type_info.Dummy() +assert d.type_info().pretty_name() == "Dummy" + +assert type_info.type_info(1).pretty_name() == "int" +assert ( + type_info.type_info("toto").pretty_name() + == "std::__1::basic_string, std::__1::allocator>" +) diff --git a/unittest/type_info.cpp b/unittest/type_info.cpp new file mode 100644 index 000000000..35e410cfc --- /dev/null +++ b/unittest/type_info.cpp @@ -0,0 +1,21 @@ +/* + * Copyright 2024 INRIA + */ + +#include + +#include "eigenpy/eigenpy.hpp" +#include "eigenpy/type_info.hpp" + +struct Dummy {}; + +BOOST_PYTHON_MODULE(type_info) { + using namespace Eigen; + namespace bp = boost::python; + eigenpy::enableEigenPy(); + + eigenpy::expose_boost_type_info(); + eigenpy::expose_boost_type_info(); + + bp::class_("Dummy").def(eigenpy::TypeInfoVisitor()); +} From b1ca46f8b2b1a465fca95fb0dc873a9517409b4c Mon Sep 17 00:00:00 2001 From: Justin Carpentier Date: Tue, 3 Sep 2024 14:28:39 +0200 Subject: [PATCH 3/8] test: fix --- unittest/python/test_type_info.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/unittest/python/test_type_info.py b/unittest/python/test_type_info.py index c95737451..e90ca2ad5 100644 --- a/unittest/python/test_type_info.py +++ b/unittest/python/test_type_info.py @@ -4,7 +4,4 @@ assert d.type_info().pretty_name() == "Dummy" assert type_info.type_info(1).pretty_name() == "int" -assert ( - type_info.type_info("toto").pretty_name() - == "std::__1::basic_string, std::__1::allocator>" -) +assert "basic_string" in type_info.type_info("toto").pretty_name() From 2a9b3b52c5537c95e3f4693318afab1cfdfb0ee4 Mon Sep 17 00:00:00 2001 From: Justin Carpentier Date: Tue, 3 Sep 2024 14:30:41 +0200 Subject: [PATCH 4/8] typeinfo: fix arguments for runtime outputs --- include/eigenpy/type_info.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/eigenpy/type_info.hpp b/include/eigenpy/type_info.hpp index 7344bf423..6601677a1 100644 --- a/include/eigenpy/type_info.hpp +++ b/include/eigenpy/type_info.hpp @@ -66,10 +66,10 @@ struct TypeInfoVisitor : public bp::def_visitor > { private: static boost::typeindex::type_index boost_type_info(const C& self) { - return boost::typeindex::type_id(); + return boost::typeindex::type_id_runtime(self); } - static std::type_index std_type_info(const C& self) { return typeid(C); } + static std::type_index std_type_info(const C& self) { return typeid(self); } }; } // namespace eigenpy From 42b309f467241a71d419c1a040a4bda3b6747f41 Mon Sep 17 00:00:00 2001 From: Justin Carpentier Date: Tue, 3 Sep 2024 14:35:46 +0200 Subject: [PATCH 5/8] typeinfo: add pretty_name to std::type_index --- src/type_info.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/type_info.cpp b/src/type_info.cpp index a3203cce5..98e1e44fc 100644 --- a/src/type_info.cpp +++ b/src/type_info.cpp @@ -31,7 +31,13 @@ void exposeStdTypeIndex() { "Returns an implementation defined null-terminated character string " "containing the name of the type. No guarantees are given; in " "particular, the returned string can be identical for several types " - "and change between invocations of the same program."); + "and change between invocations of the same program.") + .def( + "pretty_name", + +[](const Self &value) -> std::string { + return boost::core::demangle(value.name()); + }, + bp::arg("self"), "Human readible name."); } void exposeBoostTypeIndex() { From 1ba35001fe423d55da50b03d2219b348a39b2e9e Mon Sep 17 00:00:00 2001 From: Justin Carpentier Date: Tue, 3 Sep 2024 14:51:22 +0200 Subject: [PATCH 6/8] changelog: sync --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8b87459eb..264fe6f7c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,9 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). ## [Unreleased] +### Added +- Add type_info helpers ([#502](https://github.com/stack-of-tasks/eigenpy/pull/502)) + ## [3.9.0] - 2024-08-31 ### Changed From e576541ac4cce6e6add4cd19ec8dc1b17bc9ac08 Mon Sep 17 00:00:00 2001 From: Justin Carpentier Date: Tue, 3 Sep 2024 17:20:51 +0200 Subject: [PATCH 7/8] test/type_info: fix issue on Windows --- unittest/python/test_type_info.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/unittest/python/test_type_info.py b/unittest/python/test_type_info.py index e90ca2ad5..09de55928 100644 --- a/unittest/python/test_type_info.py +++ b/unittest/python/test_type_info.py @@ -1,7 +1,7 @@ import type_info d = type_info.Dummy() -assert d.type_info().pretty_name() == "Dummy" +assert "Dummy" in d.type_info().pretty_name() assert type_info.type_info(1).pretty_name() == "int" assert "basic_string" in type_info.type_info("toto").pretty_name() From 6552b9ac7a0e21a00797f4930727d7a9d43bb282 Mon Sep 17 00:00:00 2001 From: Justin Carpentier Date: Tue, 3 Sep 2024 17:21:10 +0200 Subject: [PATCH 8/8] core: avoid duplicate registration --- src/type_info.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/type_info.cpp b/src/type_info.cpp index 98e1e44fc..482370cea 100644 --- a/src/type_info.cpp +++ b/src/type_info.cpp @@ -8,10 +8,16 @@ #include #include +#include "eigenpy/registration.hpp" + namespace bp = boost::python; +namespace eigenpy { + void exposeStdTypeIndex() { typedef std::type_index Self; + if (register_symbolic_link_to_registered_type()) return; + bp::class_( "std_type_index", "The class type_index holds implementation-specific information about a " @@ -42,6 +48,8 @@ void exposeStdTypeIndex() { void exposeBoostTypeIndex() { typedef boost::typeindex::type_index Self; + if (register_symbolic_link_to_registered_type()) return; + bp::class_( "boost_type_index", "The class type_index holds implementation-specific information about a " @@ -66,7 +74,6 @@ void exposeBoostTypeIndex() { "Human readible name."); } -namespace eigenpy { void exposeTypeInfo() { exposeStdTypeIndex(); exposeBoostTypeIndex();