Line data Source code
1 1 : // Distributed under the MIT License. 2 : // See LICENSE.txt for details. 3 : 4 : /// \file 5 : /// Defines Parallel::printf for writing to stdout 6 : 7 : #pragma once 8 : 9 : #include <cstddef> 10 : #include <cstdio> 11 : #include <iomanip> 12 : #include <limits> 13 : #include <sstream> 14 : #include <string> 15 : #include <type_traits> 16 : #include <utility> 17 : #include <vector> 18 : 19 : #include "Utilities/ErrorHandling/FloatingPointExceptions.hpp" 20 : #include "Utilities/Requires.hpp" 21 : #include "Utilities/TypeTraits/IsStreamable.hpp" 22 : 23 : /// \cond 24 : namespace PUP { 25 : class er; 26 : } // namespace PUP 27 : /// \endcond 28 : 29 : #include "Parallel/Printf/Printf.decl.h" 30 : 31 : namespace Parallel { 32 : namespace detail { 33 : /*! 34 : * Fundamentals and pointers are already printable so there is no conversion 35 : * to a std::string necessary. 36 : */ 37 : template <typename T, 38 : Requires<std::is_fundamental<std::decay_t< 39 : std::remove_pointer_t<std::decay_t<T>>>>::value or 40 : std::is_pointer<T>::value or 41 : std::is_pointer<std::decay_t<T>>::value> = nullptr> 42 : inline constexpr T stream_object_to_string(T&& t) { 43 : return t; 44 : } 45 : 46 : /*! 47 : * Stream an object of type `T` into a std::stringstream and return it as a 48 : * std::string so that it is printable by calling `.c_str()` on it. 49 : * We need a 2-phase call so that the std::string doesn't go out of scope before 50 : * the C-style string is passed to printf. 51 : */ 52 : template <typename T, 53 : Requires<std::is_class<std::decay_t<T>>::value or 54 : std::is_enum<std::decay_t<T>>::value> = nullptr> 55 : inline std::string stream_object_to_string(T&& t) { 56 : using ::operator<<; 57 : static_assert(tt::is_streamable<std::stringstream, T>::value, 58 : "Cannot stream type and therefore it cannot be printed. Please " 59 : "define a stream operator for the type."); 60 : std::stringstream ss; 61 : ss << std::setprecision(std::numeric_limits<double>::digits10 + 4) 62 : << std::scientific << t; 63 : return ss.str(); 64 : } 65 : 66 : /*! 67 : * Fundamentals are already printable, so nothing to do. 68 : */ 69 : template <typename T, 70 : Requires<std::is_fundamental<std::decay_t< 71 : std::remove_pointer_t<std::decay_t<T>>>>::value> = nullptr> 72 : inline constexpr T get_printable_type(T&& t) { 73 : return t; 74 : } 75 : 76 : /*! 77 : * Get the pointer of the std::string so it can be passed to CkPrintf which 78 : * only works on fundamentals 79 : */ 80 : inline const typename std::string::value_type* get_printable_type( 81 : const std::string& t) { 82 : return t.c_str(); 83 : } 84 : 85 : void send_message(bool error, const std::vector<char>& message); 86 : void send_message_to_file(const std::string& file, 87 : const std::vector<char>& message); 88 : 89 : template <typename... Ts> 90 : inline std::vector<char> allocate_message(const char* const format, Ts&&... t) { 91 : #pragma GCC diagnostic push 92 : #pragma GCC diagnostic ignored "-Wformat-nonliteral" 93 : #pragma GCC diagnostic ignored "-Wformat-security" 94 : // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-array-to-pointer-decay) 95 : const auto length = static_cast<size_t>(snprintf(nullptr, 0, format, t...)); 96 : std::vector<char> message(length + 1); // +1 for the null byte 97 : // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-array-to-pointer-decay) 98 : snprintf(message.data(), message.size(), format, t...); 99 : #pragma GCC diagnostic pop 100 : return message; 101 : } 102 : 103 : template <typename... Args> 104 : inline std::vector<char> format_message(const std::string& format, 105 : Args&&... args) { 106 : const ScopedFpeState disable_fpes(false); 107 : return allocate_message( 108 : format.c_str(), 109 : get_printable_type(stream_object_to_string(std::forward<Args>(args)))...); 110 : } 111 : } // namespace detail 112 : 113 : /*! 114 : * \ingroup ParallelGroup 115 : * \brief Print an atomic message to stdout with C printf usage. 116 : * 117 : * Similar to Python, you can print any object that's streamable by passing it 118 : * in as an argument and using the formatter "%s". For example, 119 : * \code 120 : * std::vector<double> a{0.8, 73, 9.8}; 121 : * Parallel::printf("%s\n", a); 122 : * \endcode 123 : */ 124 : template <typename... Args> 125 1 : inline void printf(const std::string& format, Args&&... args) { 126 : detail::send_message( 127 : false, detail::format_message(format, std::forward<Args>(args)...)); 128 : } 129 : 130 : /*! 131 : * \ingroup ParallelGroup 132 : * \brief Print an atomic message to stderr with C printf usage. 133 : * 134 : * See Parallel::printf for details. 135 : */ 136 : template <typename... Args> 137 1 : inline void printf_error(const std::string& format, Args&&... args) { 138 : detail::send_message( 139 : true, detail::format_message(format, std::forward<Args>(args)...)); 140 : } 141 : 142 : /*! 143 : * \ingroup ParallelGroup 144 : * \brief Print an atomic message to a file with C printf usage. 145 : * 146 : * See Parallel::printf for details. 147 : */ 148 : template <typename... Args> 149 1 : inline void fprintf(const std::string& file, const std::string& format, 150 : Args&&... args) { 151 : detail::send_message_to_file( 152 : file, detail::format_message(format, std::forward<Args>(args)...)); 153 : } 154 : 155 : /// Chare outputting all Parallel::printf results. 156 1 : class PrinterChare : public CBase_PrinterChare { 157 : public: 158 0 : PrinterChare() = default; 159 0 : explicit PrinterChare(CkMigrateMessage* /*msg*/) {} 160 : 161 : /// Prints a message to stdout or stderr. 162 1 : static void print(bool error, const std::vector<char>& message); 163 : 164 : /// Prints a message to a file. 165 1 : static void print_to_file(const std::string& file, 166 : const std::vector<char>& message); 167 : 168 0 : static void register_with_charm(); 169 : 170 0 : void pup(PUP::er& /*p*/) override {} 171 : }; 172 : 173 : // Charm readonly variables set in Main. 174 : // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) 175 0 : extern CProxy_PrinterChare printer_chare; 176 : // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) 177 0 : extern bool printer_chare_is_set; 178 : } // namespace Parallel