auto listvals(auto const& container, const char* tail = "\n", const char* sep = ", ")
{
for (auto v = container.begin(); v != container.end(); ++v)
cout << (v == container.begin() ? "":sep)
<< *v
<< (v+1 == container.end() ? tail:"");
}
but it could just as well write to a string (and an improved one could even be nice and precalc. its length first -- and an even nicer one that also does auto-quoting, or an even nicer one that does that with multi-char quotes... :) ):
#include <string>
#include <string_view>
#include <cstring>
#include <cassert>
#define FOUND(expr) ((expr) != std::string::npos)
#define CONTAINS(str, chars) FOUND((str).find_first_of(chars))
string listvals(auto const& container, const char prewrap[] = "", const char postwrap[] = "", const char sep[] = ", ",
const char* quote = "\"", // not const char[], to hint that it accepts nullptr!
const char* scary_chars = " \t\n")
// The pre/post wrapping are optional parts that only get written if not empty,
// to support cases where callers would otherwise have to add an annoying
// `if (container.empty())` or two themselves.
{
string result;
if (!container.empty()) {
size_t QLEN = quote ? strlen(quote) : 0;
// Precalc. size... (Note: we're processing cmd args. We got time.)
size_t size = strlen(prewrap) + (container.size() - 1) * strlen(sep) + strlen(postwrap);
for (auto& v : container)
size += v.length()
+ (quote && *quote && CONTAINS(v, scary_chars) ? // add quotes...
(QLEN>1 ? QLEN:2) : 0); // special case for 1 (-> pair)!
result.reserve(size);
// Write...
result += prewrap;
for (auto v = container.begin(); v != container.end(); ++v) {
if (quote && *quote && CONTAINS(*v, scary_chars))
{ result += string_view(quote, quote + (QLEN/2 ? QLEN/2 : 1)); // special case for 1 quote!
result += *v;
result += string_view(quote + QLEN/2); }
else { result += *v; }
result += (v+1 == container.end() ? postwrap : sep);
}
//cout << "\n\n["<<result<<"]: " << "result.length() =? size: " << dec << result.length() << " vs. " << size << "\n\n";
assert(result.length() == size);
}
return result;
}
#undef FOUND
#undef CONTAINS
Just dump the current state... Well, and also quote args with spaces... and other scary chars...
mapdoesn't preserve order, so we're fucked with the named ones!... :-o (And no,ordered_mapdoesn't mean the order of insertion either!... :-/ )(A quoting example is done below, and some escaping callback lambda could also be added, too.)
-> Also, wtime does something similar, and it's super handy. See its impl. -- with escaping for
Win32::CreateProcess-- below, in another comment!<functional>to the comp. burden! :-/And then
listvals()(already there in the tests) could be added, too, reusing the same mechanics. The tests use a stream as an output:but it could just as well write to a string (and an improved one could even be nice and precalc. its length first -- and an even nicer one that also does auto-quoting, or an even nicer one that does that with multi-char quotes... :) ):
...then the args "serializer" could be as simple as (well, but still needs to write to a string, as the other!):
Could be extra useful if the named/positional accessors would drop their (pretty orthodox)
const(Drop const from named() and positional() (or have it both ways?) #55)! Then you could manipulate the arg set, and then "render" it to a new command line!