From 40f6ae1ede8592443321351195600944b278e97f Mon Sep 17 00:00:00 2001 From: Antony Polukhin Date: Fri, 21 Feb 2014 12:52:07 +0400 Subject: [PATCH] Make code thread safe even on antique/broken compilers. Improve examples and add more tests --- examples/user_defined_typeinfo.hpp | 19 +++++++------------ include/boost/type_index.hpp | 2 ++ include/boost/type_index/ctti_type_index.hpp | 20 ++++++++++++++------ test/Jamfile.v2 | 4 +++- test/type_index_test.cpp | 19 +++++++++++++++++++ test/type_index_test_ctti_construct_fail.cpp | 15 +++++++++++++++ test/type_index_test_ctti_copy_fail.cpp | 15 +++++++++++++++ 7 files changed, 75 insertions(+), 19 deletions(-) create mode 100644 test/type_index_test_ctti_construct_fail.cpp create mode 100644 test/type_index_test_ctti_copy_fail.cpp diff --git a/examples/user_defined_typeinfo.hpp b/examples/user_defined_typeinfo.hpp index 6e1e250..8d63869 100644 --- a/examples/user_defined_typeinfo.hpp +++ b/examples/user_defined_typeinfo.hpp @@ -49,15 +49,16 @@ namespace my_namespace { namespace detail { // my_typeinfo structure is used to save type number struct my_typeinfo { - // type_[0] will hold a type number - // type_[1] will be '\0', to have a zero terminated raw type name - char type_[2]; + const char* const type_; + }; + + const my_typeinfo infos[5] = { + {"void"}, {"my_class"}, {"my_struct"}, {"my_classes"}, {"my_string"} }; template inline const my_typeinfo& my_typeinfo_construct() { - static const my_typeinfo ret = {{ static_cast(typenum::value), '\0' }}; - return ret; + return infos[typenum::value]; } }} // my_namespace::detail @@ -97,13 +98,7 @@ public: } inline std::string pretty_name() const { - // Must be in sync with detail::typenum::value - static const char* names[] = { - "void", "my_class", "my_struct", "my_classes", "my_string" - }; - - const std::size_t indx = static_cast(data_->type_[0]); - return names[indx]; + return data_->type_; } template diff --git a/include/boost/type_index.hpp b/include/boost/type_index.hpp index a7c1aa1..3592bcb 100644 --- a/include/boost/type_index.hpp +++ b/include/boost/type_index.hpp @@ -61,6 +61,8 @@ namespace boost { namespace typeind { /// /// Could be a std::type_info, boost::typeind::detail::ctti_data or /// some user defined class. +/// +/// type_info \b is \b not copyable or default constructible. It is \b not assignable too! typedef type_index::type_info_t type_info; #if defined(BOOST_TYPE_INDEX_DOXYGEN_INVOKED) diff --git a/include/boost/type_index/ctti_type_index.hpp b/include/boost/type_index/ctti_type_index.hpp index b44fc22..752057a 100644 --- a/include/boost/type_index/ctti_type_index.hpp +++ b/include/boost/type_index/ctti_type_index.hpp @@ -35,17 +35,25 @@ namespace boost { namespace typeind { namespace detail { -struct ctti_data { - const char* typename_; -}; +// That's the most trickiest part of the TypeIndex library: +// 1) we do not whant to give user ability to manually construct and compare `struct-that-represents-type` +// 2) we need to distinguish beteween `struct-that-represents-type` and `const char*` +// 3) we need a thread-safe way to have references to instances `struct-that-represents-type` +// 4) we need a compile-time control to make shure that user does not copy or +// default construct `struct-that-represents-type` +// +// Solution would be a forward declared structure. +struct ctti_data; } // namespace detail /// Helper method for getting detail::ctti_data of a tempalte patameter T. template inline const detail::ctti_data& ctti_construct() BOOST_NOEXCEPT { - static const detail::ctti_data result = { boost::detail::ctti::n() }; - return result; + // Standard C++11, 5.2.10 Reinterpret cast: Converting a prvalue of type “pointer to T1” to the + // type “pointer to T2” (where T1 and T2 are object types and where the alignment requirements of T2 are no + // stricter than those of T1) and back to its original type yields the original pointer value. + return *reinterpret_cast(boost::detail::ctti::n()); } /// \class ctti_type_index @@ -111,7 +119,7 @@ inline ctti_type_index ctti_type_index::type_id_runtime(const T& variable) BOOST inline const char* ctti_type_index::raw_name() const BOOST_NOEXCEPT { - return data_->typename_; + return reinterpret_cast(data_); } diff --git a/test/Jamfile.v2 b/test/Jamfile.v2 index e690b6c..9775ba7 100644 --- a/test/Jamfile.v2 +++ b/test/Jamfile.v2 @@ -38,7 +38,9 @@ test-suite type_index [ run type_index_test.cpp $(tlib) : : : off : type_index_test_no_rtti ] [ run testing_crossmodule.cpp test_lib_rtti $(tlib) ] [ run testing_crossmodule.cpp test_lib_nortti $(tlib) : : : off : testing_crossmodule_no_rtti ] - + [ compile-fail type_index_test_ctti_copy_fail.cpp ] + [ compile-fail type_index_test_ctti_construct_fail.cpp ] + # Mixing RTTI on and off [ link-fail testing_crossmodule.cpp $(tlib) test_lib_rtti : $(nortti) : link_fail_nortti_rtti ] # MSVC sometimes overrides the /GR-, that's why the following tests is disabled diff --git a/test/type_index_test.cpp b/test/type_index_test.cpp index 8618f60..0e5a901 100644 --- a/test/type_index_test.cpp +++ b/test/type_index_test.cpp @@ -100,11 +100,30 @@ static void test_with_modofiers() { type_index t2 = type_id_with_cvr(); BOOST_CHECK_NE(t2, t1); + BOOST_CHECK(t2 != t1.type_info()); + BOOST_CHECK(t2.type_info() != t1); + BOOST_CHECK(t1 < t2 || t2 < t1); BOOST_CHECK(t1 > t2 || t2 > t1); + BOOST_CHECK(t1.type_info() < t2 || t2.type_info() < t1); + BOOST_CHECK(t1.type_info() > t2 || t2.type_info() > t1); + BOOST_CHECK(t1 < t2.type_info() || t2 < t1.type_info()); + BOOST_CHECK(t1 > t2.type_info() || t2 > t1.type_info()); + + // Chaecking that comparisom operators overloads compile + BOOST_CHECK(t1 <= t2 || t2 <= t1); + BOOST_CHECK(t1 >= t2 || t2 >= t1); + BOOST_CHECK(t1.type_info() <= t2 || t2.type_info() <= t1); + BOOST_CHECK(t1.type_info() >= t2 || t2.type_info() >= t1); + BOOST_CHECK(t1 <= t2.type_info() || t2 <= t1.type_info()); + BOOST_CHECK(t1 >= t2.type_info() || t2 >= t1.type_info()); BOOST_CHECK_EQUAL(t1, type_id_with_cvr()); BOOST_CHECK_EQUAL(t2, type_id_with_cvr()); + BOOST_CHECK(t1 == type_id_with_cvr().type_info()); + BOOST_CHECK(t2 == type_id_with_cvr().type_info()); + BOOST_CHECK(t1.type_info() == type_id_with_cvr()); + BOOST_CHECK(t2.type_info() == type_id_with_cvr()); BOOST_CHECK_EQUAL(t1.hash_code(), type_id_with_cvr().hash_code()); BOOST_CHECK_EQUAL(t2.hash_code(), type_id_with_cvr().hash_code()); diff --git a/test/type_index_test_ctti_construct_fail.cpp b/test/type_index_test_ctti_construct_fail.cpp new file mode 100644 index 0000000..1c2cdc7 --- /dev/null +++ b/test/type_index_test_ctti_construct_fail.cpp @@ -0,0 +1,15 @@ +// +// Copyright Antony Polukhin, 2012-2013. +// +// Distributed under the Boost Software License, Version 1.0. (See +// accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include + +int main() { + using namespace boost::typeind; + ctti_type_index::type_info_t t; + (void)t; +} + diff --git a/test/type_index_test_ctti_copy_fail.cpp b/test/type_index_test_ctti_copy_fail.cpp new file mode 100644 index 0000000..0133bd6 --- /dev/null +++ b/test/type_index_test_ctti_copy_fail.cpp @@ -0,0 +1,15 @@ +// +// Copyright Antony Polukhin, 2012-2013. +// +// Distributed under the Boost Software License, Version 1.0. (See +// accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include + +int main() { + using namespace boost::typeind; + ctti_type_index::type_info_t t = ctti_type_index::type_id().type_info(); + (void)t; +} +