Line data Source code
1 1 : // Distributed under the MIT License. 2 : // See LICENSE.txt for details. 3 : 4 : /// \file 5 : /// Functions for serializing factory-created classes 6 : 7 : #pragma once 8 : 9 : #include <boost/algorithm/string.hpp> 10 : #include <pup.h> 11 : #include <typeinfo> 12 : 13 : #include "Utilities/PrettyType.hpp" 14 : #include "Utilities/Serialization/CharmPupable.hpp" 15 : #include "Utilities/TMPL.hpp" 16 : 17 : /*! 18 : * \brief String representation of a type that is somewhat stable 19 : * 20 : * This string representation is used to identify the type in serialized data, 21 : * including data written to disk. Therefore, the registration name must be 22 : * somewhat reliable across invocations of the same program, across different 23 : * compilers, and across different executables. C++ provides no standard tool 24 : * for this. In particular, the name returned by `typeid().name()` explicitly 25 : * provides no guarantees, though for GCC and Clang it returns the mangled name 26 : * which should be ABI-stable. See docs: 27 : * https://en.cppreference.com/w/cpp/types/type_info/name 28 : * 29 : * To obtain a somewhat reliable string representation we use 30 : * `pretty_type::get_name` (which actually demangles the `typeid().name()`). It 31 : * also doesn't make any guarantees, but at least it gives a human-readable 32 : * string representation close to the actual source code. We remove whitespaces 33 : * from the name because we found inconsistent whitespace on different machines, 34 : * likely because the compiler's internal demangling routine got updated: 35 : * https://github.com/sxs-collaboration/spectre/issues/4944 36 : * 37 : * To improve reliability further in the future we may call a dedicated function 38 : * on `T`, such as `static std::string registration_name()`, which is expected 39 : * to return a unique name (including any template parameters etc). However, 40 : * adding this requirement to all PUP::able classes requires quite a lot of code 41 : * on our part (but is ultimately the safest option). 42 : * 43 : * \warning Renaming classes or namespaces will change the registration name and 44 : * hence break compatibility with data written by older versions of the code, as 45 : * does changing the implementation of this function. This means we can't 46 : * deserialize coordinate maps written in H5 files for interpolation of volume 47 : * data. 48 : */ 49 : template <typename T> 50 1 : std::string registration_name() { 51 : std::string result = pretty_type::get_name<T>(); 52 : boost::algorithm::erase_all(result, " "); 53 : return result; 54 : } 55 : 56 : /// Register specified classes. This function can either take classes 57 : /// to register as template arguments or take a `tmpl::list` of 58 : /// classes as a function argument. 59 : template <typename... Registrants> 60 1 : void register_classes_with_charm( 61 : const tmpl::list<Registrants...> /*meta*/ = {}) { 62 : const auto helper = [](auto class_v) { 63 : using class_to_register = typename decltype(class_v)::type; 64 : // We use PUPable_reg2 because this takes as a second argument the name of 65 : // the class (as a `const char*`), while PUPable_reg converts the argument 66 : // verbatim to a string using the `#` preprocessor operator. 67 : PUPable_reg2(class_to_register, 68 : registration_name<class_to_register>().c_str()); 69 : }; 70 : (void)helper; 71 : EXPAND_PACK_LEFT_TO_RIGHT(helper(tmpl::type_<Registrants>{})); 72 : } 73 : 74 : /// Register derived classes of the `Base` class 75 : template <typename Base> 76 1 : void register_derived_classes_with_charm() { 77 : register_classes_with_charm(typename Base::creatable_classes{}); 78 : } 79 : 80 : /// Register all classes in Metavariables::factory_classes 81 : template <typename Metavariables> 82 1 : void register_factory_classes_with_charm() { 83 : register_classes_with_charm( 84 : tmpl::filter< 85 : tmpl::flatten<tmpl::values_as_sequence< 86 : typename Metavariables::factory_creation::factory_classes>>, 87 : std::is_base_of<PUP::able, tmpl::_1>>{}); 88 : }