16concept Printable =
requires(std::ostream& os, T
a) { os <<
a; };
19concept Elemable =
requires(T elem) {
24template<
class R,
class F> std::ostream&
range(std::ostream& os,
const R& r, F f,
const char* sep =
", ") {
25 const char* cur_sep =
"";
26 for (
const auto& elem :
r) {
27 for (
auto i = cur_sep; *i !=
'\0'; ++i) os << *i;
29 if constexpr (std::is_invocable_v<
F, std::ostream&,
decltype(elem)>)
30 std::invoke(f, os, elem);
38bool match2nd(std::ostream& os,
const char* next,
const char*& s,
const char c);
82template<
class R,
class F>
struct Elem {
91std::ostream&
print(std::ostream& os,
const char* s);
93template<
class T,
class... Args> std::ostream&
print(std::ostream& os,
const char* s, T&& t, Args&&... args) {
99 if (detail::match2nd(os, next, s,
'{'))
continue;
103 while (*s !=
'\0' && *s !=
'}') spec.push_back(*s++);
104 assert(*s ==
'}' &&
"unmatched closing brace '}' in format string");
106 if constexpr (std::is_invocable_v<
decltype(t)>) {
108 }
else if constexpr (std::is_invocable_v<
decltype(t), std::ostream&>) {
110 }
else if constexpr (detail::Printable<
decltype(t)>) {
112 }
else if constexpr (detail::Elemable<
decltype(t)>) {
113 detail::range(os, t.range, t.f, spec.c_str());
114 }
else if constexpr (std::ranges::range<
decltype(t)>) {
116 os, t, [&](
const auto& x) { os << x; }, spec.c_str());
118 []<
bool flag =
false>() {
static_assert(flag,
"cannot print T t"); }
124 return print(os, s, std::forward<Args&&>(args)...);
127 if (detail::match2nd(os, next, s,
'}'))
continue;
128 assert(
false &&
"unmatched/unescaped closing brace '}' in format string");
134 assert(
false &&
"invalid format string for 's'");
139template<
class... Args> std::ostream&
println(std::ostream& os,
const char*
fmt, Args&&... args) {
140 return print(os,
fmt, std::forward<Args&&>(args)...) << std::endl;
144template<
class... Args> std::string
fmt(
const char* s, Args&&... args) {
145 std::ostringstream os;
146 print(os, s, std::forward<Args&&>(args)...);
151template<
class T = std::logic_error,
class... Args> [[noreturn]]
void error(
const char*
fmt, Args&&... args) {
152 std::ostringstream oss;
153 print(oss <<
"error: ",
fmt, std::forward<Args&&>(args)...);
158# define assertf(condition, ...) \
159 do { (void)sizeof(condition); } while (false)
161# define assertf(condition, ...) \
163 if (!(condition)) { \
164 thorin::errf("{}:{}: assertion: ", __FILE__, __LINE__); \
165 thorin::errln(__VA_ARGS__); \
176template<
class... Args> std::ostream&
outf (
const char*
fmt, Args&&... args) {
return print(std::cout,
fmt, std::forward<Args&&>(args)...); }
177template<
class... Args> std::ostream&
errf (
const char*
fmt, Args&&... args) {
return print(std::cerr,
fmt, std::forward<Args&&>(args)...); }
178template<
class... Args> std::ostream&
outln(
const char*
fmt, Args&&... args) {
return outf(
fmt, std::forward<Args&&>(args)...) << std::endl; }
179template<
class... Args> std::ostream&
errln(
const char*
fmt, Args&&... args) {
return errf(
fmt, std::forward<Args&&>(args)...) << std::endl; }
192 size_t indent()
const {
return indent_; }
193 std::string_view
tab()
const {
return tab_; }
200 template<
class... Args> std::ostream&
print(std::ostream& os,
const char* s, Args&&... args) {
201 for (
size_t i = 0; i < indent_; ++i) os << tab_;
205 template<
class... Args> std::ostream&
lnprint(std::ostream& os,
const char* s, Args&&... args) {
206 return print(os << std::endl, s, std::forward<Args>(args)...);
209 template<
class... Args> std::ostream&
println(std::ostream& os,
const char* s, Args&&... args) {
210 return print(os, s, std::forward<Args>(args)...) << std::endl;
218 [[nodiscard]]
Tab operator--(
int) { assert(indent_ > 0);
return {tab_, indent_--}; }
235 std::string_view tab_;
Keeps track of indentation level.
Tab & operator=(std::string_view tab)
std::ostream & print(std::ostream &os, const char *s, Args &&... args)
Tab operator-(size_t indent) const
Tab & operator+=(size_t indent)
Tab(std::string_view tab={" "}, size_t indent=0)
Tab & operator=(size_t indent)
std::ostream & println(std::ostream &os, const char *s, Args &&... args)
Same as Tab::print but appends a std::endl to os.
std::string_view tab() const
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 operator+(size_t indent) const
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.
void error(const Def *def, const char *fmt, Args &&... args)
std::ostream & outf(const char *fmt, Args &&... args)
std::ostream & print(std::ostream &os, const char *s)
Base case.
std::string fmt(const char *s, Args &&... args)
Wraps thorin::print to output a formatted std:string.
std::ostream & errf(const char *fmt, Args &&... args)
std::ostream & outln(const char *fmt, Args &&... args)
Use with print to output complicated std::ranges::ranges.
Elem(const R &range, const F &f)