forked from OctaForge/libostd
support for formatting tuples in format strings via %<contents%>
This commit is contained in:
parent
3f983afae5
commit
1e1f6d63a2
|
@ -43,6 +43,7 @@ struct Bar {
|
|||
int main() {
|
||||
std::vector<int> x = { 5, 10, 15, 20 };
|
||||
writefln("[%(%s|%)]", x);
|
||||
writefln("%s", x);
|
||||
|
||||
int y[] = { 2, 4, 8, 16, 32 };
|
||||
writefln("{ %(%s, %) }", y);
|
||||
|
@ -62,6 +63,12 @@ int main() {
|
|||
* # flag expands the element into multiple values
|
||||
*/
|
||||
writefln("{ %-#(%s: %d, %) }", m);
|
||||
/* without expansion */
|
||||
writefln("{ %(%s, %) }", m);
|
||||
|
||||
/* tuple formatting */
|
||||
std::tuple<std::string, int, float> tup{"hello world", 1337, 3.14f};
|
||||
writefln("the tuple contains %<%s, %d, %f%>.", tup);
|
||||
|
||||
/* tuple format test */
|
||||
std::tuple<int, float, char const *> xt[] = {
|
||||
|
|
|
@ -276,6 +276,7 @@ struct format_spec {
|
|||
string_range nested() const { return p_nested; }
|
||||
string_range nested_sep() const { return p_nested_sep; }
|
||||
|
||||
bool is_tuple() const { return p_is_tuple; }
|
||||
bool is_nested() const { return p_is_nested; }
|
||||
bool nested_escape() const { return p_nested_escape; }
|
||||
|
||||
|
@ -310,6 +311,7 @@ private:
|
|||
|
||||
byte p_index = 0;
|
||||
|
||||
bool p_is_tuple = false;
|
||||
bool p_is_nested = false;
|
||||
bool p_nested_escape = false;
|
||||
|
||||
|
@ -328,7 +330,7 @@ private:
|
|||
return false;
|
||||
}
|
||||
|
||||
bool read_spec_range() {
|
||||
bool read_spec_range(bool tuple = false) {
|
||||
int sflags = p_flags;
|
||||
p_nested_escape = !(sflags & FMT_FLAG_DASH);
|
||||
p_fmt.pop_front();
|
||||
|
@ -347,6 +349,7 @@ private:
|
|||
/* find delimiter or ending */
|
||||
string_range begin_delim(p_fmt);
|
||||
string_range p = find(begin_delim, '%');
|
||||
char need = tuple ? '>' : ')';
|
||||
for (; !p.empty(); p = find(p, '%')) {
|
||||
p.pop_front();
|
||||
/* escape, skip */
|
||||
|
@ -355,20 +358,26 @@ private:
|
|||
continue;
|
||||
}
|
||||
/* found end, in that case delimiter is after spec */
|
||||
if (p.front() == ')') {
|
||||
if (p.front() == need) {
|
||||
p_is_tuple = tuple;
|
||||
if (tuple) {
|
||||
p_nested = begin_inner.slice(0, &p[0] - &begin_inner[0] - 1);
|
||||
p_nested_sep = nullptr;
|
||||
} else {
|
||||
p_nested = begin_inner.slice(
|
||||
0, &begin_delim[0] - &begin_inner[0]
|
||||
);
|
||||
p_nested_sep = begin_delim.slice(
|
||||
0, &p[0] - &begin_delim[0] - 1
|
||||
);
|
||||
}
|
||||
p.pop_front();
|
||||
p_fmt = p;
|
||||
p_is_nested = true;
|
||||
return true;
|
||||
}
|
||||
/* found actual delimiter start... */
|
||||
if (p.front() == '|') {
|
||||
if ((p.front() == '|') && !tuple) {
|
||||
p_nested = begin_inner.slice(0, &p[0] - &begin_inner[0] - 1);
|
||||
p.pop_front();
|
||||
p_nested_sep = p;
|
||||
|
@ -427,9 +436,12 @@ private:
|
|||
}
|
||||
}
|
||||
|
||||
/* range/array formatting */
|
||||
if ((p_fmt.front() == '(') && (havepos || !(ndig - skipd))) {
|
||||
return read_spec_range();
|
||||
/* range/array/tuple formatting */
|
||||
if (
|
||||
((p_fmt.front() == '(') || (p_fmt.front() == '<')) &&
|
||||
(havepos || !(ndig - skipd))
|
||||
) {
|
||||
return read_spec_range(p_fmt.front() == '<');
|
||||
}
|
||||
|
||||
/* parse width */
|
||||
|
@ -810,18 +822,6 @@ private:
|
|||
}
|
||||
}
|
||||
|
||||
template<size_t I, size_t N, typename R, typename T>
|
||||
void write_tuple_val(
|
||||
R &writer, bool escape, string_range sep, T const &tup
|
||||
) const {
|
||||
format_spec sp{"%s", escape};
|
||||
sp.write_fmt(writer, std::get<I>(tup));
|
||||
if constexpr(I < (N - 1)) {
|
||||
range_put_all(writer, sep);
|
||||
write_tuple_val<I + 1, N>(writer, escape, sep, tup);
|
||||
}
|
||||
}
|
||||
|
||||
/* range writer */
|
||||
template<typename R, typename T, typename ...A>
|
||||
void write_range(
|
||||
|
@ -839,6 +839,39 @@ private:
|
|||
}
|
||||
}
|
||||
|
||||
template<size_t I, size_t N, typename R, typename T>
|
||||
void write_tuple_val(
|
||||
R &writer, bool escape, string_range sep, T const &tup
|
||||
) const {
|
||||
format_spec sp{"%s", escape};
|
||||
sp.write_fmt(writer, std::get<I>(tup));
|
||||
if constexpr(I < (N - 1)) {
|
||||
range_put_all(writer, sep);
|
||||
write_tuple_val<I + 1, N>(writer, escape, sep, tup);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename R, typename T, typename ...A>
|
||||
void write_tuple(
|
||||
R &writer, size_t idx, T const &val, A const &...args
|
||||
) {
|
||||
if (idx) {
|
||||
if constexpr(!sizeof...(A)) {
|
||||
throw format_error{"not enough format arguments"};
|
||||
} else {
|
||||
write_tuple(writer, idx - 1, args...);
|
||||
}
|
||||
} else {
|
||||
if constexpr(detail::is_tuple_like<T>) {
|
||||
std::apply([this, &writer, &val](auto const &...vals) mutable {
|
||||
this->write_fmt(writer, vals...);
|
||||
}, val);
|
||||
} else {
|
||||
throw format_error{"invalid value for tuple format"};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<typename R, typename ...A>
|
||||
void write_fmt(R &writer, A const &...args) {
|
||||
size_t argidx = 1;
|
||||
|
@ -850,10 +883,14 @@ private:
|
|||
}
|
||||
/* FIXME: figure out a better way */
|
||||
format_spec nspec(nested(), nested_escape());
|
||||
if (is_tuple()) {
|
||||
nspec.write_tuple(writer, argpos - 1, args...);
|
||||
} else {
|
||||
nspec.write_range(
|
||||
writer, argpos - 1, (flags() & FMT_FLAG_HASH),
|
||||
nested_sep(), args...
|
||||
);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (!argpos) {
|
||||
|
|
Loading…
Reference in a new issue