-
Notifications
You must be signed in to change notification settings - Fork 2
writer_base
The xlang::text::writer_base<T>
template structure is defined in text_writer.h
and implements simple buffered text output.
All of the types reside in the xlang::text
namespace.
The writer_base
buffers text until explicitly flushed, either to the console or to a file. If you fail to flush the writer_base
, then the output is discarded.
It supports two types of output (which we will call "plain" and "code") and two output buffers.
The writer_base
does not impose any semantics for "plain" or "code" output, but the intention is that "plain" output is similar to what you get with printf
, whereas "code" output performs transformations appropriate for emitting code.
To customize plain output, override or overload the write
method. To customize code output, override or overload the write_code
method.
The writer_base
struct provides only one type of write_code
: It accepts a std::string_view
and prints it as plain output.
To override write
or write_code
, simply implement them in your derived class.
To overload write
, import the methods from the base class via the using
declaration, and then add your own overloads.
The write_code
method cannot be overloaded.
struct my_writer : xlang::text::writer_base<my_writer>
{
// Inherit all write() methods from writer_base.
using writer_base::write;
// Add one more for our custom type.
void write(custom_type custom);
// Override write_code for custom code generation.
void write_code(std::string_view value);
};
The writer_base
actually consists of two buffers, known as first
and second
. Output is appended to the first
buffer, but you can swap
the two buffers. When the contents of the writer are flushed, the first
buffer is written first, followed by the second
buffer.
The two buffers can be used if you discover information in an order different from the order you need to output it.
Note that output always goes to first
. If you want to write to second
, you need to swap, write, and then swap back.
writer w;
w.write("// Forward declarations\n");
for (auto const item& : items)
{
// write the forward declaration to the header.
w.write("struct @;\n", item.name);
// write the implementation to the footer.
w.swap();
generate_definition(w, item);
w.swap();
}
writer_base();
It can throw due to low memory.
template<typename... Args>
write(std::string_view const& format_string, Args const&... args);
Produces plain output. The following special characters are recognized in the format_string
:
Character | Meaning |
---|---|
% |
Take the next variadic argument and write it. |
@ |
Take the next variadic argument (which must be convertible to std::string_view ) and write_code it. |
^ |
Take the next character and print it literally (escape). May not be the last character of the format string. |
Behavior is undefined if the number of insertions does not equal the number of extra arguments.
void write(std::string_view const& value);
void write(char const value);
void write(int32_t const value);
void write(int64_t const value);
void write(uint64_t const value);
void write(F const& f); // where F is a functor
Produces plain output.
In the last case, you can pass anything that supports function calls, and it will be called with the derived class as its parameter. For example,
struct my_writer : xlang::text::writer_base<my_writer>
{
};
my_writer w;
w.write("a%b%c%d", 1, [](my_writer& w) { w.flush_to_console(); }, 2);
The above example writes a1b
to the writer's buffer, and then flushes the writer's buffers to the console, and then writes c2d
to the writer's buffer.
The functor typically writes a complex data structure to the buffer. A class can implement operator()(writer&)
to make itself self-printing. See How to make something writable for more details.
template<typename... Args>
void write_printf(char const* format, Args const&... args);
Produces plain output by calling sprintf
with the specified format string and and arguments and writing the result.
If result of the sprintf
exceeds 128 characters, the behavior is undefined. (Will crash in practice.)
template<auto F, typename List, typename... Args>
void write_each(List const& list, Args const&... args);
The explicit template parameter F
is a function pointer. The function is called as follows, once for each item in the list:
F(writer, item, args...);
Example:
void write_add(my_writer& w, int i, const int extra)
{
w.write(i + extra);
}
struct my_writer : xlang::text::writer_base<my_writer>
{
};
std::vector<int> v{ 1, 2, 3 };
my_writer w;
w.write_each<write_add>(v, 1); // writes "234"
void write_code(std::string_view const& value);
Produces code output. The default implementation treats code output and plain output identically.
template<typename... Args>
std::string write_temp(std::string_view const& format_string, Args const&... args);
This produces plain output but captures the result in a std::string
instead of appending it to the buffer.
Implementation note: This method is non-const
because it is implemented by generating the output to the buffer, then resetting the buffer back to its original contents.
void write_impl(std::string_view const& value);
void write_impl(char const value);
Appends the string or character to the output buffer. You are not expected to call this directly.
Override these methods to customize output further. For example, the python type writer uses it to apply indentation. Note that if you do override this method, you will probably need to override write_temp
so its output doesn't get customized.
void flush_to_console() noexcept;
Write the contents of the buffers to stdout, and then clear the buffers. Since stdout is a text stream, newline characters in the buffer are printed as CR+LF.
void flush_to_file(std::string const& filename);
void flush_to_file(std::experimental::filesystem::path const& filename);
Writes the contents of the buffers to the specified file, and then clear the buffers. If the file already exists and the contents of the buffers are identical to the current file contents, then the file is left unmodified. This avoids altering last-modified timestamps and triggering false rebuilds.
void swap() noexcept;
Exchanges the first and second buffers. See "Multiple buffers" above.
char back();
Returns the last character in the first buffer, or '\0'
if the first buffer is empty.
bool file_equal(std::string const& filename);
Returns true
if the specified file exists and its contents are identical to the contents of the buffers.