Skip to content

Commit 4851be6

Browse files
Add ToString function for array and tuple (#4584)
## Summary This PR adds a `ToString` function that can be used with scalars, arrays and tuples to write error messages or when debugging. TODO: write a doc comment for `ToString` It might be useful to add compatibility for BaseFab and PODVector with automatic dtoh memcpy. Example (https://godbolt.org/z/vsn6Krbsj) ```C++ std::vector v{4., 7., 23., 0.000001, 7.123456789123456789, 8., 3., 0.0000011}; std::tuple t{443, 0.234423, 244.4f, "Test tuple"}; std::array a{4., 7., 23., 0.000001, 7.12345, 8., 3., 0.0000011}; std::map<int, std::vector<double>> m{{0, {32.42, 1.000324}}, {4, {12.3, 2.65775, 3.24}}}; int b[]{2, 3, 65, 3, 6, 54}; const char* c = "Test char *"; char e[] = "Test char array"; std::cout << amrex::ToString(v) << '\n'; std::cout << amrex::ToString(t, "[", ",", "]", "'", 100,std::ostringstream{} << std::setprecision(4)) << '\n'; std::cout << amrex::ToString(a, "{", " ; ", "}", "\"", 3, std::ostringstream{} << std::setprecision(10)) << '\n'; std::cout << amrex::ToString(m, "<", " | ", ">", "\"", 100, std::ostringstream{} << std::setprecision(10))<< '\n'; std::cout << amrex::ToString(b) << '\n'; std::cout << amrex::ToString(c) << '\n'; std::cout << amrex::ToString(e) << '\n'; std::cout << amrex::ToString("Test direct string") << '\n'; std::cout << amrex::ToString(std::string("Test string")) << '\n'; std::cout << amrex::ToString(std::array{"awdawd", "43tf4", "awd4t45"}) << '\n'; std::cout << amrex::ToString(std::array{'t', 'e', 's', 't'}) << '\n'; std::cout << '\n'; amrex::ToString(std::cout, v) << '\n'; amrex::ToString(std::cout << std::setprecision(4), t, "[", ",", "]", "'", 100) << '\n'; amrex::ToString(std::cout << std::setprecision(10), a, "{", " ; ", "}", "\"", 3) << '\n'; amrex::ToString(std::cout << std::setprecision(10), m, "<", " | ", ">", "\"", 100) << '\n'; amrex::ToString(std::cout, b) << '\n'; amrex::ToString(std::cout, c) << '\n'; amrex::ToString(std::cout, e) << '\n'; amrex::ToString(std::cout, "Test direct string") << '\n'; amrex::ToString(std::cout, std::string("Test string")) << '\n'; amrex::ToString(std::cout, std::array{"awdawd", "43tf4", "awd4t45"}) << '\n'; amrex::ToString(std::cout, std::array{'t', 'e', 's', 't'}) << '\n'; ``` Prints ```python [4, 7, 23, 1e-06, 7.12345, 8, 3, 1.1e-06] [443,0.2344,244.4,'Test tuple'] {4 ; 7 ; 23 ; ...} <<0 | <32.42 | 1.000324>> | <4 | <12.3 | 2.65775 | 3.24>>> [2, 3, 65, 3, 6, 54] "Test char *" "Test char array" "Test direct string" "Test string" ["awdawd", "43tf4", "awd4t45"] [t, e, s, t] [4, 7, 23, 1e-06, 7.12345, 8, 3, 1.1e-06] [443,0.2344,244.4,'Test tuple'] {4 ; 7 ; 23 ; ...} <<0 | <32.42 | 1.000324>> | <4 | <12.3 | 2.65775 | 3.24>>> [2, 3, 65, 3, 6, 54] "Test char *" "Test char array" "Test direct string" "Test string" ["awdawd", "43tf4", "awd4t45"] [t, e, s, t] ```
1 parent 8cfb8b6 commit 4851be6

File tree

1 file changed

+127
-0
lines changed

1 file changed

+127
-0
lines changed

Src/Base/AMReX_Utility.H

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,14 @@
2424
#include <iostream>
2525
#include <limits>
2626
#include <map>
27+
#include <ostream>
2728
#include <sstream>
2829
#include <string>
30+
#include <string_view>
31+
#include <tuple>
2932
#include <typeinfo>
3033
#include <type_traits>
34+
#include <utility>
3135

3236
namespace amrex
3337
{
@@ -220,6 +224,24 @@ namespace amrex
220224
template<typename T> void hash_combine (uint64_t & seed, const T & val) noexcept;
221225
template<typename T> uint64_t hash_vector (const Vector<T> & vec, uint64_t seed = 0xDEADBEEFDEADBEEF) noexcept;
222226

227+
template <class T>
228+
std::ostream& ToString(std::ostream& os,
229+
const T& t,
230+
const char* symbol_begin = "[",
231+
const char* symbol_delim = ", ",
232+
const char* symbol_end = "]",
233+
const char* symbol_str = "\"",
234+
int limit = 100);
235+
236+
template <class T>
237+
std::string ToString(const T& t,
238+
const char* symbol_begin = "[",
239+
const char* symbol_delim = ", ",
240+
const char* symbol_end = "]",
241+
const char* symbol_str = "\"",
242+
int limit = 100,
243+
std::ostringstream ss = {});
244+
223245
}
224246

225247
template <typename T>
@@ -380,4 +402,109 @@ amrex::hash_vector (const Vector<T> & vec, uint64_t seed) noexcept
380402
return seed;
381403
}
382404

