Don't rely on typeid() naming in VariantArray

This commit is contained in:
Thomas Krijnen
2026-06-14 20:27:49 +02:00
parent ca99ef3af7
commit 3e7b739d8d
3 changed files with 127 additions and 19 deletions
+16 -1
View File
@@ -34,7 +34,22 @@ namespace {
inline T dispatch_get_(AttributeValue::pointer_type array_, uint8_t storage_model_, size_t instance_name_, const IfcParse::declaration* entity_or_type, uint8_t index_)
{
if (storage_model_ == 0) {
return array_.storage_ptr->get<T>(index_);
try {
return array_.storage_ptr->get<T>(index_);
} catch (const impl::storage_type_mismatch& e) {
throw IfcParse::IfcException(
// entity_or_type not passed, but in v0.9 this is beginning to make sense
(entity_or_type
? std::string("On instance #" + std::to_string(instance_name_) + " of " + entity_or_type->name() + ": ")
: std::string("")) +
"Requested type <" + e.requested() + "> does not match actual type <" + e.actual() + "> at index " + std::to_string(index_));
} catch (const std::out_of_range& e) {
throw IfcParse::IfcException(
(entity_or_type
? std::string("On instance #" + std::to_string(instance_name_) + " of " + entity_or_type->name() + ": ")
: std::string("")) +
e.what());
}
}
#ifdef IFOPSH_WITH_ROCKSDB
else {
+77
View File
@@ -72,6 +72,83 @@ class IFC_PARSE_API Derived {};
class IFC_PARSE_API empty_aggregate_t {};
class IFC_PARSE_API empty_aggregate_of_aggregate_t {};
namespace impl {
template <>
struct VariantTypeName<Blank> {
static std::string get() { return "null"; }
};
template <>
struct VariantTypeName<Derived> {
static std::string get() { return "derived"; }
};
template <>
struct VariantTypeName<int> {
static std::string get() { return "int"; }
};
template <>
struct VariantTypeName<bool> {
static std::string get() { return "bool"; }
};
template <>
struct VariantTypeName<boost::logic::tribool> {
static std::string get() { return "logical"; }
};
template <>
struct VariantTypeName<double> {
static std::string get() { return "real"; }
};
template <>
struct VariantTypeName<std::string> {
static std::string get() { return "string"; }
};
template <>
struct VariantTypeName<boost::dynamic_bitset<>> {
static std::string get() { return "binary"; }
};
template <>
struct VariantTypeName<EnumerationReference> {
static std::string get() { return "enumeration"; }
};
template <>
struct VariantTypeName<IfcUtil::IfcBaseClass*> {
static std::string get() { return "instance"; }
};
template <>
struct VariantTypeName<empty_aggregate_t> {
static std::string get() { return "aggregate"; }
};
template <typename T, typename Allocator>
struct VariantTypeName<std::vector<T, Allocator>> {
static std::string get() { return "aggregate of " + VariantTypeName<T>::get(); }
};
template <>
struct VariantTypeName<aggregate_of_instance::ptr> {
static std::string get() { return "aggregate of instance"; }
};
template <>
struct VariantTypeName<empty_aggregate_of_aggregate_t> {
static std::string get() { return "aggregate of aggregate"; }
};
template <>
struct VariantTypeName<aggregate_of_aggregate_of_instance::ptr> {
static std::string get() { return "aggregate of aggregate of instance"; }
};
}
template<typename... Args>
struct parameter_pack {
static constexpr size_t size = sizeof...(Args);
+34 -18
View File
@@ -37,10 +37,28 @@ variant - which is the maximum size of its constituents - is reduced.
#include <cstring>
#include <cstddef>
#include <limits>
#include "IfcException.h"
#include <exception>
namespace impl {
class storage_type_mismatch : public std::exception {
private:
std::string requested_, actual__, message_;
public:
storage_type_mismatch(const std::string& requested, const std::string& actual)
: requested_(requested), actual__(actual), message_("Requested type " + requested_ + " does not match actual type " + actual__) {}
const char* what() const noexcept override {
return message_.c_str();
}
const std::string& requested() const { return requested_; }
const std::string& actual() const { return actual__; }
};
template <typename T>
struct VariantTypeName;
// Trait to detect unique_ptr
template <typename...> struct is_unique_ptr : std::false_type {};
template<class T, typename... Args>
@@ -166,14 +184,13 @@ public:
using U = std::decay_t<T>;
static_assert(::impl::TypeIndex_v<U, Types...> < sizeof...(Types), "Type not supported by variant");
if (index >= size()) {
throw std::out_of_range("Index out of range");
throw std::out_of_range("Index " + std::to_string(index) + " is out of range for storage of size " + std::to_string(size()));
}
destroy_at_index(index);
size_and_indices_[index + 1] = ::impl::TypeIndex_v<U, Types...>;
using V = typename std::tuple_element<::impl::TypeIndex_v<U, Types...>, ::impl::MapTypes_t<Types... >>::type;
// std::wcout << "setting " << index << " to " << typeid(V).name() << " (" << ::impl::TypeIndex_v<U, Types...> << ")" << std::endl;
if constexpr (::impl::is_unique_ptr<V>::value) {
new(&storage_[index]) V(new U(value));
} else {
@@ -187,8 +204,8 @@ public:
std::size_t index(std::size_t index) const {
if (index >= size()) {
throw IfcParse::IfcException(
"Index " + std::to_string(index) + " is out of range for variant of size " + std::to_string(size())
throw std::out_of_range(
"Index " + std::to_string(index) + " is out of range for storage of size " + std::to_string(size())
);
}
return size_and_indices_[index + 1];
@@ -197,8 +214,8 @@ public:
template<typename T>
T& get(std::size_t index) {
if (index >= size()) {
throw IfcParse::IfcException(
"Index " + std::to_string(index) + " is out of range for variant of size " + std::to_string(size())
throw std::out_of_range(
"Index " + std::to_string(index) + " is out of range for storage of size " + std::to_string(size())
);
}
if (!has<T>(index)) {
@@ -220,17 +237,16 @@ public:
template<typename T>
const T& get(std::size_t index) const {
if (index >= size()) {
throw IfcParse::IfcException(
"Index " + std::to_string(index) + " is out of range for variant of size " + std::to_string(size())
throw std::out_of_range(
"Index " + std::to_string(index) + " is out of range for storage of size " + std::to_string(size())
);
}
if (size_and_indices_[index + 1] != ::impl::TypeIndex<T, Types...>::value) {
// @todo this IfcException is silly. Figure out what
// to do, but at the moment it is specifically caught
// in various places.
throw IfcParse::IfcException(
"Type held at index " + std::to_string(index) + " is " +
get_type_name(size_and_indices_[index + 1]) + " and not " + typeid(T).name()
throw impl::storage_type_mismatch(
::impl::VariantTypeName<T>::get(), get_type_name(size_and_indices_[index + 1])
);
}
using V = typename std::tuple_element<::impl::TypeIndex_v<T, Types...>, ::impl::MapTypes_t<Types... >>::type;
@@ -244,8 +260,8 @@ public:
template<typename Visitor>
auto apply_visitor(Visitor&& visitor, std::size_t index) const {
if (index >= size()) {
throw IfcParse::IfcException(
"Index " + std::to_string(index) + " is out of range for variant of size " + std::to_string(size())
throw std::out_of_range(
"Index " + std::to_string(index) + " is out of range for storage of size " + std::to_string(size())
);
}
return apply_visitor_impl(std::forward<Visitor>(visitor), index, std::integral_constant<std::size_t, sizeof...(Types)>{});
@@ -312,19 +328,19 @@ private:
}
template <size_t I>
const char* get_type_name_impl(size_t i) const {
std::string get_type_name_impl(size_t i) const {
if constexpr (I == 0) {
return "";
} else {
if (i == I - 1) {
return typeid(std::tuple_element_t<I - 1, std::tuple<Types...>>).name();
return ::impl::VariantTypeName<std::tuple_element_t<I - 1, std::tuple<Types...>>>::get();
} else {
return get_type_name_impl<I - 1>(i);
}
}
}
const char* get_type_name(size_t i) const {
std::string get_type_name(size_t i) const {
return get_type_name_impl<sizeof...(Types)>(i);
}
};