Skip to content

Add ToString function for array and tuple#4584

Merged
WeiqunZhang merged 4 commits intoAMReX-Codes:developmentfrom
AlexanderSinn:Add_ToString_function_for_array_and_tuple
Aug 10, 2025
Merged

Add ToString function for array and tuple#4584
WeiqunZhang merged 4 commits intoAMReX-Codes:developmentfrom
AlexanderSinn:Add_ToString_function_for_array_and_tuple

Conversation

@AlexanderSinn
Copy link
Copy Markdown
Member

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)

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

[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]

Additional background

Checklist

The proposed changes:

  • fix a bug or incorrect behavior in AMReX
  • add new capabilities to AMReX
  • changes answers in the test suite to more than roundoff level
  • are likely to significantly affect the results of downstream AMReX users
  • include documentation in the code and/or rst files, if appropriate

@WeiqunZhang
Copy link
Copy Markdown
Member

I am not sure how to read this <<0 | <32.42 | 1.000324>> | <4 | <12.3 | 2.65775 | 3.24>>>. Maybe we don't need to support map or maybe we need to use multiple lines for a map.

@AlexanderSinn
Copy link
Copy Markdown
Member Author

AlexanderSinn commented Aug 8, 2025

I think that one is confusing because the second type of the map is vector, and the two vectors in the map have different sizes. Here is a more typical example:

std::cout << amrex::ToString(std::map<std::string, double> {
    {"pi", 3.14159265358979323846},
    {"clight", 299'792'458.},
    {"ep0",  8.8541878188e-12},
    {"mu0", 1.2566370612685e-06}
}) << '\n';
[["clight", 2.99792e+08], ["ep0", 8.85419e-12], ["mu0", 1.25664e-06], ["pi", 3.14159]]

@WeiqunZhang
Copy link
Copy Markdown
Member

Should we treat char* as string? If it's not null terminated, it's undefined behavior. In your test, char e[] = "Test char array";, it is null terminated.

@WeiqunZhang
Copy link
Copy Markdown
Member

WeiqunZhang commented Aug 10, 2025

On the other hand, if we do not treat char* as string, it's not any better either. There could still be undefined behavior if we pass a char* instead of char[].

        char e[16];
        e[0] = 'T';
        e[1] = 'e';
        e[2] = 's';
        e[3] = 't';
        char* p = e;
        amrex::Print() << amrex::ToString(p) << '\n';
        amrex::Print() << amrex::ToString(e) << '\n';

produces

Test
[T, e, s, t, , , , , �, �, �, �, $, a, , ]

@AlexanderSinn
Copy link
Copy Markdown
Member Author

It could indeed be quite tricky to see whether something is a string or a data buffer. Given the limited information, I would suggest this:

char -> character
char[] -> string
char* -> string

signed char -> int
signed char[] -> array
signed char* -> pointer

unsigned char -> int
unsigned char[] -> array
unsigned char* -> pointer

@WeiqunZhang WeiqunZhang merged commit 4851be6 into AMReX-Codes:development Aug 10, 2025
75 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants