Merge remote-tracking branch 'origin/v0.8.0' into ifcviewer-wgpu

This commit is contained in:
Thomas Krijnen
2026-07-09 13:21:39 +02:00
373 changed files with 22411 additions and 4242 deletions
+4 -4
View File
@@ -45,10 +45,10 @@ IFC_SCHEMA_API Ifc4x3_add2::IfcAlignment addAlignment(hierarchy_helper<Ifc4x3_ad
// Maps horizontal alignment business logic to geometry.
// Bloss curves have two geometry elements for one horizontal alignment segment. That is the reason for returning a pair.
// Typically the first element of the pair will have the geometry and the second element will be nullptr
IFC_SCHEMA_API std::pair<Ifc4x3_add2::IfcCurveSegment, Ifc4x3_add2::IfcCurveSegment> mapAlignmentSegment(hierarchy_helper<Ifc4x3_add2>& model, const Ifc4x3_add2::IfcAlignmentSegment& segment);
IFC_SCHEMA_API std::pair<Ifc4x3_add2::IfcCurveSegment, Ifc4x3_add2::IfcCurveSegment> mapAlignmentHorizontalSegment(hierarchy_helper<Ifc4x3_add2>& model, const Ifc4x3_add2::IfcAlignmentHorizontalSegment& segment);
IFC_SCHEMA_API std::pair<Ifc4x3_add2::IfcCurveSegment, Ifc4x3_add2::IfcCurveSegment> mapAlignmentVerticalSegment(hierarchy_helper<Ifc4x3_add2>& model, const Ifc4x3_add2::IfcAlignmentVerticalSegment& segment);
IFC_SCHEMA_API std::pair<Ifc4x3_add2::IfcCurveSegment, Ifc4x3_add2::IfcCurveSegment> mapAlignmentCantSegment(hierarchy_helper<Ifc4x3_add2>& model, const Ifc4x3_add2::IfcAlignmentCantSegment& segment);
IFC_SCHEMA_API std::pair<Ifc4x3_add2::IfcCurveSegment, Ifc4x3_add2::IfcCurveSegment> mapAlignmentSegment(hierarchy_helper<Ifc4x3_add2>& model, const Ifc4x3_add2::IfcAlignmentSegment& segment, Logger& logger = Logger::Root());
IFC_SCHEMA_API std::pair<Ifc4x3_add2::IfcCurveSegment, Ifc4x3_add2::IfcCurveSegment> mapAlignmentHorizontalSegment(hierarchy_helper<Ifc4x3_add2>& model, const Ifc4x3_add2::IfcAlignmentHorizontalSegment& segment, Logger& logger = Logger::Root());
IFC_SCHEMA_API std::pair<Ifc4x3_add2::IfcCurveSegment, Ifc4x3_add2::IfcCurveSegment> mapAlignmentVerticalSegment(hierarchy_helper<Ifc4x3_add2>& model, const Ifc4x3_add2::IfcAlignmentVerticalSegment& segment, Logger& logger = Logger::Root());
IFC_SCHEMA_API std::pair<Ifc4x3_add2::IfcCurveSegment, Ifc4x3_add2::IfcCurveSegment> mapAlignmentCantSegment(hierarchy_helper<Ifc4x3_add2>& model, const Ifc4x3_add2::IfcAlignmentCantSegment& segment, Logger& logger = Logger::Root());
#endif
+4
View File
@@ -31,6 +31,10 @@
#if defined(IFCOPENSHELL_BRANCH) && defined(IFCOPENSHELL_COMMIT)
IFC_PARSE_API const char *IFCOPENSHELL_VERSION = STRINGIFY(IFCOPENSHELL_BRANCH) "-" STRINGIFY(IFCOPENSHELL_COMMIT);
#elif defined(IFCOPENSHELL_VERSION_STRING)
// Set from CMake's RELEASE_VERSION (the repository VERSION file) so a release
// build without commit-sha info still reports the correct version. See #8164.
IFC_PARSE_API const char *IFCOPENSHELL_VERSION = STRINGIFY(IFCOPENSHELL_VERSION_STRING);
#else
IFC_PARSE_API const char *IFCOPENSHELL_VERSION = "0.8.0";
#endif
+1 -1
View File
@@ -224,7 +224,7 @@ namespace {
parse_state += PAGE;
} else if (IS_HEXADECIMAL(current_char) && EXPECTS_HEX(parse_state)) {
if (IS_LOWERCASE_HEX(current_char)) {
logger::warning("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.");
}
+3 -1
View File
@@ -28,6 +28,7 @@
#define IFCCHARACTERDECODER_H
#include "file_reader.h"
#include "logger.h"
#include <string>
@@ -43,6 +44,7 @@ template <typename Reader>
class IFC_PARSE_API character_decoder {
private:
Reader* stream_;
Logger& logger_;
int codepage_;
std::u32string builder_;
@@ -55,7 +57,7 @@ class IFC_PARSE_API character_decoder {
inline static ConversionMode mode = UTF8;
inline static char substitution_character = '_';
character_decoder(Reader* stream);
character_decoder(Reader* stream, Logger& logger = Logger::Root());
~character_decoder();
// Gets a decoded string representation at the token stream
// read pointer and advances the underlying token stream.
+16 -1
View File
@@ -34,7 +34,22 @@ namespace {
inline T dispatch_get_(attribute_value::pointer_type array_, uint8_t storage_model_, size_t instance_name_, const ifcopenshell::declaration* entity_or_type, uint8_t index_)
{
if (storage_model_ == 0) {
return array_.storage_ptr->get<T>(index_);
try {
return array_.storage_ptr->get<T>(index_);
} catch (const impl::storage_type_mismatch& e) {
throw IfcParse::IfcException(
// entity_or_type not passed, but in v0.9 this is beginning to make sense
(entity_or_type
? std::string("On instance #" + std::to_string(instance_name_) + " of " + entity_or_type->name() + ": ")
: std::string("")) +
"Requested type <" + e.requested() + "> does not match actual type <" + e.actual() + "> at index " + std::to_string(index_));
} catch (const std::out_of_range& e) {
throw IfcParse::IfcException(
(entity_or_type
? std::string("On instance #" + std::to_string(instance_name_) + " of " + entity_or_type->name() + ": ")
: std::string("")) +
e.what());
}
}
#ifdef IFOPSH_WITH_ROCKSDB
else {
+14 -10
View File
@@ -27,6 +27,7 @@
#include "storage.h"
#include "file_open_status.h"
#include <functional>
#include <boost/multi_index/ordered_index.hpp>
#include <boost/multi_index/random_access_index.hpp>
#include <boost/multi_index/sequenced_index.hpp>
@@ -102,6 +103,7 @@ private:
const ifcopenshell::schema_definition* schema_;
ifcopenshell::impl::in_memory_file_storage storage_;
ifcopenshell::file_open_status good_ = ifcopenshell::file_open_status::SUCCESS;
std::reference_wrapper<Logger> logger_;
int progress_;
ifcopenshell::unresolved_references references_to_resolve_;
int yielded_header_instances_ = 0;
@@ -155,13 +157,13 @@ private:
void push_page(const std::string& page_data);
instance_streamer(ifcopenshell::file* owner_file = nullptr);
instance_streamer(ifcopenshell::file* owner_file = nullptr, Logger& logger = Logger::Root());
instance_streamer(const std::string& path, bool use_mmap = false, ifcopenshell::file* owner_file = nullptr);
instance_streamer(const std::string& path, bool use_mmap = false, ifcopenshell::file* owner_file = nullptr, Logger& logger = Logger::Root());
instance_streamer(void* data, int data_size, ifcopenshell::file* owner_file = nullptr);
instance_streamer(void* data, int data_size, ifcopenshell::file* owner_file = nullptr, Logger& logger = Logger::Root());
instance_streamer(Reader* stream, ifcopenshell::file* owner_file = nullptr);
instance_streamer(Reader* stream, ifcopenshell::file* owner_file = nullptr, Logger& logger = Logger::Root());
void bypass_types(const std::set<std::string>& type_names);
@@ -209,6 +211,7 @@ public:
private:
file_open_status good_ = file_open_status::SUCCESS;
std::reference_wrapper<Logger> logger_;
const ifcopenshell::schema_definition* schema_;
const ifcopenshell::declaration* ifcroot_type_;
@@ -239,7 +242,7 @@ public:
/// </summary>
/// <param name="path">UTF-8 file path to an IFC-SPF file</param>
/// <param name="mmap">Whether to use memory-mapped I/O</param>
file(const std::string& path, bool use_mmap);
file(const std::string& path, bool use_mmap, Logger& logger = Logger::Root());
#endif
/// <summary>
/// Constructs an file object from a file path, supports IFC-SPF and the IfcOpenShell-specific RocksDB format.
@@ -247,17 +250,17 @@ public:
/// <param name="path">UTF-8 file path to an IFC-SPF file or RocksDB database directory</param>
/// <param name="ty">File type of the path</param>
/// <param name="readonly">Whether to open in read-only mode, only supported on RocksDB databases</param>
file(const std::string& path, filetype type = FT_AUTODETECT, bool read_only = false);
file(const std::string& path, filetype type = FT_AUTODETECT, bool read_only = false, Logger& logger = Logger::Root());
/// <summary>
/// Constructs an file object from a stream containing IFC-SPF data.
/// </summary>
file(std::istream& stream, int data_size);
file(std::istream& stream, int data_size, Logger& logger = Logger::Root());
/// <summary>
/// Constructs an file object from a memory buffer containing IFC-SPF data.
/// </summary>
file(void* data, int data_size);
file(void* data, int data_size, Logger& logger = Logger::Root());
/// <summary>
/// Constructs an file object with the specified schema, file type, and file path.
@@ -266,12 +269,12 @@ public:
/// <param name="schema">Pointer to the schema definition to use. Defaults to the IFC4 schema if not specified.</param>
/// <param name="ty">The file type to use for the file. Defaults to FT_AUTODETECT.</param>
/// <param name="path">The file system path to the IFC file. Defaults to an empty string.</param>
file(const ifcopenshell::schema_definition* schema = ifcopenshell::schema_by_name("IFC4"), filetype type = FT_AUTODETECT, const std::string& path = "");
file(const ifcopenshell::schema_definition* schema = ifcopenshell::schema_by_name("IFC4"), filetype type = FT_AUTODETECT, const std::string& path = "", Logger& logger = Logger::Root());
/// <summary>
/// Constructs an unitialized file object. Call initialize() later on. Allows to specify which types to bypass during load.
/// </summary>
file(const uninitialized_tag& tag);
file(const uninitialized_tag& tag, Logger& logger = Logger::Root());
bool initialize(const std::string& path, filetype type = FT_AUTODETECT, bool read_only = false);
#ifdef USE_MMAP
@@ -285,6 +288,7 @@ public:
~file();
ifcopenshell::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
+4 -4
View File
@@ -94,7 +94,7 @@ void expand(const std::string& s, std::vector<unsigned char>& v) {
static boost::uuids::basic_random_generator<boost::mt19937> gen;
#endif
ifcopenshell::global_id::global_id() {
ifcopenshell::global_id::global_id(Logger& logger) {
uuid_data_ = gen();
std::vector<unsigned char> v(uuid_data_.size());
std::copy(uuid_data_.begin(), uuid_data_.end(), v.begin());
@@ -111,12 +111,12 @@ ifcopenshell::global_id::global_id() {
boost::uuids::uuid test_uuid;
std::copy(test_vector.begin(), test_vector.end(), test_uuid.begin());
if (uuid_data_ != test_uuid) {
logger::message(logger::LOG_ERROR, "Internal error generating GlobalId");
logger.Message(Logger::LOG_ERROR, "SYS", 34, "Internal error generating GlobalId");
}
#endif
}
ifcopenshell::global_id::global_id(const std::string& string)
ifcopenshell::global_id::global_id(const std::string& string, Logger& logger)
: string_data_(string) {
std::vector<unsigned char> result;
expand(string_data_, result);
@@ -130,7 +130,7 @@ ifcopenshell::global_id::global_id(const std::string& string)
#ifndef NDEBUG
const std::string test_string = compress(&uuid_data_.data[0]);
if (string_data_ != test_string) {
logger::message(logger::LOG_ERROR, "Internal error generating GlobalId");
logger.Message(Logger::LOG_ERROR, "SYS", 35, "Internal error generating GlobalId");
}
#endif
}
+3 -2
View File
@@ -21,6 +21,7 @@
#define IFCGLOBALID_H
#include "ifc_parse_api.h"
#include "IfcLogger.h"
#include <boost/uuid/uuid.hpp>
#include <string>
@@ -36,8 +37,8 @@ class IFC_PARSE_API global_id {
public:
static const unsigned int length = 22;
global_id();
global_id(const std::string& value);
global_id(Logger& logger = Logger::Root());
global_id(const std::string& value, Logger& logger = Logger::Root());
operator const std::string&() const;
operator const boost::uuids::uuid&() const;
const std::string& formatted() const;
+77
View File
@@ -73,6 +73,83 @@ class IFC_PARSE_API derived {};
class IFC_PARSE_API empty_aggregate_t {};
class IFC_PARSE_API empty_aggregate_of_aggregate_t {};
namespace impl {
template <>
struct VariantTypeName<Blank> {
static std::string get() { return "null"; }
};
template <>
struct VariantTypeName<Derived> {
static std::string get() { return "derived"; }
};
template <>
struct VariantTypeName<int> {
static std::string get() { return "int"; }
};
template <>
struct VariantTypeName<bool> {
static std::string get() { return "bool"; }
};
template <>
struct VariantTypeName<boost::logic::tribool> {
static std::string get() { return "logical"; }
};
template <>
struct VariantTypeName<double> {
static std::string get() { return "real"; }
};
template <>
struct VariantTypeName<std::string> {
static std::string get() { return "string"; }
};
template <>
struct VariantTypeName<boost::dynamic_bitset<>> {
static std::string get() { return "binary"; }
};
template <>
struct VariantTypeName<EnumerationReference> {
static std::string get() { return "enumeration"; }
};
template <>
struct VariantTypeName<IfcUtil::IfcBaseClass*> {
static std::string get() { return "instance"; }
};
template <>
struct VariantTypeName<empty_aggregate_t> {
static std::string get() { return "aggregate"; }
};
template <typename T, typename Allocator>
struct VariantTypeName<std::vector<T, Allocator>> {
static std::string get() { return "aggregate of " + VariantTypeName<T>::get(); }
};
template <>
struct VariantTypeName<aggregate_of_instance::ptr> {
static std::string get() { return "aggregate of instance"; }
};
template <>
struct VariantTypeName<empty_aggregate_of_aggregate_t> {
static std::string get() { return "aggregate of aggregate"; }
};
template <>
struct VariantTypeName<aggregate_of_aggregate_of_instance::ptr> {
static std::string get() { return "aggregate of aggregate of instance"; }
};
}
template<typename... Args>
struct parameter_pack {
static constexpr size_t size = sizeof...(Args);
+117 -33
View File
@@ -29,6 +29,7 @@
#include <boost/property_tree/ptree.hpp>
#include <boost/version.hpp>
#include <chrono>
#include <cstdio>
#include <ctime>
#include <iomanip>
#include <iostream>
@@ -63,10 +64,17 @@ const std::array<std::basic_string<char>, 5> severity_strings<char>::value = {"P
template <>
const std::array<std::basic_string<wchar_t>, 5> severity_strings<wchar_t>::value = {L"Performance", L"Debug", L"notice", L"warning", L"error"};
std::string format_code(const char (&code_prefix)[4], uint16_t code_number) {
std::ostringstream oss;
oss << code_prefix[0] << code_prefix[1] << code_prefix[2] << std::setfill('0') << std::setw(3) << code_number;
return oss.str();
}
template <typename T>
void plain_text_message(T& out, const express::Base& current_product, logger::Severity type, const std::string& message, const express::Base& instance) {
void plain_text_message(T& out, const express::Base& current_product, Logger::Severity type, const std::string& code, const std::string& message, const express::Base& instance) {
out << "[" << severity_strings<typename T::char_type>::value[type] << "] ";
out << "[" << get_time(type <= logger::LOG_PERF).c_str() << "] ";
out << "[" << code.c_str() << "] ";
out << "[" << get_time(type <= Logger::LOG_PERF).c_str() << "] ";
if (current_product) {
std::string global_id = current_product.as<express::Entity>().get("GlobalId");
out << "{" << global_id.c_str() << "} ";
@@ -91,17 +99,19 @@ std::basic_string<T> string_as(const std::string& string) {
}
template <typename T>
void json_message(T& out, const express::Base& current_product, logger::Severity type, const std::string& message, const express::Base& instance) {
void json_message(T& out, const express::Base* current_product, Logger::Severity type, const std::string& code, const std::string& message, const express::Base& instance) {
boost::property_tree::basic_ptree<std::basic_string<typename T::char_type>, std::basic_string<typename T::char_type>> property_tree;
// @todo this is crazy
static const typename T::char_type time_string[] = {'t', 'i', 'm', 'e', 0};
static const typename T::char_type level_string[] = {'l', 'e', 'v', 'e', 'l', 0};
static const typename T::char_type code_string[] = {'c', 'o', 'd', 'e', 0};
static const typename T::char_type product_string[] = {'p', 'r', 'o', 'd', 'u', 'c', 't', 0};
static const typename T::char_type message_string[] = {'m', 'e', 's', 's', 'a', 'g', 'e', 0};
static const typename T::char_type instance_string[] = {'i', 'n', 's', 't', 'a', 'n', 'c', 'e', 0};
property_tree.put(level_string, severity_strings<typename T::char_type>::value[type]);
property_tree.put(code_string, string_as<typename T::char_type>(code));
if (current_product) {
std::ostringstream oss;
current_product.to_string(oss);
@@ -126,10 +136,51 @@ void json_message(T& out, const express::Base& current_product, logger::Severity
}
} // namespace
log_message::log_message(
int severity,
const char (&code_prefix)[4],
uint16_t code_number,
const std::string& timestamp,
const std::string& message,
const IfcUtil::IfcBaseInterface* inst,
const IfcUtil::IfcBaseClass* current_product)
: severity(severity)
, timestamp(timestamp)
, message(message)
{
snprintf(code, 7, "%s%03u", code_prefix, code_number);
if (inst) {
std::ostringstream oss;
inst->as<IfcUtil::IfcBaseClass>()->toString(oss);
instance = oss.str();
}
if (current_product) {
std::ostringstream oss;
current_product->toString(oss);
product = oss.str();
}
}
Logger& Logger::Root() {
static Logger logger;
return logger;
}
const express::Base& Logger::current_product() const {
return current_product_;
}
void logger::set_product(std::optional<const express::Base> product) {
if (verbosity_ <= LOG_DEBUG && product) {
message(LOG_DEBUG, "Begin processing", *product);
}
current_product_ = product;
}
void Logger::SetProduct(boost::optional<const IfcUtil::IfcBaseClass*> product) {
if (verbosity_ <= LOG_DEBUG && product) {
Message(LOG_DEBUG, "SYS", 3, "Begin processing", *product);
}
if (!product && print_perf_stats_on_element_) {
print_performance_stats();
performance_statistics_.clear();
@@ -155,13 +206,13 @@ void logger::set_output(std::wostream* stream1, std::wostream* stream2) {
}
}
void logger::message(logger::Severity type, const std::string& text, const express::Base& instance) {
void Logger::Message(Logger::Severity type, const char (&code_prefix)[4], uint16_t code_number, const std::string& message, const express::Base& instance) {
if (type < verbosity_) {
return;
}
static std::mutex mtx;
std::lock_guard<std::mutex> lock(mtx);
std::lock_guard<std::mutex> lock(mutex_);
const std::string code = format_code(code_prefix, code_number);
if (type == LOG_PERF) {
if (!first_timepoint_) {
@@ -179,25 +230,28 @@ void logger::message(logger::Severity type, const std::string& text, const expre
if (type > max_severity_) {
max_severity_ = type;
}
if (((log2_ != nullptr) || (wlog2_ != nullptr))) {
if (format_ == FMT_INMEMORY) {
log_messages_.emplace_back(type, code_prefix, code_number, get_time(), message, instance, current_product());
} else if (((log2_ != nullptr) || (wlog2_ != nullptr))) {
if (format_ == FMT_PLAIN) {
if (log2_ != nullptr) {
plain_text_message(*log2_, current_product_, type, text, instance);
plain_text_message(*log2_, current_product(), type, code, message, instance);
} else if (wlog2_ != nullptr) {
plain_text_message(*wlog2_, current_product_, type, text, instance);
plain_text_message(*wlog2_, current_product(), type, code, message, instance);
}
} else if (format_ == FMT_JSON) {
if (log2_ != nullptr) {
json_message(*log2_, current_product_, type, text, instance);
json_message(*log2_, current_product(), type, code, message, instance);
} else if (wlog2_ != nullptr) {
json_message(*wlog2_, current_product_, type, text, instance);
json_message(*wlog2_, current_product(), type, code, message, instance);
}
}
}
}
void logger::message(logger::Severity type, const std::exception& exception, const express::Base& instance) {
message(type, std::string(exception.what()), instance);
void Logger::Message(Logger::Severity type, const char (&code_prefix)[4], uint16_t code_number, const std::exception& exception, const express::Base&) {
Message(type, code_prefix, code_number, std::string(exception.what()), instance);
}
template <typename T>
@@ -226,6 +280,49 @@ std::string logger::get_log() {
return log_stream_.str();
}
std::string Logger::GetLog() {
std::lock_guard<std::mutex> lock(mutex_);
return log_stream_.str();
}
void Logger::ClearLog() {
std::lock_guard<std::mutex> 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<wchar_t>(log);
} else {
log_stream_ << log;
}
}
}
logger.log_stream_.str(std::string());
logger.log_stream_.clear();
logger.log_messages_.clear();
}
void logger::print_performance_stats() {
std::vector<std::pair<double, std::string>> items;
for (auto& stat : performance_statistics_) {
@@ -243,28 +340,15 @@ void logger::print_performance_stats() {
}
for (auto& item : items) {
auto text = item.second + std::string(max_size - item.second.size(), ' ') + ": " + std::to_string(item.first);
logger::message(LOG_PERF, text);
auto message = item.second + std::string(max_size - item.second.size(), ' ') + ": " + std::to_string(item.first);
Message(LOG_PERF, "SYS", 4, message);
}
}
void logger::verbosity(logger::Severity severity) { verbosity_ = severity; }
logger::Severity logger::verbosity() { return verbosity_; }
void Logger::Verbosity(Logger::Severity severity) { verbosity_ = severity; }
Logger::Severity Logger::Verbosity() const { return verbosity_; }
logger::Severity logger::max_severity() { return max_severity_; }
Logger::Severity Logger::MaxSeverity() const { return max_severity_; }
void logger::output_format(Format format) { format_ = format; }
logger::Format logger::output_format() { return format_; }
std::ostream* logger::log1_ = 0;
std::ostream* logger::log2_ = 0;
std::wostream* logger::wlog1_ = 0;
std::wostream* logger::wlog2_ = 0;
std::stringstream logger::log_stream_;
logger::Severity logger::verbosity_ = logger::LOG_NOTICE;
logger::Severity logger::max_severity_ = logger::LOG_NOTICE;
logger::Format logger::format_ = logger::FMT_PLAIN;
std::optional<long long> logger::first_timepoint_;
std::map<std::string, double> logger::performance_statistics_;
std::map<std::string, double> logger::performance_signal_start_;
bool logger::print_perf_stats_on_element_ = false;
void Logger::OutputFormat(Format format) { format_ = format; }
Logger::Format Logger::OutputFormat() const { return format_; }
+76 -37
View File
@@ -25,10 +25,29 @@
#include <boost/optional.hpp>
#include <boost/scope_exit.hpp>
#include <cstdint>
#include <exception>
#include <map>
#include <mutex>
#include <sstream>
#include <string>
#include <vector>
class IFC_PARSE_API log_message {
public:
char code[7];
int severity;
std::string timestamp, message, instance, product;
log_message(
int severity,
const char (&code_prefix)[4],
uint16_t code_number,
const std::string& timestamp,
const std::string& message,
const IfcUtil::IfcBaseInterface* inst = 0,
const IfcUtil::IfcBaseClass* current_product = 0);
};
class IFC_PARSE_API logger {
public:
@@ -39,76 +58,96 @@ class IFC_PARSE_API logger {
LOG_WARNING,
LOG_ERROR
} Severity;
typedef enum {
FMT_PLAIN,
FMT_JSON
FMT_JSON,
FMT_INMEMORY
} Format;
private:
std::vector<log_message> log_messages_;
// To both stream variants need to exist at runtime or should this be a
// template argument of logger or controlled using preprocessor directives?
static std::ostream* log1_;
static std::ostream* log2_;
// template argument of Logger or controlled using preprocessor directives?
std::ostream* log1_ = nullptr;
std::ostream* log2_ = nullptr;
static std::wostream* wlog1_;
static std::wostream* wlog2_;
std::wostream* wlog1_ = nullptr;
std::wostream* wlog2_ = nullptr;
static std::stringstream log_stream_;
std::stringstream log_stream_;
const IfcUtil::IfcBaseClass* current_product_ = nullptr;
static Severity verbosity_;
static Format format_;
static Severity max_severity_;
Severity verbosity_ = LOG_NOTICE;
Format format_ = FMT_PLAIN;
Severity max_severity_ = LOG_NOTICE;
static std::optional<long long> first_timepoint_;
static std::map<std::string, double> performance_statistics_;
static std::map<std::string, double> performance_signal_start_;
std::optional<long long> first_timepoint_;
std::map<std::string, double> performance_statistics_;
std::map<std::string, double> performance_signal_start_;
static bool print_perf_stats_on_element_;
bool print_perf_stats_on_element_ = false;
std::mutex mutex_;
const IfcUtil::IfcBaseClass* current_product() const;
void current_product(const IfcUtil::IfcBaseClass* product);
public:
static void set_product(std::optional<const express::Base> product);
logger() = default;
logger(const logger&) = delete;
logger& operator=(const logger&) = delete;
static logger& Root();
void set_product(std::optional<const IfcUtil::IfcBaseClass*> product);
/// Determines to what stream respectively progress and errors are logged
static void set_output(std::wostream* progress_stream, std::wostream* error_stream);
void set_output(std::wostream* stream1, std::wostream* stream2);
/// Determines to what stream respectively progress and errors are logged
static void set_output(std::ostream* progress_stream, std::ostream* error_stream);
void set_output(std::ostream* stream1, std::ostream* stream2);
/// Determines the types of log messages to get logged
static void verbosity(Severity severity);
static Severity verbosity();
static Severity max_severity();
void verbosity(Severity severity);
Severity verbosity() const;
Severity max_severity() const;
/// Determines output format: plain text or sequence of JSON objects
static void output_format(Format format);
static Format output_format();
void output_format(Format format);
Format output_format() const;
/// Log a message to the output stream
static void message(Severity severity, const std::string& text, const express::Base& instance = express::Base());
static void message(Severity severity, const std::exception& exception, const express::Base& instance = express::Base());
void message(Severity type, const char (&code_prefix)[4], uint16_t code_number, const std::string& message, const IfcUtil::IfcBaseInterface* instance = 0);
void message(Severity type, const char (&code_prefix)[4], uint16_t code_number, const std::exception& exception, const IfcUtil::IfcBaseInterface* instance = 0);
static void notice(const std::string& text, const express::Base& instance = express::Base()) { logger::message(LOG_NOTICE, text, instance); }
static void warning(const std::string& text, const express::Base& instance = express::Base()) { logger::message(LOG_WARNING, text, instance); }
static void error(const std::string& text, const express::Base& instance = express::Base()) { logger::message(LOG_ERROR, text, instance); }
void notice(const char (&code_prefix)[4], uint16_t code_number, const std::string& message, const IfcUtil::IfcBaseInterface* instance = 0) { Message(LOG_NOTICE, code_prefix, code_number, message, instance); }
void warning(const char (&code_prefix)[4], uint16_t code_number, const std::string& message, const IfcUtil::IfcBaseInterface* instance = 0) { Message(LOG_WARNING, code_prefix, code_number, message, instance); }
void error(const char (&code_prefix)[4], uint16_t code_number, const std::string& message, const IfcUtil::IfcBaseInterface* instance = 0) { Message(LOG_ERROR, code_prefix, code_number, message, instance); }
static void notice(const std::exception& exception, const express::Base& instance = express::Base()) { message(LOG_NOTICE, exception, instance); }
static void warning(const std::exception& exception, const express::Base& instance = express::Base()) { message(LOG_WARNING, exception, instance); }
static void error(const std::exception& exception, const express::Base& instance = express::Base()) { message(LOG_ERROR, exception, instance); }
void notice(const char (&code_prefix)[4], uint16_t code_number, const std::exception& exception, const IfcUtil::IfcBaseInterface* instance = 0) { Message(LOG_NOTICE, code_prefix, code_number, exception, instance); }
void warning(const char (&code_prefix)[4], uint16_t code_number, const std::exception& exception, const IfcUtil::IfcBaseInterface* instance = 0) { Message(LOG_WARNING, code_prefix, code_number, exception, instance); }
void error(const char (&code_prefix)[4], uint16_t code_number, const std::exception& exception, const IfcUtil::IfcBaseInterface* instance = 0) { Message(LOG_ERROR, code_prefix, code_number, exception, instance); }
static void status(const std::string& message, bool append_newline = true);
void status(const std::string& message, bool new_line = true);
static void progress_bar(int progress_percent);
static std::string get_log();
static void print_performance_stats();
static void print_performance_stats_on_element(bool enabled) { print_perf_stats_on_element_ = enabled; }
void progress_bar(int progress);
std::string get_log();
void clear();
void append(Logger& logger);
void print_performance_stats();
void print_performance_stats(bool b) { print_perf_stats_on_element_ = b; }
bool print_performance_stats() const { return print_perf_stats_on_element_; }
const std::vector<log_message>& log_messages() const { return log_messages_; }
};
#define PERF(x) \
\
logger::message(logger::LOG_PERF, x); \
Logger::Root().Message(Logger::LOG_PERF, "SYS", 1, x); \
\
BOOST_SCOPE_EXIT(void) { \
logger::message(logger::LOG_PERF, "done " + std::string(x)); \
Logger::Root().Message(Logger::LOG_PERF, "SYS", 2, "done " + std::string(x)); \
} \
BOOST_SCOPE_EXIT_END
+3
View File
@@ -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)
+13 -10
View File
@@ -601,20 +601,21 @@ void warn_attribute_count(
const ifcopenshell::declaration* declaration,
std::optional<size_t> instance_name,
size_t expected_size,
size_t actual_size
size_t actual_size,
::Logger& logger
) {
if (!declaration || expected_size == actual_size) {
return;
}
if (declaration->schema() == &Header_section_schema::get_schema()) {
logger::warning("Expected " + std::to_string(expected_size) + " attribute values, found " + std::to_string(actual_size) + " for header entity " + declaration->name());
logger.Warning("VAL", 15, "Expected " + std::to_string(expected_size) + " attribute values, found " + std::to_string(actual_size) + " for header entity " + declaration->name());
} else {
logger::warning("Expected " + std::to_string(expected_size) + " attribute values, found " + std::to_string(actual_size) + (instance_name ? std::string(" for instance #" + std::to_string(*instance_name)) : std::string("")));
logger.Warning("VAL", 16, "Expected " + std::to_string(expected_size) + " attribute values, found " + std::to_string(actual_size) + (instance_name ? std::string(" for instance #" + std::to_string(*instance_name)) : std::string("")));
}
}
template <typename Fn>
void dispatch_token_direct(ifcopenshell::token token, ifcopenshell::declaration* declaration, int attribute_index, Fn&& fn) {
void dispatch_token_direct(ifcopenshell::token token, ifcopenshell::declaration* declaration, int attribute_index, Logger& logger, Fn&& fn) {
if (token.is_binary()) {
fn(token.as_binary());
} else if (token.is_bool()) {
@@ -627,10 +628,10 @@ void dispatch_token_direct(ifcopenshell::token token, ifcopenshell::declaration*
try {
fn(enumeration_reference(declaration->as_enumeration_type(), declaration->as_enumeration_type()->lookup_enum_offset(value)));
} catch (ifcopenshell::exception&) {
logger::error("An enumeration literal '" + value + "' is not valid for type '" + declaration->name() + "' at offset " + std::to_string(token.start_pos));
logger.Error("VAL", 12, "An enumeration literal '" + value + "' is not valid for type '" + declaration->name() + "' at offset " + std::to_string(token.start_pos));
}
} else {
logger::error("An enumeration literal '" + value + "' is not expected at attribute index '" + std::to_string(attribute_index) + "' at offset " + std::to_string(token.start_pos));
logger.Error("VAL", 13, "An enumeration literal '" + value + "' is not expected at attribute index '" + std::to_string(attribute_index) + "' at offset " + std::to_string(token.start_pos));
}
} else if (token.is_int()) {
fn(token.as_int());
@@ -663,6 +664,7 @@ struct direct_aggregate {
direct_aggregate_storage storage;
size_t pending_empty_aggregates = 0;
size_t values = 0;
Logger& logger;
template <typename T>
void append(const T& value) {
@@ -679,7 +681,7 @@ struct direct_aggregate {
}
}
if (pending_empty_aggregates) {
logger::error("Inconsistent aggregate valuation while attempting to append " + std::string(typeid(T).name()) + " after an empty nested aggregate");
logger.Error("VAL", 14, "Inconsistent aggregate valuation while attempting to append " + std::string(typeid(T).name()) + " after an empty nested aggregate");
pending_empty_aggregates = 0;
}
if (storage.index() == 0) {
@@ -690,7 +692,7 @@ struct direct_aggregate {
append_promoted(value);
}
} else {
logger::error(std::string("Aggregates of ") + typeid(T).name() + " are not supported in the IfcOpenShell parser");
logger.Error("UNS", 31, std::string("Aggregates of ") + typeid(T).name() + " are not supported in the IfcOpenShell parser");
}
}
@@ -872,7 +874,8 @@ direct_aggregate read_direct_aggregate(
std::optional<size_t> entity_instance_name,
const ifcopenshell::entity* entity,
int attribute_index,
const ifcopenshell::aggregation_type* aggregate_type
const ifcopenshell::aggregation_type* aggregate_type,
::Logger& logger
) {
direct_aggregate aggregate;
token next = tokens->next();
@@ -900,7 +903,7 @@ direct_aggregate read_direct_aggregate(
storage.read_simple_type_instances.push_back(data);
aggregate.append(ifcopenshell::reference_or_simple_type{express::Base(data)});
} catch (exception& e) {
logger::message(logger::LOG_ERROR, std::string(e.what()) + " at offset " + std::to_string(next.start_pos));
logger.error("SYN", 123, std::string(e.what()) + " at offset " + std::to_string(next.start_pos));
}
} else {
if (next.is_identifier() && entity && entity_instance_name) {
+4 -1
View File
@@ -52,6 +52,8 @@ template <typename Reader>
class IFC_PARSE_API spf_lexer {
private:
character_decoder<Reader>* decoder_;
Logger& logger_;
size_t skip_whitespace() const;
size_t skip_comment() const;
@@ -59,6 +61,7 @@ class IFC_PARSE_API spf_lexer {
mutable size_t pool_index = 0;
public:
spf_lexer(const spf_lexer&) = delete;
spf_lexer& operator=(const spf_lexer&) = delete;
@@ -74,7 +77,7 @@ class IFC_PARSE_API spf_lexer {
Reader* stream;
// file* file;
spf_lexer(Reader* stream);
spf_lexer(Reader* stream, Logger& logger = Logger::Root());
token next();
~spf_lexer();
// void TokenString(size_t offset, std::string& result);
+1 -1
View File
@@ -11,7 +11,7 @@ using namespace ifcopenshell;
namespace {
shared_pointer_type make_header_entity(ifcopenshell::file* file, const ifcopenshell::entity& decl) {
shared_pointer_type make_header_entity(ifcopenshell::file* file, const ifcopenshell::entity& decl, Logger& logger) {
const bool in_memory = file == nullptr || std::visit([](auto& storage) {
return std::is_same_v<std::decay_t<decltype(storage)>, ifcopenshell::impl::in_memory_file_storage>;
}, file->storage_);
+13 -5
View File
@@ -23,6 +23,7 @@
#include "ifc_parse_api.h"
#include "instance_data.h"
#include "schemas/Header_section_schema.h"
#include <functional>
namespace ifcopenshell {
@@ -31,29 +32,36 @@ class file;
class IFC_PARSE_API spf_header {
private:
ifcopenshell::file* file_;
std::reference_wrapper<Logger> logger_;
IfcParse::impl::in_memory_file_storage* storage_ = nullptr;
std::array<shared_pointer_type, 3> header_entities_;
public:
explicit spf_header(ifcopenshell::file* owner_file);
~spf_header();
explicit spf_header(ifcopenshell::file* file = nullptr, Logger& logger = Logger::Root());
explicit spf_header(spf_lexer* lexer, Logger& logger = Logger::Root());
void write(std::ostream& stream) const;
ifcopenshell::file* owner_file() { return file_; }
void owner_file(ifcopenshell::file* file);
Logger& logger() const { return logger_.get(); }
void set_file_description(const shared_pointer_type& description_data);
void set_file_name(const shared_pointer_type& name_data);
void set_file_schema(const shared_pointer_type& schema_data);
const Header_section_schema::file_description file_description() const;
const Header_section_schema::file_name file_name() const;
const Header_section_schema::file_schema file_schema() const;
void assign(const IfcSpfHeader& other);
void write(std::ostream& out) const;
Header_section_schema::file_description file_description();
Header_section_schema::file_name file_name();
Header_section_schema::file_schema file_schema();
const Header_section_schema::file_description file_description() const;
const Header_section_schema::file_name file_name() const;
const Header_section_schema::file_schema file_schema() const;
};
} // namespace ifcopenshell
+7 -4
View File
@@ -24,9 +24,9 @@ namespace rocksdb {
#include "map_transformer.h"
#include "set_to_map_transformer.h"
#include "file_open_status.h"
#include "IfcLogger.h"
#include <boost/unordered_map.hpp>
#include <functional>
#include <variant>
#include <algorithm>
#include <cstdint>
@@ -38,7 +38,6 @@ namespace rocksdb {
#include <iostream>
#include <deque>
#include <vector>
#include <deque>
#include <list>
#include <mutex>
#include <set>
@@ -413,6 +412,10 @@ namespace ifcopenshell {
return std::move(read_simple_type_instances);
}
ifcopenshell::spf_lexer* tokens;
std::reference_wrapper<Logger> logger_;
// IfcParse::FileReader* stream;
// Either one of these needs to be set
ifcopenshell::file* file;
const ifcopenshell::schema_definition* schema;
@@ -427,7 +430,7 @@ namespace ifcopenshell {
typedef inverse_index entities_by_ref_t;
typedef entity_instance_by_name_t::iterator iterator;
in_memory_file_storage(ifcopenshell::file* owner_file = nullptr) : file(owner_file), schema(nullptr), byid_read_(&byid_, [this](const shared_pointer_type& data) { return express::Base(data); }) {};
in_memory_file_storage(ifcopenshell::file* owner_file = nullptr, Logger& logger = Logger::Root()) : logger_(logger), file(owner_file), schema(nullptr), byid_read_(&byid_, [this](const shared_pointer_type& data) { return express::Base(data); }) {};
in_memory_file_storage(const in_memory_file_storage& other) = delete;
in_memory_file_storage(const in_memory_file_storage&& other) = delete;
+17
View File
@@ -266,6 +266,17 @@ IFC_PARSE_API bool ifcopenshell::path::rename_file(const std::string& old_filena
return success;
}
IFC_PARSE_API bool IfcUtil::path::atomic_rename_file(const std::string& old_filename, const std::string& new_filename) {
std::wstring old_filename_w = from_utf8(old_filename);
std::wstring new_filename_w = from_utf8(new_filename);
// MOVEFILE_REPLACE_EXISTING makes the replace atomic on NTFS (no unlink
// of the destination first). MOVEFILE_WRITE_THROUGH waits until the move
// is flushed to disk before returning.
const bool success = !!MoveFileExW(old_filename_w.c_str(), new_filename_w.c_str(),
MOVEFILE_REPLACE_EXISTING | MOVEFILE_WRITE_THROUGH);
return success;
}
IFC_PARSE_API bool ifcopenshell::path::delete_file(const std::string& filename) {
std::wstring filename_w = from_utf8(filename);
const bool success = !!DeleteFileW(filename_w.c_str());
@@ -281,6 +292,12 @@ IFC_PARSE_API bool ifcopenshell::path::rename_file(const std::string& old_filena
return std::rename(old_filename.c_str(), new_filename.c_str()) == 0;
}
IFC_PARSE_API bool IfcUtil::path::atomic_rename_file(const std::string& old_filename, const std::string& new_filename) {
// POSIX rename() atomically replaces an existing destination on the same
// filesystem, so there is no window in which new_filename is missing.
return std::rename(old_filename.c_str(), new_filename.c_str()) == 0;
}
IFC_PARSE_API bool ifcopenshell::path::delete_file(const std::string& filename) {
return std::remove(filename.c_str()) != 0;
}
+7
View File
@@ -37,6 +37,13 @@ namespace path {
IFC_PARSE_API bool delete_file(const std::string& filename);
IFC_PARSE_API bool rename_file(const std::string& old_filename, const std::string& new_filename);
/// Atomically renames old_filename onto new_filename, replacing an existing
/// destination in a single filesystem operation. Unlike rename_file(), the
/// destination is never unlinked before the rename, so an interruption can
/// never leave the destination missing. This requires both paths to live on
/// the same filesystem. Returns true on success.
IFC_PARSE_API bool atomic_rename_file(const std::string& old_filename, const std::string& new_filename);
#if defined(_MSC_VER) && defined(_UNICODE)
/// Uses windows.h string conversion functions
+34 -18
View File
@@ -37,10 +37,28 @@ variant - which is the maximum size of its constituents - is reduced.
#include <cstring>
#include <cstddef>
#include <limits>
#include "exception.h"
#include <exception>
namespace impl {
class storage_type_mismatch : public std::exception {
private:
std::string requested_, actual__, message_;
public:
storage_type_mismatch(const std::string& requested, const std::string& actual)
: requested_(requested), actual__(actual), message_("Requested type " + requested_ + " does not match actual type " + actual__) {}
const char* what() const noexcept override {
return message_.c_str();
}
const std::string& requested() const { return requested_; }
const std::string& actual() const { return actual__; }
};
template <typename T>
struct VariantTypeName;
// Trait to detect unique_ptr
template <typename...> struct is_unique_ptr : std::false_type {};
template<class T, typename... Args>
@@ -166,14 +184,13 @@ public:
using U = std::decay_t<T>;
static_assert(::impl::TypeIndex_v<U, Types...> < sizeof...(Types), "Type not supported by variant");
if (index >= size()) {
throw std::out_of_range("Index out of range");
throw std::out_of_range("Index " + std::to_string(index) + " is out of range for storage of size " + std::to_string(size()));
}
destroy_at_index(index);
size_and_indices_[index + 1] = ::impl::TypeIndex_v<U, Types...>;
using V = typename std::tuple_element<::impl::TypeIndex_v<U, Types...>, ::impl::MapTypes_t<Types... >>::type;
// std::wcout << "setting " << index << " to " << typeid(V).name() << " (" << ::impl::TypeIndex_v<U, Types...> << ")" << std::endl;
if constexpr (::impl::is_unique_ptr<V>::value) {
new(&storage_[index]) V(new U(value));
} else {
@@ -187,8 +204,8 @@ public:
std::size_t index(std::size_t index) const {
if (index >= size()) {
throw ifcopenshell::exception(
"Index " + std::to_string(index) + " is out of range for variant of size " + std::to_string(size())
throw std::out_of_range(
"Index " + std::to_string(index) + " is out of range for storage of size " + std::to_string(size())
);
}
return size_and_indices_[index + 1];
@@ -197,8 +214,8 @@ public:
template<typename T>
T& get(std::size_t index) {
if (index >= size()) {
throw ifcopenshell::exception(
"Index " + std::to_string(index) + " is out of range for variant of size " + std::to_string(size())
throw std::out_of_range(
"Index " + std::to_string(index) + " is out of range for storage of size " + std::to_string(size())
);
}
if (!has<T>(index)) {
@@ -220,17 +237,16 @@ public:
template<typename T>
const T& get(std::size_t index) const {
if (index >= size()) {
throw ifcopenshell::exception(
"Index " + std::to_string(index) + " is out of range for variant of size " + std::to_string(size())
throw std::out_of_range(
"Index " + std::to_string(index) + " is out of range for storage of size " + std::to_string(size())
);
}
if (size_and_indices_[index + 1] != ::impl::TypeIndex<T, Types...>::value) {
// @todo this exception is silly. Figure out what
// to do, but at the moment it is specifically caught
// in various places.
throw ifcopenshell::exception(
"Type held at index " + std::to_string(index) + " is " +
get_type_name(size_and_indices_[index + 1]) + " and not " + typeid(T).name()
throw impl::storage_type_mismatch(
::impl::VariantTypeName<T>::get(), get_type_name(size_and_indices_[index + 1])
);
}
using V = typename std::tuple_element<::impl::TypeIndex_v<T, Types...>, ::impl::MapTypes_t<Types... >>::type;
@@ -244,8 +260,8 @@ public:
template<typename Visitor>
auto apply_visitor(Visitor&& visitor, std::size_t index) const {
if (index >= size()) {
throw ifcopenshell::exception(
"Index " + std::to_string(index) + " is out of range for variant of size " + std::to_string(size())
throw std::out_of_range(
"Index " + std::to_string(index) + " is out of range for storage of size " + std::to_string(size())
);
}
return apply_visitor_impl(std::forward<Visitor>(visitor), index, std::integral_constant<std::size_t, sizeof...(Types)>{});
@@ -316,19 +332,19 @@ private:
}
template <size_t I>
const char* get_type_name_impl(size_t type_index) const {
std::string get_type_name_impl(size_t type_index) const {
if constexpr (I == 0) {
return "";
} else {
if (type_index == I - 1) {
return typeid(std::tuple_element_t<I - 1, std::tuple<Types...>>).name();
return ::impl::VariantTypeName<std::tuple_element_t<I - 1, std::tuple<Types...>>>::get();
} else {
return get_type_name_impl<I - 1>(type_index);
}
}
}
const char* get_type_name(size_t type_index) const {
std::string get_type_name(size_t type_index) const {
return get_type_name_impl<sizeof...(Types)>(type_index);
}
};