405+
406+
namespace amrex::detail {
407+
408+
// Determine if type has .begin() and .end() member functions
409+
template <class T>
410+
constexpr decltype((std::declval<T>().begin(), std::declval<T>().end(), true)) HasBeginEnd() {
411+
return true;
412+
}
413+
template <class T, class... Args>
414+
constexpr bool HasBeginEnd(Args...) {
415+
return false;
416+
}
417+
418+
// Determine if type has a specialization for std::tuple_size
419+
template <class T>
420+
constexpr decltype((std::tuple_size<T>::value, true)) HasTupleSize() {
421+
return true;
422+
}
423+
template <class T, class... Args>
424+
constexpr bool HasTupleSize(Args...) {
425+
return false;
426+
}
427+
428+
// Helper for tuple unpacking
429+
template <class T, std::size_t... idx>
430+
void ToStringTupleImp(std::index_sequence<idx...>, std::ostream& os, const T& t,
431+
const char* symbol_begin, const char* symbol_delim,
432+
const char* symbol_end, const char* symbol_str, int limit) {
433+
os << symbol_begin;
434+
Long count = 0;
435+
auto op = [&](auto& value) {
436+
if (count > 0 && (count <= limit || limit < 0)) {
437+
os << symbol_delim;
438+
}
439+
if (count < limit || limit < 0) {
440+
ToString(os, value, symbol_begin, symbol_delim, symbol_end,
441+
symbol_str, limit);
442+
} else if (count == limit) {
443+
os << "...";
444+
}
445+
++count;
446+
};
447+
(op(std::get<idx>(t)), ...);
448+
os << symbol_end;
449+
}
450+
}
451+
452+
template <class T>
453+
std::ostream& amrex::ToString(std::ostream& os, const T& t, const char* symbol_begin,
454+
const char* symbol_delim, const char* symbol_end,
455+
const char* symbol_str, int limit)
456+
{
457+
if constexpr (std::is_same_v<std::string, T> ||
458+
std::is_same_v<std::string_view, T> ||
459+
std::is_same_v<char*, std::decay_t<T>> ||
460+
std::is_same_v<const char*, T>) {
461+
// String-like types
462+
os << symbol_str << t << symbol_str;
463+
} else if constexpr (std::is_same_v<signed char, T> ||
464+
std::is_same_v<unsigned char, T>) {
465+
// Buffer-like types, don't print as char
466+
os << static_cast<int>(t);
467+
} else if constexpr (std::is_pointer_v<T>) {
468+
// Pointer types, don't print char* as string
469+
os << static_cast<const void*>(t);
470+
} else if constexpr (detail::HasBeginEnd<T>() || std::is_array_v<T>) {
471+
// Array-like types
472+
os << symbol_begin;
473+
Long count = 0;
474+
for (auto& value : t) {
475+
if (count > 0) {
476+
os << symbol_delim;
477+
}
478+
if (count < limit || limit < 0) {
479+
ToString(os, value, symbol_begin, symbol_delim, symbol_end,
480+
symbol_str, limit);
481+
} else if (count == limit) {
482+
os << "...";
483+
break;
484+
}
485+
++count;
486+
}
487+
os << symbol_end;
488+
} else if constexpr (detail::HasTupleSize<T>()) {
489+
// Tuple-like types
490+
detail::ToStringTupleImp(std::make_index_sequence<std::tuple_size_v<T>>{},
491+
os, t, symbol_begin, symbol_delim, symbol_end,
492+
symbol_str, limit);
493+
} else {
494+
// Scalar types
495+
os << t;
496+
}
497+
return os;
498+
}
499+
500+
template <class T>
501+
std::string amrex::ToString(const T& t, const char* symbol_begin, const char* symbol_delim,
502+
const char* symbol_end, const char* symbol_str,
503+
int limit, std::ostringstream ss)
504+
{
505+
ToString(ss, t, symbol_begin, symbol_delim, symbol_end, symbol_str, limit);
506+
return ss.str();
507+
}
508+
509+
383510
#endif /*BL_UTILITY_H*/

0 commit comments

Comments
 (0)