From 0c993d32922ece7895f49410219ec8c72055a7b7 Mon Sep 17 00:00:00 2001 From: Gorgious56 Date: Thu, 11 Jun 2026 09:17:35 +0200 Subject: [PATCH 1/5] Guard HasShapeAspects access on IFC2X3 representation iteration IFC2X3 representations have no HasShapeAspects inverse; opening the Geometry & Materials subpanel on an IFC2X3 object raised AttributeError and left the items list empty. Wrap the access with a getattr default so pre-IFC4 schemas return an empty iterable, and pin the contract with an AST forward-compat guard that scans bim/, tool/, and core/ for any future direct .HasShapeAspects access. Closes #8157 Generated with the assistance of an AI coding tool. --- .../bonsai/bim/module/geometry/operator.py | 2 +- .../test/bim/module/geometry/__init__.py | 17 ++++++ .../test_shape_aspects_forward_compat.py | 61 +++++++++++++++++++ 3 files changed, 79 insertions(+), 1 deletion(-) create mode 100644 src/bonsai/test/bim/module/geometry/__init__.py create mode 100644 src/bonsai/test/bim/module/geometry/test_shape_aspects_forward_compat.py diff --git a/src/bonsai/bonsai/bim/module/geometry/operator.py b/src/bonsai/bonsai/bim/module/geometry/operator.py index 05a7b43625..e34b5f7428 100644 --- a/src/bonsai/bonsai/bim/module/geometry/operator.py +++ b/src/bonsai/bonsai/bim/module/geometry/operator.py @@ -3173,7 +3173,7 @@ class EnableEditingRepresentationItems(bpy.types.Operator, tool.Ifc.Operator): product_reps = element.RepresentationMaps item_aspect = {} for product_rep in product_reps: - for aspect in product_rep.HasShapeAspects: + for aspect in getattr(product_rep, "HasShapeAspects", ()): for aspect_rep in aspect.ShapeRepresentations: if aspect_rep.ContextOfItems != representation.ContextOfItems: continue diff --git a/src/bonsai/test/bim/module/geometry/__init__.py b/src/bonsai/test/bim/module/geometry/__init__.py new file mode 100644 index 0000000000..fa692422fc --- /dev/null +++ b/src/bonsai/test/bim/module/geometry/__init__.py @@ -0,0 +1,17 @@ +# Bonsai - OpenBIM Blender Add-on +# Copyright (C) 2021 Dion Moult +# +# This file is part of Bonsai. +# +# Bonsai is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Bonsai is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Bonsai. If not, see . diff --git a/src/bonsai/test/bim/module/geometry/test_shape_aspects_forward_compat.py b/src/bonsai/test/bim/module/geometry/test_shape_aspects_forward_compat.py new file mode 100644 index 0000000000..ecfd0c9621 --- /dev/null +++ b/src/bonsai/test/bim/module/geometry/test_shape_aspects_forward_compat.py @@ -0,0 +1,61 @@ +# Bonsai - OpenBIM Blender Add-on +# Copyright (C) 2026 +# +# This file is part of Bonsai. +# +# Bonsai is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Bonsai is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Bonsai. If not, see . +# +# This file was generated with the assistance of an AI coding tool. + +"""Forward-compat AST contract: ``HasShapeAspects`` is an IFC4+ inverse; +direct attribute access raises ``AttributeError`` on pre-IFC4 entity +instances. Production code must read it through ``getattr`` so the +absence in earlier schemas degrades to an empty iterable.""" + +import ast +from pathlib import Path + +import pytest + +pytestmark = pytest.mark.geometry + + +BONSAI_ROOT = Path(__file__).parent.parent.parent.parent.parent / "bonsai" +PRODUCTION_DIRS = (BONSAI_ROOT / "bim", BONSAI_ROOT / "tool", BONSAI_ROOT / "core") + +ATTR_NAME = "HasShapeAspects" + + +def _iter_production_sources(): + for root in PRODUCTION_DIRS: + yield from root.rglob("*.py") + + +def test_has_shape_aspects_access_uses_getattr_guard(): + """Every read of ``HasShapeAspects`` in production code must go through + ``getattr(, "HasShapeAspects", )`` so files using + schemas that omit the inverse return the default instead of raising.""" + offenders = [] + for source in _iter_production_sources(): + tree = ast.parse(source.read_text(encoding="utf-8")) + for node in ast.walk(tree): + if isinstance(node, ast.Attribute) and node.attr == ATTR_NAME: + offenders.append(f"{source.relative_to(BONSAI_ROOT.parent)}:{node.lineno}") + if offenders: + joined = "\n ".join(sorted(offenders)) + pytest.fail( + f"Direct .{ATTR_NAME} attribute access in production code:\n {joined}\n" + f"Wrap with getattr(, '{ATTR_NAME}', ()) so pre-IFC4 schemas " + f"do not raise AttributeError." + ) From 347a3c80bb1dc6b920c89ce6af6313efbd91f4c5 Mon Sep 17 00:00:00 2001 From: Thomas Krijnen Date: Thu, 11 Jun 2026 21:04:44 +0200 Subject: [PATCH 2/5] More logger changes --- src/ifcconvert/IfcConvert.cpp | 4 +- src/ifcgeom/AbstractKernel.h | 2 +- src/ifcgeom/Iterator.cpp | 57 +++++-- src/ifcgeom/Iterator.h | 7 +- src/ifcgeom/hybrid_kernel.h | 6 +- src/ifcgeom/kernels/cgal/CgalKernel.cpp | 66 ++++---- src/ifcgeom/kernels/cgal/CgalKernel.h | 4 +- .../kernels/opencascade/OpenCascadeKernel.h | 4 +- src/ifcgeom/mapping/IfcCurveSegment.cpp | 8 +- src/ifcparse/IfcAlignmentHelper.cpp | 90 +++++----- src/ifcparse/IfcAlignmentHelper.h | 10 +- src/ifcparse/IfcCharacterDecoder.cpp | 12 +- src/ifcparse/IfcCharacterDecoder.h | 4 +- src/ifcparse/IfcFile.cpp | 46 ++--- src/ifcparse/IfcFile.h | 28 ++-- src/ifcparse/IfcGlobalId.cpp | 8 +- src/ifcparse/IfcGlobalId.h | 5 +- src/ifcparse/IfcHierarchyHelper.cpp | 10 +- src/ifcparse/IfcHierarchyHelper.h | 10 +- src/ifcparse/IfcLogger.cpp | 52 ++++-- src/ifcparse/IfcLogger.h | 14 +- src/ifcparse/IfcParse.cpp | 157 +++++++++++------- src/ifcparse/IfcParse.h | 4 +- src/ifcparse/IfcSpfHeader.cpp | 25 ++- src/ifcparse/IfcSpfHeader.h | 8 +- src/ifcparse/parse_ifcxml.cpp | 51 +++--- src/ifcparse/storage.h | 8 +- src/serializers/RocksDbSerializer.cpp | 2 +- 28 files changed, 418 insertions(+), 284 deletions(-) diff --git a/src/ifcconvert/IfcConvert.cpp b/src/ifcconvert/IfcConvert.cpp index ea72305cb6..acd5421638 100644 --- a/src/ifcconvert/IfcConvert.cpp +++ b/src/ifcconvert/IfcConvert.cpp @@ -1279,11 +1279,11 @@ bool init_input_file(const std::string& filename, IfcParse::IfcFile*& ifc_file, #ifdef WITH_IFCXML if (boost::ends_with(boost::to_lower_copy(filename), ".ifcxml")) { - ifc_file = IfcParse::parse_ifcxml(filename); + ifc_file = IfcParse::parse_ifcxml(filename, logger); } else #endif { - ifc_file = new IfcParse::IfcFile(IfcParse::uninitialized_tag{}); + ifc_file = new IfcParse::IfcFile(IfcParse::uninitialized_tag{}, logger); requires_init = true; } diff --git a/src/ifcgeom/AbstractKernel.h b/src/ifcgeom/AbstractKernel.h index 400c2b38f6..24abf84ce3 100644 --- a/src/ifcgeom/AbstractKernel.h +++ b/src/ifcgeom/AbstractKernel.h @@ -129,7 +129,7 @@ namespace ifcopenshell { const IfcGeom::ConversionResults& entity_shapes, const ifcopenshell::geometry::taxonomy::matrix4& entity_trsf, IfcGeom::ConversionResults& cut_shapes) = 0; virtual bool unify_shapes(const IfcGeom::ConversionResults&, IfcGeom::ConversionResults&) { throw not_implemented_error(); } - virtual AbstractKernel* clone() const = 0; + virtual AbstractKernel* clone(Logger& logger) const = 0; }; } } diff --git a/src/ifcgeom/Iterator.cpp b/src/ifcgeom/Iterator.cpp index c55a4ee95b..914d55f20a 100644 --- a/src/ifcgeom/Iterator.cpp +++ b/src/ifcgeom/Iterator.cpp @@ -167,7 +167,15 @@ bool IfcGeom::Iterator::initialize() { return *initialization_outcome_; } -void IfcGeom::Iterator::process_finished_rep(geometry_conversion_result* rep) { +void IfcGeom::Iterator::flush_worker_log(ifcopenshell::geometry::Converter* kernel) { + if (kernel && &kernel->logger() != &logger_) { + logger_.Append(kernel->logger()); + } +} + +void IfcGeom::Iterator::process_finished_rep(geometry_conversion_result* rep, ifcopenshell::geometry::Converter* kernel) { + flush_worker_log(kernel); + if (rep->elements.empty()) { return; } @@ -193,8 +201,17 @@ void IfcGeom::Iterator::process_concurrently() { } kernel_pool.reserve(conc_threads); + worker_loggers_.reserve(conc_threads); for (unsigned i = 0; i < conc_threads; ++i) { - kernel_pool.push_back(new ifcopenshell::geometry::Converter(std::unique_ptr(converter_->kernel()->clone()), ifc_file, settings_, logger_)); + worker_loggers_.emplace_back(std::make_unique()); + Logger& worker_logger = *worker_loggers_.back(); + worker_logger.Verbosity(logger_.Verbosity()); + worker_logger.OutputFormat(logger_.OutputFormat()); + worker_logger.PrintPerformanceStatsOnElement(logger_.PrintPerformanceStatsOnElement()); + if (worker_logger.OutputFormat() != Logger::FMT_INMEMORY) { + worker_logger.SetOutput(static_cast(nullptr), static_cast(nullptr)); + } + kernel_pool.push_back(new ifcopenshell::geometry::Converter(std::unique_ptr(converter_->kernel()->clone(worker_logger)), ifc_file, settings_, worker_logger)); } std::vector> threadpool; @@ -211,11 +228,12 @@ void IfcGeom::Iterator::process_concurrently() { std::future_status status; status = fu.wait_for(std::chrono::seconds(0)); if (status == std::future_status::ready) { - process_finished_rep(fu.get()); + process_finished_rep(fu.get(), kernel_pool[i]); std::swap(threadpool[i], threadpool.back()); threadpool.pop_back(); std::swap(kernel_pool[i], kernel_pool.back()); + std::swap(worker_loggers_[i], worker_loggers_.back()); K = kernel_pool.back(); break; } // if @@ -231,14 +249,14 @@ void IfcGeom::Iterator::process_concurrently() { try { this->create_element_(kernel, settings, rep); } catch (const std::exception& e) { - logger_.Error("GEO", 52, + kernel->logger().Error("GEO", 52, std::string("Exception '") + e.what() + std::string("' occurred while iterator was creating a shape: "), rep->item->instance ); had_error_processing_elements_ = true; } catch (...) { - logger_.Error("GEO", 53, + kernel->logger().Error("GEO", 53, "Unknown exception occurred while iteartor was creating a shape: ", rep->item->instance ); @@ -257,8 +275,8 @@ void IfcGeom::Iterator::process_concurrently() { threadpool.emplace_back(std::move(fu)); } - for (auto& fu : threadpool) { - process_finished_rep(fu.get()); + for (size_t i = 0; i < threadpool.size(); ++i) { + process_finished_rep(threadpool[i].get(), kernel_pool[i]); } finished_ = true; @@ -344,6 +362,8 @@ const IfcUtil::IfcBaseClass* IfcGeom::Iterator::create_shape_model_for_next_enti void IfcGeom::Iterator::create_element_(ifcopenshell::geometry::Converter* kernel, ifcopenshell::geometry::Settings settings, geometry_conversion_result* rep) { + Logger& kernel_logger = kernel->logger(); + if (!settings_.get().get()) { rep->item = kernel->mapping()->map(rep->representation); if (!rep->item) { @@ -360,20 +380,20 @@ void IfcGeom::Iterator::create_element_(ifcopenshell::geometry::Converter* kerne const IfcUtil::IfcBaseEntity* product = product_node.first; const auto& place = product_node.second; - logger_.SetProduct(product); + kernel_logger.SetProduct(product); IfcGeom::BRepElement* brep = static_cast(decorate_with_cache_(GeometrySerializer::READ_BREP, (std::string)product->get("GlobalId"), std::to_string(rep->item->instance->as()->id()), [kernel, settings, product, place, rep]() { return kernel->create_brep_for_representation_and_product(rep->item, product, place); })); if (!brep) { - logger_.SetProduct(boost::none); + kernel_logger.SetProduct(boost::none); return; } - auto elem = process_based_on_settings(settings, brep); + auto elem = process_based_on_settings(settings, brep, kernel_logger); if (!elem) { - logger_.SetProduct(boost::none); + kernel_logger.SetProduct(boost::none); return; } @@ -385,11 +405,13 @@ void IfcGeom::Iterator::create_element_(ifcopenshell::geometry::Converter* kerne const IfcUtil::IfcBaseEntity* product2 = p.first; const auto& place2 = p.second; + kernel_logger.SetProduct(product2); + IfcGeom::BRepElement* brep2 = static_cast(decorate_with_cache_(GeometrySerializer::READ_BREP, (std::string)product2->get("GlobalId"), std::to_string(rep->item->instance->as()->id()), [kernel, settings, product2, place2, brep]() { return kernel->create_brep_for_processed_representation(product2, place2, brep); })); if (brep2) { - auto elem2 = process_based_on_settings(settings, brep2, dynamic_cast(elem)); + auto elem2 = process_based_on_settings(settings, brep2, kernel_logger, dynamic_cast(elem)); if (elem2) { rep->breps.push_back(brep2); rep->elements.push_back(elem2); @@ -397,16 +419,16 @@ void IfcGeom::Iterator::create_element_(ifcopenshell::geometry::Converter* kerne } } - logger_.SetProduct(boost::none); + kernel_logger.SetProduct(boost::none); } -IfcGeom::Element* IfcGeom::Iterator::process_based_on_settings(ifcopenshell::geometry::Settings settings, IfcGeom::BRepElement* elem, IfcGeom::TriangulationElement* previous) +IfcGeom::Element* IfcGeom::Iterator::process_based_on_settings(ifcopenshell::geometry::Settings settings, IfcGeom::BRepElement* elem, Logger& logger, IfcGeom::TriangulationElement* previous) { if (settings.get().get() == ifcopenshell::geometry::settings::SERIALIZED) { try { return new IfcGeom::SerializedElement(*elem); } catch (...) { - logger_.Message(Logger::LOG_ERROR, "GEO", 54, "Getting a serialized element from model failed."); + logger.Message(Logger::LOG_ERROR, "GEO", 54, "Getting a serialized element from model failed."); return nullptr; } } else if (settings.get().get() == ifcopenshell::geometry::settings::TRIANGULATED) { @@ -417,7 +439,7 @@ IfcGeom::Element* IfcGeom::Iterator::process_based_on_settings(ifcopenshell::geo gid2 = gid2.substr(0, hyphen); } - return decorate_with_cache_(GeometrySerializer::READ_TRIANGULATION, elem->guid(), gid2, [this, elem, previous]() { + return decorate_with_cache_(GeometrySerializer::READ_TRIANGULATION, elem->guid(), gid2, [&logger, elem, previous]() { try { if (!previous) { return new TriangulationElement(*elem); @@ -425,7 +447,7 @@ IfcGeom::Element* IfcGeom::Iterator::process_based_on_settings(ifcopenshell::geo return new TriangulationElement(*elem, previous->geometry_pointer()); } } catch (...) { - logger_.Message(Logger::LOG_ERROR, "GEO", 55, "Getting a triangulation element from model failed."); + logger.Message(Logger::LOG_ERROR, "GEO", 55, "Getting a triangulation element from model failed."); } return (TriangulationElement*)nullptr; }); @@ -824,6 +846,7 @@ IfcGeom::Iterator::~Iterator() { } for (auto& k : kernel_pool) { + flush_worker_log(k); delete k; } diff --git a/src/ifcgeom/Iterator.h b/src/ifcgeom/Iterator.h index a10021b943..5b849da362 100644 --- a/src/ifcgeom/Iterator.h +++ b/src/ifcgeom/Iterator.h @@ -79,6 +79,7 @@ #include #include #include +#include namespace IfcGeom { @@ -133,6 +134,7 @@ namespace IfcGeom { // When multi-threaded std::vector kernel_pool; + std::vector> worker_loggers_; // The object is fetched beforehand to be sure that get() returns a valid element TriangulationElement* current_triangulation; @@ -200,8 +202,11 @@ namespace IfcGeom { IfcGeom::Element* process_based_on_settings( ifcopenshell::geometry::Settings settings, IfcGeom::BRepElement* elem, + Logger& logger, IfcGeom::TriangulationElement* previous = nullptr); + void flush_worker_log(ifcopenshell::geometry::Converter* kernel); + bool wait_for_element(); void log_timepoints() const; @@ -293,7 +298,7 @@ namespace IfcGeom { size_t processed_ = 0; - void process_finished_rep(geometry_conversion_result* rep); + void process_finished_rep(geometry_conversion_result* rep, ifcopenshell::geometry::Converter* kernel = nullptr); void process_concurrently(); diff --git a/src/ifcgeom/hybrid_kernel.h b/src/ifcgeom/hybrid_kernel.h index 5f8a672261..d19b587ebc 100644 --- a/src/ifcgeom/hybrid_kernel.h +++ b/src/ifcgeom/hybrid_kernel.h @@ -156,14 +156,14 @@ namespace ifcopenshell { } return false; } - virtual AbstractKernel* clone() const + virtual AbstractKernel* clone(Logger& logger) const { std::vector> ks; for (auto& k : kernels_) { - ks.emplace_back(k->clone()); + ks.emplace_back(k->clone(logger)); } // @todo ugly - return new HybridKernel(geometry_library(), file_, const_cast(settings()), std::move(ks), logger()); + return new HybridKernel(geometry_library(), file_, const_cast(settings()), std::move(ks), logger); } }; diff --git a/src/ifcgeom/kernels/cgal/CgalKernel.cpp b/src/ifcgeom/kernels/cgal/CgalKernel.cpp index 6a0935559b..f0747338d3 100644 --- a/src/ifcgeom/kernels/cgal/CgalKernel.cpp +++ b/src/ifcgeom/kernels/cgal/CgalKernel.cpp @@ -1,4 +1,4 @@ -/******************************************************************************** +/******************************************************************************** * * * This file is part of IfcOpenShell. * * * @@ -162,13 +162,13 @@ CGAL::Nef_polyhedron_3 ifcopenshell::geometry::utils::create_nef_polyhe bool CgalKernel::convert(const taxonomy::shell::ptr l, cgal_shape_t& shape) { for (auto& f : l->children) { if (f->basis && f->basis->kind() != taxonomy::PLANE) { - logger_.Error("UNS", 3, "CGAL Kernel: Non-planar faces not supported at the moment"); + logger().Error("UNS", 3, "CGAL Kernel: Non-planar faces not supported at the moment"); throw not_supported_error(); } for (auto& w : f->children) { for (auto& e : w->children) { if (e->basis && e->basis->kind() == taxonomy::BSPLINE_CURVE) { - logger_.Error("UNS", 4, "CGAL Kernel: B-spline edge curves not supported at the moment"); + logger().Error("UNS", 4, "CGAL Kernel: B-spline edge curves not supported at the moment"); throw not_supported_error(); } } @@ -197,9 +197,9 @@ bool CgalKernel::convert(const taxonomy::shell::ptr l, cgal_shape_t& shape) { double volume = diag(0) * diag(1) * diag(2); // @todo volume van be zero also.. double density = num_points / volume; - logger_.Notice("GEO", 77, "Density " + boost::lexical_cast(density), l->instance); + logger().Notice("GEO", 77, "Density " + boost::lexical_cast(density), l->instance); if (density > 5000) { - logger_.Notice("GEO", 78, "Substituted element with " + boost::lexical_cast(density) + " vertices / m3 with a bounding box"); + logger().Notice("GEO", 78, "Substituted element with " + boost::lexical_cast(density) + " vertices / m3 with a bounding box"); CGAL::Point_3 lower(minmax.first(0), minmax.first(1), minmax.first(2)); CGAL::Point_3 upper(minmax.second(0), minmax.second(1), minmax.second(2)); shape = utils::create_cube(lower, upper); @@ -215,7 +215,7 @@ bool CgalKernel::convert(const taxonomy::shell::ptr l, cgal_shape_t& shape) { } catch (...) {} if (!success) { - logger_.Message(Logger::LOG_WARNING, "GEO", 79, "Failed to convert face:", f->instance); + logger().Message(Logger::LOG_WARNING, "GEO", 79, "Failed to convert face:", f->instance); continue; } @@ -237,7 +237,7 @@ bool CgalKernel::convert(const taxonomy::face::ptr face, std::list& } if (face->children.size() > 1 && num_outer_bounds > 1 && face->children.size() != num_outer_bounds) { - logger_.Message(Logger::LOG_ERROR, "GEO", 80, "Invalid configuration of boundaries for:", face->instance); + logger().Message(Logger::LOG_ERROR, "GEO", 80, "Invalid configuration of boundaries for:", face->instance); return false; } @@ -250,7 +250,7 @@ bool CgalKernel::convert(const taxonomy::face::ptr face, std::list& cgal_wire_t wire; if (!convert(bound, wire)) { - logger_.Message(Logger::LOG_ERROR, "GEO", 81, "Failed to process face boundary loop", bound->instance); + logger().Message(Logger::LOG_ERROR, "GEO", 81, "Failed to process face boundary loop", bound->instance); return false; } @@ -704,7 +704,7 @@ bool CgalKernel::convert(const taxonomy::loop::ptr loop, cgal_wire_t& result) { if (d < 1.e-5) { points.erase(points.end() - 1); } else { - logger_.Warning("GEO", 82, "Loop not closed", loop->instance); + logger().Warning("GEO", 82, "Loop not closed", loop->instance); } } @@ -718,7 +718,7 @@ bool CgalKernel::convert(const taxonomy::loop::ptr loop, cgal_wire_t& result) { // A loop should consist of at least three vertices std::size_t original_count = polygon.size(); if (original_count < 3) { - logger_.Warning("GEO", 83, "Not enough edges for:", loop->instance); + logger().Warning("GEO", 83, "Not enough edges for:", loop->instance); return false; } @@ -729,14 +729,14 @@ bool CgalKernel::convert(const taxonomy::loop::ptr loop, cgal_wire_t& result) { std::size_t count = polygon.size(); if (original_count - count != 0) { std::stringstream ss; ss << (original_count - count) << " edges removed for:"; - logger_.Warning("GEO", 84, ss.str(), loop->instance); + logger().Warning("GEO", 84, ss.str(), loop->instance); } { std::set visited_points; for (auto& p : polygon) { if (visited_points.find(p) != visited_points.end()) { - logger_.Error("GEO", 85, "Skipping self-intersecting loop", loop->instance); + logger().Error("GEO", 85, "Skipping self-intersecting loop", loop->instance); // @todo signal somehow that occt kernel might be able to solve this // @todo implement cycle detection using Arrangement_2, but that only works in exact kernel return false; @@ -758,7 +758,7 @@ bool CgalKernel::convert(const taxonomy::loop::ptr loop, cgal_wire_t& result) { } if (do_segments_intersect(segments)) { - logger_.Message(Logger::LOG_WARNING, "GEO", 86, "Skipping self-intersecting loop", loop->instance); + logger().Message(Logger::LOG_WARNING, "GEO", 86, "Skipping self-intersecting loop", loop->instance); return false; } @@ -786,7 +786,7 @@ bool CgalKernel::convert(const taxonomy::loop::ptr loop, cgal_wire_t& result) { */ if (count < 3) { - logger_.Message(Logger::LOG_ERROR, "GEO", 87, "Not enough edges for:", loop->instance); + logger().Message(Logger::LOG_ERROR, "GEO", 87, "Not enough edges for:", loop->instance); return false; } @@ -820,7 +820,7 @@ bool CgalKernel::convert_impl(const taxonomy::shell::ptr shell, ConversionResult bool CgalKernel::convert_impl(const taxonomy::solid::ptr solid, ConversionResults& results) { if (solid->children.size() > 1) { - logger_.Error("UNS", 5, "Multiple shells in solid not supported at the moment"); + logger().Error("UNS", 5, "Multiple shells in solid not supported at the moment"); return false; } cgal_shape_t shape; @@ -965,7 +965,7 @@ bool ifcopenshell::geometry::kernels::CgalKernel::convert_openings(const IfcUtil try { a.convert_to_polyhedron(a_poly); } catch (...) { - logger_.Message(Logger::LOG_ERROR, "GEO", 88, "Could not convert from Nef:", entity); + logger().Message(Logger::LOG_ERROR, "GEO", 88, "Could not convert from Nef:", entity); return false; } @@ -1190,7 +1190,7 @@ bool CgalKernel::process_extrusion(const cgal_face_t& bottom_face, taxonomy::dir bool CgalKernel::convert(const taxonomy::extrusion::ptr extrusion, cgal_shape_t &shape) { const double& height = extrusion->depth; if (height < settings_.get().get()) { - logger_.Message(Logger::LOG_ERROR, "GEO", 89, "Non-positive extrusion height encountered for:", extrusion->instance); + logger().Message(Logger::LOG_ERROR, "GEO", 89, "Non-positive extrusion height encountered for:", extrusion->instance); return false; } @@ -1326,13 +1326,13 @@ bool CgalKernel::preprocess_boolean_operand(const IfcUtil::IfcBaseClass* log_ref cgal_shape_t shape = shape_const; if (!shape.is_valid()) { - logger_.Message(Logger::LOG_ERROR, "GEO", 90, "Conversion to Nef will fail. Invalid geometry:", log_reference); + logger().Message(Logger::LOG_ERROR, "GEO", 90, "Conversion to Nef will fail. Invalid geometry:", log_reference); return false; } if (!shape.is_closed()) { // TODO: There can be substractions to remove parts of non-volumetric objects. Maybe iterate over all faces of an entity and put them in a Nef_polyhedron_3 through Boolean union? Highly inefficient but maybe desirable... - logger_.Message(Logger::LOG_ERROR, "UNS", 6, "Subtraction of openings not supported for non-closed geometry:", log_reference); + logger().Message(Logger::LOG_ERROR, "UNS", 6, "Subtraction of openings not supported for non-closed geometry:", log_reference); return false; } @@ -1341,18 +1341,18 @@ bool CgalKernel::preprocess_boolean_operand(const IfcUtil::IfcBaseClass* log_ref try { success = CGAL::Polygon_mesh_processing::triangulate_faces(shape); } catch (CGAL::Failure_exception& e) { - logger_.Notice("GEO", 91, e); - logger_.Message(Logger::LOG_ERROR, "GEO", 92, "Triangulation of geometry crashed:", log_reference); + logger().Notice("GEO", 91, e); + logger().Message(Logger::LOG_ERROR, "GEO", 92, "Triangulation of geometry crashed:", log_reference); return false; } if (!success) { - logger_.Message(Logger::LOG_ERROR, "GEO", 93, "Triangulation of geometry failed:", log_reference); + logger().Message(Logger::LOG_ERROR, "GEO", 93, "Triangulation of geometry failed:", log_reference); return false; } if (CGAL::Polygon_mesh_processing::does_self_intersect(shape)) { - logger_.Message(Logger::LOG_ERROR, "GEO", 94, "Conversion to Nef will fail. Self-intersecting geometry:", log_reference); + logger().Message(Logger::LOG_ERROR, "GEO", 94, "Conversion to Nef will fail. Self-intersecting geometry:", log_reference); return false; } @@ -1424,8 +1424,8 @@ bool CgalKernel::preprocess_boolean_operand(const IfcUtil::IfcBaseClass* log_ref try { result = CGAL::Nef_polyhedron_3(shape); } catch (CGAL::Failure_exception& e) { - logger_.Notice("GEO", 95, e); - logger_.Message(Logger::LOG_ERROR, "GEO", 96, "Could not convert geometry to Nef:", log_reference); + logger().Notice("GEO", 95, e); + logger().Message(Logger::LOG_ERROR, "GEO", 96, "Could not convert geometry to Nef:", log_reference); return false; } @@ -1497,8 +1497,8 @@ bool CgalKernel::preprocess_boolean_operand(const IfcUtil::IfcBaseClass* log_ref // @todo don't dilate in 3 dimensions but only in the XY plane, orthogonal to wall axis. result = CGAL::minkowski_sum_3(result, precision_cube_); } catch (CGAL::Failure_exception& e) { - logger_.Notice("GEO", 97, e); - logger_.Message(Logger::LOG_ERROR, "GEO", 98, "Could not dilate boolean operand", log_reference); + logger().Notice("GEO", 97, e); + logger().Message(Logger::LOG_ERROR, "GEO", 98, "Could not dilate boolean operand", log_reference); return false; } } @@ -1523,8 +1523,8 @@ bool CgalKernel::preprocess_boolean_operand(const IfcUtil::IfcBaseClass* log_ref cgal_shape_t convert_back; result.convert_to_polyhedron(convert_back); } catch (CGAL::Failure_exception& e) { - logger_.Notice("GEO", 99, e); - logger_.Message(Logger::LOG_WARNING, "GEO", 100, "Final conversion will likely fail. Could not convert geometry from Nef:", log_reference); + logger().Notice("GEO", 99, e); + logger().Message(Logger::LOG_WARNING, "GEO", 100, "Final conversion will likely fail. Could not convert geometry from Nef:", log_reference); } return true; @@ -1846,7 +1846,7 @@ bool CgalKernel::convert_impl(const taxonomy::boolean_result::ptr br, Conversion // even-odd fill rule will result in incorrect results. // See for example the Duplex model roof. - logger_.Notice("GEO", 101, "Holes are not disjoint"); + logger().Notice("GEO", 101, "Holes are not disjoint"); CGAL::Polygon_set_2 result; auto it = loops.begin(); @@ -1899,7 +1899,7 @@ bool CgalKernel::convert_impl(const taxonomy::boolean_result::ptr br, Conversion ); }); - logger_.Notice("GEO", 102, "Processed boolean operation as 2d arrangement"); + logger().Notice("GEO", 102, "Processed boolean operation as 2d arrangement"); return true; @@ -1985,7 +1985,7 @@ bool CgalKernel::convert_impl(const taxonomy::boolean_result::ptr br, Conversion ps.push_back({ p.x(), p.y() }); } if (!ps.is_simple()) { - logger_.Warning("GEO", 103, "Polygonal boundary not simple", face->children[0]->instance); + logger().Warning("GEO", 103, "Polygonal boundary not simple", face->children[0]->instance); continue; } @@ -2131,7 +2131,7 @@ bool CgalKernel::convert_impl(const taxonomy::boolean_result::ptr br, Conversion try { a.convert_to_polyhedron(a_poly); } catch (...) { - logger_.Message(Logger::LOG_ERROR, "GEO", 104, "Could not convert geometry with openings from Nef:", br->instance); + logger().Message(Logger::LOG_ERROR, "GEO", 104, "Could not convert geometry with openings from Nef:", br->instance); return false; } diff --git a/src/ifcgeom/kernels/cgal/CgalKernel.h b/src/ifcgeom/kernels/cgal/CgalKernel.h index 1c9af2546e..cf48c6e15a 100644 --- a/src/ifcgeom/kernels/cgal/CgalKernel.h +++ b/src/ifcgeom/kernels/cgal/CgalKernel.h @@ -97,8 +97,8 @@ namespace ifcopenshell { : AbstractKernel("cgal", settings, logger) {} - virtual AbstractKernel* clone() const { - return new CgalKernel(settings(), logger()); + virtual AbstractKernel* clone(Logger& logger) const { + return new CgalKernel(settings(), logger); } virtual bool supports_boolean_operations() const { diff --git a/src/ifcgeom/kernels/opencascade/OpenCascadeKernel.h b/src/ifcgeom/kernels/opencascade/OpenCascadeKernel.h index 359ccbd01d..9bbd0d0f72 100644 --- a/src/ifcgeom/kernels/opencascade/OpenCascadeKernel.h +++ b/src/ifcgeom/kernels/opencascade/OpenCascadeKernel.h @@ -118,8 +118,8 @@ public: , precision_(settings.get().get()) {} - virtual AbstractKernel* clone() const { - return new OpenCascadeKernel(settings(), logger()); + virtual AbstractKernel* clone(Logger& logger) const { + return new OpenCascadeKernel(settings(), logger); } virtual bool supports_boolean_operations() const { return true; } diff --git a/src/ifcgeom/mapping/IfcCurveSegment.cpp b/src/ifcgeom/mapping/IfcCurveSegment.cpp index 04907655e3..ce49fd73b7 100644 --- a/src/ifcgeom/mapping/IfcCurveSegment.cpp +++ b/src/ifcgeom/mapping/IfcCurveSegment.cpp @@ -216,6 +216,7 @@ struct cant_curve_segment_function { class curve_segment_evaluator { private: mapping* mapping_ = nullptr; + Logger& logger_; const IfcSchema::IfcCurveSegment* inst_ = nullptr; // this curve segment instance double length_unit_; double start_; @@ -234,6 +235,7 @@ class curve_segment_evaluator { public: curve_segment_evaluator(mapping* mapping, const IfcSchema::IfcCurveSegment* inst, double length_unit) : mapping_(mapping), + logger_(mapping->logger()), inst_(inst), length_unit_(length_unit), parent_curve_(inst->ParentCurve()) { @@ -1136,18 +1138,18 @@ class curve_segment_evaluator { // A numerical solution is required. // This functor finds the value of x such that s(x) - u = 0, where u is the input value and s is the // computed curve length. - x_at_dist_along = [curve_length_fn,mapping=mapping_](double u) -> double { + x_at_dist_along = [curve_length_fn, this](double u) -> double { std::uintmax_t max_iter = 9000; auto tol = [](double a, double b) { return fabs(b - a) < 1.0E-11; }; auto x = u; // start by assuming u = x (it's not, but it will be close) try { // set up the root finding function that evaluates s(x) - u - auto f = [curve_length_fn, u,mapping=mapping](double x) -> double { return curve_length_fn(x) - u; }; + auto f = [curve_length_fn, u](double x) -> double { return curve_length_fn(x) - u; }; // use a root finder to get x auto result = boost::math::tools::bracket_and_solve_root(f, x, 2.0, true, tol, max_iter); x = result.first; } catch (...) { - mapping->logger().Warning("GEO", 253, "root solver failed"); + logger_.Warning("GEO", 253, "root solver failed"); } return x; }; diff --git a/src/ifcparse/IfcAlignmentHelper.cpp b/src/ifcparse/IfcAlignmentHelper.cpp index 5edba1022c..b64b243d21 100644 --- a/src/ifcparse/IfcAlignmentHelper.cpp +++ b/src/ifcparse/IfcAlignmentHelper.cpp @@ -119,11 +119,11 @@ std::tuple::ptr, typenam { auto pt = file.addDoublet(xBT, yBT); auto design_parameters = new Ifc4x3_add2::IfcAlignmentHorizontalSegment(boost::none, boost::none, pt, angleBT, 0.0, 0.0, tangent_run, boost::none, Ifc4x3_add2::IfcAlignmentHorizontalSegmentTypeEnum::IfcAlignmentHorizontalSegmentType_LINE); - auto alignment_segment = new Ifc4x3_add2::IfcAlignmentSegment(IfcParse::IfcGlobalId(), nullptr, boost::none, boost::none, boost::none, nullptr, nullptr, design_parameters); + auto alignment_segment = new Ifc4x3_add2::IfcAlignmentSegment(IfcParse::IfcGlobalId(file.logger()), nullptr, boost::none, boost::none, boost::none, nullptr, nullptr, design_parameters); horizontal_segments->push(alignment_segment); if (include_geometry) { - horizontal_curve_segments->push(mapAlignmentHorizontalSegment(design_parameters).first); + horizontal_curve_segments->push(mapAlignmentHorizontalSegment(design_parameters, file.logger()).first); } } @@ -131,11 +131,11 @@ std::tuple::ptr, typenam { auto pc = file.addDoublet(xPC, yPC); auto design_parameters = new Ifc4x3_add2::IfcAlignmentHorizontalSegment(boost::none, boost::none, pc, angleBT, radius, radius, lc, boost::none, Ifc4x3_add2::IfcAlignmentHorizontalSegmentTypeEnum::IfcAlignmentHorizontalSegmentType_CIRCULARARC); - auto alignment_segment = new Ifc4x3_add2::IfcAlignmentSegment(IfcParse::IfcGlobalId(), nullptr, boost::none, boost::none, boost::none, nullptr, nullptr, design_parameters); + auto alignment_segment = new Ifc4x3_add2::IfcAlignmentSegment(IfcParse::IfcGlobalId(file.logger()), nullptr, boost::none, boost::none, boost::none, nullptr, nullptr, design_parameters); horizontal_segments->push(alignment_segment); if (include_geometry) { - horizontal_curve_segments->push(mapAlignmentHorizontalSegment(design_parameters).first); + horizontal_curve_segments->push(mapAlignmentHorizontalSegment(design_parameters, file.logger()).first); } } @@ -152,19 +152,19 @@ std::tuple::ptr, typenam auto tangent_run = sqrt(dx * dx + dy * dy); auto pt = file.addDoublet(xBT, yBT); auto design_parameters = new Ifc4x3_add2::IfcAlignmentHorizontalSegment(boost::none, boost::none, pt, angleBT, 0.0, 0.0, tangent_run, boost::none, Ifc4x3_add2::IfcAlignmentHorizontalSegmentTypeEnum::IfcAlignmentHorizontalSegmentType_LINE); - auto alignment_segment = new Ifc4x3_add2::IfcAlignmentSegment(IfcParse::IfcGlobalId(), nullptr, boost::none, boost::none, boost::none, nullptr, nullptr, design_parameters); + auto alignment_segment = new Ifc4x3_add2::IfcAlignmentSegment(IfcParse::IfcGlobalId(file.logger()), nullptr, boost::none, boost::none, boost::none, nullptr, nullptr, design_parameters); horizontal_segments->push(alignment_segment); if (include_geometry) { - horizontal_curve_segments->push(mapAlignmentHorizontalSegment(design_parameters).first); + horizontal_curve_segments->push(mapAlignmentHorizontalSegment(design_parameters, file.logger()).first); } // create zero length terminator segment auto poe = file.addDoublet(xPI, yPI); design_parameters = new Ifc4x3_add2::IfcAlignmentHorizontalSegment(boost::none, boost::none, poe, angleBT, 0.0, 0.0, 0.0, boost::none, Ifc4x3_add2::IfcAlignmentHorizontalSegmentTypeEnum::IfcAlignmentHorizontalSegmentType_LINE); - alignment_segment = new Ifc4x3_add2::IfcAlignmentSegment(IfcParse::IfcGlobalId(), nullptr, boost::none, boost::none, boost::none, nullptr, nullptr, design_parameters); + alignment_segment = new Ifc4x3_add2::IfcAlignmentSegment(IfcParse::IfcGlobalId(file.logger()), nullptr, boost::none, boost::none, boost::none, nullptr, nullptr, design_parameters); horizontal_segments->push(alignment_segment); if (include_geometry) { - auto segment = mapAlignmentHorizontalSegment(design_parameters).first; + auto segment = mapAlignmentHorizontalSegment(design_parameters, file.logger()).first; segment->setTransition(Ifc4x3_add2::IfcTransitionCode::IfcTransitionCode_DISCONTINUOUS); horizontal_curve_segments->push(segment); } @@ -186,10 +186,10 @@ Ifc4x3_add2::IfcAlignment* addHorizontalAlignment(IfcHierarchyHelper::ptr, typenam { auto gradient_length = dxBG - length/2; auto design_parameters = new Ifc4x3_add2::IfcAlignmentVerticalSegment(boost::none, boost::none, xPBG, gradient_length, yPBG, start_slope, start_slope, boost::none, Ifc4x3_add2::IfcAlignmentVerticalSegmentTypeEnum::IfcAlignmentVerticalSegmentType_CONSTANTGRADIENT); - auto alignment_segment = new Ifc4x3_add2::IfcAlignmentSegment(IfcParse::IfcGlobalId(), nullptr, boost::none, boost::none, boost::none, nullptr, nullptr, design_parameters); + auto alignment_segment = new Ifc4x3_add2::IfcAlignmentSegment(IfcParse::IfcGlobalId(file.logger()), nullptr, boost::none, boost::none, boost::none, nullptr, nullptr, design_parameters); vertical_segments->push(alignment_segment); if (include_geometry) { - vertical_curve_segments->push(mapAlignmentVerticalSegment(design_parameters).first); + vertical_curve_segments->push(mapAlignmentVerticalSegment(design_parameters, file.logger()).first); } } @@ -274,10 +274,10 @@ std::tuple::ptr, typenam double yBVC = yPVI - start_slope * length / 2; auto design_parameters = new Ifc4x3_add2::IfcAlignmentVerticalSegment(boost::none, boost::none, xBVC, length, yBVC, start_slope, end_slope, 1 / k, Ifc4x3_add2::IfcAlignmentVerticalSegmentTypeEnum::IfcAlignmentVerticalSegmentType_PARABOLICARC); - auto alignment_segment = new Ifc4x3_add2::IfcAlignmentSegment(IfcParse::IfcGlobalId(), nullptr, boost::none, boost::none, boost::none, nullptr, nullptr, design_parameters); + auto alignment_segment = new Ifc4x3_add2::IfcAlignmentSegment(IfcParse::IfcGlobalId(file.logger()), nullptr, boost::none, boost::none, boost::none, nullptr, nullptr, design_parameters); vertical_segments->push(alignment_segment); if (include_geometry) { - vertical_curve_segments->push(mapAlignmentVerticalSegment(design_parameters).first); + vertical_curve_segments->push(mapAlignmentVerticalSegment(design_parameters, file.logger()).first); } } @@ -294,18 +294,18 @@ std::tuple::ptr, typenam auto gradient_length = dx; auto design_parameters = new Ifc4x3_add2::IfcAlignmentVerticalSegment(boost::none, boost::none, xPBG, gradient_length, yPBG, slope, slope, boost::none, Ifc4x3_add2::IfcAlignmentVerticalSegmentTypeEnum::IfcAlignmentVerticalSegmentType_CONSTANTGRADIENT); - auto alignment_segment = new Ifc4x3_add2::IfcAlignmentSegment(IfcParse::IfcGlobalId(), nullptr, boost::none, boost::none, boost::none, nullptr, nullptr, design_parameters); + auto alignment_segment = new Ifc4x3_add2::IfcAlignmentSegment(IfcParse::IfcGlobalId(file.logger()), nullptr, boost::none, boost::none, boost::none, nullptr, nullptr, design_parameters); vertical_segments->push(alignment_segment); if (include_geometry) { - vertical_curve_segments->push(mapAlignmentVerticalSegment(design_parameters).first); + vertical_curve_segments->push(mapAlignmentVerticalSegment(design_parameters, file.logger()).first); } // create zero length terminator segment design_parameters = new Ifc4x3_add2::IfcAlignmentVerticalSegment(boost::none, boost::none, xPVI, 0.0, yPVI, slope, slope, boost::none, Ifc4x3_add2::IfcAlignmentVerticalSegmentTypeEnum::IfcAlignmentVerticalSegmentType_CONSTANTGRADIENT); - alignment_segment = new Ifc4x3_add2::IfcAlignmentSegment(IfcParse::IfcGlobalId(), nullptr, boost::none, boost::none, boost::none, nullptr, nullptr, design_parameters); + alignment_segment = new Ifc4x3_add2::IfcAlignmentSegment(IfcParse::IfcGlobalId(file.logger()), nullptr, boost::none, boost::none, boost::none, nullptr, nullptr, design_parameters); vertical_segments->push(alignment_segment); if (include_geometry) { - auto segment = mapAlignmentVerticalSegment(design_parameters).first; + auto segment = mapAlignmentVerticalSegment(design_parameters, file.logger()).first; segment->setTransition(Ifc4x3_add2::IfcTransitionCode::IfcTransitionCode_DISCONTINUOUS); vertical_curve_segments->push(segment); } @@ -329,19 +329,19 @@ Ifc4x3_add2::IfcAlignment* addAlignment(IfcHierarchyHelper& file, c // // Create the horizontal alignment (IfcAlignmentHorizontal) and nest the segments // - auto horizontal_alignment = new Ifc4x3_add2::IfcAlignmentHorizontal(IfcParse::IfcGlobalId(), nullptr, alignment_name + std::string(" - Horizontal"), boost::none, boost::none, nullptr, nullptr); + auto horizontal_alignment = new Ifc4x3_add2::IfcAlignmentHorizontal(IfcParse::IfcGlobalId(file.logger()), nullptr, alignment_name + std::string(" - Horizontal"), boost::none, boost::none, nullptr, nullptr); file.addEntity(horizontal_alignment); - auto nests_horizontal_segments = new Ifc4x3_add2::IfcRelNests(IfcParse::IfcGlobalId(), nullptr, boost::none, std::string("Nests horizontal alignment segments with horizontal alignment"), horizontal_alignment, horizontal_segments); + auto nests_horizontal_segments = new Ifc4x3_add2::IfcRelNests(IfcParse::IfcGlobalId(file.logger()), nullptr, boost::none, std::string("Nests horizontal alignment segments with horizontal alignment"), horizontal_alignment, horizontal_segments); file.addEntity(nests_horizontal_segments); // // Create the vertical alignment (IfcAlignmentVertical) and nest the segments // - auto vertical_profile = new Ifc4x3_add2::IfcAlignmentVertical(IfcParse::IfcGlobalId(), nullptr, alignment_name + std::string("- Vertical"), boost::none, boost::none, nullptr, nullptr); + auto vertical_profile = new Ifc4x3_add2::IfcAlignmentVertical(IfcParse::IfcGlobalId(file.logger()), nullptr, alignment_name + std::string("- Vertical"), boost::none, boost::none, nullptr, nullptr); file.addEntity(vertical_profile); - auto nests_vertical_segments = new Ifc4x3_add2::IfcRelNests(IfcParse::IfcGlobalId(), nullptr, boost::none, std::string("Nests vertical alignment segments with vertical alignment"), vertical_profile, vertical_segments); + auto nests_vertical_segments = new Ifc4x3_add2::IfcRelNests(IfcParse::IfcGlobalId(file.logger()), nullptr, boost::none, std::string("Nests vertical alignment segments with vertical alignment"), vertical_profile, vertical_segments); file.addEntity(nests_vertical_segments); Ifc4x3_add2::IfcLocalPlacement* placement = nullptr; @@ -383,7 +383,7 @@ Ifc4x3_add2::IfcAlignment* addAlignment(IfcHierarchyHelper& file, c // Create the IfcAlignment // - auto alignment = new Ifc4x3_add2::IfcAlignment(IfcParse::IfcGlobalId(), nullptr, alignment_name, boost::none, boost::none, placement, product_definition_shape, boost::none); + auto alignment = new Ifc4x3_add2::IfcAlignment(IfcParse::IfcGlobalId(file.logger()), nullptr, alignment_name, boost::none, boost::none, placement, product_definition_shape, boost::none); file.addEntity(alignment); // Nest the IfcAlignmentHorizontal and IfcAlignmentVertical with the IfcAlignment to complete the business logic @@ -393,31 +393,31 @@ Ifc4x3_add2::IfcAlignment* addAlignment(IfcHierarchyHelper& file, c alignment_layout_list->push(horizontal_alignment); alignment_layout_list->push(vertical_profile); - auto nests_alignment_layouts = new Ifc4x3_add2::IfcRelNests(IfcParse::IfcGlobalId(), nullptr, std::string("Nest horizontal and vertical alignment layouts with the alignment"), boost::none, alignment, alignment_layout_list); + auto nests_alignment_layouts = new Ifc4x3_add2::IfcRelNests(IfcParse::IfcGlobalId(file.logger()), nullptr, std::string("Nest horizontal and vertical alignment layouts with the alignment"), boost::none, alignment, alignment_layout_list); file.addEntity(nests_alignment_layouts); return alignment; } -std::pair mapAlignmentSegment(const Ifc4x3_add2::IfcAlignmentSegment* segment) { +std::pair mapAlignmentSegment(const Ifc4x3_add2::IfcAlignmentSegment* segment, Logger& logger) { std::pair result(nullptr, nullptr); auto design_parameters = segment->DesignParameters(); auto horizontal = design_parameters->as(); auto vertical = design_parameters->as(); auto cant = design_parameters->as(); if (horizontal) { - result = mapAlignmentHorizontalSegment(horizontal); + result = mapAlignmentHorizontalSegment(horizontal, logger); } else if (vertical) { - result = mapAlignmentVerticalSegment(vertical); + result = mapAlignmentVerticalSegment(vertical, logger); } else if (cant) { - result = mapAlignmentCantSegment(cant); + result = mapAlignmentCantSegment(cant, logger); } else { - Logger::Root().Error("VAL", 8, std::string("Unexpected IfcAlignmentSegment subtype encountered")); + logger.Error("VAL", 8, std::string("Unexpected IfcAlignmentSegment subtype encountered")); } return result; } -std::pair mapAlignmentHorizontalSegment(const Ifc4x3_add2::IfcAlignmentHorizontalSegment* segment) { +std::pair mapAlignmentHorizontalSegment(const Ifc4x3_add2::IfcAlignmentHorizontalSegment* segment, Logger& logger) { std::pair result(nullptr, nullptr); auto start_point = segment->StartPoint(); auto start_direction = segment->StartDirection(); @@ -661,15 +661,15 @@ std::pair mapAlign result.first = curve_segment; } else if (type == Ifc4x3_add2::IfcAlignmentHorizontalSegmentTypeEnum::IfcAlignmentHorizontalSegmentType_VIENNESEBEND) { - Logger::Root().Warning("UNS", 22, std::string("mapping of AlignmentHorizontalSegmentType VIENNESEBEND not supported")); + logger.Warning("UNS", 22, std::string("mapping of AlignmentHorizontalSegmentType VIENNESEBEND not supported")); } else { - Logger::Root().Error("VAL", 9, std::string("unexpected AlignmentHorizontalSegmentType encountered")); + logger.Error("VAL", 9, std::string("unexpected AlignmentHorizontalSegmentType encountered")); } return result; } -std::pair mapAlignmentVerticalSegment(const Ifc4x3_add2::IfcAlignmentVerticalSegment* segment) { +std::pair mapAlignmentVerticalSegment(const Ifc4x3_add2::IfcAlignmentVerticalSegment* segment, Logger& logger) { std::pair result(nullptr, nullptr); auto start_distance_along = segment->StartDistAlong(); auto horizontal_length = segment->HorizontalLength(); @@ -732,7 +732,7 @@ std::pair mapAlign result.first = curve_segment; } else if (type == Ifc4x3_add2::IfcAlignmentVerticalSegmentTypeEnum::IfcAlignmentVerticalSegmentType_CLOTHOID) { - Logger::Root().Warning("UNS", 23, std::string("mapping of AlignmentVerticalSegmentType CLOTHOID not supported")); + logger.Warning("UNS", 23, std::string("mapping of AlignmentVerticalSegmentType CLOTHOID not supported")); } else if (type == Ifc4x3_add2::IfcAlignmentVerticalSegmentTypeEnum::IfcAlignmentVerticalSegmentType_CIRCULARARC) { auto start_angle = atan(start_gradient); auto end_angle = atan(end_gradient); @@ -760,31 +760,31 @@ std::pair mapAlign result.first = curve_segment; } else { - Logger::Root().Error("VAL", 10, std::string("unexpected AlignmentVerticalSegmentType encountered")); + logger.Error("VAL", 10, std::string("unexpected AlignmentVerticalSegmentType encountered")); } return result; } -std::pair mapAlignmentCantSegment(const Ifc4x3_add2::IfcAlignmentCantSegment* segment) { +std::pair mapAlignmentCantSegment(const Ifc4x3_add2::IfcAlignmentCantSegment* segment, Logger& logger) { std::pair result(nullptr, nullptr); auto type = segment->PredefinedType(); if (type == Ifc4x3_add2::IfcAlignmentCantSegmentTypeEnum::IfcAlignmentCantSegmentType_BLOSSCURVE) { - Logger::Root().Warning("UNS", 24, std::string("mapping of AlignmentCantSegmentType BLOSSCURVE not supported")); + logger.Warning("UNS", 24, std::string("mapping of AlignmentCantSegmentType BLOSSCURVE not supported")); } else if (type == Ifc4x3_add2::IfcAlignmentCantSegmentTypeEnum::IfcAlignmentCantSegmentType_CONSTANTCANT) { - Logger::Root().Warning("UNS", 25, std::string("mapping of AlignmentCantSegmentType CONSTANTCANT not supported")); + logger.Warning("UNS", 25, std::string("mapping of AlignmentCantSegmentType CONSTANTCANT not supported")); } else if (type == Ifc4x3_add2::IfcAlignmentCantSegmentTypeEnum::IfcAlignmentCantSegmentType_COSINECURVE) { - Logger::Root().Warning("UNS", 26, std::string("mapping of AlignmentCantSegmentType COSINECURVE not supported")); + logger.Warning("UNS", 26, std::string("mapping of AlignmentCantSegmentType COSINECURVE not supported")); } else if (type == Ifc4x3_add2::IfcAlignmentCantSegmentTypeEnum::IfcAlignmentCantSegmentType_HELMERTCURVE) { - Logger::Root().Warning("UNS", 27, std::string("mapping of AlignmentCantSegmentType HELMERTCURVE not supported")); + logger.Warning("UNS", 27, std::string("mapping of AlignmentCantSegmentType HELMERTCURVE not supported")); } else if (type == Ifc4x3_add2::IfcAlignmentCantSegmentTypeEnum::IfcAlignmentCantSegmentType_LINEARTRANSITION) { - Logger::Root().Warning("UNS", 28, std::string("mapping of AlignmentCantSegmentType LINEARTRANSTION not supported")); + logger.Warning("UNS", 28, std::string("mapping of AlignmentCantSegmentType LINEARTRANSTION not supported")); } else if (type == Ifc4x3_add2::IfcAlignmentCantSegmentTypeEnum::IfcAlignmentCantSegmentType_SINECURVE) { - Logger::Root().Warning("UNS", 29, std::string("mapping of AlignmentCantSegmentType SINECURVE not supported")); + logger.Warning("UNS", 29, std::string("mapping of AlignmentCantSegmentType SINECURVE not supported")); } else if (type == Ifc4x3_add2::IfcAlignmentCantSegmentTypeEnum::IfcAlignmentCantSegmentType_VIENNESEBEND) { - Logger::Root().Warning("UNS", 30, std::string("mapping of AlignmentCantSegmentType VIENNESEBEND not supported")); + logger.Warning("UNS", 30, std::string("mapping of AlignmentCantSegmentType VIENNESEBEND not supported")); } else { - Logger::Root().Error("VAL", 11, std::string("unexpected AlignmentCantSegmentType encountered")); + logger.Error("VAL", 11, std::string("unexpected AlignmentCantSegmentType encountered")); } return result; } diff --git a/src/ifcparse/IfcAlignmentHelper.h b/src/ifcparse/IfcAlignmentHelper.h index 444cedd3fd..b01a39be08 100644 --- a/src/ifcparse/IfcAlignmentHelper.h +++ b/src/ifcparse/IfcAlignmentHelper.h @@ -49,11 +49,11 @@ IFC_PARSE_API Ifc4x3_add2::IfcAlignment* addAlignment(IfcHierarchyHelper mapAlignmentSegment(const Ifc4x3_add2::IfcAlignmentSegment* segment); -IFC_PARSE_API std::pair mapAlignmentHorizontalSegment(const Ifc4x3_add2::IfcAlignmentHorizontalSegment* segment); -IFC_PARSE_API std::pair mapAlignmentVerticalSegment(const Ifc4x3_add2::IfcAlignmentVerticalSegment* segment); -IFC_PARSE_API std::pair mapAlignmentCantSegment(const Ifc4x3_add2::IfcAlignmentCantSegment* segment); +IFC_PARSE_API std::pair mapAlignmentSegment(const Ifc4x3_add2::IfcAlignmentSegment* segment, Logger& logger = Logger::Root()); +IFC_PARSE_API std::pair mapAlignmentHorizontalSegment(const Ifc4x3_add2::IfcAlignmentHorizontalSegment* segment, Logger& logger = Logger::Root()); +IFC_PARSE_API std::pair mapAlignmentVerticalSegment(const Ifc4x3_add2::IfcAlignmentVerticalSegment* segment, Logger& logger = Logger::Root()); +IFC_PARSE_API std::pair mapAlignmentCantSegment(const Ifc4x3_add2::IfcAlignmentCantSegment* segment, Logger& logger = Logger::Root()); #endif -#endif \ No newline at end of file +#endif diff --git a/src/ifcparse/IfcCharacterDecoder.cpp b/src/ifcparse/IfcCharacterDecoder.cpp index 10606a9cf6..019499a49e 100644 --- a/src/ifcparse/IfcCharacterDecoder.cpp +++ b/src/ifcparse/IfcCharacterDecoder.cpp @@ -77,7 +77,9 @@ using namespace IfcParse; -IfcCharacterDecoder::IfcCharacterDecoder(IfcParse::FileReader* stream) { +IfcCharacterDecoder::IfcCharacterDecoder(IfcParse::FileReader* stream, Logger& logger) + : logger_(logger) +{ stream_ = stream; codepage_ = 0; } @@ -86,7 +88,7 @@ IfcCharacterDecoder::~IfcCharacterDecoder() { } namespace { - std::string read_string(IfcParse::FileReader& stream_, IfcParse::IfcCharacterDecoder::ConversionMode mode, char substitution_character) { + std::string read_string(IfcParse::FileReader& stream_, Logger& logger, IfcParse::IfcCharacterDecoder::ConversionMode mode, char substitution_character) { std::u32string builder_; unsigned int parse_state = 0; @@ -137,7 +139,7 @@ namespace { parse_state += PAGE; } else if (IS_HEXADECIMAL(current_char) && EXPECTS_HEX(parse_state)) { if (IS_LOWERCASE_HEX(current_char)) { - Logger::Root().Warning("SYN", 2, "Lowercase hexadecimal character '" + std::string(1, current_char) + + logger.Warning("SYN", 2, "Lowercase hexadecimal character '" + std::string(1, current_char) + "' found at offset " + std::to_string(stream_.tell()) + ". It is recommended to use uppercase for hexadecimal."); } @@ -211,13 +213,13 @@ namespace { } // namespace IfcCharacterDecoder::operator std::string() { - return read_string(*stream_, mode, substitution_character); + return read_string(*stream_, logger_, mode, substitution_character); } std::string IfcCharacterDecoder::get(size_t& ptr) { auto local_stream = *stream_; local_stream.seek(ptr); - auto s = read_string(local_stream, mode, substitution_character); + auto s = read_string(local_stream, logger_, mode, substitution_character); ptr = local_stream.tell(); return s; } diff --git a/src/ifcparse/IfcCharacterDecoder.h b/src/ifcparse/IfcCharacterDecoder.h index e978a5b4c2..416c2ac9f9 100644 --- a/src/ifcparse/IfcCharacterDecoder.h +++ b/src/ifcparse/IfcCharacterDecoder.h @@ -28,6 +28,7 @@ #define IFCCHARACTERDECODER_H #include "FileReader.h" +#include "IfcLogger.h" #include @@ -42,6 +43,7 @@ namespace IfcParse { class IFC_PARSE_API IfcCharacterDecoder { private: IfcParse::FileReader* stream_; + Logger& logger_; int codepage_; public: @@ -52,7 +54,7 @@ class IFC_PARSE_API IfcCharacterDecoder { }; static ConversionMode mode; static char substitution_character; - IfcCharacterDecoder(IfcParse::FileReader* stream); + IfcCharacterDecoder(IfcParse::FileReader* stream, Logger& logger = Logger::Root()); ~IfcCharacterDecoder(); // Only advances the underlying token stream read pointer // to the next token. diff --git a/src/ifcparse/IfcFile.cpp b/src/ifcparse/IfcFile.cpp index 7b45430088..1648842bcf 100644 --- a/src/ifcparse/IfcFile.cpp +++ b/src/ifcparse/IfcFile.cpp @@ -59,7 +59,7 @@ namespace { constexpr bool is_type_in_variant_v = is_type_in_variant::value; template - void dispatch_token(boost::optional instance_id, int attribute_id, IfcParse::Token t, IfcParse::declaration* decl, Fn fn) { + void dispatch_token(boost::optional instance_id, int attribute_id, IfcParse::Token t, IfcParse::declaration* decl, Logger& logger, Fn fn) { if (t.type == IfcParse::Token_BINARY) { fn(IfcParse::TokenFunc::asBinary(t)); } else if (IfcParse::TokenFunc::isBool(t)) { @@ -72,10 +72,10 @@ namespace { try { fn(EnumerationReference(decl->as_enumeration_type(), decl->as_enumeration_type()->lookup_enum_offset(s))); } catch (IfcParse::IfcException& e) { - Logger::Root().Error("VAL", 12, "An enumeration literal '" + s + "' is not valid for type '" + decl->name() + "' at offset " + std::to_string(t.startPos)); + logger.Error("VAL", 12, "An enumeration literal '" + s + "' is not valid for type '" + decl->name() + "' at offset " + std::to_string(t.startPos)); } } else { - Logger::Root().Error("VAL", 13, "An enumeration literal '" + s + "' is not expected at attribute index '" + std::to_string(attribute_id) + "' at offset " + std::to_string(t.startPos)); + logger.Error("VAL", 13, "An enumeration literal '" + s + "' is not expected at attribute index '" + std::to_string(attribute_id) + "' at offset " + std::to_string(t.startPos)); } } else if (t.type == IfcParse::Token_FLOAT) { fn(IfcParse::TokenFunc::asFloat(t)); @@ -92,7 +92,7 @@ namespace { } template - void construct_(boost::optional instance_id, int attribute_id, IfcParse::parse_context& p, const IfcParse::aggregation_type* aggr, Fn fn) { + void construct_(boost::optional instance_id, int attribute_id, IfcParse::parse_context& p, const IfcParse::aggregation_type* aggr, Logger& logger, Fn fn) { if (p.tokens_.empty()) { // @todo instead of ugly if-else we could also default initialize the respective // variant types below. @@ -135,7 +135,7 @@ namespace { possible_aggregation_types_t aggregate_storage; - auto append_to_aggregate_storage = [&aggregate_storage](const auto& v) { + auto append_to_aggregate_storage = [&aggregate_storage, &logger](const auto& v) { if constexpr (is_type_in_variant_v>>) { if (aggregate_storage.index() == 0) { aggregate_storage = std::vector>{ v }; @@ -194,7 +194,7 @@ namespace { } }, aggregate_storage); - Logger::Root().Error("VAL", 14, "Inconsistent aggregate valuation while attempting to append " + std::string(typeid(decltype(v)).name()) + " to an aggregate of " + current); + logger.Error("VAL", 14, "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.. @@ -213,19 +213,19 @@ namespace { } } else { // @todo would be cool if we can trace this back to file offset - Logger::Root().Error("UNS", 31, std::string("Aggregates of ") + typeid(decltype(v)).name() + " are not supported in the IfcOpenShell parser"); + logger.Error("UNS", 31, 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) { + std::visit([&aggregate_storage, &append_to_aggregate_storage, aggr, instance_id, attribute_id, &logger](const auto& v) { if constexpr (std::is_same_v, IfcParse::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); + 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, logger, append_to_aggregate_storage); } else if constexpr (std::is_same_v, IfcParse::parse_context*>) { // nested list if constexpr (Depth < 3) { - construct_(instance_id, attribute_id, *v, nullptr, append_to_aggregate_storage); + construct_(instance_id, attribute_id, *v, nullptr, logger, append_to_aggregate_storage); } } else { append_to_aggregate_storage(IfcParse::reference_or_simple_type{ v }); @@ -237,7 +237,7 @@ namespace { } } -IfcEntityInstanceData IfcParse::parse_context::construct(boost::optional name, unresolved_references& references_to_resolve, const IfcParse::declaration* decl, boost::optional expected_size, int resolve_reference_index, bool coerce_attribute_count) { +IfcEntityInstanceData IfcParse::parse_context::construct(boost::optional name, unresolved_references& references_to_resolve, const IfcParse::declaration* decl, boost::optional expected_size, int resolve_reference_index, Logger& logger, bool coerce_attribute_count) { std::vector parameter_types; std::unique_ptr transient_named_type; @@ -263,9 +263,9 @@ IfcEntityInstanceData IfcParse::parse_context::construct(boost::optional { size_t expected = expected_size ? *expected_size : parameter_types.size(); if (decl != nullptr && decl->schema() == &Header_section_schema::get_schema()) { - Logger::Root().Warning("VAL", 15, "Expected " + std::to_string(expected) + " attribute values, found " + std::to_string(tokens_.size()) + " for header entity " + decl->name()); + logger.Warning("VAL", 15, "Expected " + std::to_string(expected) + " attribute values, found " + std::to_string(tokens_.size()) + " for header entity " + decl->name()); } else { - Logger::Root().Warning("VAL", 16, "Expected " + std::to_string(expected) + " attribute values, found " + std::to_string(tokens_.size()) + (name ? std::string(" for instance #" + std::to_string(*name)) : std::string(""))); + logger.Warning("VAL", 16, "Expected " + std::to_string(expected) + " attribute values, found " + std::to_string(tokens_.size()) + (name ? std::string(" for instance #" + std::to_string(*name)) : std::string(""))); } } @@ -292,9 +292,9 @@ IfcEntityInstanceData IfcParse::parse_context::construct(boost::optional 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) { + std::visit([this, &storage, name, &references_to_resolve, index, param_type, resolve_reference_index, &logger](const auto& v) { if constexpr (std::is_same_v, IfcParse::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) { + dispatch_token(name, index, v, param_type && param_type->as_named_type() ? param_type->as_named_type()->declared_type() : nullptr, logger, [this, &storage, name, &references_to_resolve, index, resolve_reference_index](auto v) { if constexpr (std::is_same_v, IfcParse::reference_or_simple_type>) { if (name) { references_to_resolve.push_back(std::make_pair( @@ -316,7 +316,7 @@ IfcEntityInstanceData IfcParse::parse_context::construct(boost::optional 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) { + construct_<0>(name, index, *v, pt ? pt->as_aggregation_type() : nullptr, logger, [this, &storage, name, &references_to_resolve, index, resolve_reference_index](const auto& v) { if constexpr (std::is_same_v, std::vector>) { if (name) { references_to_resolve.push_back({ { (uint32_t) *name, resolve_reference_index == -1 ? index : (uint8_t)resolve_reference_index }, v }); @@ -719,13 +719,13 @@ std::optionaldeclaration_by_name(TokenFunc::asStringRef(token_stream_[2])); } catch (const IfcException& ex) { - Logger::Root().Message(Logger::LOG_ERROR, "SYN", 3, std::string(ex.what()) + " at offset " + std::to_string(token_stream_[2].startPos)); + logger_.get().Message(Logger::LOG_ERROR, "SYN", 3, std::string(ex.what()) + " at offset " + std::to_string(token_stream_[2].startPos)); current_id = 0; goto advance; } if (entity_type->as_entity() == nullptr) { - Logger::Root().Message(Logger::LOG_ERROR, "SYN", 4, "Non entity type " + entity_type->name() + " at offset " + std::to_string(token_stream_[2].startPos)); + logger_.get().Message(Logger::LOG_ERROR, "SYN", 4, "Non entity type " + entity_type->name() + " at offset " + std::to_string(token_stream_[2].startPos)); goto advance; } @@ -744,7 +744,7 @@ std::optionalas_entity(), ps, -1); } catch (const IfcInvalidTokenException& e) { good_ = file_open_status::INVALID_SYNTAX; - Logger::Root().Error("SYN", 5, e); + logger_.get().Error("SYN", 5, e); break; } @@ -753,10 +753,10 @@ std::optionalNext(); } catch (const IfcException& e) { - Logger::Root().Message(Logger::LOG_ERROR, "SYN", 6, std::string(e.what()) + ". Parsing terminated"); + logger_.get().Message(Logger::LOG_ERROR, "SYN", 6, std::string(e.what()) + ". Parsing terminated"); } catch (...) { - Logger::Root().Message(Logger::LOG_ERROR, "SYN", 7, "Parsing terminated"); + logger_.get().Message(Logger::LOG_ERROR, "SYN", 7, "Parsing terminated"); } if (!lexer_->stream->eof() && next_token.type == Token_NONE) { diff --git a/src/ifcparse/IfcFile.h b/src/ifcparse/IfcFile.h index 1092bb5e4f..5ccfc1944d 100644 --- a/src/ifcparse/IfcFile.h +++ b/src/ifcparse/IfcFile.h @@ -27,6 +27,7 @@ #include "storage.h" #include "file_open_status.h" +#include #include #include #include @@ -96,6 +97,7 @@ private: const IfcParse::schema_definition* schema_; IfcParse::impl::in_memory_file_storage storage_; IfcParse::file_open_status good_ = IfcParse::file_open_status::SUCCESS; + std::reference_wrapper logger_; int progress_; IfcParse::unresolved_references references_to_resolve_; int yielded_header_instances_ = 0; @@ -144,13 +146,13 @@ private: void pushPage(const std::string& page); - InstanceStreamer(); + InstanceStreamer(Logger& logger = Logger::Root()); - InstanceStreamer(const std::string& fn, bool mmap=false); + InstanceStreamer(const std::string& fn, bool mmap=false, Logger& logger = Logger::Root()); - InstanceStreamer(void* data, int length); + InstanceStreamer(void* data, int length, Logger& logger = Logger::Root()); - InstanceStreamer(const IfcParse::schema_definition* schema, IfcParse::IfcSpfLexer* lexer); + InstanceStreamer(const IfcParse::schema_definition* schema, IfcParse::IfcSpfLexer* lexer, Logger& logger = Logger::Root()); void bypassTypes(const std::set& type_names); @@ -199,6 +201,7 @@ public: private: file_open_status good_ = file_open_status::SUCCESS; + std::reference_wrapper logger_; const IfcParse::schema_definition* schema_; const IfcParse::declaration* ifcroot_type_; @@ -229,7 +232,7 @@ public: /// /// UTF-8 file path to an IFC-SPF file /// Whether to use memory-mapped I/O - IfcFile(const std::string& path, bool mmap); + IfcFile(const std::string& path, bool mmap, Logger& logger = Logger::Root()); #endif /// /// Constructs an IfcFile object from a file path, supports IFC-SPF and the IfcOpenShell-specific RocksDB format. @@ -237,23 +240,23 @@ public: /// UTF-8 file path to an IFC-SPF file or RocksDB database directory /// File type of the path /// Whether to open in read-only mode, only supported on RocksDB databases - IfcFile(const std::string& path, filetype ty=FT_AUTODETECT, bool readonly=false); + IfcFile(const std::string& path, filetype ty=FT_AUTODETECT, bool readonly=false, Logger& logger = Logger::Root()); /// /// Constructs an IfcFile object from a stream containing IFC-SPF data. /// - IfcFile(std::istream& stream, int length); + IfcFile(std::istream& stream, int length, Logger& logger = Logger::Root()); /// /// Constructs an IfcFile object from a memory buffer containing IFC-SPF data. /// - IfcFile(void* data, int length); + IfcFile(void* data, int length, Logger& logger = Logger::Root()); /// /// Constructs an IfcFile object from a given IFC SPF stream. /// /// A pointer to an IfcParse::FileReader object representing the input IFC SPF data stream. - IfcFile(IfcParse::FileReader* stream); + IfcFile(IfcParse::FileReader* stream, Logger& logger = Logger::Root()); /// /// Constructs an IfcFile object with the specified schema, file type, and file path. @@ -262,12 +265,12 @@ public: /// Pointer to the schema definition to use. Defaults to the IFC4 schema if not specified. /// The file type to use for the file. Defaults to FT_AUTODETECT. /// The file system path to the IFC file. Defaults to an empty string. - IfcFile(const IfcParse::schema_definition* schema = IfcParse::schema_by_name("IFC4"), filetype ty = FT_AUTODETECT, const std::string& path = ""); + IfcFile(const IfcParse::schema_definition* schema = IfcParse::schema_by_name("IFC4"), filetype ty = FT_AUTODETECT, const std::string& path = "", Logger& logger = Logger::Root()); /// /// Constructs an unitialized IfcFile object. Call initialize() later on. Allows to specify which types to bypass during load. /// - IfcFile(const uninitialized_tag&); + IfcFile(const uninitialized_tag&, Logger& logger = Logger::Root()); bool initialize(const std::string& path, filetype ty = FT_AUTODETECT, bool readonly = false); #ifdef USE_MMAP @@ -281,6 +284,7 @@ public: ~IfcFile(); IfcParse::file_open_status good() const { return good_; } + Logger& logger() const { return logger_.get(); } /// Returns the first entity in the range of instances contained in the model, /// in arbitrary order @@ -443,7 +447,7 @@ public: }; #ifdef WITH_IFCXML -IFC_PARSE_API IfcFile* parse_ifcxml(const std::string& filename); +IFC_PARSE_API IfcFile* parse_ifcxml(const std::string& filename, Logger& logger = Logger::Root()); #endif namespace impl { diff --git a/src/ifcparse/IfcGlobalId.cpp b/src/ifcparse/IfcGlobalId.cpp index af2e4893f5..975bb0df5d 100644 --- a/src/ifcparse/IfcGlobalId.cpp +++ b/src/ifcparse/IfcGlobalId.cpp @@ -94,7 +94,7 @@ void expand(const std::string& s, std::vector& v) { static boost::uuids::basic_random_generator gen; #endif -IfcParse::IfcGlobalId::IfcGlobalId() { +IfcParse::IfcGlobalId::IfcGlobalId(Logger& logger) { uuid_data_ = gen(); std::vector v(uuid_data_.size()); std::copy(uuid_data_.begin(), uuid_data_.end(), v.begin()); @@ -111,12 +111,12 @@ IfcParse::IfcGlobalId::IfcGlobalId() { boost::uuids::uuid test_uuid; std::copy(test_vector.begin(), test_vector.end(), test_uuid.begin()); if (uuid_data_ != test_uuid) { - Logger::Root().Message(Logger::LOG_ERROR, "SYS", 34, "Internal error generating GlobalId"); + logger.Message(Logger::LOG_ERROR, "SYS", 34, "Internal error generating GlobalId"); } #endif } -IfcParse::IfcGlobalId::IfcGlobalId(const std::string& string) +IfcParse::IfcGlobalId::IfcGlobalId(const std::string& string, Logger& logger) : string_data_(string) { std::vector result; expand(string_data_, result); @@ -130,7 +130,7 @@ IfcParse::IfcGlobalId::IfcGlobalId(const std::string& string) #ifndef NDEBUG const std::string test_string = compress(&uuid_data_.data[0]); if (string_data_ != test_string) { - Logger::Root().Message(Logger::LOG_ERROR, "SYS", 35, "Internal error generating GlobalId"); + logger.Message(Logger::LOG_ERROR, "SYS", 35, "Internal error generating GlobalId"); } #endif } diff --git a/src/ifcparse/IfcGlobalId.h b/src/ifcparse/IfcGlobalId.h index d9f3cb5155..f281e08f2e 100644 --- a/src/ifcparse/IfcGlobalId.h +++ b/src/ifcparse/IfcGlobalId.h @@ -21,6 +21,7 @@ #define IFCGLOBALID_H #include "ifc_parse_api.h" +#include "IfcLogger.h" #include #include @@ -36,8 +37,8 @@ class IFC_PARSE_API IfcGlobalId { public: static const unsigned int length = 22; - IfcGlobalId(); - IfcGlobalId(const std::string&); + IfcGlobalId(Logger& logger = Logger::Root()); + IfcGlobalId(const std::string&, Logger& logger = Logger::Root()); operator const std::string&() const; operator const boost::uuids::uuid&() const; const std::string& formatted() const; diff --git a/src/ifcparse/IfcHierarchyHelper.cpp b/src/ifcparse/IfcHierarchyHelper.cpp index cc3b198e91..2566e204f0 100644 --- a/src/ifcparse/IfcHierarchyHelper.cpp +++ b/src/ifcparse/IfcHierarchyHelper.cpp @@ -129,7 +129,7 @@ typename Schema::IfcProject* IfcHierarchyHelper::addProject(typename Sch typename Schema::IfcUnitAssignment* unit_assignment = new typename Schema::IfcUnitAssignment(units); - typename Schema::IfcProject* project = new typename Schema::IfcProject(IfcParse::IfcGlobalId(), + typename Schema::IfcProject* project = new typename Schema::IfcProject(IfcParse::IfcGlobalId(this->logger()), owner_hist, boost::none, boost::none, @@ -159,7 +159,7 @@ void IfcHierarchyHelper::relatePlacements(typename Schema::IfcProduct* p if (local_place != parent->ObjectPlacement()) { local_place->setPlacementRelTo(parent->ObjectPlacement()); } else { - Logger::Root().Notice("SYN", 8, "Placement cannot be relative to self"); + this->logger().Notice("SYN", 8, "Placement cannot be relative to self"); } } } @@ -180,7 +180,7 @@ typename Schema::IfcSite* IfcHierarchyHelper::addSite(typename Schema::I proj = addProject(owner_hist); } - typename Schema::IfcSite* site = new typename Schema::IfcSite(IfcParse::IfcGlobalId(), + typename Schema::IfcSite* site = new typename Schema::IfcSite(IfcParse::IfcGlobalId(this->logger()), owner_hist, boost::none, boost::none, @@ -214,7 +214,7 @@ typename Schema::IfcBuilding* IfcHierarchyHelper::addBuilding(typename S if (!site) { site = addSite(0, owner_hist); } - typename Schema::IfcBuilding* building = new typename Schema::IfcBuilding(IfcParse::IfcGlobalId(), + typename Schema::IfcBuilding* building = new typename Schema::IfcBuilding(IfcParse::IfcGlobalId(this->logger()), owner_hist, boost::none, boost::none, @@ -249,7 +249,7 @@ typename Schema::IfcBuildingStorey* IfcHierarchyHelper::addBuildingStore if (!building) { building = addBuilding(0, owner_hist); } - typename Schema::IfcBuildingStorey* storey = new typename Schema::IfcBuildingStorey(IfcParse::IfcGlobalId(), + typename Schema::IfcBuildingStorey* storey = new typename Schema::IfcBuildingStorey(IfcParse::IfcGlobalId(this->logger()), owner_hist, boost::none, boost::none, diff --git a/src/ifcparse/IfcHierarchyHelper.h b/src/ifcparse/IfcHierarchyHelper.h index e397f1f89a..c4c9500572 100644 --- a/src/ifcparse/IfcHierarchyHelper.h +++ b/src/ifcparse/IfcHierarchyHelper.h @@ -362,7 +362,7 @@ void set_children_of_relation(IfcUtil::IfcBaseClass* t, aggregate_of_instance::p template class IFC_PARSE_API IfcHierarchyHelper : public IfcParse::IfcFile { public: - IfcHierarchyHelper() : IfcParse::IfcFile(&Schema::get_schema()) {} + IfcHierarchyHelper(Logger& logger = Logger::Root()) : IfcParse::IfcFile(&Schema::get_schema(), IfcParse::FT_AUTODETECT, "", logger) {} template T* addTriplet(double x, double y, double z) { @@ -436,7 +436,7 @@ class IFC_PARSE_API IfcHierarchyHelper : public IfcParse::IfcFile { } typename Schema::IfcObject::list::ptr related_objects(new aggregate_of()); related_objects->push(related_object->template as()); - typename Schema::IfcRelDefinesByType* t = new typename Schema::IfcRelDefinesByType(IfcParse::IfcGlobalId(), owner_hist, boost::none, boost::none, related_objects, relating_object->template as()); + typename Schema::IfcRelDefinesByType* t = new typename Schema::IfcRelDefinesByType(IfcParse::IfcGlobalId(this->logger()), owner_hist, boost::none, boost::none, related_objects, relating_object->template as()); addEntity(t); } @@ -454,9 +454,9 @@ class IFC_PARSE_API IfcHierarchyHelper : public IfcParse::IfcFile { break; } } catch (std::exception& e) { - Logger::Root().Error("SYN", 9, e); + this->logger().Error("SYN", 9, e); } catch (...) { - Logger::Root().Error("SYN", 10, "Unknown error in addRelatedObject()"); + this->logger().Error("SYN", 10, "Unknown error in addRelatedObject()"); } } if (!found) { @@ -471,7 +471,7 @@ class IFC_PARSE_API IfcHierarchyHelper : public IfcParse::IfcFile { related_objects->push(related_object); T* t = create(&T::Class())->template as(); - t->set_attribute_value(0, (std::string)IfcParse::IfcGlobalId()); + t->set_attribute_value(0, (std::string)IfcParse::IfcGlobalId(this->logger())); t->set_attribute_value(1, owner_hist); int relating_index = 4; int related_index = 5; diff --git a/src/ifcparse/IfcLogger.cpp b/src/ifcparse/IfcLogger.cpp index 2146146098..0b454b8563 100644 --- a/src/ifcparse/IfcLogger.cpp +++ b/src/ifcparse/IfcLogger.cpp @@ -32,8 +32,6 @@ #include #include -static my_thread_local std::map current_products_; - namespace { std::string get_time(bool with_milliseconds = false) { @@ -139,16 +137,11 @@ Logger& Logger::Root() { } const IfcUtil::IfcBaseClass* Logger::current_product() const { - auto it = current_products_.find(this); - return it == current_products_.end() ? nullptr : it->second; + return current_product_; } void Logger::current_product(const IfcUtil::IfcBaseClass* product) { - if (product) { - current_products_[this] = product; - } else { - current_products_.erase(this); - } + current_product_ = product; } void Logger::SetProduct(boost::optional product) { @@ -206,7 +199,7 @@ void Logger::Message(Logger::Severity type, const char (&code_prefix)[4], uint16 } if (format_ == FMT_INMEMORY) { - log_messages_.emplace_back(type, code_prefix, code_number, message, instance); + log_messages_.emplace_back(type, code_prefix, code_number, message, instance, current_product()); } else if (((log2_ != nullptr) || (wlog2_ != nullptr))) { if (format_ == FMT_PLAIN) { if (log2_ != nullptr) { @@ -251,9 +244,48 @@ void Logger::ProgressBar(int progress) { } std::string Logger::GetLog() { + std::lock_guard lock(mutex_); return log_stream_.str(); } +void Logger::ClearLog() { + std::lock_guard lock(mutex_); + log_stream_.str(std::string()); + log_stream_.clear(); + log_messages_.clear(); +} + +void Logger::Append(Logger& logger) { + if (&logger == this) { + return; + } + + std::scoped_lock lock(mutex_, logger.mutex_); + + if (logger.max_severity_ > max_severity_) { + max_severity_ = logger.max_severity_; + } + + if (format_ == FMT_INMEMORY) { + log_messages_.insert(log_messages_.end(), logger.log_messages_.begin(), logger.log_messages_.end()); + } else { + const std::string log = logger.log_stream_.str(); + if (!log.empty()) { + if (log2_ != nullptr) { + *log2_ << log; + } else if (wlog2_ != nullptr) { + *wlog2_ << string_as(log); + } else { + log_stream_ << log; + } + } + } + + logger.log_stream_.str(std::string()); + logger.log_stream_.clear(); + logger.log_messages_.clear(); +} + void Logger::PrintPerformanceStats() { std::vector> items; for (auto& stat : performance_statistics_) { diff --git a/src/ifcparse/IfcLogger.h b/src/ifcparse/IfcLogger.h index ac5a817d5a..19fcdd9738 100644 --- a/src/ifcparse/IfcLogger.h +++ b/src/ifcparse/IfcLogger.h @@ -31,14 +31,15 @@ #include #include #include +#include class IFC_PARSE_API log_message { public: char code[7]; int severity; - std::string message, instance; + std::string message, instance, product; - log_message(int severity, const char (&code_prefix)[4], uint16_t code_number, const std::string& message, const IfcUtil::IfcBaseInterface* inst = 0) + log_message(int severity, const char (&code_prefix)[4], uint16_t code_number, const std::string& message, const IfcUtil::IfcBaseInterface* inst = 0, const IfcUtil::IfcBaseClass* current_product = 0) : severity(severity) , message(message) { @@ -48,6 +49,11 @@ class IFC_PARSE_API log_message { inst->data().toString(nullptr, nullptr, 0, oss, true); instance = oss.str(); } + if (current_product) { + std::ostringstream oss; + current_product->toString(oss); + product = oss.str(); + } } }; @@ -79,6 +85,7 @@ class IFC_PARSE_API Logger { std::wostream* wlog2_ = nullptr; std::stringstream log_stream_; + const IfcUtil::IfcBaseClass* current_product_ = nullptr; Severity verbosity_ = LOG_NOTICE; Format format_ = FMT_PLAIN; @@ -134,8 +141,11 @@ class IFC_PARSE_API Logger { void ProgressBar(int progress); std::string GetLog(); + void ClearLog(); + void Append(Logger& logger); void PrintPerformanceStats(); void PrintPerformanceStatsOnElement(bool b) { print_perf_stats_on_element_ = b; } + bool PrintPerformanceStatsOnElement() const { return print_perf_stats_on_element_; } const std::vector& log_messages() const { return log_messages_; } }; diff --git a/src/ifcparse/IfcParse.cpp b/src/ifcparse/IfcParse.cpp index ca10bc4ef1..1fb3d2a170 100644 --- a/src/ifcparse/IfcParse.cpp +++ b/src/ifcparse/IfcParse.cpp @@ -102,9 +102,11 @@ void init_locale() { #endif -IfcSpfLexer::IfcSpfLexer(IfcParse::FileReader* stream_) { +IfcSpfLexer::IfcSpfLexer(IfcParse::FileReader* stream_, Logger& logger) + : logger_(logger) +{ stream = stream_; - decoder_ = new IfcCharacterDecoder(stream_); + decoder_ = new IfcCharacterDecoder(stream_, logger_); } IfcSpfLexer::~IfcSpfLexer() { @@ -320,7 +322,7 @@ Token IfcParse::GeneralTokenPtr(IfcSpfLexer* lexer, size_t start, const std::str if (first == '#') { token.type = Token_IDENTIFIER; if (!ParseInt(tokenStr.c_str() + 1, token.value_int)) { - Logger::Root().Message(Logger::LOG_ERROR, "SYN", 11, "Token '" + tokenStr + "' at offset " + std::to_string(token.startPos) + " is not valid"); + lexer->logger().Message(Logger::LOG_ERROR, "SYN", 11, "Token '" + tokenStr + "' at offset " + std::to_string(token.startPos) + " is not valid"); token.type = Token_OPERATOR; token.value_char = '$'; } @@ -562,13 +564,13 @@ void IfcParse::impl::in_memory_file_storage::load(boost::optional entity // type) and to be able to actually register the references in // the 2nd pass. load(entity_instance_name, entity, ps, attribute_index == -1 ? (int)attribute_index_within_data : attribute_index); - auto* simple_type_instance = (schema ? schema : file->schema())->instantiate(decl, ps.construct(entity_instance_name, *references_to_resolve, decl, boost::none, attribute_index == -1 ? (int)attribute_index_within_data : attribute_index)); + auto* simple_type_instance = (schema ? schema : file->schema())->instantiate(decl, ps.construct(entity_instance_name, *references_to_resolve, decl, boost::none, attribute_index == -1 ? (int)attribute_index_within_data : attribute_index, logger())); read_simple_type_instances.emplace_back(simple_type_instance); //@todo decide addEntity(((IfcUtil::IfcBaseClass*)*entity)); context.push(simple_type_instance); simple_type_instance->file_ = file; } catch (IfcException& e) { - Logger::Root().Message(Logger::LOG_ERROR, "SYN", 12, std::string(e.what()) + " at offset " + std::to_string(next.startPos)); + logger().Message(Logger::LOG_ERROR, "SYN", 12, std::string(e.what()) + " at offset " + std::to_string(next.startPos)); // #4070 We didn't actually capture an aggregate entry, undo length increment. return_value--; } @@ -592,7 +594,7 @@ IfcEntityInstanceData IfcParse::impl::in_memory_file_storage::read(unsigned int parse_context pc; tokens->Next(); load(i, ty->as_entity(), pc, -1); - return IfcEntityInstanceData(pc.construct(i, *references_to_resolve, ty, boost::none, -1)); + return IfcEntityInstanceData(pc.construct(i, *references_to_resolve, ty, boost::none, -1, logger())); } void IfcParse::impl::in_memory_file_storage::try_read_semicolon() const { @@ -663,7 +665,7 @@ void IfcParse::impl::rocks_db_file_storage::unregister_inverse(unsigned id_from, if (it != vals.end()) { vals.erase(it); } else { - Logger::Root().Error("VAL", 17, "Unregistering non-existant inverse #" + std::to_string(id_from) + " on instance #" + std::to_string(inst_id) + " at attribute " + std::to_string(attribute_index)); + file->logger().Error("VAL", 17, "Unregistering non-existant inverse #" + std::to_string(id_from) + " on instance #" + std::to_string(inst_id) + " at attribute " + std::to_string(attribute_index)); } s.resize(vals.size() * sizeof(uint32_t)); memcpy(s.data(), vals.data(), s.size()); @@ -1122,7 +1124,7 @@ IfcUtil::IfcBaseClass::set_attribute_value(size_t i, const T& t) { } } } catch (IfcParse::IfcException& e) { - Logger::Root().Error("SYN", 13, e); + file_->logger().Error("SYN", 13, e); } } @@ -1159,11 +1161,11 @@ IfcUtil::IfcBaseClass::set_attribute_value(size_t i, const T& t) { auto guid = (std::string) new_attribute; auto it = file_->internal_guid_map().find(guid); if (it != file_->internal_guid_map().end()) { - Logger::Root().Warning("VAL", 18, "Duplicate guid " + guid); + file_->logger().Warning("VAL", 18, "Duplicate guid " + guid); } file_->internal_guid_map().insert({ guid, this }); } catch (IfcParse::IfcException& e) { - Logger::Root().Error("SYN", 14, e); + file_->logger().Error("SYN", 14, e); } } } @@ -1182,7 +1184,13 @@ IfcUtil::IfcBaseClass::set_attribute_value(const std::string& s, const T& t) { // Creates the maps // #ifdef USE_MMAP -IfcFile::IfcFile(const std::string& fn, bool mmap) { +IfcFile::IfcFile(const std::string& fn, bool mmap, Logger& logger) + : logger_(logger) + , schema_(nullptr) + , ifcroot_type_(nullptr) + , max_id_(0) + , _header(this, logger) +{ initialize(fn, mmap); } @@ -1194,7 +1202,7 @@ bool IfcParse::IfcFile::initialize(const std::string& fn, bool mmap) { s = std::make_unique(fn); } - storage_.emplace<1>(this); + storage_.emplace<1>(this, logger_.get()); std::get(storage_).read_from_stream(&*s, schema_, max_id_, types_to_bypass_loading_); if ((good_ = std::get(storage_).good_)) { @@ -1209,8 +1217,8 @@ bool IfcParse::IfcFile::initialize(const std::string& fn, bool mmap) { } #endif -IfcFile::IfcFile(const uninitialized_tag&) - : schema_(nullptr), max_id_(0), _header(this), good_(file_open_status::UNKNOWN), ifcroot_type_(nullptr) {} +IfcFile::IfcFile(const uninitialized_tag&, Logger& logger) + : logger_(logger), schema_(nullptr), ifcroot_type_(nullptr), max_id_(0), _header(this, logger), good_(file_open_status::UNKNOWN) {} bool IfcParse::IfcFile::initialize(const std::string& path, filetype ty, bool readonly) { if (ty == FT_AUTODETECT) { @@ -1218,7 +1226,7 @@ bool IfcParse::IfcFile::initialize(const std::string& path, filetype ty, bool re } if (ty == FT_IFCSPF) { FileReader s(path); - storage_.emplace<1>(this); + storage_.emplace<1>(this, logger_.get()); std::get(storage_).read_from_stream(&s, schema_, max_id_, types_to_bypass_loading_); if ((good_ = std::get(storage_).good_)) { @@ -1261,17 +1269,22 @@ void IfcParse::IfcFile::bypass_type(const std::string& type_name) { types_to_bypass_loading_.insert(type_name); } -IfcFile::IfcFile(const std::string& path, filetype ty, bool readonly) - : schema_(nullptr) +IfcFile::IfcFile(const std::string& path, filetype ty, bool readonly, Logger& logger) + : logger_(logger) + , schema_(nullptr) + , ifcroot_type_(nullptr) , max_id_(0) - , _header(this) + , _header(this, logger) { initialize(path, ty, readonly); } -IfcFile::IfcFile(std::istream& stream, int length) - : schema_(nullptr) +IfcFile::IfcFile(std::istream& stream, int length, Logger& logger) + : logger_(logger) + , schema_(nullptr) + , ifcroot_type_(nullptr) , max_id_(0) + , _header(this, logger) { FileReader s(FileReader::caller_fed_tag{}); @@ -1280,7 +1293,7 @@ IfcFile::IfcFile(std::istream& stream, int length) stream.read(string_data.data(), length); s.pushNextPage(string_data); - storage_.emplace<1>(this); + storage_.emplace<1>(this, logger_.get()); std::get(storage_).read_from_stream(&s, schema_, max_id_, types_to_bypass_loading_); good_ = std::get(storage_).good_; ifcroot_type_ = schema_ ? schema_->declaration_by_name("IfcRoot") : nullptr; @@ -1290,13 +1303,16 @@ IfcFile::IfcFile(std::istream& stream, int length) byguid_ = decltype(byguid_)(&std::get(storage_).byguid_); } -IfcFile::IfcFile(void* data, int length) - : schema_(nullptr) +IfcFile::IfcFile(void* data, int length, Logger& logger) + : logger_(logger) + , schema_(nullptr) + , ifcroot_type_(nullptr) , max_id_(0) + , _header(this, logger) { FileReader s(std::string((char*)data, length), FileReader::caller_fed_tag{}); - storage_.emplace<1>(this); + storage_.emplace<1>(this, logger_.get()); std::get(storage_).read_from_stream(&s, schema_, max_id_, types_to_bypass_loading_); good_ = std::get(storage_).good_; ifcroot_type_ = schema_ ? schema_->declaration_by_name("IfcRoot") : nullptr; @@ -1306,11 +1322,14 @@ IfcFile::IfcFile(void* data, int length) byguid_ = decltype(byguid_)(&std::get(storage_).byguid_); } -IfcFile::IfcFile(IfcParse::FileReader* s) - : schema_(nullptr) +IfcFile::IfcFile(IfcParse::FileReader* s, Logger& logger) + : logger_(logger) + , schema_(nullptr) + , ifcroot_type_(nullptr) , max_id_(0) + , _header(this, logger) { - storage_.emplace<1>(this); + storage_.emplace<1>(this, logger_.get()); std::get(storage_).read_from_stream(s, schema_, max_id_, types_to_bypass_loading_); good_ = std::get(storage_).good_; ifcroot_type_ = schema_ ? schema_->declaration_by_name("IfcRoot") : nullptr; @@ -1320,16 +1339,18 @@ IfcFile::IfcFile(IfcParse::FileReader* s) byguid_ = decltype(byguid_)(&std::get(storage_).byguid_); } -IfcFile::IfcFile(const IfcParse::schema_definition* schema, filetype ty, const std::string& path) - : schema_(schema) +IfcFile::IfcFile(const IfcParse::schema_definition* schema, filetype ty, const std::string& path, Logger& logger) + : logger_(logger) + , schema_(schema) , ifcroot_type_(schema_->declaration_by_name("IfcRoot")) , max_id_(0) + , _header(this, logger) { if (ty == FT_AUTODETECT) { ty = guess_file_type(path); } if (ty == FT_IFCSPF) { - storage_.emplace<1>(this); + storage_.emplace<1>(this, logger_.get()); byid_ = decltype(byid_)(&std::get(storage_).byid_); byref_excl_ = decltype(byref_excl_)(&std::get(storage_).byref_excl_); @@ -1347,13 +1368,12 @@ IfcFile::IfcFile(const IfcParse::schema_definition* schema, filetype ty, const s } else { throw std::runtime_error("Unsupported file format"); } - _header = IfcSpfHeader(this); setDefaultHeaderValues(); } bool IfcParse::InstanceStreamer::hasSemicolon() const { auto local_stream = stream_->clone(); - auto local_lexer = IfcSpfLexer(&local_stream); + auto local_lexer = IfcSpfLexer(&local_stream, logger_.get()); Token t; try { t = local_lexer.Next(); @@ -1376,7 +1396,7 @@ bool IfcParse::InstanceStreamer::hasSemicolon() const { size_t IfcParse::InstanceStreamer::semicolonCount() const { auto local_stream = stream_->clone(); - auto local_lexer = IfcSpfLexer(&local_stream); + auto local_lexer = IfcSpfLexer(&local_stream, logger_.get()); Token t; size_t count = 0; try { @@ -1402,7 +1422,7 @@ void IfcParse::InstanceStreamer::pushPage(const std::string& page) { stream_->pushNextPage(page); if (good_ == file_open_status::NO_HEADER) { - header_ = new IfcParse::IfcSpfHeader(lexer_); + header_ = new IfcParse::IfcSpfHeader(lexer_, logger_.get()); if (header_->tryRead() && header_->file_schema()->schema_identifiers().size() == 1) { try { schema_ = IfcParse::schema_by_name(header_->file_schema()->schema_identifiers().front()); @@ -1417,29 +1437,35 @@ void IfcParse::InstanceStreamer::pushPage(const std::string& page) } } -IfcParse::InstanceStreamer::InstanceStreamer() +IfcParse::InstanceStreamer::InstanceStreamer(Logger& logger) : stream_(new FileReader(FileReader::caller_fed_tag{})) - , lexer_(new IfcSpfLexer(stream_)) + , lexer_(new IfcSpfLexer(stream_, logger)) + , header_(nullptr) , token_stream_(3, Token{}) , schema_(nullptr) + , storage_(nullptr, logger) + , logger_(logger) , progress_(0) { init_locale(); good_ = file_open_status::NO_HEADER; } -IfcParse::InstanceStreamer::InstanceStreamer(const std::string& fn, bool mmap) +IfcParse::InstanceStreamer::InstanceStreamer(const std::string& fn, bool mmap, Logger& logger) : stream_(mmap ? new FileReader(fn, FileReader::mmap_tag{}) : new FileReader(fn)) - , lexer_(new IfcSpfLexer(stream_)) + , lexer_(new IfcSpfLexer(stream_, logger)) + , header_(nullptr) , token_stream_(3, Token{}) , schema_(nullptr) + , storage_(nullptr, logger) + , logger_(logger) , progress_(0) { init_locale(); good_ = file_open_status::NO_HEADER; if (stream_->size() && !stream_->eof()) { - header_ = new IfcParse::IfcSpfHeader(lexer_); + header_ = new IfcParse::IfcSpfHeader(lexer_, logger_.get()); if (header_->tryRead() && header_->file_schema()->schema_identifiers().size() == 1) { try { schema_ = IfcParse::schema_by_name(header_->file_schema()->schema_identifiers().front()); @@ -1454,18 +1480,21 @@ IfcParse::InstanceStreamer::InstanceStreamer(const std::string& fn, bool mmap) } } -IfcParse::InstanceStreamer::InstanceStreamer(void* data, int length) +IfcParse::InstanceStreamer::InstanceStreamer(void* data, int length, Logger& logger) : stream_(new FileReader(std::string((char*) data, length), FileReader::caller_fed_tag{})) - , lexer_(new IfcSpfLexer(stream_)) + , lexer_(new IfcSpfLexer(stream_, logger)) + , header_(nullptr) , token_stream_(3, Token{}) , schema_(nullptr) + , storage_(nullptr, logger) + , logger_(logger) , progress_(0) { init_locale(); good_ = file_open_status::NO_HEADER; if (stream_->size() && !stream_->eof()) { - header_ = new IfcParse::IfcSpfHeader(lexer_); + header_ = new IfcParse::IfcSpfHeader(lexer_, logger_.get()); if (header_->tryRead() && header_->file_schema()->schema_identifiers().size() == 1) { try { schema_ = IfcParse::schema_by_name(header_->file_schema()->schema_identifiers().front()); @@ -1480,12 +1509,14 @@ IfcParse::InstanceStreamer::InstanceStreamer(void* data, int length) } } -IfcParse::InstanceStreamer::InstanceStreamer(const IfcParse::schema_definition* schema, IfcParse::IfcSpfLexer* lexer) +IfcParse::InstanceStreamer::InstanceStreamer(const IfcParse::schema_definition* schema, IfcParse::IfcSpfLexer* lexer, Logger& logger) : stream_(nullptr) , lexer_(lexer) , header_(nullptr) , token_stream_(3, Token{}) , schema_(schema) + , storage_(nullptr, logger) + , logger_(logger) , progress_(0) { init_locale(); @@ -1509,7 +1540,7 @@ void IfcParse::impl::in_memory_file_storage::read_from_stream(IfcParse::FileRead return; } - tokens = new IfcSpfLexer(s); + tokens = new IfcSpfLexer(s, logger()); std::vector schemas; @@ -1531,21 +1562,21 @@ void IfcParse::impl::in_memory_file_storage::read_from_stream(IfcParse::FileRead schema = IfcParse::schema_by_name(schemas.front()); } catch (const IfcParse::IfcException& e) { good_ = file_open_status::UNSUPPORTED_SCHEMA; - Logger::Root().Error("SYN", 15, e); + logger().Error("SYN", 15, e); } } if (schema == nullptr) { - Logger::Root().Message(Logger::LOG_ERROR, "UNS", 32, "No support for file schema encountered (" + boost::algorithm::join(schemas, ", ") + ")"); + logger().Message(Logger::LOG_ERROR, "UNS", 32, "No support for file schema encountered (" + boost::algorithm::join(schemas, ", ") + ")"); return; } auto ifcroot_type_ = schema->declaration_by_name("IfcRoot"); - InstanceStreamer streamer(schema, tokens); + InstanceStreamer streamer(schema, tokens, logger()); streamer.bypassTypes(typed_to_bypass); - Logger::Root().Status("Scanning file..."); + logger().Status("Scanning file..."); while (streamer) { @@ -1569,11 +1600,11 @@ void IfcParse::impl::in_memory_file_storage::read_from_stream(IfcParse::FileRead if (byguid_.find(guid) != byguid_.end()) { std::stringstream ss; ss << "Instance encountered with non-unique GlobalId " << guid; - Logger::Root().Message(Logger::LOG_WARNING, "SYN", 16, ss.str()); + logger().Message(Logger::LOG_WARNING, "SYN", 16, ss.str()); } byguid_[guid] = instance; } catch (const IfcException& ex) { - Logger::Root().Message(Logger::LOG_ERROR, "SYN", 17, ex.what()); + logger().Message(Logger::LOG_ERROR, "SYN", 17, ex.what()); } } @@ -1589,7 +1620,7 @@ void IfcParse::impl::in_memory_file_storage::read_from_stream(IfcParse::FileRead if (byid_.find(current_id) != byid_.end()) { std::stringstream ss; ss << "Overwriting instance with name #" << current_id; - Logger::Root().Message(Logger::LOG_WARNING, "SYN", 18, ss.str()); + logger().Message(Logger::LOG_WARNING, "SYN", 18, ss.str()); } // byidentity_[instance->identity()] = instance; @@ -1612,7 +1643,7 @@ void IfcParse::impl::in_memory_file_storage::read_from_stream(IfcParse::FileRead inst->file_ = file; } - Logger::Root().Status("\rDone scanning file "); + logger().Status("\rDone scanning file "); delete tokens; @@ -1632,7 +1663,7 @@ void IfcParse::impl::in_memory_file_storage::read_from_stream(IfcParse::FileRead } auto it = byid_.find(*name); if (it == byid_.end()) { - Logger::Root().Error("SYN", 19, "Instance reference #" + std::to_string(*name) + " used by instance #" + std::to_string(ref) + " at attribute index " + std::to_string(refattr) + " not found at offset " + std::to_string(name->file_offset)); + logger().Error("SYN", 19, "Instance reference #" + std::to_string(*name) + " used by instance #" + std::to_string(ref) + " at attribute index " + std::to_string(refattr) + " not found at offset " + std::to_string(name->file_offset)); } else { auto* storage = &byid_[p.first.name_]->data(); auto attr_index = p.first.index_; @@ -1649,7 +1680,7 @@ void IfcParse::impl::in_memory_file_storage::read_from_stream(IfcParse::FileRead if (storage->has_attribute_value(nullptr, nullptr, 0, attr_index)) { storage->set_attribute_value(nullptr, nullptr, 0, attr_index, it->second); } else { - Logger::Root().Error("SYN", 20, "Duplicate definition for instance reference"); + logger().Error("SYN", 20, "Duplicate definition for instance reference"); } } } else if (auto* inst = std::get_if(v)) { @@ -1665,7 +1696,7 @@ void IfcParse::impl::in_memory_file_storage::read_from_stream(IfcParse::FileRead } auto it = byid_.find(*name); if (it == byid_.end()) { - Logger::Root().Error("SYN", 21, "Instance reference #" + std::to_string(*name) + " used by instance #" + std::to_string(ref) + " at attribute index " + std::to_string(refattr) + " not found at offset " + std::to_string(name->file_offset)); + logger().Error("SYN", 21, "Instance reference #" + std::to_string(*name) + " used by instance #" + std::to_string(ref) + " at attribute index " + std::to_string(refattr) + " not found at offset " + std::to_string(name->file_offset)); } else { instances->push(it->second); } @@ -1689,7 +1720,7 @@ void IfcParse::impl::in_memory_file_storage::read_from_stream(IfcParse::FileRead if (storage->has_attribute_value(nullptr, nullptr, 0, attr_index)) { storage->set_attribute_value(nullptr, nullptr, 0, attr_index, instances); } else { - Logger::Root().Error("SYN", 22, "Duplicate definition for instance reference"); + logger().Error("SYN", 22, "Duplicate definition for instance reference"); } } else if (auto* vvv = std::get_if>>(&p.second)) { aggregate_of_aggregate_of_instance::ptr instances(new aggregate_of_aggregate_of_instance); @@ -1702,7 +1733,7 @@ void IfcParse::impl::in_memory_file_storage::read_from_stream(IfcParse::FileRead } auto it = byid_.find(*name); if (it == byid_.end()) { - Logger::Root().Error("SYN", 23, "Instance reference #" + std::to_string(*name) + " used by instance #" + std::to_string(ref) + " at attribute index " + std::to_string(refattr) + " not found at offset " + std::to_string(name->file_offset)); + logger().Error("SYN", 23, "Instance reference #" + std::to_string(*name) + " used by instance #" + std::to_string(ref) + " at attribute index " + std::to_string(refattr) + " not found at offset " + std::to_string(name->file_offset)); } else { inner.push_back(it->second); } @@ -1728,12 +1759,12 @@ void IfcParse::impl::in_memory_file_storage::read_from_stream(IfcParse::FileRead if (storage->has_attribute_value(nullptr, nullptr, 0, attr_index)) { storage->set_attribute_value(nullptr, nullptr, 0, attr_index, instances); } else { - Logger::Root().Error("SYN", 24, "Duplicate definition for instance reference"); + logger().Error("SYN", 24, "Duplicate definition for instance reference"); } } } - Logger::Root().Status("Done resolving references"); + logger().Status("Done resolving references"); } void IfcFile::recalculate_id_counter() { @@ -1893,7 +1924,7 @@ IfcUtil::IfcBaseClass* IfcFile::addEntity(IfcUtil::IfcBaseClass* entity, int id) } } } catch (...) { - Logger::Root().Message(Logger::LOG_ERROR, "SYN", 25, "Failed to visit forward references of", entity); + logger().Message(Logger::LOG_ERROR, "SYN", 25, "Failed to visit forward references of", entity); } // See whether the instance is already part of a file @@ -2066,11 +2097,11 @@ IfcUtil::IfcBaseClass* IfcFile::addEntity(IfcUtil::IfcBaseClass* entity, int id) if (byguid_.find(guid) != byguid_.end()) { std::stringstream ss; ss << "Overwriting entity with guid " << guid; - Logger::Root().Message(Logger::LOG_WARNING, "SYN", 26, ss.str()); + logger().Message(Logger::LOG_WARNING, "SYN", 26, ss.str()); } byguid_.insert({ guid, new_entity }); } catch (const std::exception& ex) { - Logger::Root().Message(Logger::LOG_ERROR, "SYN", 27, ex.what()); + logger().Message(Logger::LOG_ERROR, "SYN", 27, ex.what()); } } @@ -2243,7 +2274,7 @@ void IfcFile::process_deletion_(IfcUtil::IfcBaseClass* entity) { if (it != byguid_.end()) { byguid_.erase(it); } else { - Logger::Root().Warning("VAL", 19, "GlobalId on rooted instance not encountered in map"); + logger().Warning("VAL", 19, "GlobalId on rooted instance not encountered in map"); } } diff --git a/src/ifcparse/IfcParse.h b/src/ifcparse/IfcParse.h index a034fde218..547220b00b 100644 --- a/src/ifcparse/IfcParse.h +++ b/src/ifcparse/IfcParse.h @@ -110,6 +110,7 @@ Token GeneralTokenPtr(IfcSpfLexer* tokens, size_t start, const std::string& data class IFC_PARSE_API IfcSpfLexer { private: IfcCharacterDecoder* decoder_; + Logger& logger_; size_t skipWhitespace() const; size_t skipComment() const; @@ -120,7 +121,8 @@ class IFC_PARSE_API IfcSpfLexer { } FileReader* stream; // IfcFile* file; - IfcSpfLexer(FileReader* stream); + IfcSpfLexer(FileReader* stream, Logger& logger = Logger::Root()); + Logger& logger() const { return logger_; } Token Next(); ~IfcSpfLexer(); void TokenString(size_t offset, std::string& result); diff --git a/src/ifcparse/IfcSpfHeader.cpp b/src/ifcparse/IfcSpfHeader.cpp index a55319696d..fd4c80e51a 100644 --- a/src/ifcparse/IfcSpfHeader.cpp +++ b/src/ifcparse/IfcSpfHeader.cpp @@ -30,12 +30,12 @@ static const char* const DATA = "DATA"; using namespace IfcParse; namespace { - IfcEntityInstanceData read_from_spf_file(IfcParse::impl::in_memory_file_storage* storage, const IfcParse::entity* decl) { + IfcEntityInstanceData read_from_spf_file(IfcParse::impl::in_memory_file_storage* storage, const IfcParse::entity* decl, Logger& logger) { if (storage != nullptr) { parse_context pc; storage->tokens->Next(); storage->load(-1, nullptr, pc, -1); - return pc.construct(boost::none, *storage->references_to_resolve, decl, decl->as_entity()->attribute_count(), -1); + return pc.construct(boost::none, *storage->references_to_resolve, decl, decl->as_entity()->attribute_count(), -1, logger); } else { // std::unreachable(); return IfcEntityInstanceData(in_memory_attribute_storage(10)); @@ -66,14 +66,19 @@ void IfcSpfHeader::readTerminal(const std::string& term, Trail trail) { } } -IfcParse::IfcSpfHeader::IfcSpfHeader(IfcParse::IfcFile* file) +IfcParse::IfcSpfHeader::IfcSpfHeader(IfcParse::IfcFile* file, Logger& logger) : file_(file), + logger_(logger), file_description_(nullptr), file_name_(nullptr), file_schema_(nullptr) { Header_section_schema::get_schema(); + if (file != nullptr) { + logger_ = file->logger(); + } + if (file == nullptr) { // overwritten later in IfcFile::setDefaultHeaderValues() when we know the schema identifier file_description_ = new Header_section_schema::file_description({}, ""); @@ -101,11 +106,12 @@ IfcParse::IfcSpfHeader::IfcSpfHeader(IfcParse::IfcFile* file) } } -IfcParse::IfcSpfHeader::IfcSpfHeader(IfcParse::IfcSpfLexer* lexer) +IfcParse::IfcSpfHeader::IfcSpfHeader(IfcParse::IfcSpfLexer* lexer, Logger& logger) + : logger_(logger) { Header_section_schema::get_schema(); - storage_ = new impl::in_memory_file_storage; + storage_ = new impl::in_memory_file_storage(nullptr, logger_.get()); storage_->tokens = lexer; file_ = nullptr; @@ -128,6 +134,7 @@ void IfcParse::IfcSpfHeader::file(IfcParse::IfcFile* file) { this->file_ = file; if (file != nullptr) { + logger_ = file->logger(); storage_ = std::visit([this](auto& m) -> decltype(storage_) { if constexpr (std::is_same_v, impl::in_memory_file_storage>) { return &m; @@ -153,19 +160,19 @@ void IfcSpfHeader::read() { readTerminal(Header_section_schema::file_description::Class().name_uc(), NONE); delete file_description_; - file_description_ = new Header_section_schema::file_description(read_from_spf_file(storage_, &Header_section_schema::file_description::Class())); + file_description_ = new Header_section_schema::file_description(read_from_spf_file(storage_, &Header_section_schema::file_description::Class(), logger_.get())); file_description_->file_ = file_; readSemicolon(); readTerminal(Header_section_schema::file_name::Class().name_uc(), NONE); delete file_name_; - file_name_ = new Header_section_schema::file_name(read_from_spf_file(storage_, &Header_section_schema::file_name::Class())); + file_name_ = new Header_section_schema::file_name(read_from_spf_file(storage_, &Header_section_schema::file_name::Class(), logger_.get())); file_name_->file_ = file_; readSemicolon(); readTerminal(Header_section_schema::file_schema::Class().name_uc(), NONE); delete file_schema_; - file_schema_ = new Header_section_schema::file_schema(read_from_spf_file(storage_, &Header_section_schema::file_schema::Class())); + file_schema_ = new Header_section_schema::file_schema(read_from_spf_file(storage_, &Header_section_schema::file_schema::Class(), logger_.get())); file_schema_->file_ = file_; readSemicolon(); } @@ -175,7 +182,7 @@ bool IfcSpfHeader::tryRead() { read(); return true; } catch (const std::exception& e) { - Logger::Root().Error("SYN", 28, e); + logger_.get().Error("SYN", 28, e); return false; } } diff --git a/src/ifcparse/IfcSpfHeader.h b/src/ifcparse/IfcSpfHeader.h index 6d47617316..fb6438745b 100644 --- a/src/ifcparse/IfcSpfHeader.h +++ b/src/ifcparse/IfcSpfHeader.h @@ -25,6 +25,8 @@ #include "Header_section_schema.h" #include "storage.h" +#include + namespace IfcParse { class IfcFile; @@ -32,6 +34,7 @@ class IfcFile; class IFC_PARSE_API IfcSpfHeader { private: IfcFile* file_; + std::reference_wrapper logger_; IfcParse::impl::in_memory_file_storage* storage_ = nullptr; mutable Header_section_schema::file_description* file_description_; @@ -45,13 +48,14 @@ class IFC_PARSE_API IfcSpfHeader { void readTerminal(const std::string& term, Trail trail); public: - explicit IfcSpfHeader(IfcParse::IfcFile* file = nullptr); - explicit IfcSpfHeader(IfcParse::IfcSpfLexer* lexer); + explicit IfcSpfHeader(IfcParse::IfcFile* file = nullptr, Logger& logger = Logger::Root()); + explicit IfcSpfHeader(IfcParse::IfcSpfLexer* lexer, Logger& logger = Logger::Root()); ~IfcSpfHeader(); IfcParse::IfcFile* file() { return file_; } void file(IfcParse::IfcFile* file); + Logger& logger() const { return logger_.get(); } void read(); bool tryRead(); diff --git a/src/ifcparse/parse_ifcxml.cpp b/src/ifcparse/parse_ifcxml.cpp index 66ac6a98a8..8ebb4c4fe3 100644 --- a/src/ifcparse/parse_ifcxml.cpp +++ b/src/ifcparse/parse_ifcxml.cpp @@ -206,11 +206,18 @@ class stack_node { }; struct ifcxml_parse_state { + explicit ifcxml_parse_state(Logger& logger) + : file(nullptr) + , dialect(ifcxml_dialect_unknown) + , logger(logger) + {} + IfcParse::IfcFile* file; std::vector stack; std::map idmap; std::vector> forward_references; ifcxml_dialect dialect; + Logger& logger; }; // ifc4 allows for aggregates to be concatenated using whitespace. @@ -227,7 +234,7 @@ std::vector split(const std::string& value) { return r; } -boost::any parse_attribute_value(const IfcParse::parameter_type* ty, const std::string& value) { +boost::any parse_attribute_value(const IfcParse::parameter_type* ty, const std::string& value, Logger& logger) { boost::any any; auto cpp_type = IfcUtil::from_parameter_type(ty); @@ -255,7 +262,7 @@ boost::any parse_attribute_value(const IfcParse::parameter_type* ty, const std:: } if (any.empty()) { - Logger::Root().Error("SYN", 29, "Attribute '" + value + "' not successfully parsed"); + logger.Error("SYN", 29, "Attribute '" + value + "' not successfully parsed"); } return any; @@ -293,7 +300,7 @@ static void end_element(void* user, const xmlChar* tag) { // ignore uos ex:iso_10303_28 (ifc2x3) and ifc:ifcXML (ifc4) if (tagname != "uos" && tagname != "ex:iso_10303_28" && tagname != "ifc:ifcXML" && tagname != "ifcXML") { if (state->stack.empty()) { - Logger::Root().Error("SYN", 30, "Mismatch in parse stack due to previous errors"); + state->logger.Error("SYN", 30, "Mismatch in parse stack due to previous errors"); } else { state->stack.pop_back(); } @@ -318,9 +325,9 @@ static void process_characters(void* user, const xmlChar* character, int len) { const auto* pt = state->stack.back().inst()->declaration().as_type_declaration()->declared_type(); boost::any val; try { - val = parse_attribute_value(pt, txt); + val = parse_attribute_value(pt, txt, state->logger); } catch (const std::exception& e) { - Logger::Root().Error("SYN", 31, e, state->stack.back().inst()); + state->logger.Error("SYN", 31, e, state->stack.back().inst()); } if (!val.empty()) { // type declaration always at idx 0 @@ -348,13 +355,13 @@ static void process_characters(void* user, const xmlChar* character, int len) { } else if (tagname == "documentation") { header.file_description()->setdescription({txt}); } else { - Logger::Root().Error("SYN", 32, "Unrecognized header entry " + tagname); + state->logger.Error("SYN", 32, "Unrecognized header entry " + tagname); } } else if (state_type == stack_node::node_instance_attribute) { const auto* pt = state->stack.back().inst()->declaration().as_entity()->attribute_by_index(state->stack.back().idx())->type_of_attribute(); auto cpp_type = IfcUtil::from_parameter_type(pt); if (cpp_type != IfcUtil::Argument_ENTITY_INSTANCE) { - auto val = parse_attribute_value(pt, txt); + auto val = parse_attribute_value(pt, txt, state->logger); if (!val.empty()) { visit_any([&state](auto& v) { state->stack.back().inst()->set_attribute_value(state->stack.back().idx(), v); @@ -363,7 +370,7 @@ static void process_characters(void* user, const xmlChar* character, int len) { } } else if (state_type == stack_node::node_aggregate_element) { const auto* pt = state->stack.back().aggregate_elem_type(); - auto val = parse_attribute_value(pt, txt); + auto val = parse_attribute_value(pt, txt, state->logger); if (!val.empty()) { (*(state->stack.rbegin() + 1)).aggregate_elements.push_back(val); } @@ -417,12 +424,12 @@ static void start_element(void* user, const xmlChar* tag, const xmlChar** attrs) if (it != end) { std::string schema_name(&it->front(), it->size()); boost::to_upper(schema_name); - state->file = new IfcParse::IfcFile(IfcParse::schema_by_name(schema_name)); + state->file = new IfcParse::IfcFile(IfcParse::schema_by_name(schema_name), IfcParse::FT_AUTODETECT, "", state->logger); state->dialect = ifcxml_dialect_ifc4; } goto end; } else if (tagname == "ex:iso_10303_28" && attrname == "xsi:schemaLocation" && boost::starts_with(value, "http://www.iai-tech.org/ifcXML/IFC2x3")) { - state->file = new IfcParse::IfcFile(IfcParse::schema_by_name("IFC2X3")); + state->file = new IfcParse::IfcFile(IfcParse::schema_by_name("IFC2X3"), IfcParse::FT_AUTODETECT, "", state->logger); state->dialect = ifcxml_dialect_ifc2x3; goto end; } @@ -491,14 +498,14 @@ static void start_element(void* user, const xmlChar* tag, const xmlChar** attrs) auto idx = entity->attribute_index(pair.first); if (idx != -1) { const auto* attr = entity->attribute_by_index(idx); - auto val = parse_attribute_value(attr->type_of_attribute(), pair.second); + auto val = parse_attribute_value(attr->type_of_attribute(), pair.second, state->logger); if (!val.empty()) { visit_any([&untyped, idx](auto& v) { untyped.set_attribute_value(idx, v); }, val); } } else { - Logger::Root().Error("SYN", 33, "Unknown attribute '" + pair.first + "' on entity '" + entity->name() + "' with value '" + pair.second + "'"); + state->logger.Error("SYN", 33, "Unknown attribute '" + pair.first + "' on entity '" + entity->name() + "' with value '" + pair.second + "'"); } } } @@ -556,7 +563,7 @@ static void start_element(void* user, const xmlChar* tag, const xmlChar** attrs) try { decl = state->file->schema()->declaration_by_name(tagname_copy); } catch (const std::exception& e) { - Logger::Root().Error("SYN", 34, e); + state->logger.Error("SYN", 34, e); } if (decl != nullptr) { auto inst_or_ref = create_instance(decl); @@ -571,7 +578,7 @@ static void start_element(void* user, const xmlChar* tag, const xmlChar** attrs) } else if (state_type == stack_node::node_instance) { const IfcParse::entity* current = state->stack.back().inst()->declaration().as_entity(); if (current == nullptr) { - Logger::Root().Error("SYN", 35, "'" + state->stack.back().inst()->declaration().name() + "' is not an entity, unable to set attribute '" + tagname + "'"); + state->logger.Error("SYN", 35, "'" + state->stack.back().inst()->declaration().name() + "' is not an entity, unable to set attribute '" + tagname + "'"); // We need to push something on the stack. Likely there has been some extra indirection that is not understood. state->stack.push_back(state->stack.back()); } else { @@ -582,7 +589,7 @@ static void start_element(void* user, const xmlChar* tag, const xmlChar** attrs) return attr->name() == tagname; }); if (found == inverses.end()) { - Logger::Root().Error("SYN", 36, "Unknown attribute " + tagname); + state->logger.Error("SYN", 36, "Unknown attribute " + tagname); state->stack.push_back(state->stack.back()); } else { if ((*found)->bound1() == 0 && (*found)->bound2() == 1) { @@ -595,7 +602,7 @@ static void start_element(void* user, const xmlChar* tag, const xmlChar** attrs) inst->set_attribute_value(idx, state->stack.back().inst()); state->stack.push_back(stack_node::instance(id, inst)); } else { - Logger::Root().Error("SYN", 37, "Unknown attribute " + tagname); + state->logger.Error("SYN", 37, "Unknown attribute " + tagname); state->stack.push_back(state->stack.back()); } } else { @@ -642,7 +649,7 @@ static void start_element(void* user, const xmlChar* tag, const xmlChar** attrs) try { decl = state->file->schema()->declaration_by_name(tagname); } catch (const std::exception& e) { - Logger::Root().Error("SYN", 38, e); + state->logger.Error("SYN", 38, e); } if (decl == nullptr) { @@ -651,7 +658,7 @@ static void start_element(void* user, const xmlChar* tag, const xmlChar** attrs) const IfcParse::entity* entity = decl->as_entity(); if ((entity == nullptr) && state_type != stack_node::node_instance_attribute) { - Logger::Root().Error("SYN", 39, "Not an entity definition " + tagname); + state->logger.Error("SYN", 39, "Not an entity definition " + tagname); goto end; } @@ -665,7 +672,7 @@ static void start_element(void* user, const xmlChar* tag, const xmlChar** attrs) if (inst != nullptr) { inst->set_attribute_value(idx, state->stack.back().inst()); } else { - Logger::Root().Error("SYN", 40, "Internal error, inverse attribute not processed"); + state->logger.Error("SYN", 40, "Internal error, inverse attribute not processed"); } } else if (state_type == stack_node::node_instance_attribute) { state->stack.back().inst()->set_attribute_value(state->stack.back().idx(), inst); @@ -688,12 +695,10 @@ end: return; } -IFC_PARSE_API IfcParse::IfcFile* IfcParse::parse_ifcxml(const std::string& filename) { +IFC_PARSE_API IfcParse::IfcFile* IfcParse::parse_ifcxml(const std::string& filename, Logger& logger) { throw std::runtime_error("IFC-XML import temporarily disabled"); - ifcxml_parse_state state; - state.file = nullptr; - state.dialect = ifcxml_dialect_unknown; + ifcxml_parse_state state(logger); xmlSAXHandler handler; memset(&handler, 0, sizeof(xmlSAXHandler)); diff --git a/src/ifcparse/storage.h b/src/ifcparse/storage.h index 39e5139040..8c46d59640 100644 --- a/src/ifcparse/storage.h +++ b/src/ifcparse/storage.h @@ -20,9 +20,11 @@ namespace rocksdb { #include "map_transformer.h" #include "set_to_map_transformer.h" #include "file_open_status.h" +#include "IfcLogger.h" #include +#include #include #include #include @@ -187,7 +189,7 @@ namespace IfcParse { void push(IfcUtil::IfcBaseClass* inst); - IfcEntityInstanceData construct(boost::optional name, unresolved_references& references_to_resolve, const IfcParse::declaration* decl, boost::optional expected_size, int resolve_reference_index, bool coerce_attribute_count=true); + IfcEntityInstanceData construct(boost::optional name, unresolved_references& references_to_resolve, const IfcParse::declaration* decl, boost::optional expected_size, int resolve_reference_index, Logger& logger, bool coerce_attribute_count=true); }; namespace impl { @@ -198,6 +200,7 @@ namespace IfcParse { } IfcParse::IfcSpfLexer* tokens; + std::reference_wrapper logger_; // IfcParse::FileReader* stream; // Either one of these needs to be set @@ -219,9 +222,10 @@ namespace IfcParse { typedef std::map> entities_by_ref_t; typedef entity_instance_by_name_t::iterator iterator; - in_memory_file_storage(IfcParse::IfcFile* f = nullptr) : tokens(nullptr), file(f), schema(nullptr) {} + in_memory_file_storage(IfcParse::IfcFile* f = nullptr, Logger& logger = Logger::Root()) : tokens(nullptr), logger_(logger), file(f), schema(nullptr) {} in_memory_file_storage(const in_memory_file_storage&) = delete; in_memory_file_storage(const in_memory_file_storage&&) = delete; + Logger& logger() const { return logger_.get(); } class type_iterator : public entities_by_type_t::const_iterator { diff --git a/src/serializers/RocksDbSerializer.cpp b/src/serializers/RocksDbSerializer.cpp index 112445d3f6..6ac8ec114b 100644 --- a/src/serializers/RocksDbSerializer.cpp +++ b/src/serializers/RocksDbSerializer.cpp @@ -19,7 +19,7 @@ RocksDbSerializer::RocksDbSerializer(IfcParse::IfcFile* file, const std::string& options.merge_operator.reset(new ConcatenateIdMergeOperator()); rocksdb::Status status = rocksdb::DB::Open(options, rocksdb_filename, &db_);*/ - output_file_ = new IfcParse::IfcFile(file->schema(), IfcParse::FT_ROCKSDB, rocksdb_filename_); + output_file_ = new IfcParse::IfcFile(file->schema(), IfcParse::FT_ROCKSDB, rocksdb_filename_, logger); // We promise never to add the same instance twice output_file_->check_existance_before_adding = false; From 3136c74c2f29648304ce2d8cbedcb6fe1421489a Mon Sep 17 00:00:00 2001 From: Thomas Krijnen Date: Thu, 11 Jun 2026 21:57:42 +0200 Subject: [PATCH 3/5] Pass logger to proj callback --- src/serializers/GltfSerializer.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/serializers/GltfSerializer.cpp b/src/serializers/GltfSerializer.cpp index 2d3b12c5e3..91ed2a5dbb 100644 --- a/src/serializers/GltfSerializer.cpp +++ b/src/serializers/GltfSerializer.cpp @@ -518,8 +518,11 @@ namespace { result[2] = v1[0] * v2[1] - v1[1] * v2[0]; } - void proj_log(void *, int, const char* c) { - logger_.Error("SER", 1, "PROJ: " + std::string(c)); + void proj_log(void* data, int, const char* c) { + auto logger = static_cast(data); + if (logger) { + logger->Error("SER", 1, "PROJ: " + std::string(c)); + } } } @@ -628,7 +631,7 @@ void GltfSerializer::setFile(IfcParse::IfcFile* f) { PJ_COORD wgs84_point; auto C = proj_context_create(); - proj_log_func(C, nullptr, proj_log); + proj_log_func(C, &logger_, proj_log); // @todo a bit ugly we assume a proj.db in current working directory. // a very simplistic but at least portable solution. From 4d22a3fdb95dcbe8a1ad2d1252feb9149b8a03f4 Mon Sep 17 00:00:00 2001 From: Thomas Krijnen Date: Fri, 12 Jun 2026 11:04:18 +0200 Subject: [PATCH 4/5] Enable retargeting of example schema --- src/examples/CMakeLists.txt | 51 +++++++++---- src/examples/IfcAdvancedHouse.cpp | 10 ++- src/examples/IfcOpenHouse.cpp | 67 ++++++++++------- src/examples/IfcParseExamples.cpp | 43 ++++------- src/examples/arbitrary_open_profile_def.cpp | 11 ++- src/examples/composite_profile_def.cpp | 49 ++++++++++--- src/examples/csg_primitive.cpp | 9 ++- src/examples/ellipse_pies.cpp | 43 +++++++---- src/examples/faces.cpp | 11 ++- src/examples/ifc_curve_rebar.cpp | 25 ++++++- src/examples/profiles.cpp | 81 ++++++++++++++++----- src/examples/triangulated_faceset.cpp | 15 +++- src/ifcgeom/mapping/mapping.h | 8 +- src/ifcparse/macros.h | 3 + 14 files changed, 294 insertions(+), 132 deletions(-) diff --git a/src/examples/CMakeLists.txt b/src/examples/CMakeLists.txt index a7c99043bd..a7a797b6f4 100644 --- a/src/examples/CMakeLists.txt +++ b/src/examples/CMakeLists.txt @@ -36,7 +36,17 @@ else() endif() macro(build_example exe_name) + set(_target_schema "") set(additional_targets ${ARGN}) + list(LENGTH additional_targets _argc) + if(_argc GREATER 0) + list(GET additional_targets 0 _first_arg) + if("${_first_arg}" IN_LIST SCHEMA_VERSIONS) + set(_target_schema ${_first_arg}) + list(REMOVE_AT additional_targets 0) + endif() + endif() + add_executable(${exe_name} ${exe_name}.cpp) if(STANDALONE_PROJECT) @@ -50,27 +60,38 @@ macro(build_example exe_name) target_link_libraries(${exe_name} IfcParse ${additional_targets}) set_target_properties(${exe_name} PROPERTIES FOLDER Examples) endif() + + if(_target_schema) + set_target_properties( + ${exe_name} + PROPERTIES COMPILE_FLAGS "-DIfcSchema=Ifc${_target_schema}" + ) + endif() + + unset(_target_schema) + unset(_argc) + unset(_first_arg) install(TARGETS ${exe_name}) endmacro() -if("4" IN_LIST SCHEMA_VERSIONS) - build_example(arbitrary_open_profile_def) - build_example(triangulated_faceset) +if(SCHEMA_VERSIONS) + list(GET SCHEMA_VERSIONS -1 schema) endif() -if("2x3" IN_LIST SCHEMA_VERSIONS) - build_example(composite_profile_def) - build_example(csg_primitive) - build_example(ellipse_pies) - build_example(faces) - build_example(ifc_curve_rebar) - build_example(profiles) - build_example(IfcParseExamples) +build_example(arbitrary_open_profile_def ${schema}) +build_example(triangulated_faceset ${schema}) - if(WITH_OPENCASCADE) - build_example(IfcOpenHouse geometry_serializer) - build_example(IfcAdvancedHouse geometry_serializer) - endif() +build_example(composite_profile_def ${schema}) +build_example(csg_primitive ${schema}) +build_example(ellipse_pies ${schema}) +build_example(faces ${schema}) +build_example(ifc_curve_rebar ${schema}) +build_example(profiles ${schema}) +build_example(IfcParseExamples ${schema}) + +if(WITH_OPENCASCADE) + build_example(IfcOpenHouse ${schema} geometry_serializer) + build_example(IfcAdvancedHouse ${schema} geometry_serializer) endif() if("4x3_add2" IN_LIST SCHEMA_VERSIONS) diff --git a/src/examples/IfcAdvancedHouse.cpp b/src/examples/IfcAdvancedHouse.cpp index 9b680bd79b..83909ce4c6 100644 --- a/src/examples/IfcAdvancedHouse.cpp +++ b/src/examples/IfcAdvancedHouse.cpp @@ -38,9 +38,15 @@ #include -#define IfcSchema Ifc2x3 #include "ifcparse/macros.h" -#include "ifcparse/Ifc2x3.h" + +#ifndef IfcSchema +#define IfcSchema Ifc2x3 +#endif + +#include INCLUDE_SCHEMA(ifcparse, IfcSchema) +#include INCLUDE_SCHEMA_DEFINITIONS(ifcparse, IfcSchema) + #include "ifcparse/IfcBaseClass.h" #include "ifcparse/IfcHierarchyHelper.h" diff --git a/src/examples/IfcOpenHouse.cpp b/src/examples/IfcOpenHouse.cpp index 24654adca7..944f6f3eae 100644 --- a/src/examples/IfcOpenHouse.cpp +++ b/src/examples/IfcOpenHouse.cpp @@ -35,9 +35,15 @@ #include -#define IfcSchema Ifc2x3 #include "ifcparse/macros.h" -#include "ifcparse/Ifc2x3.h" + +#ifndef IfcSchema +#define IfcSchema Ifc2x3 +#endif + +#include INCLUDE_SCHEMA(ifcparse, IfcSchema) +#include INCLUDE_SCHEMA_DEFINITIONS(ifcparse, IfcSchema) + #include "ifcparse/IfcBaseClass.h" #include "ifcparse/IfcHierarchyHelper.h" @@ -52,6 +58,11 @@ using namespace std::string_literals; // Some convenience typedefs and definitions. typedef IfcParse::IfcGlobalId guid; typedef std::pair XY; +#ifdef SCHEMA_HAS_IfcPresentationStyleAssignment +typedef IfcSchema::IfcPresentationStyleAssignment surface_style_t; +#else +typedef IfcSchema::IfcPresentationStyle surface_style_t; +#endif boost::none_t const null = boost::none; // The creation of Nurbs-surface for the IfcSite mesh, to be implemented lateron @@ -74,7 +85,7 @@ int main() { 0, // ObjectPlacement 0, // Representation null // Tag -#ifdef USE_IFC4 +#ifdef SCHEMA_IfcWall_HAS_PredefinedType , IfcSchema::IfcWallTypeEnum::IfcWallType_STANDARD #endif ); @@ -105,7 +116,7 @@ int main() { south_wall->setObjectPlacement(file.addLocalPlacement(storey_placement)); // A pale white colour is assigned to the wall. - IfcSchema::IfcPresentationStyleAssignment* wall_colour = setSurfaceColour(file, south_wall_shape, 0.75, 0.73, 0.68); + surface_style_t* wall_colour = setSurfaceColour(file, south_wall_shape, 0.75, 0.73, 0.68); // Now create a footing for the wall to rest on. IfcSchema::IfcFooting* footing = new IfcSchema::IfcFooting(guid(), file.getSingle(), @@ -119,7 +130,7 @@ int main() { footing->setRepresentation(file.addBox(10100, 5460, 2000)); footing->setObjectPlacement(file.addLocalPlacement(storey_placement, 0, 2500, -2000)); // The footing will have a dark gray colour - IfcSchema::IfcPresentationStyleAssignment* footing_colour = setSurfaceColour(file,footing->Representation(), 0.26, 0.22, 0.18); + surface_style_t* footing_colour = setSurfaceColour(file,footing->Representation(), 0.26, 0.22, 0.18); // IFC has two ways to apply boolean operations to geometry. IfcBooleanResults are commonly used // to clip geometry to a surface, for example to a slanted roof. For openings that are filled @@ -129,7 +140,7 @@ int main() { IfcSchema::IfcOpeningElement* west_opening = new IfcSchema::IfcOpeningElement(guid(), file.getSingle(), null, null, null, file.addLocalPlacement(south_wall->ObjectPlacement(), -2500, 0, 400), file.addBox(6000, 3630, 1600), null -#ifdef USE_IFC4 +#ifdef SCHEMA_IfcOpeningElement_HAS_PredefinedType , IfcSchema::IfcOpeningElementTypeEnum::IfcOpeningElementType_OPENING #endif ); @@ -144,7 +155,7 @@ int main() { IfcSchema::IfcOpeningElement* south_opening = new IfcSchema::IfcOpeningElement(guid(), file.getSingle(), null, null, null, file.addLocalPlacement(storey_placement, 3000, 0, 400), file.addBox(1860, 3000, 1600), null -#ifdef USE_IFC4 +#ifdef SCHEMA_IfcOpeningElement_HAS_PredefinedType , IfcSchema::IfcOpeningElementTypeEnum::IfcOpeningElementType_OPENING #endif ); @@ -194,7 +205,7 @@ int main() { // Copy the south wall to the north IfcSchema::IfcWallStandardCase* north_wall = new IfcSchema::IfcWallStandardCase(guid(), file.getSingle(), "North wall"s, null, null, file.addLocalPlacement(storey_placement, 0, 5000, 0), file.addAxisBox(10000, 360, 3000), null -#ifdef USE_IFC4 +#ifdef SCHEMA_IfcWall_HAS_PredefinedType , IfcSchema::IfcWallTypeEnum::IfcWallType_STANDARD #endif ); @@ -226,7 +237,7 @@ int main() { // Now create a wall on the east of the building, again starting with just a box shape IfcSchema::IfcWallStandardCase* east_wall = new IfcSchema::IfcWallStandardCase(guid(), file.getSingle(), "East wall"s, null, null, file.addLocalPlacement(storey_placement, 4820, 2500, 0, 0, 0, 1, 0, 1, 0), clipped_wall_body_reps[0], null -#ifdef USE_IFC4 +#ifdef SCHEMA_IfcWall_HAS_PredefinedType , IfcSchema::IfcWallTypeEnum::IfcWallType_STANDARD #endif ); @@ -235,7 +246,7 @@ int main() { // The east wall is copied to the west location of the house IfcSchema::IfcWallStandardCase* west_wall = new IfcSchema::IfcWallStandardCase(guid(), file.getSingle(), "West wall"s, null, null, file.addLocalPlacement(storey_placement, -4820, 2500, 0, 0, 0, 1, 0, -1, 0), clipped_wall_body_reps[1], null -#ifdef USE_IFC4 +#ifdef SCHEMA_IfcWall_HAS_PredefinedType , IfcSchema::IfcWallTypeEnum::IfcWallType_STANDARD #endif ); @@ -252,7 +263,7 @@ int main() { IfcSchema::IfcOpeningElement* west_opening_copy = new IfcSchema::IfcOpeningElement(guid(), file.getSingle(), null, null, null, file.addLocalPlacement(west_wall->ObjectPlacement(), 2500, -2500+4820, 400, 0, 0, 1, 0, 1, 0), file.addBox(6000, 3630, 1600), null -#ifdef USE_IFC4 +#ifdef SCHEMA_IfcOpeningElement_HAS_PredefinedType , IfcSchema::IfcOpeningElementTypeEnum::IfcOpeningElementType_OPENING #endif ); @@ -274,7 +285,7 @@ int main() { IfcSchema::IfcProperty::list::ptr properties(new IfcSchema::IfcProperty::list); properties->push(new IfcSchema::IfcPropertySingleValue("TotalArea", null, new IfcSchema::IfcAreaMeasure(site_area), 0)); IfcSchema::IfcPropertySet* pset = new IfcSchema::IfcPropertySet(guid(), file.getSingle(), "Pset_SiteCommon"s, null, properties); -#ifdef USE_IFC4 +#ifdef SCHEMA_HAS_IfcDefinitionSelect IfcSchema::IfcObjectDefinition::list::ptr related_objs(new IfcSchema::IfcObjectDefinition::list); #else IfcSchema::IfcObject::list::ptr related_objs(new IfcSchema::IfcObject::list); @@ -297,7 +308,7 @@ int main() { // Some BIM authoring applications, such as Autodesk Revit, ignore the geometrical representation // by and large and construct native walls using the layer thickness and reference line offset // provided here. -#ifdef USE_IFC4 +#ifdef SCHEMA_IfcMaterial_HAS_Description IfcSchema::IfcMaterial* material = new IfcSchema::IfcMaterial("Brick", null, null); #else IfcSchema::IfcMaterial* material = new IfcSchema::IfcMaterial("Brick"); @@ -306,7 +317,7 @@ int main() { material, 360, null -#ifdef USE_IFC4 +#ifdef SCHEMA_IfcMaterialLayer_HAS_Name , null , null , null @@ -318,7 +329,7 @@ int main() { IfcSchema::IfcMaterialLayerSet* layer_set = new IfcSchema::IfcMaterialLayerSet( layers, "Wall"s -#ifdef USE_IFC4 +#ifdef SCHEMA_IfcMaterialLayerSet_HAS_Description , null #endif ); @@ -327,7 +338,7 @@ int main() { IfcSchema::IfcLayerSetDirectionEnum::IfcLayerSetDirection_AXIS2, IfcSchema::IfcDirectionSenseEnum::IfcDirectionSense_POSITIVE, -180 -#ifdef USE_IFC4 +#ifdef SCHEMA_IfcMaterialLayerSetUsage_HAS_ReferenceExtent , null #endif ); @@ -337,7 +348,7 @@ int main() { file.getSingle(), null, null, -#ifdef USE_IFC4 +#ifdef SCHEMA_HAS_IfcDefinitionSelect file.instances_by_type()->as(), #else file.instances_by_type()->as(), @@ -362,7 +373,7 @@ int main() { IfcSchema::IfcStairFlight* stair = new IfcSchema::IfcStairFlight(guid(), file.getSingle(), null, null, null, file.addLocalPlacement(storey_placement, 5050, 1000, 0, 0, 1, 0, 1, 0, 0), file.addExtrudedPolyline(stair_points, 1200), null, 2, 2, 0.2, 0.25 -#ifdef USE_IFC4 +#ifdef SCHEMA_IfcStairFlight_HAS_PredefinedType , IfcSchema::IfcStairFlightTypeEnum::IfcStairFlightType_STRAIGHT #endif ); @@ -372,7 +383,7 @@ int main() { IfcSchema::IfcOpeningElement* door_opening = new IfcSchema::IfcOpeningElement(guid(), file.getSingle(), null, null, null, file.addLocalPlacement(storey_placement, 5000-180, 2500-900, 0), file.addBox(1000, 1000, 2200), null -#ifdef USE_IFC4 +#ifdef SCHEMA_IfcOpeningElement_HAS_PredefinedType , IfcSchema::IfcOpeningElementTypeEnum::IfcOpeningElementType_OPENING #endif ); @@ -384,7 +395,7 @@ int main() { // which constitute the door and its frame. IfcSchema::IfcDoor* door = new IfcSchema::IfcDoor(guid(), file.getSingle(), null, null, null, file.addLocalPlacement(storey_placement, 4800, 1600, 0, 0, 0, 1, 0, 1, 0), 0, null, 2200, 1000 -#ifdef USE_IFC4 +#ifdef SCHEMA_IfcDoor_HAS_PredefinedType , IfcSchema::IfcDoorTypeEnum::IfcDoorType_DOOR , IfcSchema::IfcDoorTypeOperationEnum::IfcDoorTypeOperation_SINGLE_SWING_LEFT , null @@ -406,11 +417,15 @@ int main() { setSurfaceColour(file, door->Representation(), 0.9, 0.9, 0.9); file.addEntity(new IfcSchema::IfcRelFillsElement(guid(), file.getSingle(), null, null, door_opening, door)); +#ifdef SCHEMA_HAS_IfcDoorType + IfcSchema::IfcDoorType* door_type = new IfcSchema::IfcDoorType(guid(), file.getSingle(), "Door type"s, null, null, null, null, null, null, + IfcSchema::IfcDoorTypeEnum::IfcDoorType_DOOR, IfcSchema::IfcDoorTypeOperationEnum::IfcDoorTypeOperation_SINGLE_SWING_LEFT, false, null); + file.addRelatedObject(door_type, door); +#elif defined(SCHEMA_HAS_IfcDoorStyle) IfcSchema::IfcDoorStyle* door_style = new IfcSchema::IfcDoorStyle(guid(), file.getSingle(), "Door type"s, null, null, null, null, null, IfcSchema::IfcDoorStyleOperationEnum::IfcDoorStyleOperation_SINGLE_SWING_LEFT, IfcSchema::IfcDoorStyleConstructionEnum::IfcDoorStyleConstruction_WOOD, false, false); - // NOTE: typing by IfcDoorStyle will cause validation errors in IFC4+ but it's allowed for backwards compatibility - // better to use IfcDoorType in the actual use case file.addRelatedObject(door_style, door); +#endif // Surface styles are assigned to representation items, hence there is no real limitation to // assign different colours within the same representation. However, some viewers have @@ -436,7 +451,7 @@ int main() { frame_representations->push(vertical_bar); // Add another reference to the vertical bar created above // The beams all have the same surface style assigned - IfcSchema::IfcPresentationStyleAssignment* frame_style = 0; + surface_style_t* frame_style = 0; for (IfcSchema::IfcShapeRepresentation::list::it i = frame_representations->begin(); i != frame_representations->end(); i += 2) { if (frame_style) { setSurfaceColour(file,*i, frame_style); @@ -461,7 +476,7 @@ int main() { IfcSchema::IfcLocalPlacement* place = *it; IfcSchema::IfcWindow* window = new IfcSchema::IfcWindow(guid(), file.getSingle(), null, null, null, place, 0, null, 1600, 1860 -#ifdef USE_IFC4 +#ifdef SCHEMA_IfcWindow_HAS_PredefinedType , IfcSchema::IfcWindowTypeEnum::IfcWindowType_WINDOW , IfcSchema::IfcWindowTypePartitioningEnum::IfcWindowTypePartitioning_SINGLE_PANEL , null @@ -489,7 +504,7 @@ int main() { { IfcSchema::IfcMember* frame_part = new IfcSchema::IfcMember(guid(), file.getSingle(), null, null, null, *frame_placement, file.addMappedItem(*frame_representation), null -#ifdef USE_IFC4 +#ifdef SCHEMA_IfcMember_HAS_PredefinedType , IfcSchema::IfcMemberTypeEnum::IfcMemberType_MULLION #endif ); @@ -501,7 +516,7 @@ int main() { // Add the glass plate to the list of parts IfcSchema::IfcPlate* glass_part = new IfcSchema::IfcPlate(guid(), file.getSingle(), null, null, null, file.addLocalPlacement(storey_placement, 930, 45, 90), file.addBox(1680, 10, 1420), null -#ifdef USE_IFC4 +#ifdef SCHEMA_IfcPlate_HAS_PredefinedType , IfcSchema::IfcPlateTypeEnum::IfcPlateType_SHEET #endif ); diff --git a/src/examples/IfcParseExamples.cpp b/src/examples/IfcParseExamples.cpp index cf29151116..8af17f7717 100644 --- a/src/examples/IfcParseExamples.cpp +++ b/src/examples/IfcParseExamples.cpp @@ -17,12 +17,16 @@ * * ********************************************************************************/ -// TODO: Multiple schemas +#include "ifcparse/macros.h" + +#ifndef IfcSchema #define IfcSchema Ifc2x3 +#endif #include "ifcparse/IfcFile.h" #include "ifcparse/IfcLogger.h" -#include "ifcparse/Ifc2x3.h" +#include INCLUDE_SCHEMA(ifcparse, IfcSchema) +#include INCLUDE_SCHEMA_DEFINITIONS(ifcparse, IfcSchema) #include #include @@ -39,33 +43,6 @@ static_assert(false, "A boost preprocessor sequence of schema identifiers is needed for this file to compile."); #endif -// @todo duplicated with Kernel.h - -// @tfk A macro cannot define an include (I think), so here we can't -// loop over the sequence of schema identifiers, but rather we have -// unroll the loop with at least the amount of schemas we'd like support -// for and then overflow into an existing empty include file. - -#define INCLUDE_SCHEMA(n) \ - BOOST_PP_IIF(BOOST_PP_GREATER(BOOST_PP_SEQ_SIZE(SCHEMA_SEQ), n), BOOST_PP_STRINGIZE(../ifcparse/BOOST_PP_CAT(Ifc,BOOST_PP_SEQ_ELEM(BOOST_PP_MIN(n, BOOST_PP_SEQ_SIZE(BOOST_PP_SEQ_POP_BACK(SCHEMA_SEQ))),SCHEMA_SEQ)).h), "../ifcgeom/empty.h") - -#include INCLUDE_SCHEMA(0) -#include INCLUDE_SCHEMA(1) -#include INCLUDE_SCHEMA(2) -#include INCLUDE_SCHEMA(3) -#include INCLUDE_SCHEMA(4) -#include INCLUDE_SCHEMA(5) -#include INCLUDE_SCHEMA(6) -#include INCLUDE_SCHEMA(7) -#include INCLUDE_SCHEMA(8) -#include INCLUDE_SCHEMA(9) -#include INCLUDE_SCHEMA(10) -#include INCLUDE_SCHEMA(11) -#include INCLUDE_SCHEMA(12) -#include INCLUDE_SCHEMA(13) -#include INCLUDE_SCHEMA(14) -#include INCLUDE_SCHEMA(15) - #include #if USE_VLD @@ -80,6 +57,12 @@ struct is_ifc4_or_higher> : s typedef std::map> element_properties; +#ifdef SCHEMA_HAS_IfcBuildingElement +typedef IfcSchema::IfcBuildingElement element_t +#else +typedef IfcSchema::IfcBuiltElement element_t +#endif + std::string format_string(const AttributeValue& argument) { // Argument is a runtime tagged variant for the various data types in a IFC model, // in this particular case we only care about flattening it to a string. @@ -259,7 +242,7 @@ int main(int argc, char** argv) { // we need to cast them to IfcWindows. Since these properties // are optional we need to make sure the properties are // defined for the window in question before accessing them. - IfcSchema::IfcBuildingElement::list::ptr elements = file.instances_by_type(); + example_element_type::list::ptr elements = file.instances_by_type(); std::cout << "Found " << elements->size() << " elements in " << argv[1] << ":" << std::endl; diff --git a/src/examples/arbitrary_open_profile_def.cpp b/src/examples/arbitrary_open_profile_def.cpp index 1346c56a5d..c79a1cf2e4 100644 --- a/src/examples/arbitrary_open_profile_def.cpp +++ b/src/examples/arbitrary_open_profile_def.cpp @@ -28,8 +28,15 @@ #include #include +#include "ifcparse/macros.h" + +#ifndef IfcSchema #define IfcSchema Ifc4 -#include "ifcparse/Ifc4.h" +#endif + +#include INCLUDE_SCHEMA(ifcparse, IfcSchema) +#include INCLUDE_SCHEMA_DEFINITIONS(ifcparse, IfcSchema) + #include "ifcparse/IfcHierarchyHelper.h" typedef std::string S; @@ -138,4 +145,4 @@ int main(int argc, char** argv) { std::ofstream f(filename); f << file; -} \ No newline at end of file +} diff --git a/src/examples/composite_profile_def.cpp b/src/examples/composite_profile_def.cpp index 2b5f082f55..98b5c52c00 100644 --- a/src/examples/composite_profile_def.cpp +++ b/src/examples/composite_profile_def.cpp @@ -27,14 +27,45 @@ #include #include +#include "ifcparse/macros.h" + +#ifndef IfcSchema #define IfcSchema Ifc2x3 -#include "ifcparse/Ifc2x3.h" +#endif + +#include INCLUDE_SCHEMA(ifcparse, IfcSchema) +#include INCLUDE_SCHEMA_DEFINITIONS(ifcparse, IfcSchema) + #include "ifcparse/IfcHierarchyHelper.h" typedef std::string S; typedef IfcParse::IfcGlobalId guid; boost::none_t const null = boost::none; +#ifdef SCHEMA_IfcIShapeProfileDef_HAS_FlangeEdgeRadius +#define IFC_I_SHAPE_PROFILE_DEF_EXTRA_ARGS , null, null +#else +#define IFC_I_SHAPE_PROFILE_DEF_EXTRA_ARGS +#endif + +#ifdef SCHEMA_IfcLShapeProfileDef_HAS_CentreOfGravityInX +#define IFC_L_SHAPE_PROFILE_DEF_EXTRA_ARGS , null, null +#else +#define IFC_L_SHAPE_PROFILE_DEF_EXTRA_ARGS +#endif + +#ifdef SCHEMA_IfcTShapeProfileDef_HAS_CentreOfGravityInY +#define IFC_T_SHAPE_PROFILE_DEF_EXTRA_ARGS , null +#else +#define IFC_T_SHAPE_PROFILE_DEF_EXTRA_ARGS +#endif + +#ifdef SCHEMA_IfcCShapeProfileDef_HAS_CentreOfGravityInX +#define IFC_C_SHAPE_PROFILE_DEF_EXTRA_ARGS , null +#else +#define IFC_C_SHAPE_PROFILE_DEF_EXTRA_ARGS +#endif + int main(int argc, char** argv) { const char filename[] = "composite_profile_def.ifc"; IfcHierarchyHelper file; @@ -49,21 +80,21 @@ int main(int argc, char** argv) { IfcSchema::IfcCartesianTransformationOperator2D* transform1 = new IfcSchema::IfcCartesianTransformationOperator2D(file.addDoublet(1, 0), file.addDoublet(0, -1), file.addDoublet(40, 0), null); IfcSchema::IfcCartesianTransformationOperator2D* transform2 = new IfcSchema::IfcCartesianTransformationOperator2D(file.addDoublet(0, -1), file.addDoublet(1, 0), file.addDoublet(40, 0), 0.3); - IfcSchema::IfcProfileDef* p1 = new Ifc2x3::IfcIShapeProfileDef( + IfcSchema::IfcProfileDef* p1 = new IfcSchema::IfcIShapeProfileDef( IfcSchema::IfcProfileTypeEnum::IfcProfileType_AREA, - null, file.addPlacement2d(), 25.0, 50.0, 5.0, 5.0, 2.0); + null, file.addPlacement2d(), 25.0, 50.0, 5.0, 5.0, 2.0 IFC_I_SHAPE_PROFILE_DEF_EXTRA_ARGS); - IfcSchema::IfcProfileDef* p2 = new Ifc2x3::IfcLShapeProfileDef( + IfcSchema::IfcProfileDef* p2 = new IfcSchema::IfcLShapeProfileDef( IfcSchema::IfcProfileTypeEnum::IfcProfileType_AREA, - null, file.addPlacement2d(), 50.0, 25.0, 5.0, 1.0, 2.0, 2.0, null, null); + null, file.addPlacement2d(), 50.0, 25.0, 5.0, 1.0, 2.0, 2.0 IFC_L_SHAPE_PROFILE_DEF_EXTRA_ARGS); - IfcSchema::IfcProfileDef* p3 = new Ifc2x3::IfcTShapeProfileDef( + IfcSchema::IfcProfileDef* p3 = new IfcSchema::IfcTShapeProfileDef( IfcSchema::IfcProfileTypeEnum::IfcProfileType_AREA, - null, file.addPlacement2d(), 50.0, 40.0, 10.0, 10.0, 3.0, 2.0, 1.0, 2.0, 2.0, null); + null, file.addPlacement2d(), 50.0, 40.0, 10.0, 10.0, 3.0, 2.0, 1.0, 2.0, 2.0 IFC_T_SHAPE_PROFILE_DEF_EXTRA_ARGS); - IfcSchema::IfcProfileDef* p4 = new Ifc2x3::IfcCShapeProfileDef( + IfcSchema::IfcProfileDef* p4 = new IfcSchema::IfcCShapeProfileDef( IfcSchema::IfcProfileTypeEnum::IfcProfileType_AREA, - null, file.addPlacement2d(80.), 50.0, 25.0, 5.0, 10.0, 2.0, null); + null, file.addPlacement2d(80.), 50.0, 25.0, 5.0, 10.0, 2.0 IFC_C_SHAPE_PROFILE_DEF_EXTRA_ARGS); file.addEntity(p2); file.addEntity(p3); diff --git a/src/examples/csg_primitive.cpp b/src/examples/csg_primitive.cpp index 4d805a3375..238f911741 100644 --- a/src/examples/csg_primitive.cpp +++ b/src/examples/csg_primitive.cpp @@ -27,8 +27,15 @@ #include #include +#include "ifcparse/macros.h" + +#ifndef IfcSchema #define IfcSchema Ifc2x3 -#include "ifcparse/Ifc2x3.h" +#endif + +#include INCLUDE_SCHEMA(ifcparse, IfcSchema) +#include INCLUDE_SCHEMA_DEFINITIONS(ifcparse, IfcSchema) + #include "ifcparse/IfcHierarchyHelper.h" typedef std::string S; diff --git a/src/examples/ellipse_pies.cpp b/src/examples/ellipse_pies.cpp index 8128c4377c..fec702b405 100644 --- a/src/examples/ellipse_pies.cpp +++ b/src/examples/ellipse_pies.cpp @@ -27,14 +27,27 @@ #include #include +#include "ifcparse/macros.h" + +#ifndef IfcSchema #define IfcSchema Ifc2x3 -#include "ifcparse/Ifc2x3.h" +#endif + +#include INCLUDE_SCHEMA(ifcparse, IfcSchema) +#include INCLUDE_SCHEMA_DEFINITIONS(ifcparse, IfcSchema) + #include "ifcparse/IfcHierarchyHelper.h" typedef std::string S; typedef IfcParse::IfcGlobalId guid; boost::none_t const null = boost::none; +#ifdef SCHEMA_HAS_IfcSegment +typedef IfcSchema::IfcSegment curve_segment_tt; +#else +typedef IfcSchema::IfcCompositeCurveSegment curve_segment_tt; +#endif + typedef struct { double r1; double r2; @@ -54,44 +67,44 @@ void create_testcase_for(IfcHierarchyHelper& file, const EllipsePie& std::vector coords2(flt2, flt2 + 2); std::vector coords3(flt3, flt3 + 2); - Ifc2x3::IfcCartesianPoint* p1 = new Ifc2x3::IfcCartesianPoint(coords1); - Ifc2x3::IfcCartesianPoint* p2 = new Ifc2x3::IfcCartesianPoint(coords2); - Ifc2x3::IfcCartesianPoint* p3 = new Ifc2x3::IfcCartesianPoint(coords3); + IfcSchema::IfcCartesianPoint* p1 = new IfcSchema::IfcCartesianPoint(coords1); + IfcSchema::IfcCartesianPoint* p2 = new IfcSchema::IfcCartesianPoint(coords2); + IfcSchema::IfcCartesianPoint* p3 = new IfcSchema::IfcCartesianPoint(coords3); - Ifc2x3::IfcCartesianPoint::list::ptr points(new Ifc2x3::IfcCartesianPoint::list()); + IfcSchema::IfcCartesianPoint::list::ptr points(new IfcSchema::IfcCartesianPoint::list()); points->push(p3); points->push(p1); points->push(p2); file.addEntities(points->generalize()); - Ifc2x3::IfcEllipse* ellipse = new Ifc2x3::IfcEllipse(file.addPlacement2d(), pie.r1, pie.r2); + IfcSchema::IfcEllipse* ellipse = new IfcSchema::IfcEllipse(file.addPlacement2d(), pie.r1, pie.r2); file.addEntity(ellipse); aggregate_of_instance::ptr trim1(new aggregate_of_instance); aggregate_of_instance::ptr trim2(new aggregate_of_instance); if (pref == IfcSchema::IfcTrimmingPreference::IfcTrimmingPreference_PARAMETER) { - trim1->push(new Ifc2x3::IfcParameterValue(pie.t1)); - trim2->push(new Ifc2x3::IfcParameterValue(pie.t2)); + trim1->push(new IfcSchema::IfcParameterValue(pie.t1)); + trim2->push(new IfcSchema::IfcParameterValue(pie.t2)); } else { trim1->push(p2); trim2->push(p3); } - Ifc2x3::IfcTrimmedCurve* trim = new Ifc2x3::IfcTrimmedCurve(ellipse, trim1->as(), trim2->as(), true, pref); + IfcSchema::IfcTrimmedCurve* trim = new IfcSchema::IfcTrimmedCurve(ellipse, trim1->as(), trim2->as(), true, pref); file.addEntity(trim); - Ifc2x3::IfcCompositeCurveSegment::list::ptr segments(new Ifc2x3::IfcCompositeCurveSegment::list()); - Ifc2x3::IfcCompositeCurveSegment* s2 = new Ifc2x3::IfcCompositeCurveSegment(Ifc2x3::IfcTransitionCode::IfcTransitionCode_CONTINUOUS, true, trim); + curve_segment_tt::list::ptr segments(new curve_segment_tt::list()); + IfcSchema::IfcCompositeCurveSegment* s2 = new IfcSchema::IfcCompositeCurveSegment(IfcSchema::IfcTransitionCode::IfcTransitionCode_CONTINUOUS, true, trim); - Ifc2x3::IfcPolyline* poly = new Ifc2x3::IfcPolyline(points); + IfcSchema::IfcPolyline* poly = new IfcSchema::IfcPolyline(points); file.addEntity(poly); - Ifc2x3::IfcCompositeCurveSegment* s1 = new Ifc2x3::IfcCompositeCurveSegment(Ifc2x3::IfcTransitionCode::IfcTransitionCode_CONTINUOUS, true, poly); + IfcSchema::IfcCompositeCurveSegment* s1 = new IfcSchema::IfcCompositeCurveSegment(IfcSchema::IfcTransitionCode::IfcTransitionCode_CONTINUOUS, true, poly); segments->push(s1); segments->push(s2); file.addEntities(segments->generalize()); - Ifc2x3::IfcCompositeCurve* ccurve = new Ifc2x3::IfcCompositeCurve(segments, false); - Ifc2x3::IfcArbitraryClosedProfileDef* profile = new Ifc2x3::IfcArbitraryClosedProfileDef(Ifc2x3::IfcProfileTypeEnum::IfcProfileType_AREA, null, ccurve); + IfcSchema::IfcCompositeCurve* ccurve = new IfcSchema::IfcCompositeCurve(segments, false); + IfcSchema::IfcArbitraryClosedProfileDef* profile = new IfcSchema::IfcArbitraryClosedProfileDef(IfcSchema::IfcProfileTypeEnum::IfcProfileType_AREA, null, ccurve); file.addEntity(ccurve); file.addEntity(profile); diff --git a/src/examples/faces.cpp b/src/examples/faces.cpp index f7e3858c58..550ed06e13 100644 --- a/src/examples/faces.cpp +++ b/src/examples/faces.cpp @@ -24,8 +24,15 @@ ********************************************************************************/ #include +#include "ifcparse/macros.h" + +#ifndef IfcSchema #define IfcSchema Ifc2x3 -#include "ifcparse/Ifc2x3.h" +#endif + +#include INCLUDE_SCHEMA(ifcparse, IfcSchema) +#include INCLUDE_SCHEMA_DEFINITIONS(ifcparse, IfcSchema) + #include "ifcparse/IfcHierarchyHelper.h" typedef std::string S; @@ -205,4 +212,4 @@ int main(int argc, char** argv) { file.header().file_name()->setname(filename); std::ofstream f(filename); f << file; -} \ No newline at end of file +} diff --git a/src/examples/ifc_curve_rebar.cpp b/src/examples/ifc_curve_rebar.cpp index 0f66f7b45a..5553123b13 100644 --- a/src/examples/ifc_curve_rebar.cpp +++ b/src/examples/ifc_curve_rebar.cpp @@ -27,8 +27,15 @@ #include #include +#include "ifcparse/macros.h" + +#ifndef IfcSchema #define IfcSchema Ifc2x3 -#include "ifcparse/Ifc2x3.h" +#endif + +#include INCLUDE_SCHEMA(ifcparse, IfcSchema) +#include INCLUDE_SCHEMA_DEFINITIONS(ifcparse, IfcSchema) + #include "ifcparse/IfcHierarchyHelper.h" #include @@ -38,6 +45,18 @@ typedef std::string S; typedef IfcParse::IfcGlobalId guid; boost::none_t const null = boost::none; +#ifdef SCHEMA_HAS_IfcSegment +typedef IfcSchema::IfcSegment curve_segment_t; +#else +typedef IfcSchema::IfcCompositeCurveSegment curve_segment_t; +#endif + +#ifdef SCHEMA_IfcReinforcingBar_HAS_PredefinedType +#define IFC_REINFORCING_BAR_TYPE IfcSchema::IfcReinforcingBarTypeEnum::IfcReinforcingBarType_LIGATURE +#else +#define IFC_REINFORCING_BAR_TYPE IfcSchema::IfcReinforcingBarRoleEnum::IfcReinforcingBarRole_LIGATURE +#endif + void create_curve_rebar(IfcHierarchyHelper& file) { int dia = 24; @@ -52,14 +71,14 @@ void create_curve_rebar(IfcHierarchyHelper& file) dia, //diameter crossSectionarea, //crossSectionarea = math.pi*(12.0/2)**2 0, - IfcSchema::IfcReinforcingBarRoleEnum::IfcReinforcingBarRoleEnum::IfcReinforcingBarRole_LIGATURE, + IFC_REINFORCING_BAR_TYPE, IfcSchema::IfcReinforcingBarSurfaceEnum::IfcReinforcingBarSurfaceEnum::IfcReinforcingBarSurface_PLAIN //PLAIN or TEXTURED ); file.addBuildingProduct(rebar); rebar->setOwnerHistory(file.getSingle()); - IfcSchema::IfcCompositeCurveSegment::list::ptr segments(new IfcSchema::IfcCompositeCurveSegment::list()); + curve_segment_t::list::ptr segments(new curve_segment_t::list()); IfcSchema::IfcCartesianPoint* p1 = file.addTriplet(0, 0, 1000.); IfcSchema::IfcCartesianPoint* p2 = file.addTriplet(0, 0, 0); diff --git a/src/examples/profiles.cpp b/src/examples/profiles.cpp index 78cc67f13f..5392ce7195 100644 --- a/src/examples/profiles.cpp +++ b/src/examples/profiles.cpp @@ -27,14 +27,57 @@ #include #include +#include "ifcparse/macros.h" + +#ifndef IfcSchema #define IfcSchema Ifc2x3 -#include "ifcparse/Ifc2x3.h" +#endif + +#include INCLUDE_SCHEMA(ifcparse, IfcSchema) +#include INCLUDE_SCHEMA_DEFINITIONS(ifcparse, IfcSchema) + #include "ifcparse/IfcHierarchyHelper.h" typedef std::string S; typedef IfcParse::IfcGlobalId guid; boost::none_t const null = boost::none; +#ifdef SCHEMA_IfcUShapeProfileDef_HAS_CentreOfGravityInX +#define IFC_U_SHAPE_PROFILE_DEF_EXTRA_ARGS , null +#else +#define IFC_U_SHAPE_PROFILE_DEF_EXTRA_ARGS +#endif + +#ifdef SCHEMA_IfcTShapeProfileDef_HAS_CentreOfGravityInY +#define IFC_T_SHAPE_PROFILE_DEF_EXTRA_ARGS , null +#else +#define IFC_T_SHAPE_PROFILE_DEF_EXTRA_ARGS +#endif + +#ifdef SCHEMA_IfcIShapeProfileDef_HAS_FlangeEdgeRadius +#define IFC_I_SHAPE_PROFILE_DEF_EXTRA_ARGS , null, null +#else +#define IFC_I_SHAPE_PROFILE_DEF_EXTRA_ARGS +#endif + +#ifdef SCHEMA_IfcAsymmetricIShapeProfileDef_HAS_BottomFlangeSlope +#define IFC_ASYMMETRIC_I_SHAPE_PROFILE_DEF_EXTRA_ARGS , null, null, null +#else +#define IFC_ASYMMETRIC_I_SHAPE_PROFILE_DEF_EXTRA_ARGS +#endif + +#ifdef SCHEMA_IfcLShapeProfileDef_HAS_CentreOfGravityInX +#define IFC_L_SHAPE_PROFILE_DEF_EXTRA_ARGS , null, null +#else +#define IFC_L_SHAPE_PROFILE_DEF_EXTRA_ARGS +#endif + +#ifdef SCHEMA_IfcCShapeProfileDef_HAS_CentreOfGravityInX +#define IFC_C_SHAPE_PROFILE_DEF_EXTRA_ARGS , null +#else +#define IFC_C_SHAPE_PROFILE_DEF_EXTRA_ARGS +#endif + void create_testcase_for(IfcSchema::IfcProfileDef::list::ptr profiles) { IfcSchema::IfcProfileDef* profile = *profiles->begin(); const std::string& profile_type = profile->declaration().name(); @@ -88,31 +131,31 @@ int main(int argc, char** argv) { { IfcSchema::IfcProfileDef::list::ptr profiles (new IfcSchema::IfcProfileDef::list); profiles->push(new IfcSchema::IfcUShapeProfileDef( IfcSchema::IfcProfileTypeEnum::IfcProfileType_AREA, - null, 0, 50.0, 25.0, 5.0, 5.0, null, null, null, null)); + null, 0, 50.0, 25.0, 5.0, 5.0, null, null, null IFC_U_SHAPE_PROFILE_DEF_EXTRA_ARGS)); profiles->push(new IfcSchema::IfcUShapeProfileDef( IfcSchema::IfcProfileTypeEnum::IfcProfileType_AREA, - null, 0, 50.0, 25.0, 5.0, 5.0, 2.0, 2.0, null, null)); + null, 0, 50.0, 25.0, 5.0, 5.0, 2.0, 2.0, null IFC_U_SHAPE_PROFILE_DEF_EXTRA_ARGS)); profiles->push(new IfcSchema::IfcUShapeProfileDef( IfcSchema::IfcProfileTypeEnum::IfcProfileType_AREA, - null, 0, 50.0, 25.0, 5.0, 5.0, null, null, 4.0, null)); + null, 0, 50.0, 25.0, 5.0, 5.0, null, null, 4.0 IFC_U_SHAPE_PROFILE_DEF_EXTRA_ARGS)); profiles->push(new IfcSchema::IfcUShapeProfileDef( IfcSchema::IfcProfileTypeEnum::IfcProfileType_AREA, - null, 0, 50.0, 25.0, 5.0, 5.0, 1.0, 3.0, 6.0, null)); + null, 0, 50.0, 25.0, 5.0, 5.0, 1.0, 3.0, 6.0 IFC_U_SHAPE_PROFILE_DEF_EXTRA_ARGS)); create_testcase_for(profiles); } { IfcSchema::IfcProfileDef::list::ptr profiles (new IfcSchema::IfcProfileDef::list); profiles->push(new IfcSchema::IfcTShapeProfileDef( IfcSchema::IfcProfileTypeEnum::IfcProfileType_AREA, - null, 0, 50.0, 25.0, 5.0, 5.0, null, null, null, null, null, null)); + null, 0, 50.0, 25.0, 5.0, 5.0, null, null, null, null, null IFC_T_SHAPE_PROFILE_DEF_EXTRA_ARGS)); profiles->push(new IfcSchema::IfcTShapeProfileDef( IfcSchema::IfcProfileTypeEnum::IfcProfileType_AREA, - null, 0, 50.0, 25.0, 5.0, 5.0, 2.0, 2.0, 2.0, null, null, null)); + null, 0, 50.0, 25.0, 5.0, 5.0, 2.0, 2.0, 2.0, null, null IFC_T_SHAPE_PROFILE_DEF_EXTRA_ARGS)); profiles->push(new IfcSchema::IfcTShapeProfileDef( IfcSchema::IfcProfileTypeEnum::IfcProfileType_AREA, - null, 0, 50.0, 25.0, 5.0, 5.0, null, null, null, 2.0, 2.0, null)); + null, 0, 50.0, 25.0, 5.0, 5.0, null, null, null, 2.0, 2.0 IFC_T_SHAPE_PROFILE_DEF_EXTRA_ARGS)); profiles->push(new IfcSchema::IfcTShapeProfileDef( IfcSchema::IfcProfileTypeEnum::IfcProfileType_AREA, - null, 0, 50.0, 25.0, 5.0, 5.0, 3.0, 2.0, 1.0, 2.0, 2.0, null)); + null, 0, 50.0, 25.0, 5.0, 5.0, 3.0, 2.0, 1.0, 2.0, 2.0 IFC_T_SHAPE_PROFILE_DEF_EXTRA_ARGS)); create_testcase_for(profiles); } { IfcSchema::IfcProfileDef::list::ptr profiles (new IfcSchema::IfcProfileDef::list); @@ -133,40 +176,40 @@ int main(int argc, char** argv) { null, 0, 15.0, 25.0)); create_testcase_for(profiles); } - { IfcSchema::IfcProfileDef::list::ptr profiles (new IfcSchema::IfcProfileDef::list); + { IfcSchema::IfcProfileDef::list::ptr profiles (new IfcSchema::IfcProfileDef::list); profiles->push(new IfcSchema::IfcIShapeProfileDef( IfcSchema::IfcProfileTypeEnum::IfcProfileType_AREA, - null, 0, 25.0, 50.0, 5.0, 5.0, null)); + null, 0, 25.0, 50.0, 5.0, 5.0, null IFC_I_SHAPE_PROFILE_DEF_EXTRA_ARGS)); profiles->push(new IfcSchema::IfcIShapeProfileDef( IfcSchema::IfcProfileTypeEnum::IfcProfileType_AREA, - null, 0, 25.0, 50.0, 5.0, 5.0, 2.0)); + null, 0, 25.0, 50.0, 5.0, 5.0, 2.0 IFC_I_SHAPE_PROFILE_DEF_EXTRA_ARGS)); profiles->push(new IfcSchema::IfcAsymmetricIShapeProfileDef( IfcSchema::IfcProfileTypeEnum::IfcProfileType_AREA, - null, 0, 25.0, 50.0, 5.0, 5.0, 2.0, 20.0, 10.0, 5.0, null)); + null, 0, 25.0, 50.0, 5.0, 5.0, 2.0, 20.0, 10.0, 5.0, null IFC_ASYMMETRIC_I_SHAPE_PROFILE_DEF_EXTRA_ARGS)); create_testcase_for(profiles); } { IfcSchema::IfcProfileDef::list::ptr profiles (new IfcSchema::IfcProfileDef::list); profiles->push(new IfcSchema::IfcLShapeProfileDef( IfcSchema::IfcProfileTypeEnum::IfcProfileType_AREA, - null, 0, 50.0, 25.0, 5.0, null, null, null, null, null)); + null, 0, 50.0, 25.0, 5.0, null, null, null IFC_L_SHAPE_PROFILE_DEF_EXTRA_ARGS)); profiles->push(new IfcSchema::IfcLShapeProfileDef( IfcSchema::IfcProfileTypeEnum::IfcProfileType_AREA, - null, 0, 50.0, 25.0, 5.0, 2.0, 2.0, null, null, null)); + null, 0, 50.0, 25.0, 5.0, 2.0, 2.0, null IFC_L_SHAPE_PROFILE_DEF_EXTRA_ARGS)); profiles->push(new IfcSchema::IfcLShapeProfileDef( IfcSchema::IfcProfileTypeEnum::IfcProfileType_AREA, - null, 0, 50.0, 25.0, 5.0, null, null, 2.0, null, null)); + null, 0, 50.0, 25.0, 5.0, null, null, 2.0 IFC_L_SHAPE_PROFILE_DEF_EXTRA_ARGS)); profiles->push(new IfcSchema::IfcLShapeProfileDef( IfcSchema::IfcProfileTypeEnum::IfcProfileType_AREA, - null, 0, 50.0, 25.0, 5.0, 1.0, 2.0, 2.0, null, null)); + null, 0, 50.0, 25.0, 5.0, 1.0, 2.0, 2.0 IFC_L_SHAPE_PROFILE_DEF_EXTRA_ARGS)); create_testcase_for(profiles); } { IfcSchema::IfcProfileDef::list::ptr profiles (new IfcSchema::IfcProfileDef::list); profiles->push(new IfcSchema::IfcCShapeProfileDef( IfcSchema::IfcProfileTypeEnum::IfcProfileType_AREA, - null, 0, 50.0, 25.0, 5.0, 10.0, null, null)); + null, 0, 50.0, 25.0, 5.0, 10.0, null IFC_C_SHAPE_PROFILE_DEF_EXTRA_ARGS)); profiles->push(new IfcSchema::IfcCShapeProfileDef( IfcSchema::IfcProfileTypeEnum::IfcProfileType_AREA, - null, 0, 50.0, 25.0, 5.0, 10.0, 2.0, null)); + null, 0, 50.0, 25.0, 5.0, 10.0, 2.0 IFC_C_SHAPE_PROFILE_DEF_EXTRA_ARGS)); create_testcase_for(profiles); } { IfcSchema::IfcProfileDef::list::ptr profiles (new IfcSchema::IfcProfileDef::list); diff --git a/src/examples/triangulated_faceset.cpp b/src/examples/triangulated_faceset.cpp index 18eb1c3b73..b2545e83dd 100644 --- a/src/examples/triangulated_faceset.cpp +++ b/src/examples/triangulated_faceset.cpp @@ -27,8 +27,15 @@ #include #include +#include "ifcparse/macros.h" + +#ifndef IfcSchema #define IfcSchema Ifc4 -#include "ifcparse/Ifc4.h" +#endif + +#include INCLUDE_SCHEMA(ifcparse, IfcSchema) +#include INCLUDE_SCHEMA_DEFINITIONS(ifcparse, IfcSchema) + #include "ifcparse/IfcHierarchyHelper.h" #include "suzanne_geometry.h" @@ -69,7 +76,11 @@ int main(int argc, char** argv) { std::vector< std::vector< double > > vertices_vector = create_vector_from_array(vertices, sizeof(vertices) / sizeof(vertices[0])); std::vector< std::vector< int > > indices_vector = create_vector_from_array(indices, sizeof(indices) / sizeof(indices[0])); - IfcSchema::IfcCartesianPointList3D* coordinates = new IfcSchema::IfcCartesianPointList3D(vertices_vector); + IfcSchema::IfcCartesianPointList3D* coordinates = new IfcSchema::IfcCartesianPointList3D(vertices_vector +#ifdef SCHEMA_IfcCartesianPointList3D_HAS_TagList + , boost::none +#endif + ); IfcSchema::IfcTriangulatedFaceSet* faceset = new IfcSchema::IfcTriangulatedFaceSet(coordinates, null, null, indices_vector, null); items->push(faceset); diff --git a/src/ifcgeom/mapping/mapping.h b/src/ifcgeom/mapping/mapping.h index 2ddfadd728..cdc9cdfbb8 100644 --- a/src/ifcgeom/mapping/mapping.h +++ b/src/ifcgeom/mapping/mapping.h @@ -9,12 +9,8 @@ #include #include -#define INCLUDE_SCHEMA(x) STRINGIFY(../../ifcparse/x.h) -#include INCLUDE_SCHEMA(IfcSchema) -#undef INCLUDE_SCHEMA -#define INCLUDE_SCHEMA(x) STRINGIFY(../../ifcparse/x-definitions.h) -#include INCLUDE_SCHEMA(IfcSchema) -#undef INCLUDE_SCHEMA +#include INCLUDE_SCHEMA(../../ifcparse, IfcSchema) +#include INCLUDE_SCHEMA_DEFINITIONS(../../ifcparse, IfcSchema) namespace ifcopenshell { diff --git a/src/ifcparse/macros.h b/src/ifcparse/macros.h index 113152a5f2..77b6ba26dd 100644 --- a/src/ifcparse/macros.h +++ b/src/ifcparse/macros.h @@ -27,6 +27,9 @@ #define STRINGIFY_(x) #x #define STRINGIFY(x) STRINGIFY_(x) +#define INCLUDE_SCHEMA(prefix, x) STRINGIFY(prefix/x.h) +#define INCLUDE_SCHEMA_DEFINITIONS(prefix, x) STRINGIFY(prefix/x-definitions.h) + #define MAKE_INIT_FN__(a, b) init_##a##_##b #define MAKE_INIT_FN_(a, b) MAKE_INIT_FN__(a, b) #define MAKE_INIT_FN(t) MAKE_INIT_FN_(t, IfcSchema) From dcebf23af85b36f22950ea1d880e0383159c5ff6 Mon Sep 17 00:00:00 2001 From: Thomas Krijnen Date: Fri, 12 Jun 2026 11:41:11 +0200 Subject: [PATCH 5/5] Workaround for header construction order --- src/ifcparse/IfcSpfHeader.cpp | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/src/ifcparse/IfcSpfHeader.cpp b/src/ifcparse/IfcSpfHeader.cpp index fd4c80e51a..efb30c90a2 100644 --- a/src/ifcparse/IfcSpfHeader.cpp +++ b/src/ifcparse/IfcSpfHeader.cpp @@ -95,14 +95,8 @@ IfcParse::IfcSpfHeader::IfcSpfHeader(IfcParse::IfcFile* file, Logger& logger) return nullptr; }, file_->storage_); - if (storage_ == nullptr) { - file_description_ = Header_section_schema::get_schema().instantiate(&Header_section_schema::file_description::Class(), IfcEntityInstanceData(rocks_db_attribute_storage{}))->as(); - file_description_->file_ = file_; - file_name_ = Header_section_schema::get_schema().instantiate(&Header_section_schema::file_name::Class(), IfcEntityInstanceData(rocks_db_attribute_storage{}))->as(); - file_name_->file_ = file_; - file_schema_ = Header_section_schema::get_schema().instantiate(&Header_section_schema::file_schema::Class(), IfcEntityInstanceData(rocks_db_attribute_storage{}))->as(); - file_schema_->file_ = file_; - } + // IfcFile constructs _header before it emplaces the selected storage backend. + // Keep header entities lazy so the accessors below allocate against the final storage. } }