From 17aba88117bf99985b22c446746a896ca2c6c5e0 Mon Sep 17 00:00:00 2001 From: Christopher Lackner Date: Sat, 8 Dec 2018 16:10:29 +0100 Subject: [PATCH] [ngcore] follow cpp guidelines, check them with clang-tidy --- CMakeLists.txt | 15 ++ cmake/SuperBuild.cmake | 1 + libsrc/core/CMakeLists.txt | 12 +- libsrc/core/archive.cpp | 57 +++++ libsrc/core/archive.hpp | 496 +++++++++++++++++++++++++++++++++++- libsrc/core/basearchive.cpp | 44 ---- libsrc/core/basearchive.hpp | 480 ---------------------------------- libsrc/core/ngcore.hpp | 50 +--- libsrc/core/ngcore_api.hpp | 29 +++ libsrc/core/type_traits.hpp | 4 +- libsrc/core/version.cpp | 11 + libsrc/core/version.hpp | 19 +- tests/catch/CMakeLists.txt | 6 + 13 files changed, 636 insertions(+), 588 deletions(-) create mode 100644 libsrc/core/archive.cpp delete mode 100644 libsrc/core/basearchive.cpp delete mode 100644 libsrc/core/basearchive.hpp create mode 100644 libsrc/core/ngcore_api.hpp create mode 100644 libsrc/core/version.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index c947e762..33d2d835 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -18,6 +18,7 @@ option( INSTALL_PROFILES "install environment variable settings to /etc/profile. option( USE_CCACHE "use ccache") option( USE_INTERNAL_TCL "Compile tcl files into the code and don't install them" ON) option( ENABLE_UNIT_TESTS "Enable Catch unit tests") +option( ENABLE_CPP_CORE_GUIDELINES_CHECK "Enable cpp core guideline checks on ngcore" OFF) option( USE_SUPERBUILD "use ccache" ON) @@ -349,6 +350,20 @@ endif(ENABLE_UNIT_TESTS) ####################################################################### +if(ENABLE_CPP_CORE_GUIDELINES_CHECK) + find_program( + CLANG_TIDY_EXE + NAMES "clang-tidy" + DOC "Path to clang-tidy executable" + ) + if(NOT CLANG_TIDY_EXE) + message(WARNING "clang-tidy not found.") + else() + message(STATUS "clang-tidy found: ${CLANG_TIDY_EXE}") + set(DO_CLANG_TIDY "${CLANG_TIDY_EXE}" "-checks=*,-clang-analyzer-alpha.*,-*braces-around-statements,-fuchsia-default-arguments") + endif() +endif(ENABLE_CPP_CORE_GUIDELINES_CHECK) + add_subdirectory(libsrc) add_subdirectory(ng) add_subdirectory(tutorials) diff --git a/cmake/SuperBuild.cmake b/cmake/SuperBuild.cmake index c5b3cb47..36ea3fcb 100644 --- a/cmake/SuperBuild.cmake +++ b/cmake/SuperBuild.cmake @@ -141,6 +141,7 @@ set_vars( NETGEN_CMAKE_ARGS CMAKE_PREFIX_PATH CMAKE_INSTALL_PREFIX ENABLE_UNIT_TESTS + ENABLE_CPP_CORE_GUIDELINES_CHECK ) # propagate all variables set on the command line using cmake -DFOO=BAR diff --git a/libsrc/core/CMakeLists.txt b/libsrc/core/CMakeLists.txt index ef983cc9..337bbfaa 100644 --- a/libsrc/core/CMakeLists.txt +++ b/libsrc/core/CMakeLists.txt @@ -1,10 +1,14 @@ -add_library(ngcore basearchive.cpp) + +add_library(ngcore archive.cpp version.cpp) set_target_properties(ngcore PROPERTIES POSITION_INDEPENDENT_CODE ON ) target_compile_definitions(ngcore PRIVATE -DNGCORE_EXPORTS) install(TARGETS ngcore DESTINATION ${NG_INSTALL_DIR} COMPONENT netgen) -install(FILES ngcore.hpp archive.hpp basearchive.hpp version.hpp type_traits.hpp - DESTINATION ${NG_INSTALL_DIR_INCLUDE}/core COMPONENT netgen_devel -) +install(FILES ngcore.hpp archive.hpp type_traits.hpp version.hpp ngcore_api.hpp + DESTINATION ${NG_INSTALL_DIR_INCLUDE}/core COMPONENT netgen_devel) + +if(ENABLE_CPP_CORE_GUIDELINES_CHECK) + set_target_properties(ngcore PROPERTIES CXX_CLANG_TIDY "${DO_CLANG_TIDY}") +endif(ENABLE_CPP_CORE_GUIDELINES_CHECK) diff --git a/libsrc/core/archive.cpp b/libsrc/core/archive.cpp new file mode 100644 index 00000000..9fe54704 --- /dev/null +++ b/libsrc/core/archive.cpp @@ -0,0 +1,57 @@ + +#include "archive.hpp" + +#ifndef WIN32 +#include +#endif + +namespace ngcore +{ + void VersionInfo :: DoArchive(Archive& ar) + { + ar & mayor_ & minor_ & release & patch & git_hash; + } + + // clang-tidy should ignore this static object + static std::map library_versions; // NOLINT + std::map& Archive :: GetLibraryVersions() + { + return library_versions; + } + const VersionInfo& GetLibraryVersion(const std::string& library) + { return library_versions[library]; } + + void SetLibraryVersion(const std::string& library, const VersionInfo& version) + { library_versions[library] = version; } + +#ifdef WIN + // windows does demangling in typeid(T).name() + std::string demangle(const char* typeinfo) { return typeinfo; } +#else + std::string demangle(const char* typeinfo) { int status; return abi::__cxa_demangle(typeinfo, + nullptr, + nullptr, + &status); } +#endif + + // clang-tidy should ignore this static object + static std::unique_ptr> type_register; // NOLINT + const ClassArchiveInfo& Archive :: GetArchiveRegister(const std::string& classname) + { + if(type_register == nullptr) type_register = + std::make_unique>(); + return (*type_register)[classname]; + } + void Archive :: SetArchiveRegister(const std::string& classname, const ClassArchiveInfo& info) + { + if(type_register == nullptr) type_register = + std::make_unique>(); + (*type_register)[classname] = info; + } + bool Archive :: IsRegistered(const std::string& classname) + { + if(type_register == nullptr) type_register = + std::make_unique>(); + return type_register->count(classname) != 0; + } +} // namespace ngcore diff --git a/libsrc/core/archive.hpp b/libsrc/core/archive.hpp index bbacab8d..89656ad5 100644 --- a/libsrc/core/archive.hpp +++ b/libsrc/core/archive.hpp @@ -1,8 +1,493 @@ -#ifndef NG_ARCHIVE_HPP -#define NG_ARCHIVE_HPP +#ifndef NETGEN_CORE_ARCHIVE_HPP +#define NETGEN_CORE_ARCHIVE_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ngcore_api.hpp" +#include "type_traits.hpp" +#include "version.hpp" namespace ngcore { + // Libraries using this archive can store their version here to implement backwards compatibility + const VersionInfo& GetLibraryVersion(const std::string& library); + void SetLibraryVersion(const std::string& library, const VersionInfo& version); + + class Archive; + NGCORE_API std::string demangle(const char* typeinfo); + + // create new pointer of type T if it is default constructible, else throw + template + T* constructIfPossible_impl(...) + { throw std::runtime_error(std::string(demangle(typeid(T).name())) + " is not default constructible!"); } + + template::value>::type> + T* constructIfPossible_impl(int) { return new T; } + + template + T* constructIfPossible() { return constructIfPossible_impl(int{}); } + + //Type trait to check if a class implements a 'void DoArchive(Archive&)' function + template + struct has_DoArchive + { + private: + template + static constexpr auto check(T2*) -> + typename std::is_same().DoArchive(std::declval())),void>::type; + template + static constexpr std::false_type check(...); + typedef decltype(check(0)) type; + public: + NGCORE_API static constexpr bool value = type::value; + }; + + // Check if class is archivable + template + struct is_Archivable + { + private: + template + static constexpr auto check(T2*) -> + typename std::is_same() & std::declval()),Archive&>::type; + template + static constexpr std::false_type check(...); + typedef decltype(check(nullptr)) type; + public: + NGCORE_API static constexpr bool value = type::value; + }; + + struct ClassArchiveInfo + { + // 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; + // 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; + // This caster takes a void* pointer to the (base)class type_info and returns void* pointing + // to the type stored in this info + std::function downcaster; + }; + + // Base Archive class + class NGCORE_API Archive + { + bool is_output; + // how many different shared_ptr/pointer have been (un)archived + int shared_ptr_count, ptr_count; + // maps for archived shared pointers and pointers + std::map shared_ptr2nr, ptr2nr; + // vectors for storing the unarchived (shared) pointers + std::vector> nr2shared_ptr; + std::vector nr2ptr; + + // Helper class for up-/downcasting + template + struct Caster + { + static void* tryUpcast(const std::type_info& ti, T* p); + static void* tryDowncast(const std::type_info& ti, void* p); + }; + template + friend class RegisterClassForArchive; + + // Returns ClassArchiveInfo of demangled typeid + static const ClassArchiveInfo& GetArchiveRegister(const std::string& classname); + // Set ClassArchiveInfo for demangled typeid, this is done by creating an instance of + // RegisterClassForArchive + static void SetArchiveRegister(const std::string& classname, const ClassArchiveInfo& info); + static bool IsRegistered(const std::string& classname); + public: + Archive (bool ais_output) : is_output(ais_output), shared_ptr_count(0), ptr_count(0) { ; } + virtual ~Archive() { ; } + + bool Output () { return is_output; } + bool Input () { return !is_output; } + virtual const VersionInfo& getVersion(const std::string& library) = 0; + + // Pure virtual functions that have to be implemented by In-/OutArchive + virtual Archive & operator & (double & d) = 0; + virtual Archive & operator & (int & i) = 0; + virtual Archive & operator & (long & i) = 0; + virtual Archive & operator & (size_t & i) = 0; + virtual Archive & operator & (short & i) = 0; + virtual Archive & operator & (unsigned char & i) = 0; + virtual Archive & operator & (bool & b) = 0; + virtual Archive & operator & (std::string & str) = 0; + virtual Archive & operator & (char *& str) = 0; + + + // Archive std classes ================================================ + template + Archive& operator & (std::complex& c) + { + if(is_output) + (*this) << c.real() << c.imag(); + else + { + T tmp; + (*this) & tmp; + c.real(tmp); + (*this) & tmp; + c.imag(tmp); + } + return (*this); + } + template + Archive& operator & (std::vector& v) + { + size_t size; + if(is_output) + size = v.size(); + (*this) & size; + if(!is_output) + v.resize(size); + Do(&v[0], size); + return (*this); + } + template + Archive& operator& (std::map& map) + { + if(is_output) + { + (*this) << size_t(map.size()); + for(auto& pair : map) + (*this) << pair.first << pair.second; + } + else + { + size_t size; + (*this) & size; + T1 key; T2 val; + for(size_t i = 0; i < size; i++) + { + T1 key; T2 val; + (*this) & key & val; + map[key] = val; + } + } + return (*this); + } + // Archive arrays ===================================================== + // this functions can be overloaded in Archive implementations for more efficiency + template ::value>::type> + Archive & Do (T * data, size_t n) + { for (size_t j = 0; j < n; j++) { (*this) & data[j]; }; return *this; }; + + virtual Archive & Do (double * d, size_t n) + { for (size_t j = 0; j < n; j++) { (*this) & d[j]; }; return *this; }; + + virtual Archive & Do (int * i, size_t n) + { for (size_t j = 0; j < n; j++) { (*this) & i[j]; }; return *this; }; + + virtual Archive & Do (long * i, size_t n) + { for (size_t j = 0; j < n; j++) { (*this) & i[j]; }; return *this; }; + + virtual Archive & Do (size_t * i, size_t n) + { for (size_t j = 0; j < n; j++) { (*this) & i[j]; }; return *this; }; + + virtual Archive & Do (short * i, size_t n) + { for (size_t j = 0; j < n; j++) { (*this) & i[j]; }; return *this; }; + + virtual Archive & Do (unsigned char * i, size_t n) + { for (size_t j = 0; j < n; j++) { (*this) & i[j]; }; return *this; }; + + virtual Archive & Do (bool * b, size_t n) + { for (size_t j = 0; j < n; j++) { (*this) & b[j]; }; return *this; }; + + // Archive a class implementing a (void DoArchive(Archive&)) method ======= + template::value>> + Archive& operator & (T& val) + { + val.DoArchive(*this); return *this; + } + + // Archive shared_ptrs ================================================= + template + Archive& operator & (std::shared_ptr& ptr) + { + if(Output()) + { + // save -2 for nullptr + if(!ptr) + return (*this) << -2; + + void* reg_ptr = ptr.get(); + bool neededDowncast = false; + // Downcasting is only possible for our registered classes + if(typeid(T) != typeid(*ptr)) + { + if(!IsRegistered(demangle(typeid(*ptr).name()))) + throw std::runtime_error(std::string("Archive error: Polymorphic type ") + + demangle(typeid(*ptr).name()) + + " not registered for archive"); + reg_ptr = GetArchiveRegister(demangle(typeid(*ptr).name())).downcaster(typeid(T), ptr.get()); + // if there was a true downcast we have to store more information + if(reg_ptr != (void*) ptr.get()) + neededDowncast = true; + } + auto pos = shared_ptr2nr.find(reg_ptr); + // if not found store -1 and the pointer + if(pos == shared_ptr2nr.end()) + { + auto p = ptr.get(); + (*this) << -1; + (*this) & neededDowncast & p; + // if we did downcast we store the true type as well + if(neededDowncast) + (*this) << demangle(typeid(*ptr).name()); + shared_ptr2nr[reg_ptr] = shared_ptr_count++; + return *this; + } + // if found store the position and if it has to be downcasted and how + (*this) << pos->second << neededDowncast; + if(neededDowncast) + (*this) << demangle(typeid(*ptr).name()); + return (*this); + } + else // Input + { + int nr; + (*this) & nr; + // -2 restores a nullptr + if(nr == -2) + { + ptr = nullptr; + return *this; + } + // -1 restores a new shared ptr by restoring the inner pointer and creating a shared_ptr to it + else if (nr == -1) + { + T* p; + bool neededDowncast; + (*this) & neededDowncast & p; + ptr = std::shared_ptr(p); + // if we did downcast we need to store a shared_ptr to the true object + if(neededDowncast) + { + std::string name; + (*this) & name; + auto info = GetArchiveRegister(name); + // for this we use an aliasing constructor to create a shared pointer sharing lifetime + // with our shared ptr, but pointing to the true object + nr2shared_ptr.push_back(std::shared_ptr(std::static_pointer_cast(ptr), + info.downcaster(typeid(T), + ptr.get()))); + } + else + nr2shared_ptr.push_back(ptr); + } + else + { + auto other = nr2shared_ptr[nr]; + bool neededDowncast; + (*this) & neededDowncast; + if(neededDowncast) + { + // if there was a downcast we can expect the class to be registered (since archiving + // wouldn't have worked else) + std::string name; + (*this) & name; + auto info = GetArchiveRegister(name); + // same trick as above, create a shared ptr sharing lifetime with + // the shared_ptr in the register, but pointing to our object + ptr = std::static_pointer_cast(std::shared_ptr(other, + info.upcaster(typeid(T), + other.get()))); + } + else + ptr = std::static_pointer_cast(other); + } + } + return *this; + } + + // Archive pointers ======================================================= + template + Archive & operator& (T *& p) + { + if (Output()) + { + // if the pointer is null store -2 + if (!p) + return (*this) << -2; + void* reg_ptr = (void*)p; + if(typeid(T) != typeid(*p)) + { + if(!IsRegistered(demangle(typeid(*p).name()))) + throw std::runtime_error(std::string("Archive error: Polymorphic type ") + + demangle(typeid(*p).name()) + + " not registered for archive"); + else + reg_ptr = GetArchiveRegister(demangle(typeid(*p).name())).downcaster(typeid(T), (void*) p); + } + auto pos = ptr2nr.find(reg_ptr); + // if the pointer is not found in the map create a new entry + if (pos == ptr2nr.end()) + { + ptr2nr[reg_ptr] = ptr_count++; + if(typeid(*p) == typeid(T)) + if (std::is_constructible::value) + { + return (*this) << -1 & (*p); + } + else + throw std::runtime_error(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 + // to avoid compile time issues we allow this behaviour only for "our" classes that + // implement a void DoArchive(Archive&) member function + // To recreate the object we need to store the true type of it + if(!IsRegistered(demangle(typeid(*p).name()))) + throw std::runtime_error(std::string("Archive error: Polymorphic type ") + + demangle(typeid(*p).name()) + + " not registered for archive"); + else + return (*this) << -3 << demangle(typeid(*p).name()) & (*p); + } + } + else + { + (*this) & pos->second; + bool downcasted = !(reg_ptr == (void*) p); + // store if the class has been downcasted and the name + (*this) << downcasted << demangle(typeid(*p).name()); + } + } + else + { + int nr; + (*this) & nr; + if (nr == -2) // restore a nullptr + p = nullptr; + else if (nr == -1) // create a new pointer of standard type (no virtual or multiple inheritance,...) + { + p = constructIfPossible(); + nr2ptr.push_back(p); + (*this) & *p; + } + else if(nr == -3) // restore one of our registered classes that can have multiple inheritance,... + { + // As stated above, we want this special behaviour only for our classes that implement DoArchive + std::string name; + (*this) & name; + 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 = (T*) info.creator(typeid(T)); + // 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)); + (*this) & *p; + } + else + { + bool downcasted; + std::string name; + (*this) & downcasted & name; + if(downcasted) + { + // if the class has been downcasted we can assume it is in the register + auto info = GetArchiveRegister(name); + p = (T*) info.upcaster(typeid(T), nr2ptr[nr]); + } + else + p = (T*) nr2ptr[nr]; + } + } + return *this; + } + + // const ptr + template + Archive& operator &(const T*& t) + { + return (*this) & const_cast(t); + } + + // Write a read only variable + template + Archive & operator << (const T & t) + { + T ht(t); + (*this) & ht; + return *this; + } + + virtual void FlushBuffer() {} + + protected: + static std::map& GetLibraryVersions(); + private: + template + struct Caster + { + static void* tryUpcast (const std::type_info& ti, T* p) + { + throw std::runtime_error("Upcast not successful, some classes are not registered properly for archiving!"); + } + static void* tryDowncast (const std::type_info& ti, void* p) + { + throw std::runtime_error("Downcast not successful, some classes are not registered properly for archiving!"); + } + }; + + template + struct Caster + { + static void* tryUpcast(const std::type_info& ti, T* p) + { + try + { return GetArchiveRegister(demangle(typeid(B1).name())).upcaster(ti, (void*) (dynamic_cast(p))); } + catch(std::exception) + { return Caster::tryUpcast(ti, p); } + } + + static void* tryDowncast(const std::type_info& ti, void* p) + { + if(typeid(B1) == ti) + return dynamic_cast((B1*) p); + try + { return GetArchiveRegister(demangle(typeid(B1).name())).downcaster(ti, (void*) ((B1*)p)); } + catch(std::exception) + { return Caster::tryDowncast(ti, p); } + } + }; + }; + + template + class NGCORE_API RegisterClassForArchive + { + public: + RegisterClassForArchive() + { + static_assert(all_of_tmpl::value...>, + "Variadic template arguments must be base classes of T"); + ClassArchiveInfo info; + info.creator = [this,&info](const std::type_info& ti) -> void* + { return typeid(T) == ti ? constructIfPossible() + : Archive::Caster::tryUpcast(ti, constructIfPossible()); }; + info.upcaster = [this](const std::type_info& ti, void* p) -> void* + { return typeid(T) == ti ? p : Archive::Caster::tryUpcast(ti, (T*) p); }; + info.downcaster = [this](const std::type_info& ti, void* p) -> void* + { return typeid(T) == ti ? p : Archive::Caster::tryDowncast(ti, p); }; + Archive::SetArchiveRegister(std::string(demangle(typeid(T).name())),info); + } + + + }; + // BinaryOutArchive ====================================================================== class NGCORE_API BinaryOutArchive : public Archive { @@ -159,7 +644,7 @@ namespace ngcore (*this) & GetLibraryVersions(); } TextOutArchive (std::string filename) : - TextOutArchive(std::make_shared(filename.c_str())) { } + TextOutArchive(std::make_shared(filename)) { } const VersionInfo& getVersion(const std::string& library) { return GetLibraryVersions()[library]; } @@ -265,5 +750,6 @@ namespace ngcore return *this; } }; -} -#endif // NG_ARCHIVE_HPP +} // namespace ngcore + +#endif // NETGEN_CORE_ARCHIVE_HPP diff --git a/libsrc/core/basearchive.cpp b/libsrc/core/basearchive.cpp deleted file mode 100644 index ad0d1866..00000000 --- a/libsrc/core/basearchive.cpp +++ /dev/null @@ -1,44 +0,0 @@ - -#include "ngcore.hpp" - -#ifndef WIN -#include -#endif - -namespace ngcore -{ - static std::map library_versions; - std::map& Archive :: GetLibraryVersions() - { - return library_versions; - } - VersionInfo GetLibraryVersion(const std::string& library) - { return library_versions[library]; } - - void SetLibraryVersion(const std::string& library, VersionInfo version) - { library_versions[library] = version; } - -#ifdef WIN - // windows does demangling in typeid(T).name() - std::string demangle(const char* typeinfo) { return typeinfo; } -#else - std::string demangle(const char* typeinfo) { int status; return abi::__cxa_demangle(typeinfo, 0, 0, &status); } -#endif - - static std::map* type_register; - const ClassArchiveInfo& Archive :: GetArchiveRegister(const std::string& classname) - { - if(!type_register) type_register = new std::map(); - return (*type_register)[classname]; - } - void Archive :: SetArchiveRegister(const std::string& classname, ClassArchiveInfo info) - { - if(!type_register) type_register = new std::map(); - (*type_register)[classname] = info; - } - bool Archive :: IsRegistered(const std::string& classname) - { - if(!type_register) type_register = new std::map(); - return type_register->count(classname) != 0; - } -} diff --git a/libsrc/core/basearchive.hpp b/libsrc/core/basearchive.hpp deleted file mode 100644 index 7e1f7335..00000000 --- a/libsrc/core/basearchive.hpp +++ /dev/null @@ -1,480 +0,0 @@ -#ifndef NG_BASEARCHIVE_HPP -#define NG_BASEARCHIVE_HPP - -namespace ngcore -{ - class VersionInfo; - // Libraries using this archive can store their version here to implement backwards compatibility - VersionInfo GetLibraryVersion(const std::string& library); - void SetLibraryVersion(const std::string& library, VersionInfo version); - - class Archive; - NGCORE_API std::string demangle(const char* typeinfo); - - // create new pointer of type T if it is default constructible, else throw - template - T* constructIfPossible_impl(...) - { throw std::runtime_error(std::string(demangle(typeid(T).name())) + " is not default constructible!"); } - - template::value>::type> - T* constructIfPossible_impl(int) { return new T; } - - template - T* constructIfPossible() { return constructIfPossible_impl(int{}); } - - //Type trait to check if a class implements a 'void DoArchive(Archive&)' function - template - struct has_DoArchive - { - private: - template - static constexpr auto check(T2*) -> - typename std::is_same().DoArchive(std::declval())),void>::type; - template - static constexpr std::false_type check(...); - typedef decltype(check(0)) type; - public: - NGCORE_API static constexpr bool value = type::value; - }; - - // Check if class is archivable - template - struct is_Archivable - { - private: - template - static constexpr auto check(T2*) -> - typename std::is_same() & std::declval()),Archive&>::type; - template - static constexpr std::false_type check(...); - typedef decltype(check(nullptr)) type; - public: - NGCORE_API static constexpr bool value = type::value; - }; - - struct ClassArchiveInfo - { - // 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; - // 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; - // This caster takes a void* pointer to the (base)class type_info and returns void* pointing - // to the type stored in this info - std::function downcaster; - }; - - // Base Archive class - class NGCORE_API Archive - { - bool is_output; - // how many different shared_ptr/pointer have been (un)archived - int shared_ptr_count, ptr_count; - // maps for archived shared pointers and pointers - std::map shared_ptr2nr, ptr2nr; - // vectors for storing the unarchived (shared) pointers - std::vector> nr2shared_ptr; - std::vector nr2ptr; - - // Helper class for up-/downcasting - template - struct Caster - { - static void* tryUpcast(const std::type_info& ti, T* p); - static void* tryDowncast(const std::type_info& ti, void* p); - }; - template - friend class RegisterClassForArchive; - - // Returns ClassArchiveInfo of demangled typeid - static const ClassArchiveInfo& GetArchiveRegister(const std::string& classname); - // Set ClassArchiveInfo for demangled typeid, this is done by creating an instance of - // RegisterClassForArchive - static void SetArchiveRegister(const std::string& classname, ClassArchiveInfo info); - static bool IsRegistered(const std::string& classname); - public: - Archive (bool ais_output) : is_output(ais_output), shared_ptr_count(0), ptr_count(0) { ; } - virtual ~Archive() { ; } - - bool Output () { return is_output; } - bool Input () { return !is_output; } - virtual const VersionInfo& getVersion(const std::string& library) = 0; - - // Pure virtual functions that have to be implemented by In-/OutArchive - virtual Archive & operator & (double & d) = 0; - virtual Archive & operator & (int & i) = 0; - virtual Archive & operator & (long & i) = 0; - virtual Archive & operator & (size_t & i) = 0; - virtual Archive & operator & (short & i) = 0; - virtual Archive & operator & (unsigned char & i) = 0; - virtual Archive & operator & (bool & b) = 0; - virtual Archive & operator & (std::string & str) = 0; - virtual Archive & operator & (char *& str) = 0; - - - // Archive std classes ================================================ - template - Archive& operator & (std::complex& c) - { - if(is_output) - (*this) << c.real() << c.imag(); - else - { - T tmp; - (*this) & tmp; - c.real(tmp); - (*this) & tmp; - c.imag(tmp); - } - return (*this); - } - template - Archive& operator & (std::vector& v) - { - size_t size; - if(is_output) - size = v.size(); - (*this) & size; - if(!is_output) - v.resize(size); - Do(&v[0], size); - return (*this); - } - template - Archive& operator& (std::map& map) - { - if(is_output) - { - (*this) << size_t(map.size()); - for(auto& pair : map) - (*this) << pair.first << pair.second; - } - else - { - size_t size; - (*this) & size; - T1 key; T2 val; - for(size_t i = 0; i < size; i++) - { - T1 key; T2 val; - (*this) & key & val; - map[key] = val; - } - } - return (*this); - } - // Archive arrays ===================================================== - // this functions can be overloaded in Archive implementations for more efficiency - template ::value>::type> - Archive & Do (T * data, size_t n) - { for (size_t j = 0; j < n; j++) { (*this) & data[j]; }; return *this; }; - - virtual Archive & Do (double * d, size_t n) - { for (size_t j = 0; j < n; j++) { (*this) & d[j]; }; return *this; }; - - virtual Archive & Do (int * i, size_t n) - { for (size_t j = 0; j < n; j++) { (*this) & i[j]; }; return *this; }; - - virtual Archive & Do (long * i, size_t n) - { for (size_t j = 0; j < n; j++) { (*this) & i[j]; }; return *this; }; - - virtual Archive & Do (size_t * i, size_t n) - { for (size_t j = 0; j < n; j++) { (*this) & i[j]; }; return *this; }; - - virtual Archive & Do (short * i, size_t n) - { for (size_t j = 0; j < n; j++) { (*this) & i[j]; }; return *this; }; - - virtual Archive & Do (unsigned char * i, size_t n) - { for (size_t j = 0; j < n; j++) { (*this) & i[j]; }; return *this; }; - - virtual Archive & Do (bool * b, size_t n) - { for (size_t j = 0; j < n; j++) { (*this) & b[j]; }; return *this; }; - - // Archive a class implementing a (void DoArchive(Archive&)) method ======= - template::value>> - Archive& operator & (T& val) - { - val.DoArchive(*this); return *this; - } - - // Archive shared_ptrs ================================================= - template - Archive& operator & (std::shared_ptr& ptr) - { - if(Output()) - { - // save -2 for nullptr - if(!ptr) - return (*this) << -2; - - void* reg_ptr = ptr.get(); - bool neededDowncast = false; - // Downcasting is only possible for our registered classes - if(typeid(T) != typeid(*ptr)) - { - if(!IsRegistered(demangle(typeid(*ptr).name()))) - throw std::runtime_error(std::string("Archive error: Polymorphic type ") - + demangle(typeid(*ptr).name()) - + " not registered for archive"); - reg_ptr = GetArchiveRegister(demangle(typeid(*ptr).name())).downcaster(typeid(T), ptr.get()); - // if there was a true downcast we have to store more information - if(reg_ptr != (void*) ptr.get()) - neededDowncast = true; - } - auto pos = shared_ptr2nr.find(reg_ptr); - // if not found store -1 and the pointer - if(pos == shared_ptr2nr.end()) - { - auto p = ptr.get(); - (*this) << -1; - (*this) & neededDowncast & p; - // if we did downcast we store the true type as well - if(neededDowncast) - (*this) << demangle(typeid(*ptr).name()); - shared_ptr2nr[reg_ptr] = shared_ptr_count++; - return *this; - } - // if found store the position and if it has to be downcasted and how - (*this) << pos->second << neededDowncast; - if(neededDowncast) - (*this) << demangle(typeid(*ptr).name()); - return (*this); - } - else // Input - { - int nr; - (*this) & nr; - // -2 restores a nullptr - if(nr == -2) - { - ptr = nullptr; - return *this; - } - // -1 restores a new shared ptr by restoring the inner pointer and creating a shared_ptr to it - else if (nr == -1) - { - T* p; - bool neededDowncast; - (*this) & neededDowncast & p; - ptr = std::shared_ptr(p); - // if we did downcast we need to store a shared_ptr to the true object - if(neededDowncast) - { - std::string name; - (*this) & name; - auto info = GetArchiveRegister(name); - // for this we use an aliasing constructor to create a shared pointer sharing lifetime - // with our shared ptr, but pointing to the true object - nr2shared_ptr.push_back(std::shared_ptr(std::static_pointer_cast(ptr), - info.downcaster(typeid(T), - ptr.get()))); - } - else - nr2shared_ptr.push_back(ptr); - } - else - { - auto other = nr2shared_ptr[nr]; - bool neededDowncast; - (*this) & neededDowncast; - if(neededDowncast) - { - // if there was a downcast we can expect the class to be registered (since archiving - // wouldn't have worked else) - std::string name; - (*this) & name; - auto info = GetArchiveRegister(name); - // same trick as above, create a shared ptr sharing lifetime with - // the shared_ptr in the register, but pointing to our object - ptr = std::static_pointer_cast(std::shared_ptr(other, - info.upcaster(typeid(T), - other.get()))); - } - else - ptr = std::static_pointer_cast(other); - } - } - return *this; - } - - // Archive pointers ======================================================= - template - Archive & operator& (T *& p) - { - if (Output()) - { - // if the pointer is null store -2 - if (!p) - return (*this) << -2; - void* reg_ptr = (void*)p; - if(typeid(T) != typeid(*p)) - { - if(!IsRegistered(demangle(typeid(*p).name()))) - throw std::runtime_error(std::string("Archive error: Polymorphic type ") - + demangle(typeid(*p).name()) - + " not registered for archive"); - else - reg_ptr = GetArchiveRegister(demangle(typeid(*p).name())).downcaster(typeid(T), (void*) p); - } - auto pos = ptr2nr.find(reg_ptr); - // if the pointer is not found in the map create a new entry - if (pos == ptr2nr.end()) - { - ptr2nr[reg_ptr] = ptr_count++; - if(typeid(*p) == typeid(T)) - if (std::is_constructible::value) - { - return (*this) << -1 & (*p); - } - else - throw std::runtime_error(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 - // to avoid compile time issues we allow this behaviour only for "our" classes that - // implement a void DoArchive(Archive&) member function - // To recreate the object we need to store the true type of it - if(!IsRegistered(demangle(typeid(*p).name()))) - throw std::runtime_error(std::string("Archive error: Polymorphic type ") - + demangle(typeid(*p).name()) - + " not registered for archive"); - else - return (*this) << -3 << demangle(typeid(*p).name()) & (*p); - } - } - else - { - (*this) & pos->second; - bool downcasted = !(reg_ptr == (void*) p); - // store if the class has been downcasted and the name - (*this) << downcasted << demangle(typeid(*p).name()); - } - } - else - { - int nr; - (*this) & nr; - if (nr == -2) // restore a nullptr - p = nullptr; - else if (nr == -1) // create a new pointer of standard type (no virtual or multiple inheritance,...) - { - p = constructIfPossible(); - nr2ptr.push_back(p); - (*this) & *p; - } - else if(nr == -3) // restore one of our registered classes that can have multiple inheritance,... - { - // As stated above, we want this special behaviour only for our classes that implement DoArchive - std::string name; - (*this) & name; - 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 = (T*) info.creator(typeid(T)); - // 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)); - (*this) & *p; - } - else - { - bool downcasted; - std::string name; - (*this) & downcasted & name; - if(downcasted) - { - // if the class has been downcasted we can assume it is in the register - auto info = GetArchiveRegister(name); - p = (T*) info.upcaster(typeid(T), nr2ptr[nr]); - } - else - p = (T*) nr2ptr[nr]; - } - } - return *this; - } - - // const ptr - template - Archive& operator &(const T*& t) - { - return (*this) & const_cast(t); - } - - // Write a read only variable - template - Archive & operator << (const T & t) - { - T ht(t); - (*this) & ht; - return *this; - } - - virtual void FlushBuffer() {} - - protected: - static std::map& GetLibraryVersions(); - private: - template - struct Caster - { - static void* tryUpcast (const std::type_info& ti, T* p) - { - throw std::runtime_error("Upcast not successful, some classes are not registered properly for archiving!"); - } - static void* tryDowncast (const std::type_info& ti, void* p) - { - throw std::runtime_error("Downcast not successful, some classes are not registered properly for archiving!"); - } - }; - - template - struct Caster - { - static void* tryUpcast(const std::type_info& ti, T* p) - { - try - { return GetArchiveRegister(demangle(typeid(B1).name())).upcaster(ti, (void*) (dynamic_cast(p))); } - catch(std::exception) - { return Caster::tryUpcast(ti, p); } - } - - static void* tryDowncast(const std::type_info& ti, void* p) - { - if(typeid(B1) == ti) - return dynamic_cast((B1*) p); - try - { return GetArchiveRegister(demangle(typeid(B1).name())).downcaster(ti, (void*) ((B1*)p)); } - catch(std::exception) - { return Caster::tryDowncast(ti, p); } - } - }; - }; - - template - class NGCORE_API RegisterClassForArchive - { - public: - RegisterClassForArchive() - { - static_assert(all_of_tmpl::value...>, - "Variadic template arguments must be base classes of T"); - ClassArchiveInfo info; - info.creator = [this,&info](const std::type_info& ti) -> void* - { return typeid(T) == ti ? constructIfPossible() - : Archive::Caster::tryUpcast(ti, constructIfPossible()); }; - info.upcaster = [this](const std::type_info& ti, void* p) -> void* - { return typeid(T) == ti ? p : Archive::Caster::tryUpcast(ti, (T*) p); }; - info.downcaster = [this](const std::type_info& ti, void* p) -> void* - { return typeid(T) == ti ? p : Archive::Caster::tryDowncast(ti, p); }; - Archive::SetArchiveRegister(std::string(demangle(typeid(T).name())),info); - } - - - }; - -} - -#endif // NG_BASEARCHIVE_HPP diff --git a/libsrc/core/ngcore.hpp b/libsrc/core/ngcore.hpp index 1c364730..1c8fa591 100644 --- a/libsrc/core/ngcore.hpp +++ b/libsrc/core/ngcore.hpp @@ -1,49 +1,7 @@ -#ifndef NG_CORE_HPP -#define NG_CORE_HPP +#ifndef NETGEN_CORE_NGCORE_HPP +#define NETGEN_CORE_NGCORE_HPP -// std includes -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#ifdef WIN32 - #define NGCORE_API_EXPORT __declspec(dllexport) - #define NGCORE_API_IMPORT __declspec(dllimport) -#else - #define NGCORE_API_EXPORT - #define NGCORE_API_IMPORT -#endif - -#ifdef NGCORE_EXPORTS - #define NGCORE_API NGCORE_API_EXPORT -#else - #define NGCORE_API NGCORE_API_IMPORT -#endif - -namespace ngcore -{ -#if defined(__GNUC__) - inline bool likely (bool x) { return __builtin_expect((x), true); } - inline bool unlikely (bool x) { return __builtin_expect((x), false); } -#else - inline bool likely (bool x) { return x; } - inline bool unlikely (bool x) { return x; } -#endif -} - -// own includes -#include "type_traits.hpp" -#include "basearchive.hpp" -#include "version.hpp" #include "archive.hpp" +#include "version.hpp" -#endif // NG_CORE_HPP +#endif // NETGEN_CORE_NGCORE_HPP diff --git a/libsrc/core/ngcore_api.hpp b/libsrc/core/ngcore_api.hpp new file mode 100644 index 00000000..c08d0ac9 --- /dev/null +++ b/libsrc/core/ngcore_api.hpp @@ -0,0 +1,29 @@ +#ifndef NETGEN_CORE_NGCORE_API_HPP +#define NETGEN_CORE_NGCORE_API_HPP + +#ifdef WIN32 + #define NGCORE_API_EXPORT __declspec(dllexport) + #define NGCORE_API_IMPORT __declspec(dllimport) +#else + #define NGCORE_API_EXPORT + #define NGCORE_API_IMPORT +#endif + +#ifdef NGCORE_EXPORTS + #define NGCORE_API NGCORE_API_EXPORT +#else + #define NGCORE_API NGCORE_API_IMPORT +#endif + +namespace ngcore +{ +#if defined(__GNUC__) + inline bool likely (bool x) { return bool(__builtin_expect(long(x), 1L)); } + inline bool unlikely (bool x) { return bool(__builtin_expect(long(x), 0L)); } +#else + inline bool likely (bool x) { return x; } + inline bool unlikely (bool x) { return x; } +#endif +} // namespace ngcore + +#endif // NETGEN_CORE_NGCORE_API_HPP diff --git a/libsrc/core/type_traits.hpp b/libsrc/core/type_traits.hpp index 894a5d07..c298aedc 100644 --- a/libsrc/core/type_traits.hpp +++ b/libsrc/core/type_traits.hpp @@ -1,11 +1,13 @@ #ifndef NGS_TYPE_TRAITS_HPP #define NGS_TYPE_TRAITS_HPP +#include + namespace ngcore { template struct _BoolArray{}; template constexpr bool all_of_tmpl = std::is_same<_BoolArray, _BoolArray<(vals || true)...>>::value; -} +} // namespace ngcore #endif // NGS_TYPE_TRAITS_HPP diff --git a/libsrc/core/version.cpp b/libsrc/core/version.cpp new file mode 100644 index 00000000..7f6bac69 --- /dev/null +++ b/libsrc/core/version.cpp @@ -0,0 +1,11 @@ + +#include "archive.hpp" +#include "version.hpp" + +namespace ngcore +{ + void VersionInfo :: DoArchive(Archive& ar) + { + ar & mayor_ & minor_ & release & patch & git_hash; + } +} // namespace ngcore diff --git a/libsrc/core/version.hpp b/libsrc/core/version.hpp index f73aad23..f46e74af 100644 --- a/libsrc/core/version.hpp +++ b/libsrc/core/version.hpp @@ -1,8 +1,13 @@ -#ifndef NGS_VERSION_HPP -#define NGS_VERSION_HPP +#ifndef NETGEN_CORE_VERSION_HPP +#define NETGEN_CORE_VERSION_HPP + +#include +#include +#include "ngcore_api.hpp" namespace ngcore { + class Archive; class VersionInfo { private: @@ -78,10 +83,8 @@ namespace ngcore bool operator <=(const VersionInfo& other) const { return !((*this) > other); } bool operator >=(const VersionInfo& other) const { return !((*this) < other); } - void DoArchive(Archive& ar) - { - ar & mayor_ & minor_ & release & patch & git_hash; - } + void DoArchive(Archive& ar); }; -} -#endif // NGS_VERSION_HPP +} // namespace ngcore + +#endif // NETGEN_CORE_VERSION_HPP diff --git a/tests/catch/CMakeLists.txt b/tests/catch/CMakeLists.txt index 53cacdda..fc0ebe41 100644 --- a/tests/catch/CMakeLists.txt +++ b/tests/catch/CMakeLists.txt @@ -28,4 +28,10 @@ endmacro() add_unit_test(archive archive.cpp) add_unit_test(version version.cpp) + +if(ENABLE_CPP_CORE_GUIDELINES_CHECK) + set_target_properties(test_archive PROPERTIES CXX_CLANG_TIDY "${DO_CLANG_TIDY}") + set_target_properties(test_version PROPERTIES CXX_CLANG_TIDY "${DO_CLANG_TIDY}") +endif(ENABLE_CPP_CORE_GUIDELINES_CHECK) + endif(ENABLE_UNIT_TESTS)