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
|