From 90729810d4f14a127e7a28ac02bf7b13b560abb9 Mon Sep 17 00:00:00 2001 From: "Hochsteger, Matthias" Date: Mon, 28 Aug 2023 10:02:22 +0200 Subject: [PATCH] Archive with nondefault constructor --- CMakeLists.txt | 1 - libsrc/core/CMakeLists.txt | 3 +- libsrc/core/archive.hpp | 110 ++++++++++++++++++++++++++----- libsrc/core/python_ngcore.hpp | 24 ------- libsrc/core/register_archive.hpp | 73 +++++++++++++++----- libsrc/core/type_traits.hpp | 9 +++ libsrc/core/utils.cpp | 8 +++ libsrc/csg/surface.cpp | 2 +- libsrc/geom2d/geometry2d.cpp | 2 +- libsrc/meshing/CMakeLists.txt | 2 +- libsrc/meshing/meshclass.cpp | 11 ++-- libsrc/stlgeom/stlgeom.cpp | 2 +- tests/catch/archive.cpp | 37 ++++++++++- 13 files changed, 211 insertions(+), 73 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 21edc576..76884b5a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -255,7 +255,6 @@ target_include_directories(nglib PRIVATE ${ZLIB_INCLUDE_DIRS}) if(USE_GUI) target_include_directories(nggui PRIVATE ${ZLIB_INCLUDE_DIRS}) endif(USE_GUI) -target_link_libraries(nglib PRIVATE ${ZLIB_LIBRARIES}) ####################################################################### if(WIN32) diff --git a/libsrc/core/CMakeLists.txt b/libsrc/core/CMakeLists.txt index b763246a..037d29d8 100644 --- a/libsrc/core/CMakeLists.txt +++ b/libsrc/core/CMakeLists.txt @@ -18,7 +18,8 @@ string(REPLACE "|" ";" ng_compile_flags_replace_sep "${NG_COMPILE_FLAGS}") target_compile_options(ngcore PUBLIC ${ng_compile_flags_replace_sep}) if(EMSCRIPTEN) - target_link_options(nglib PUBLIC -sALLOW_MEMORY_GROWTH -sENVIRONMENT=web) + target_link_options(ngcore PUBLIC -sALLOW_MEMORY_GROWTH -sENVIRONMENT=web) + target_compile_options(ngcore PUBLIC -sNO_DISABLE_EXCEPTION_CATCHING) endif() if (CMAKE_CXX_COMPILER_ID MATCHES "GNU" AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 9) diff --git a/libsrc/core/archive.hpp b/libsrc/core/archive.hpp index b4e370df..2aad63e8 100644 --- a/libsrc/core/archive.hpp +++ b/libsrc/core/archive.hpp @@ -41,16 +41,35 @@ namespace ngcore class NGCORE_API Archive; namespace detail { + template + T* construct_from_tuple(Tuple&& tuple, std::index_sequence ) { + return new T{std::get(std::forward(tuple))...}; + } + + template + T* construct_from_tuple(Tuple&& tuple) { + return construct_from_tuple(std::forward(tuple), + std::make_index_sequence>::value>{} + ); + } + // create new pointer of type T if it is default constructible, else throw - template - T* constructIfPossible_impl(Rest... /*unused*/) - { throw Exception(std::string(Demangle(typeid(T).name())) + " is not default constructible!"); } + template + T* constructIfPossible(std::tuple args) + { + if constexpr(std::is_constructible_v) + return construct_from_tuple(args); + throw Exception(std::string(Demangle(typeid(T).name())) + + " is not constructible!"); + } - template::value>> - T* constructIfPossible_impl(int /*unused*/) { return new T; } // NOLINT - - template - T* constructIfPossible() { return constructIfPossible_impl(int{}); } + template T *constructIfPossible() + { + if constexpr(std::is_constructible_v) + return new T(); + throw Exception(std::string(Demangle(typeid(T).name())) + + " is not default constructible!"); + } //Type trait to check if a class implements a 'void DoArchive(Archive&)' function template @@ -87,7 +106,7 @@ namespace ngcore // create new object of this type and return a void* pointer that is points to the location // of the (base)class given by type_info // std::function creator; - void* (*creator)(const std::type_info&); + void* (*creator)(const std::type_info&, Archive&); // This caster takes a void* pointer to the type stored in this info and casts it to a // void* pointer pointing to the (base)class type_info // std::function upcaster; @@ -97,6 +116,9 @@ namespace ngcore // std::function downcaster; void* (*downcaster)(const std::type_info&, void*); + // Archive constructor arguments + std::function cargs_archiver; + #ifdef NETGEN_PYTHON // std::function anyToPyCaster; pybind11::object (*anyToPyCaster)(const std::any&); @@ -528,8 +550,18 @@ namespace ngcore if (std::is_constructible::value) return (*this) << -1 & (*p); else - throw Exception(std::string("Archive error: Class ") + - Demangle(typeid(*p).name()) + " does not provide a default constructor!"); + { + if (IsRegistered(Demangle(typeid(*p).name()))) + { + (*this) << -3 << Demangle(typeid(*p).name()); + GetArchiveRegister(Demangle(typeid(*p).name())). + cargs_archiver(*this, p); + return (*this) & (*p); + } + else + throw Exception(std::string("Archive error: Class ") + + Demangle(typeid(*p).name()) + " does not provide a default constructor!"); + } else { // if a pointer to a base class is archived, the class hierarchy must be registered @@ -540,7 +572,10 @@ namespace ngcore throw Exception(std::string("Archive error: Polymorphic type ") + Demangle(typeid(*p).name()) + " not registered for archive"); - return (*this) << -3 << Demangle(typeid(*p).name()) & (*p); + (*this) << -3 << Demangle(typeid(*p).name()); + GetArchiveRegister(Demangle(typeid(*p).name())). + cargs_archiver(*this, p); + return (*this) & (*p); } } else @@ -571,7 +606,7 @@ namespace ngcore auto info = GetArchiveRegister(name); // the creator creates a new object of type name, and returns a void* pointing // to T (which may have an offset) - p = static_cast(info.creator(typeid(T))); + p = static_cast(info.creator(typeid(T), *this)); // we store the downcasted pointer (to be able to find it again from // another class in a multiple inheritance tree) nr2ptr.push_back(info.downcaster(typeid(T),p)); @@ -595,6 +630,16 @@ namespace ngcore return *this; } + Archive& operator&(std::tuple<>&) { return *this; } + + template + Archive& operator&(std::tuple &t) + { + // call operator& for each element of the tuple + std::apply([this](auto&... arg) { std::make_tuple(((*this) & arg).IsParallel()...);}, t); + return *this; + } + // const ptr template Archive& operator &(const T*& t) @@ -618,7 +663,7 @@ namespace ngcore void SetParallel (bool _parallel) { parallel = _parallel; } private: - template + template friend class RegisterClassForArchive; #ifdef NETGEN_PYTHON @@ -637,7 +682,7 @@ namespace ngcore struct Caster{}; template - struct Caster + struct Caster> { static void* tryUpcast (const std::type_info& /*unused*/, T* /*unused*/) { @@ -649,8 +694,37 @@ namespace ngcore } }; + template + struct Caster + { + static void* tryUpcast(const std::type_info& ti, T* p) + { + try { + return GetArchiveRegister(Demangle(typeid(B1).name())) + .upcaster(ti, static_cast(dynamic_cast(p))); + } catch (const Exception &) { + throw Exception("Upcast not successful, some classes are not " + "registered properly for archiving!"); + } + } + + static void* tryDowncast(const std::type_info& ti, void* p) + { + if(typeid(B1) == ti) + return dynamic_cast(static_cast(p)); + try + { + return dynamic_cast(static_cast(GetArchiveRegister(Demangle(typeid(B1).name())). + downcaster(ti, p))); + } catch (const Exception &) { + throw Exception("Downcast not successful, some classes are not " + "registered properly for archiving!"); + } + } + }; + template - struct Caster + struct Caster> { static void* tryUpcast(const std::type_info& ti, T* p) { @@ -658,7 +732,7 @@ namespace ngcore { return GetArchiveRegister(Demangle(typeid(B1).name())). upcaster(ti, static_cast(dynamic_cast(p))); } catch(const Exception&) - { return Caster::tryUpcast(ti, p); } + { return Caster>::tryUpcast(ti, p); } } static void* tryDowncast(const std::type_info& ti, void* p) @@ -672,7 +746,7 @@ namespace ngcore } catch(const Exception&) { - return Caster::tryDowncast(ti, p); + return Caster>::tryDowncast(ti, p); } } }; diff --git a/libsrc/core/python_ngcore.hpp b/libsrc/core/python_ngcore.hpp index 6c195eca..8ba09915 100644 --- a/libsrc/core/python_ngcore.hpp +++ b/libsrc/core/python_ngcore.hpp @@ -156,30 +156,6 @@ namespace ngcore { return std::string("sp_")+GetPyName(); } }; - // *************** Archiving functionality ************** - - template - Archive& Archive :: Shallow(T& val) - { - static_assert(detail::is_any_pointer, "ShallowArchive must be given pointer type!"); -#ifdef NETGEN_PYTHON - if(shallow_to_python) - { - if(is_output) - ShallowOutPython(pybind11::cast(val)); - else - { - pybind11::object obj; - ShallowInPython(obj); - val = pybind11::cast(obj); - } - } - else -#endif // NETGEN_PYTHON - *this & val; - return *this; - } - template class NGCORE_API_EXPORT PyArchive : public ARCHIVE { diff --git a/libsrc/core/register_archive.hpp b/libsrc/core/register_archive.hpp index 93221cd6..f5a3dde5 100644 --- a/libsrc/core/register_archive.hpp +++ b/libsrc/core/register_archive.hpp @@ -5,35 +5,72 @@ #include #include #endif // NETGEN_PYTHON +#include #include "archive.hpp" namespace ngcore { + // *************** Archiving functionality ************** - template +#ifdef NETGEN_PYTHON + template + Archive& Archive :: Shallow(T& val) + { + static_assert(detail::is_any_pointer, "ShallowArchive must be given pointer type!"); + if(shallow_to_python) + { + if(is_output) + ShallowOutPython(pybind11::cast(val)); + else + { + pybind11::object obj; + ShallowInPython(obj); + val = pybind11::cast(obj); + } + } + else + *this & val; + return *this; + } +#endif // NETGEN_PYTHON + + + template, typename CArgs=std::tuple<>> class RegisterClassForArchive { public: - RegisterClassForArchive() + std::function get_cargs; + RegisterClassForArchive(std::function _get_cargs = + [](T&) -> std::tuple<> { return std::tuple<>{}; }) : + get_cargs(_get_cargs) { - static_assert(detail::all_of_tmpl::value...>, - "Variadic template arguments must be base classes of T"); + static_assert(std::is_base_of::value || + detail::is_base_of_tuple, + "Second argument must be base class or tuple of base classes of T"); detail::ClassArchiveInfo info {}; - info.creator = [](const std::type_info& ti) -> void* - { return typeid(T) == ti ? detail::constructIfPossible() - : Archive::Caster::tryUpcast(ti, detail::constructIfPossible()); }; - info.upcaster = [/*this*/](const std::type_info& ti, void* p) -> void* - { return typeid(T) == ti ? p : Archive::Caster::tryUpcast(ti, static_cast(p)); }; - info.downcaster = [/*this*/](const std::type_info& ti, void* p) -> void* - { return typeid(T) == ti ? p : Archive::Caster::tryDowncast(ti, p); }; -#ifdef NETGEN_PYTHON - info.anyToPyCaster = [](const std::any& a) + info.creator = [](const std::type_info& ti, Archive& ar) -> void* { - const T* val = std::any_cast(&a); - return pybind11::cast(val); }; + CArgs args; + ar &args; + auto nT = detail::constructIfPossible(args); + return typeid(T) == ti ? nT + : Archive::Caster::tryUpcast(ti, nT); + }; + info.upcaster = [/*this*/](const std::type_info& ti, void* p) -> void* + { return typeid(T) == ti ? p : Archive::Caster::tryUpcast(ti, static_cast(p)); }; + info.downcaster = [/*this*/](const std::type_info& ti, void* p) -> void* + { return typeid(T) == ti ? p : Archive::Caster::tryDowncast(ti, p); }; + info.cargs_archiver = [this](Archive &ar, void* p) { + ar << get_cargs(*static_cast(p)); + }; +#ifdef NETGEN_PYTHON + info.anyToPyCaster = [](const std::any &a) { + const T* val = std::any_cast(&a); + return pybind11::cast(val); + }; #endif // NETGEN_PYTHON - Archive::SetArchiveRegister(std::string(Demangle(typeid(T).name())),info); - } - }; + Archive::SetArchiveRegister(std::string(Demangle(typeid(T).name())),info); + } +}; } // namespace ngcore #endif // NETGEN_REGISTER_ARCHIVE_HPP diff --git a/libsrc/core/type_traits.hpp b/libsrc/core/type_traits.hpp index 17da6253..361a31e9 100644 --- a/libsrc/core/type_traits.hpp +++ b/libsrc/core/type_traits.hpp @@ -16,6 +16,15 @@ namespace ngcore template struct is_any_pointer_impl : std::false_type {}; + // check if second template argument is tuple of base classes to first + // template argument, return constexpr bool + template + constexpr bool is_base_of_tuple = false; + + template + constexpr bool is_base_of_tuple> = + all_of_tmpl::value...>; + template struct is_any_pointer_impl : std::true_type {}; diff --git a/libsrc/core/utils.cpp b/libsrc/core/utils.cpp index 062114e6..eb03eb98 100644 --- a/libsrc/core/utils.cpp +++ b/libsrc/core/utils.cpp @@ -43,6 +43,14 @@ namespace ngcore { for(const auto & [r, sub] : demangle_regexes) s = std::regex_replace (s,r,sub); +#ifdef EMSCRIPTEN + // for some reason regex_replace is not working at all + std::string temp = s; + s = ""; + for(auto c : temp) + if(c!=' ') + s+=c; +#endif // EMSCRIPTEN return s; } diff --git a/libsrc/csg/surface.cpp b/libsrc/csg/surface.cpp index e31e9fce..76ed14d5 100644 --- a/libsrc/csg/surface.cpp +++ b/libsrc/csg/surface.cpp @@ -580,5 +580,5 @@ void ProjectToEdge (const Surface * f1, const Surface * f2, Point<3> & hp) RegisterClassForArchive regsurf; RegisterClassForArchive regprim; -RegisterClassForArchive regosf; +RegisterClassForArchive> regosf; } diff --git a/libsrc/geom2d/geometry2d.cpp b/libsrc/geom2d/geometry2d.cpp index f3332258..014be264 100644 --- a/libsrc/geom2d/geometry2d.cpp +++ b/libsrc/geom2d/geometry2d.cpp @@ -1105,6 +1105,6 @@ namespace netgen }; SplineGeoInit sginit; - static RegisterClassForArchive, NetgenGeometry> regspg2; + static RegisterClassForArchive, NetgenGeometry>> regspg2; static RegisterClassForArchive> regssext; } diff --git a/libsrc/meshing/CMakeLists.txt b/libsrc/meshing/CMakeLists.txt index 7fff8d26..8ca9a0cc 100644 --- a/libsrc/meshing/CMakeLists.txt +++ b/libsrc/meshing/CMakeLists.txt @@ -15,7 +15,7 @@ target_sources(nglib PRIVATE boundarylayer2d.cpp ) -target_link_libraries( nglib PRIVATE netgen_metis "$" ${ZLIB_LIBRARIES} ) +target_link_libraries( nglib PRIVATE netgen_metis "$" ) install(FILES adfront2.hpp adfront3.hpp basegeom.hpp bcfunctions.hpp bisect.hpp diff --git a/libsrc/meshing/meshclass.cpp b/libsrc/meshing/meshclass.cpp index 16749a25..87d25274 100644 --- a/libsrc/meshing/meshclass.cpp +++ b/libsrc/meshing/meshclass.cpp @@ -4,10 +4,7 @@ #include "meshing.hpp" #include "../general/gzstream.h" -#ifdef NG_PYTHON -// must be included to instantiate Archive::Shallow(NetgenGeometry&) -#include -#endif +#include namespace netgen { @@ -4493,7 +4490,11 @@ namespace netgen double local_sum = 0.0; double teterrpow = mp.opterrpow; - std::array classes_local{}; + // std::array classes_local{}; + size_t n_classes = tets_in_qualclass.Size(); + Array classes_local(n_classes); + for (int i = 0; i < n_classes; i++) + classes_local[i] = 0; for (auto i : myrange) { diff --git a/libsrc/stlgeom/stlgeom.cpp b/libsrc/stlgeom/stlgeom.cpp index a596cfc4..f02495fd 100644 --- a/libsrc/stlgeom/stlgeom.cpp +++ b/libsrc/stlgeom/stlgeom.cpp @@ -3756,5 +3756,5 @@ void STLGeometry :: WriteChartToFile( ChartId chartnumber, filesystem::path file STLInit stlinit; -static RegisterClassForArchive stlgeo; +static RegisterClassForArchive> stlgeo; } diff --git a/tests/catch/archive.cpp b/tests/catch/archive.cpp index 351401f7..26543695 100644 --- a/tests/catch/archive.cpp +++ b/tests/catch/archive.cpp @@ -86,12 +86,32 @@ public: const int* getPtr() { return ptr; } }; -class OneMoreDerivedClass : public SharedPtrAndPtrHolder {}; +class ClassWithoutDefaultConstructor +{ +public: + int a; + double b; + double c; + ClassWithoutDefaultConstructor(int aa, double c) : a(aa), c(c) {} + + void DoArchive(Archive& ar) + { + ar & b; + } +}; + +static RegisterClassForArchive, tuple> +regwdc([](ClassWithoutDefaultConstructor& self) + { return make_tuple(self.a, self.c); }); + +class OneMoreDerivedClass : public SharedPtrAndPtrHolder { +}; static RegisterClassForArchive regb; static RegisterClassForArchive regsp; static RegisterClassForArchive regp; -static RegisterClassForArchive regspp; +static RegisterClassForArchive> regspp; static RegisterClassForArchive regom; void testNullPtr(Archive& in, Archive& out) @@ -334,6 +354,19 @@ void testArchive(Archive& in, Archive& out) SharedPtrAndPtrHolder* p = new NotRegisteredForArchive; REQUIRE_THROWS(out & p, Catch::Contains("not registered for archive")); } + SECTION("Non-default constructor") + { + ClassWithoutDefaultConstructor c(5, 2.2); + c.b = 3.2; + auto p = &c; + out & p; + out.FlushBuffer(); + ClassWithoutDefaultConstructor* cin; + in & cin; + CHECK(cin->a == 5); + CHECK(cin->b == 3.2); + CHECK(cin->c == 2.2); + } SECTION("nullptr") { testNullPtr(in, out);