forked from OctaForge/libostd
use a sink for to_string methods on objects (no extra allocs)
This commit is contained in:
parent
9446470f3c
commit
ad2d69e83d
|
@ -595,29 +595,6 @@ namespace detail {
|
|||
return ret;
|
||||
}
|
||||
|
||||
template<typename R>
|
||||
struct FmtWriteRange: OutputRange<FmtWriteRange<R>, char> {
|
||||
FmtWriteRange() = delete;
|
||||
FmtWriteRange(R &out): p_out(out), p_written(0) {}
|
||||
bool put(char v) {
|
||||
bool ret = p_out.put(v);
|
||||
p_written += ret;
|
||||
return ret;
|
||||
}
|
||||
Size put_n(const char *v, Size n) {
|
||||
Size ret = p_out.put_n(v, n);
|
||||
p_written += ret;
|
||||
return ret;
|
||||
}
|
||||
Size put_string(ConstCharRange r) {
|
||||
return put_n(&r[0], r.size());
|
||||
}
|
||||
Size get_written() const { return p_written; }
|
||||
private:
|
||||
R &p_out;
|
||||
Size p_written;
|
||||
};
|
||||
|
||||
template<typename T, typename R>
|
||||
static True test_tofmt(decltype(to_format(declval<const T &>(),
|
||||
declval<R &>(),
|
||||
|
@ -768,7 +745,7 @@ namespace detail {
|
|||
!IsArithmetic<T>::value &&
|
||||
!IsConstructible<ConstCharRange, const T &>::value &&
|
||||
FmtTostrTest<T>::value &&
|
||||
!FmtTofmtTest<T, FmtWriteRange<R>>::value, bool
|
||||
!FmtTofmtTest<T, TostrRange<R>>::value, bool
|
||||
> = true) {
|
||||
if (this->spec() != 's') {
|
||||
assert(false && "custom objects need '%s' format");
|
||||
|
@ -780,9 +757,9 @@ namespace detail {
|
|||
/* custom format case */
|
||||
template<typename R, typename T>
|
||||
Ptrdiff write(R &writer, bool, const T &val,
|
||||
EnableIf<FmtTofmtTest<T, FmtWriteRange<R>>::value, bool
|
||||
EnableIf<FmtTofmtTest<T, TostrRange<R>>::value, bool
|
||||
> = true) {
|
||||
FmtWriteRange<R> sink(writer);
|
||||
TostrRange<R> sink(writer);
|
||||
if (!to_format(val, sink, *this)) return -1;
|
||||
return sink.get_written();
|
||||
}
|
||||
|
@ -793,7 +770,7 @@ namespace detail {
|
|||
!IsArithmetic<T>::value &&
|
||||
!IsConstructible<ConstCharRange, const T &>::value &&
|
||||
!FmtTostrTest<T>::value &&
|
||||
!FmtTofmtTest<T, FmtWriteRange<R>>::value, bool
|
||||
!FmtTofmtTest<T, TostrRange<R>>::value, bool
|
||||
> = true) {
|
||||
assert(false && "value cannot be formatted");
|
||||
return -1;
|
||||
|
|
|
@ -288,6 +288,7 @@ public:
|
|||
}
|
||||
|
||||
~StringBase() {
|
||||
if (!p_cap) return;
|
||||
allocator_deallocate(p_buf.second(), p_buf.first(), p_cap + 1);
|
||||
}
|
||||
|
||||
|
@ -686,14 +687,42 @@ String concat(std::initializer_list<T> v, ConstCharRange sep = " ") {
|
|||
}
|
||||
|
||||
namespace detail {
|
||||
template<typename T>
|
||||
template<typename R>
|
||||
struct TostrRange: OutputRange<TostrRange<R>, char> {
|
||||
TostrRange() = delete;
|
||||
TostrRange(R &out): p_out(out), p_written(0) {}
|
||||
bool put(char v) {
|
||||
bool ret = p_out.put(v);
|
||||
p_written += ret;
|
||||
return ret;
|
||||
}
|
||||
Size put_n(const char *v, Size n) {
|
||||
Size ret = p_out.put_n(v, n);
|
||||
p_written += ret;
|
||||
return ret;
|
||||
}
|
||||
Size put_string(ConstCharRange r) {
|
||||
return put_n(&r[0], r.size());
|
||||
}
|
||||
Size get_written() const { return p_written; }
|
||||
private:
|
||||
R &p_out;
|
||||
Size p_written;
|
||||
};
|
||||
|
||||
template<typename T, typename R>
|
||||
auto test_tostring(int) ->
|
||||
decltype(IsSame<decltype(declval<T>().to_string()), String>());
|
||||
template<typename>
|
||||
|
||||
template<typename T, typename R>
|
||||
static True test_tostring(decltype(declval<const T &>().to_string
|
||||
(declval<R &>())) *);
|
||||
|
||||
template<typename, typename>
|
||||
False test_tostring(...);
|
||||
|
||||
template<typename T>
|
||||
using ToStringTest = decltype(test_tostring<T>(0));
|
||||
template<typename T, typename R>
|
||||
using ToStringTest = decltype(test_tostring<T, R>(0));
|
||||
|
||||
template<typename T>
|
||||
True test_iterable(decltype(ostd::iter(declval<T>())) *);
|
||||
|
@ -724,12 +753,17 @@ struct ToString<T, EnableIf<detail::IterableTest<T>::value>> {
|
|||
};
|
||||
|
||||
template<typename T>
|
||||
struct ToString<T, EnableIf<detail::ToStringTest<T>::value>> {
|
||||
struct ToString<T, EnableIf<
|
||||
detail::ToStringTest<T, detail::TostrRange<AppenderRange<String>>>::value
|
||||
>> {
|
||||
using Argument = RemoveCv<RemoveReference<T>>;
|
||||
using Result = String;
|
||||
|
||||
String operator()(const T &v) const {
|
||||
return v.to_string();
|
||||
auto app = appender<String>();
|
||||
detail::TostrRange<AppenderRange<String>> sink(app);
|
||||
if (!v.to_string(sink)) return String();
|
||||
return move(app.get());
|
||||
}
|
||||
};
|
||||
|
||||
|
|
Loading…
Reference in a new issue