17concept Streamable =
requires(std::ostream& os, T
a) { os <<
a; };
21concept Elemable =
requires(T elem) {
26template<
class R,
class F>
27std::ostream& range(std::ostream& os,
const R& r, F f,
const char* sep =
", ") {
28 for (
const char* curr_sep =
"";
const auto& elem :
r) {
29 for (
auto i = curr_sep; *i !=
'\0'; ++i)
32 if constexpr (std::is_invocable_v<
F, std::ostream&,
decltype(elem)>)
33 std::invoke(f, os, elem);
41bool match2nd(std::ostream& os,
const char* next,
const char*& s,
const char c);
85template<
class R,
class F>
97auto elems(std::ostream& os,
const auto& range) {
98 return Elem(range, [&os](
const auto& elem) { os << T(elem); });
111std::ostream&
print(std::ostream& os,
const char* s);
113template<
class T,
class... Args>
114std::ostream&
print(std::ostream& os,
const char* s, T&& t, Args&&... args) {
120 if (detail::match2nd(os, next, s,
'{'))
continue;
124 while (*s !=
'\0' && *s !=
'}')
125 spec.push_back(*s++);
126 assert(*s ==
'}' &&
"unmatched closing brace '}' in format string");
128 if constexpr (std::is_invocable_v<
decltype(t)>) {
130 }
else if constexpr (std::is_invocable_v<
decltype(t), std::ostream&>) {
132 }
else if constexpr (detail::Streamable<
decltype(t)>) {
133 auto flags = std::ios_base::fmtflags(os.flags());
136 assert(
false &&
"TODO");
137 else if (spec ==
"o")
139 else if (spec ==
"d")
141 else if (spec ==
"x")
146 }
else if constexpr (detail::Elemable<
decltype(t)>) {
147 detail::range(os, t.range, t.f, spec.c_str());
148 }
else if constexpr (std::ranges::range<
decltype(t)>) {
149 detail::range(os, t, [&](
const auto& x) { os << x; }, spec.c_str());
151 []<
bool flag =
false>() {
static_assert(flag,
"cannot print T t"); }();
156 return print(os, s, std::forward<Args>(args)...);
159 if (detail::match2nd(os, next, s,
'}'))
continue;
160 assert(
false &&
"unmatched/unescaped closing brace '}' in format string");
166 assert(
false &&
"invalid format string for 's'");
171template<
class... Args>
172std::ostream&
println(std::ostream& os,
const char*
fmt, Args&&... args) {
173 return print(os,
fmt, std::forward<Args>(args)...) << std::endl;
177template<
class... Args>
178std::string
fmt(
const char* s, Args&&... args) {
179 std::ostringstream os;
180 print(os, s, std::forward<Args>(args)...);
185template<
class T = std::logic_error,
class... Args>
186[[noreturn]]
void error(
const char*
fmt, Args&&... args) {
187 std::ostringstream oss;
188 print(oss <<
"error: ",
fmt, std::forward<Args>(args)...);
193# define assertf(condition, ...) \
195 (void)sizeof(condition); \
198# define assertf(condition, ...) \
200 if (!(condition)) { \
201 mim::errf("{}:{}: assertion: ", __FILE__, __LINE__); \
202 mim::errln(__VA_ARGS__); \
213template<
class... Args> std::ostream&
outf (
const char*
fmt, Args&&... args) {
return print(std::cout,
fmt, std::forward<Args>(args)...); }
214template<
class... Args> std::ostream&
errf (
const char*
fmt, Args&&... args) {
return print(std::cerr,
fmt, std::forward<Args>(args)...); }
215template<
class... Args> std::ostream&
outln(
const char*
fmt, Args&&... args) {
return outf(
fmt, std::forward<Args>(args)...) << std::endl; }
216template<
class... Args> std::ostream&
errln(
const char*
fmt, Args&&... args) {
return errf(
fmt, std::forward<Args>(args)...) << std::endl; }
229 size_t indent()
const {
return indent_; }
230 std::string_view
tab()
const {
return tab_; }
237 template<
class... Args>
238 std::ostream&
print(std::ostream& os,
const char* s, Args&&... args) {
239 for (
size_t i = 0; i < indent_; ++i)
241 return mim::print(os, s, std::forward<Args>(args)...);
244 template<
class... Args>
245 std::ostream&
lnprint(std::ostream& os,
const char* s, Args&&... args) {
246 return print(os << std::endl, s, std::forward<Args>(args)...);
249 template<
class... Args>
250 std::ostream&
println(std::ostream& os,
const char* s, Args&&... args) {
251 return print(os, s, std::forward<Args>(args)...) << std::endl;
259 [[nodiscard]]
Tab operator--(
int) { assert(indent_ > 0);
return {tab_, indent_--}; }
276 std::string_view tab_;
Tab operator-(size_t indent) const
Tab & operator=(size_t indent)
std::string_view tab() const
std::ostream & println(std::ostream &os, const char *s, Args &&... args)
Same as Tab::print but appends a std::endl to os.
Tab & operator=(std::string_view tab)
Tab & operator-=(size_t indent)
std::ostream & lnprint(std::ostream &os, const char *s, Args &&... args)
Same as Tab::print but prepends a std::endl to os.
Tab & operator+=(size_t indent)
Tab(std::string_view tab={" "}, size_t indent=0)
Tab operator+(size_t indent) const
std::ostream & print(std::ostream &os, const char *s, Args &&... args)
std::ostream & print(std::ostream &os, const char *s)
Base case.
std::string fmt(const char *s, Args &&... args)
Wraps mim::print to output a formatted std:string.
void error(Loc loc, const char *f, Args &&... args)
std::ostream & errf(const char *fmt, Args &&... args)
auto elems(std::ostream &os, const auto &range)
Wrap all elements in range through os << T(elem).
StreamFn(F) -> StreamFn< F >
std::ostream & outf(const char *fmt, Args &&... args)
std::ostream & outln(const char *fmt, Args &&... args)
std::ostream & errln(const char *fmt, Args &&... args)
std::ostream & println(std::ostream &os, const char *fmt, Args &&... args)
As above but end with std::endl.
Use with print to output complicated std::ranges::ranges.
Elem(const R &range, const F &f)
Create function-wrapper objects amenable for opeartor<<.
friend std::ostream & operator<<(std::ostream &os, const StreamFn &s)