Configurable pointer type; std::from_chars(); aggregate inverses in vector; skip parse_context

This commit is contained in:
Thomas Krijnen
2026-06-11 15:51:40 +02:00
parent c8d39cc481
commit 4c13e2424c
11 changed files with 860 additions and 624 deletions
+2 -332
View File
@@ -10,336 +10,6 @@
#include <sys/types.h>
#include <sys/stat.h>
ifcopenshell::parse_context::~parse_context() {
for (auto& t : tokens_) {
std::visit([](auto& v) {
if constexpr (std::is_same_v<std::decay_t<decltype(v)>, parse_context*>) {
delete v;
}
}, t);
}
}
ifcopenshell::parse_context& ifcopenshell::parse_context::push() {
auto* pc = new parse_context;
tokens_.push_back(pc);
return *pc;
}
void ifcopenshell::parse_context::push(token t) {
tokens_.push_back(t);
}
void ifcopenshell::parse_context::push(const express::Base& inst) {
tokens_.push_back(inst);
}
namespace {
template<typename Variant, typename T>
struct is_type_in_variant;
// Specialization when there are multiple types in the variant
template<typename T, typename First, typename... Rest>
struct is_type_in_variant<std::variant<First, Rest...>, T>
{
static constexpr bool value = std::is_same<T, First>::value || is_type_in_variant<std::variant<Rest...>, T>::value;
};
// Specialization when there is only one type left in the variant
template<typename T, typename Last>
struct is_type_in_variant<std::variant<Last>, T>
{
static constexpr bool value = std::is_same<T, Last>::value;
};
template<typename Variant, typename T>
constexpr bool is_type_in_variant_v = is_type_in_variant<Variant, T>::value;
template <typename Fn>
void dispatch_token(std::optional<size_t> instance_id, int attribute_id, ifcopenshell::token t, ifcopenshell::declaration* decl, Fn fn) {
if (t.is_binary()) {
fn(t.as_binary());
} else if (t.is_bool()) {
fn(t.as_bool());
} else if (t.is_logical()) {
fn(t.as_logical());
} else if (t.is_enumeration()) {
const auto& s = t.as_string();
if (decl && decl->as_enumeration_type()) {
try {
fn(enumeration_reference(decl->as_enumeration_type(), decl->as_enumeration_type()->lookup_enum_offset(s)));
} catch (ifcopenshell::exception& e) {
logger::error("An enumeration literal '" + s + "' is not valid for type '" + decl->name() + "' at offset " + std::to_string(t.start_pos));
}
} else {
logger::error("An enumeration literal '" + s + "' is not expected at attribute index '" + std::to_string(attribute_id) + "' at offset " + std::to_string(t.start_pos));
}
} else if (t.is_int()) {
// @nb make sure is_int() comes before is_float()
fn(t.as_int());
} else if (t.is_float()) {
fn(t.as_float());
} else if (t.is_identifier()) {
fn(ifcopenshell::reference_or_simple_type{ifcopenshell::instance_reference{(int) t.as_identifier(), t.start_pos}});
} else if (t.is_string()) {
fn(t.as_string());
} else if (t.is_operator('*')) {
// This is only in place for the validator
fn(derived{});
}
}
template <size_t Depth, typename Fn>
void construct_(std::optional<size_t> instance_id, int attribute_id, ifcopenshell::parse_context& p, const ifcopenshell::aggregation_type* aggr, Fn fn) {
if (p.tokens_.empty()) {
// @todo instead of ugly if-else we could also default initialize the respective
// variant types below.
if (aggr) {
auto aggr_type = ifcopenshell::make_aggregate(ifcopenshell::from_parameter_type(aggr->type_of_element()));
if (aggr_type == ifcopenshell::Argument_AGGREGATE_OF_INT) {
fn(std::vector<int>{});
} else if (aggr_type == ifcopenshell::Argument_AGGREGATE_OF_DOUBLE) {
fn(std::vector<double>{});
} else if (aggr_type == ifcopenshell::Argument_AGGREGATE_OF_STRING) {
fn(std::vector<std::string>{});
} else if (aggr_type == ifcopenshell::Argument_AGGREGATE_OF_BINARY) {
fn(std::vector<boost::dynamic_bitset<>>{});
} else if (aggr_type == ifcopenshell::Argument_AGGREGATE_OF_ENTITY_INSTANCE) {
fn(std::vector<express::Base>{});
} else if (aggr_type == ifcopenshell::Argument_AGGREGATE_OF_AGGREGATE_OF_INT) {
fn(std::vector<std::vector<int>>{});
} else if (aggr_type == ifcopenshell::Argument_AGGREGATE_OF_AGGREGATE_OF_DOUBLE) {
fn(std::vector<std::vector<double>>{});
} else if (aggr_type == ifcopenshell::Argument_AGGREGATE_OF_AGGREGATE_OF_ENTITY_INSTANCE) {
fn(std::vector<std::vector<express::Base>>{});
}
}
return;
}
typedef std::variant<
blank,
std::vector<int>,
std::vector<double>,
std::vector<std::string>,
std::vector<boost::dynamic_bitset<>>,
std::vector<ifcopenshell::reference_or_simple_type>,
std::vector<std::vector<int>>,
std::vector<std::vector<double>>,
std::vector<std::vector<ifcopenshell::reference_or_simple_type>>
> possible_aggregation_types_t;
possible_aggregation_types_t aggregate_storage;
auto append_to_aggregate_storage = [&aggregate_storage](const auto& v) {
if constexpr (is_type_in_variant_v<possible_aggregation_types_t, std::vector<std::decay_t<decltype(v)>>>) {
if (aggregate_storage.index() == 0) {
aggregate_storage = std::vector<std::decay_t<decltype(v)>>{ v };
} else {
if (auto* vec_ptr = std::get_if<std::vector<std::decay_t<decltype(v)>>>(&aggregate_storage)) {
vec_ptr->push_back(v);
} else {
if constexpr (std::is_same_v<std::decay_t<decltype(v)>, int>) {
auto* vec_ptr2 = std::get_if<std::vector<double>>(&aggregate_storage);
if (vec_ptr2) {
// double[] + int
vec_ptr2->push_back((double) v);
}
}
if constexpr (std::is_same_v<std::decay_t<decltype(v)>, double>) {
auto* vec_ptr2 = std::get_if<std::vector<int>>(&aggregate_storage);
if (vec_ptr2) {
// int[] -> double[] + double
std::vector<double> ps(vec_ptr2->begin(), vec_ptr2->end());
ps.push_back(v);
aggregate_storage = ps;
}
}
if constexpr (std::is_same_v<std::decay_t<decltype(v)>, std::vector<int>>) {
auto* vec_ptr2 = std::get_if<std::vector<std::vector<double>>>(&aggregate_storage);
if (vec_ptr2) {
// double[][] + int[]
std::vector<double> vd(v.begin(), v.end());
vec_ptr2->push_back(vd);
}
}
if constexpr (std::is_same_v<std::decay_t<decltype(v)>, std::vector<double>>) {
auto* vec_ptr2 = std::get_if<std::vector<std::vector<int>>>(&aggregate_storage);
if (vec_ptr2) {
// int[][] -> double[][] + double[]
std::vector<std::vector<double>> vvd;
for (auto& vv : *vec_ptr2) {
std::vector<double> vd(vv.begin(), vv.end());
vvd.push_back(vd);
}
vvd.push_back(v);
aggregate_storage = vvd;
}
}
// @todo would be cool if we can trace this back to file offset
auto current = std::visit([](auto v) {
if constexpr (!std::is_same_v<decltype(v), blank>) {
return std::string(typeid(typename decltype(v)::value_type).name());
} else {
// Cannot occur as aggregate_storage.which() == 0
// is another branch several statements up. But is
// needed for consistency of return type.
return std::string{};
}
}, aggregate_storage);
logger::error("Inconsistent aggregate valuation while attempting to append " + std::string(typeid(decltype(v)).name()) + " to an aggregate of " + current);
// @todo boolean -> logical upgrade
// wait a second... there are no aggregate of bool / logical in the schema..
//
// if constexpr (std::is_same_v<std::decay_t<decltype(v)>, bool>) {
// auto* vec_ptr = boost::get<std::vector<boost::tribool>(&aggregate_storage);
// vec_ptr->push_back(v);
// }
// if constexpr (std::is_same_v<std::decay_t<decltype(v)>, boost::tribool>) {
// auto* vec_ptr = boost::get<std::vector<bool>(&aggregate_storage);
// std::vector<boost::tribool> ps(vec_ptr->begin(), vec_ptr->end());
// ps.push_back(v);
// aggregate_storage = ps;
// }
}
}
} else {
// @todo would be cool if we can trace this back to file offset
logger::error(std::string("Aggregates of ") + typeid(decltype(v)).name() + " are not supported in the IfcOpenShell parser");
}
};
for (auto& t : p.tokens_) {
std::visit([&aggregate_storage, &append_to_aggregate_storage, aggr, instance_id, attribute_id](const auto& v) {
if constexpr (std::is_same_v<std::decay_t<decltype(v)>, ifcopenshell::token>) {
// @todo get aggregate of enumeration
dispatch_token(instance_id, attribute_id, v, aggr && aggr->type_of_element()->as_named_type() ? aggr->type_of_element()->as_named_type()->declared_type() : nullptr, append_to_aggregate_storage);
} else if constexpr (std::is_same_v<std::decay_t<decltype(v)>, ifcopenshell::parse_context*>) {
// nested list
if constexpr (Depth < 3) {
construct_<Depth + 1>(instance_id, attribute_id, *v, nullptr, append_to_aggregate_storage);
}
} else {
append_to_aggregate_storage(ifcopenshell::reference_or_simple_type{ v });
}
}, t);
}
std::visit(fn, aggregate_storage);
}
}
std::shared_ptr<instance_data> ifcopenshell::parse_context::construct(ifcopenshell::file* owner, std::optional<size_t> name, unresolved_references& references_to_resolve, const ifcopenshell::declaration* decl, std::optional<size_t> expected_size, int resolve_reference_index, bool coerce_attribute_count) {
std::vector<const ifcopenshell::parameter_type*> parameter_types;
std::unique_ptr<ifcopenshell::named_type> transient_named_type;
if ((decl != nullptr) && (decl->as_type_declaration() != nullptr)) {
parameter_types = { decl->as_type_declaration()->declared_type() };
} else if ((decl != nullptr) && (decl->as_enumeration_type() != nullptr)) {
transient_named_type.reset(new ifcopenshell::named_type(const_cast<ifcopenshell::declaration*>(decl)));
parameter_types = { &*transient_named_type };
} else if ((decl != nullptr) && (decl->as_entity() != nullptr)) {
const auto& entity_attrs = decl->as_entity()->all_attributes();
std::transform(
entity_attrs.begin(),
entity_attrs.end(),
std::back_inserter(parameter_types),
[](auto* attr) {
return attr->type_of_attribute();
}
);
}
if (((decl != nullptr) && (tokens_.size() != parameter_types.size())) ||
expected_size && *expected_size != tokens_.size())
{
size_t expected = expected_size ? *expected_size : parameter_types.size();
if (decl != nullptr && decl->schema() == &Header_section_schema::get_schema()) {
logger::warning("Expected " + std::to_string(expected) + " attribute values, found " + std::to_string(tokens_.size()) + " for header entity " + decl->name());
} else {
logger::warning("Expected " + std::to_string(expected) + " attribute values, found " + std::to_string(tokens_.size()) + (name ? std::string(" for instance #" + std::to_string(*name)) : std::string("")));
}
}
if (tokens_.empty()) {
return std::make_shared<instance_data>(owner, decl, name.value_or(0), in_memory_attribute_storage(0));
}
in_memory_attribute_storage storage(coerce_attribute_count
? (decl != nullptr
? (std::min)(parameter_types.size(), tokens_.size())
: tokens_.size())
: tokens_.size()
);
auto it = tokens_.begin();
auto kt = parameter_types.begin();
for (; it != tokens_.end() && ((decl == nullptr) || kt != parameter_types.end()); ++it) {
auto& token = *it;
// @todo coerce to expected type, e.g empty -> std::vector<int>, bool -> logical
const ifcopenshell::parameter_type* param_type = nullptr;
if (decl != nullptr) {
param_type = *kt;
}
auto index = (uint8_t) std::distance(tokens_.begin(), it);
std::visit([this, &storage, name, &references_to_resolve, index, param_type, resolve_reference_index](const auto& v) {
if constexpr (std::is_same_v<std::decay_t<decltype(v)>, ifcopenshell::token>) {
dispatch_token(name, index, v, param_type && param_type->as_named_type() ? param_type->as_named_type()->declared_type() : nullptr, [this, &storage, name, &references_to_resolve, index, resolve_reference_index](auto v) {
if constexpr (std::is_same_v<std::decay_t<decltype(v)>, ifcopenshell::reference_or_simple_type>) {
if (name) {
references_to_resolve.push_back(std::make_pair(
// @todo previously this was storage but apparently the
// pointer is not constant with the moving and temporary nature
// maybe it ought to be and in that case a pointer is more direct
mutable_attribute_value{ (uint32_t) *name, resolve_reference_index == -1 ? index : (uint8_t) resolve_reference_index },
v
));
}
} else {
storage.set(index, v);
}
});
} else if constexpr (std::is_same_v<std::decay_t<decltype(v)>, ifcopenshell::parse_context*>) {
const auto *pt = param_type;
if (pt) {
while (pt->as_named_type() && pt->as_named_type()->declared_type()->as_type_declaration()) {
pt = pt->as_named_type()->declared_type()->as_type_declaration()->declared_type();
}
}
construct_<0>(name, index, *v, pt ? pt->as_aggregation_type() : nullptr, [this, &storage, name, &references_to_resolve, index, resolve_reference_index](const auto& v) {
if constexpr (std::is_same_v<std::decay_t<decltype(v)>, std::vector<reference_or_simple_type>>) {
if (name) {
references_to_resolve.push_back({ { (uint32_t) *name, resolve_reference_index == -1 ? index : (uint8_t)resolve_reference_index }, v });
}
} else if constexpr (std::is_same_v<std::decay_t<decltype(v)>, std::vector<std::vector<reference_or_simple_type>>>) {
if (name) {
references_to_resolve.push_back({ { (uint32_t) *name, resolve_reference_index == -1 ? index : (uint8_t)resolve_reference_index }, v });
}
} else {
storage.set(index, v);
}
});
} else {
storage.set(index, v);
}
}, token);
if (decl != nullptr) {
++kt;
}
}
return std::make_shared<instance_data>(owner, decl, (decl && decl->as_entity()) ? name.value_or(0) : 0, std::move(storage));
}
/*
ifcopenshell::IfcBaseClass* ifcopenshell::impl::rocks_db_file_storage::rocksdb_instance_iterator::operator*() const {
auto it = storage_->byid_.find(*read_id_());
@@ -391,7 +61,7 @@ express::Base ifcopenshell::impl::rocks_db_file_storage::assert_existance(size_t
}
// @nb note that in case of type declarations we pass the identity as the number so
// that we can read back the attributes from the db (we cannot assign to identity).
auto data = std::make_shared<instance_data>(file, decl, number, rocks_db_attribute_storage{});
auto data = ifcopenshell::make_pointer_type<instance_data>(file, decl, number, rocks_db_attribute_storage{});
if (r == ifcopenshell::impl::rocks_db_file_storage::entityinstance_ref) {
instance_cache_.insert({number, data});
} else {
@@ -674,7 +344,7 @@ express::Base ifcopenshell::impl::in_memory_file_storage::create(const ifcopensh
} else {
throw std::runtime_error("Requires and entity or type declaration");
}
auto data = std::make_shared<instance_data>(file, decl, instance_name, decl->as_entity() ? in_memory_attribute_storage(decl->as_entity()->attribute_count()) : in_memory_attribute_storage(1));
auto data = ifcopenshell::make_pointer_type<instance_data>(file, decl, instance_name, decl->as_entity() ? in_memory_attribute_storage(decl->as_entity()->attribute_count()) : in_memory_attribute_storage(1));
if (instance_name) {
byid_.insert({instance_name, data});
} else {