use a sink for to_string methods on objects (no extra allocs)

master
Daniel Kolesa 2015-07-23 00:40:07 +01:00
parent 9446470f3c
commit ad2d69e83d
2 changed files with 44 additions and 33 deletions

View File

@ -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;

View File

@ -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());
}
};