diff --git a/gtsam/CMakeLists.txt b/gtsam/CMakeLists.txt index 1079b845f..cabd5e402 100644 --- a/gtsam/CMakeLists.txt +++ b/gtsam/CMakeLists.txt @@ -172,6 +172,10 @@ if(WIN32) # Add 'lib' prefix to static library to avoid filename collision with endif() endif() +if(WIN32) # library to help with demangling variable names on Windows + target_link_libraries(gtsam PRIVATE Dbghelp) +endif() + install( TARGETS gtsam EXPORT GTSAM-exports diff --git a/gtsam/base/GenericValue.h b/gtsam/base/GenericValue.h index aee6c0e62..e1cb3bc2c 100644 --- a/gtsam/base/GenericValue.h +++ b/gtsam/base/GenericValue.h @@ -20,6 +20,7 @@ #pragma once #include +#include #include #include @@ -83,7 +84,7 @@ public: /// Virtual print function, uses traits virtual void print(const std::string& str) const { - std::cout << "(" << typeid(T).name() << ") "; + std::cout << "(" << demangle(typeid(T).name()) << ") "; traits::Print(value_, str); } diff --git a/gtsam/base/types.cpp b/gtsam/base/types.cpp new file mode 100644 index 000000000..edc449b12 --- /dev/null +++ b/gtsam/base/types.cpp @@ -0,0 +1,67 @@ +/* ---------------------------------------------------------------------------- + + * GTSAM Copyright 2010, Georgia Tech Research Corporation, + * Atlanta, Georgia 30332-0415 + * All Rights Reserved + * Authors: Frank Dellaert, et al. (see THANKS for the full author list) + + * See LICENSE for the license information + + * -------------------------------------------------------------------------- */ + +/** + * @file types.cpp + * @brief Functions for handling type information + * @author Varun Agrawal + * @date May 18, 2020 + * @addtogroup base + */ + +#include + +#ifdef _WIN32 +#define WIN32_LEAN_AND_MEAN +#include +#include +#endif + +#ifdef __GNUG__ +#include +#include +#include +#endif + +namespace gtsam { + +/// Pretty print Value type name +std::string demangle(const char* name) { + // by default set to the original mangled name + std::string demangled_name = std::string(name); + +#ifdef __GNUG__ + // g++ version of demangle + char* demangled = nullptr; + int status = -1; // some arbitrary value to eliminate the compiler warning + demangled = abi::__cxa_demangle(name, nullptr, nullptr, &status), + + demangled_name = (status == 0) ? std::string(demangled) : std::string(name); + + std::free(demangled); + +#elif _WIN32 + char undecorated_name[1024]; + + if (UnDecorateSymbolName( + name, undecorated_name, sizeof(undecorated_name), + UNDNAME_COMPLETE)) + { + // successful conversion, take value from: undecorated_name + demangled_name = std::string(undecorated_name); + } + // else keep using mangled name +#endif + + return demangled_name; +} + +} /* namespace gtsam */ diff --git a/gtsam/base/types.h b/gtsam/base/types.h index d12a4209a..2fa6eebb6 100644 --- a/gtsam/base/types.h +++ b/gtsam/base/types.h @@ -53,6 +53,9 @@ namespace gtsam { + /// Function to demangle type name of variable, e.g. demangle(typeid(x).name()) + std::string demangle(const char* name); + /// Integer nonlinear key type typedef std::uint64_t Key; diff --git a/gtsam/nonlinear/Values.cpp b/gtsam/nonlinear/Values.cpp index 1bd8b3a73..98790ccd9 100644 --- a/gtsam/nonlinear/Values.cpp +++ b/gtsam/nonlinear/Values.cpp @@ -232,10 +232,21 @@ namespace gtsam { /* ************************************************************************* */ const char* ValuesIncorrectType::what() const throw() { - if(message_.empty()) - message_ = - "Attempting to retrieve value with key \"" + DefaultKeyFormatter(key_) + "\", type stored in Values is " + - std::string(storedTypeId_.name()) + " but requested type was " + std::string(requestedTypeId_.name()); + if(message_.empty()) { + std::string storedTypeName = demangle(storedTypeId_.name()); + std::string requestedTypeName = demangle(requestedTypeId_.name()); + + if (storedTypeName == requestedTypeName) { + message_ = "WARNING: Detected types with same name but different `typeid`. \ + This is usually caused by incorrect linking/inlining settings when compiling libraries using GTSAM. \ + If you are a user, please report to the author of the library using GTSAM. \ + If you are a package maintainer, please consult `cmake/GtsamPybindWrap.cmake`, line 74 for details."; + } else { + message_ = + "Attempting to retrieve value with key \"" + DefaultKeyFormatter(key_) + "\", type stored in Values is " + + storedTypeName + " but requested type was " + requestedTypeName; + } + } return message_.c_str(); } diff --git a/gtsam/nonlinear/internal/ExpressionNode.h b/gtsam/nonlinear/internal/ExpressionNode.h index 6752d7fc0..0011efb74 100644 --- a/gtsam/nonlinear/internal/ExpressionNode.h +++ b/gtsam/nonlinear/internal/ExpressionNode.h @@ -84,7 +84,7 @@ public: /// Streaming GTSAM_EXPORT friend std::ostream& operator<<(std::ostream& os, const ExpressionNode& node) { - os << "Expression of type " << typeid(T).name(); + os << "Expression of type " << demangle(typeid(T).name()); if (node.traceSize_ > 0) os << ", trace size = " << node.traceSize_; os << "\n"; return os; @@ -219,7 +219,7 @@ static void PrintJacobianAndTrace(const std::string& indent, const typename Jacobian::type& dTdA, const ExecutionTrace trace) { static const Eigen::IOFormat kMatlabFormat(0, 1, " ", "; ", "", "", "[", "]"); - std::cout << indent << "D(" << typeid(T).name() << ")/D(" << typeid(A).name() + std::cout << indent << "D(" << demangle(typeid(T).name()) << ")/D(" << demangle(typeid(A).name()) << ") = " << dTdA.format(kMatlabFormat) << std::endl; trace.print(indent); } @@ -605,7 +605,7 @@ class ScalarMultiplyNode : public ExpressionNode { /// Print to std::cout void print(const std::string& indent) const { std::cout << indent << "ScalarMultiplyNode::Record {" << std::endl; - std::cout << indent << "D(" << typeid(T).name() << ")/D(" << typeid(T).name() + std::cout << indent << "D(" << demangle(typeid(T).name()) << ")/D(" << demangle(typeid(T).name()) << ") = " << scalar_dTdA << std::endl; trace.print(); std::cout << indent << "}" << std::endl; diff --git a/gtsam/nonlinear/tests/testValues.cpp b/gtsam/nonlinear/tests/testValues.cpp index b8eee540d..2f624f527 100644 --- a/gtsam/nonlinear/tests/testValues.cpp +++ b/gtsam/nonlinear/tests/testValues.cpp @@ -589,6 +589,23 @@ TEST(Values, MatrixDynamicInsertFixedRead) { CHECK_EXCEPTION(values.at(key1), exception); } +TEST(Values, Demangle) { + Values values; + Matrix13 v; v << 5.0, 6.0, 7.0; + values.insert(key1, v); + string expected = "Values with 1 values:\nValue v1: (Eigen::Matrix) [\n 5, 6, 7\n]\n\n"; + + stringstream buffer; + streambuf * old = cout.rdbuf(buffer.rdbuf()); + + values.print(); + + string actual = buffer.str(); + cout.rdbuf(old); + + EXPECT(assert_equal(expected, actual)); +} + /* ************************************************************************* */ int main() { TestResult tr; return TestRegistry::runAllTests(tr); } /* ************************************************************************* */