提交 8c2334c1 创建 作者: Austin Clements's avatar Austin Clements 提交者: Silas Boyd-Wickizer

Extensible, type-safe, usable print streams

These have the type-safety and extensibility of ostreams, but the simplicity and almost the terseness of printf.
上级 94b0fcc3
#pragma once
// Extensible, type-safe, usable print streams.
//
// External users of this API should call the print or println methods
// of a print_stream. To specially format numbers, use sfmt or shex.
// To output byte buffers, use sbuf.
//
// Extensions of this API to add printable types should be implemented
// as overloads of to_stream and should call other to_stream
// functions.
//
// Output stream types should be implemented as subclasses of
// print_stream.
#include <utility>
struct sbuf
{
const char *base;
size_t len;
sbuf(const char *b, size_t l) : base(b), len(l) { }
sbuf(const sbuf &o) = default;
sbuf(sbuf &&o) = default;
};
class print_stream
{
public:
// By making this constexpr (and assuming derived classes have
// either no constructor or a constexpr constructor), print_streams
// can be fully initialized at compile time, including the vtable
// pointer (meaning global print_streams don't require static
// construction).
constexpr print_stream() : enabled(true) { }
virtual ~print_stream() { }
// Write each of the arguments to this stream in order.
template<typename... T>
void print(T&&... args)
{
if (enabled)
_print(std::forward<T>(args)...);
}
// Like print, but append a newline.
template<typename... T>
void println(T&&... args)
{
if (enabled) {
_print(std::forward<T>(args)...);
write('\n');
}
}
protected:
bool enabled;
constexpr print_stream(bool enabled) : enabled(enabled) { }
virtual void write(char c)
{
write(sbuf(&c, 1));
}
virtual void write(sbuf buf) = 0;
friend void to_stream(print_stream *s, char c);
friend void to_stream(print_stream *s, sbuf b);
private:
template<typename T1, typename... T>
void _print(T1 &&arg1, T&&... rest)
{
to_stream(this, std::forward<T1>(arg1));
_print(std::forward<T>(rest)...);
}
void _print() { }
};
class null_stream : public print_stream
{
public:
constexpr null_stream() : print_stream(false) { }
protected:
void write(char c) { }
void write(sbuf buf) { }
};
inline
void to_stream(print_stream *s, char c)
{
s->write(c);
}
inline
void to_stream(print_stream *s, sbuf b)
{
s->write(b);
}
void to_stream(print_stream *s, int v);
void to_stream(print_stream *s, unsigned v);
void to_stream(print_stream *s, long v);
void to_stream(print_stream *s, unsigned long v);
void to_stream(print_stream *s, long long v);
void to_stream(print_stream *s, unsigned long long v);
void to_stream(print_stream *s, const char *str);
void to_stream(print_stream *s, void *ptr);
class integer_formatter
{
unsigned long long val_;
int width_;
unsigned char base_;
char pad_;
bool neg_;
bool alt_;
friend void to_stream(print_stream *s, const integer_formatter &n);
public:
integer_formatter(unsigned long long v, bool neg)
: val_(v), width_(0), base_(10), pad_(' '), neg_(neg), alt_(false) { }
integer_formatter &width(int width)
{
width_ = width;
return *this;
}
integer_formatter &base(unsigned base)
{
if (base == 0 || base > 16)
base = 10;
base_ = base;
return *this;
}
integer_formatter &pad(char pad)
{
pad_ = pad;
return *this;
}
// Format the number using an alternate form. If base is 8 and the
// number is non-zero, this will prefix the output with "0". If
// base is 16 and the number of non-zero, this will prefix the
// output with "0x".
integer_formatter &alt(bool alt = true)
{
alt_ = alt;
return *this;
}
};
void to_stream(print_stream *s, const integer_formatter &n);
// Format any integral value. The default formatting is equivalent to
// passing the value directly to the print stream, but can be
// manipulated using the methods of integer_formatter.
template<typename T>
integer_formatter sfmt(T v)
{
bool neg = v < 0;
if (neg)
v = -v;
return integer_formatter(v, neg);
}
// Format v in hexadecimal and, if non-zero, preceded by an 0x.
template<typename T>
integer_formatter shex(T v)
{
return sfmt(v).base(16).alt();
}
......@@ -2,7 +2,8 @@ $(O)/lib/%.o: CFLAGS:=$(CFLAGS) -DXV6_USER
$(O)/lib/%.o: CXXFLAGS:=$(CXXFLAGS) -DXV6_USER -fno-exceptions -fno-rtti
ULIB = ulib.o printf.o umalloc.o uthread.o fmt.o stream.o ipc.o \
threads.o crt.o wqlib.o wquser.o perf.o wqalloc.o sysstubs.o
threads.o crt.o wqlib.o wquser.o perf.o wqalloc.o sysstubs.o \
pstream.o
ULIB := $(addprefix $(O)/lib/, $(ULIB))
$(O)/lib/sysstubs.S: tools/syscalls.py kernel/*.cc
......
#include "pstream.hh"
#include "user.h" // For strlen
static void
streamnum (print_stream *s, unsigned long long num,
bool neg = false, unsigned base = 10, int width = 0, char pad = 0,
bool alt = false)
{
char buf[68], *x = buf + sizeof(buf);
if (num == 0)
*--x = '0';
else {
for (; num; num /= base)
*--x = "0123456789abcdef"[num % base];
if (alt) {
if (base == 16) {
*--x = 'x';
*--x = '0';
} else if (base == 8) {
*--x = '0';
}
}
if (neg)
*--x = '-';
}
size_t len = buf + sizeof(buf) - x;
for (; width > len; width--)
to_stream(s, pad);
to_stream(s, sbuf(x, len));
for (; width < 0; width++)
to_stream(s, pad);
}
#define INT_TO_STREAM(typ) \
void to_stream(print_stream *s, typ v) \
{ \
streamnum(s, v); \
} \
\
void to_stream(print_stream *s, unsigned typ v) \
{ \
if (v < 0) \
streamnum(s, -v, true); \
else \
streamnum(s, v, false); \
} \
static_assert(true, "need a semicolon")
INT_TO_STREAM(int);
INT_TO_STREAM(long);
INT_TO_STREAM(long long);
void to_stream(print_stream *s, const char *str)
{
to_stream(s, sbuf(str, strlen(str)));
}
void to_stream(print_stream *s, void *ptr)
{
to_stream(s, sbuf("0x", 2));
streamnum(s, (unsigned long long)ptr, false, 16);
}
void to_stream(print_stream *s, const integer_formatter &n)
{
streamnum(s, n.val_, n.neg_, n.base_, n.width_, n.pad_, n.alt_);
}
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论