File: DataWriter.h

package info (click to toggle)
endless-sky 0.10.16-3
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 414,608 kB
  • sloc: cpp: 73,435; python: 893; xml: 666; sh: 271; makefile: 28
file content (151 lines) | stat: -rw-r--r-- 5,268 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
/* DataWriter.h
Copyright (c) 2014 by Michael Zahniser

Endless Sky is free software: you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
Foundation, either version 3 of the License, or (at your option) any later version.

Endless Sky is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with
this program. If not, see <https://www.gnu.org/licenses/>.
*/

#pragma once

#include <algorithm>
#include <filesystem>
#include <map>
#include <sstream>
#include <string>
#include <vector>

class DataNode;



// This class writes data in a hierarchical format, where an indented line is
// considered the "child" of the first line above it that is less indented. By
// using this class, you can have a function add data to the file without having
// to tell that function what indentation level it is at. This class also
// automatically adds quotation marks around strings if they contain whitespace.
class DataWriter {
public:
	// Constructor, specifying the file to write.
	explicit DataWriter(const std::filesystem::path &path);
	// Constructor for a DataWriter that will not save its contents automatically
	DataWriter();
	DataWriter(const DataWriter &) = delete;
	DataWriter(DataWriter &&) = delete;
	DataWriter &operator=(const DataWriter &) = delete;
	DataWriter operator=(DataWriter &&) = delete;
	// The file is not actually saved until the destructor is called. This makes
	// it possible to write the whole file in a single chunk.
	~DataWriter();

	// Save the contents to a file.
	void SaveToPath(const std::filesystem::path &path);
	// Get the contents as a string.
	std::string SaveToString() const;

	// The Write() function can take any number of arguments. Each argument is
	// converted to a token. Arguments may be strings or numeric values.
	template<class A, class ...B>
	void Write(const A &a, B... others);
	// Write the entire structure represented by a DataNode, including any
	// children that it has.
	void Write(const DataNode &node);
	// End the current line. This can be used to add line breaks or to terminate
	// a line you have been writing token by token with WriteToken().
	void Write();

	// Begin a new line that is a "child" of the previous line.
	void BeginChild();
	// Finish writing a block of child nodes and decrease the indentation.
	void EndChild();

	// Write a comment. It will be at the current indentation level, and will
	// have "# " inserted before it.
	void WriteComment(const std::string &str);

	// Write a token, without writing a whole line. Use this very carefully.
	void WriteToken(const char *a);
	void WriteToken(const std::string &a);
	// Write a token of any arithmetic type.
	template<class A>
	void WriteToken(const A &a);

	// Enclose a string in the correct quotation marks.
	static std::string Quote(const std::string &text);


private:
	// Save path (in UTF-8). Empty string for in-memory DataWriter.
	std::filesystem::path path;
	// Current indentation level.
	std::string indent;
	// Before writing each token, we will write either the indentation string
	// above, or this string.
	static const std::string space;
	// Remember which string should be written before the next token. This is
	// "indent" for the first token in a line and "space" for subsequent tokens.
	const std::string *before;
	// Compose the output in memory before writing it to file.
	std::ostringstream out;
};



// The Write() function can take any number of arguments, each of which becomes
// a token. They must be either strings or numeric types.
template<class A, class ...B>
void DataWriter::Write(const A &a, B... others)
{
	WriteToken(a);
	Write(others...);
}



// Write any numeric type as a single token.
template<class A>
void DataWriter::WriteToken(const A &a)
{
	static_assert(std::is_arithmetic_v<A>,
		"DataWriter cannot output anything but strings and arithmetic types.");

	out << *before << a;
	before = &space;
}



// Encapsulate the logic for writing the contents of a collection in a sorted manner. The caller
// should provide a sorting method; it will be called with pointers to the type of the container.
// The provided write method will be called for each element of the container.
template<class T, template<class, class...> class C, class... Args, typename A, typename B>
void WriteSorted(const C<T, Args...> &container, A sortFn, B writeFn)
{
	std::vector<const T *> sorted;
	sorted.reserve(container.size());
	for(const auto &it : container)
		sorted.emplace_back(&it);
	std::sort(sorted.begin(), sorted.end(), sortFn);

	for(const auto &sit : sorted)
		writeFn(*sit);
}
template<class K, class V, class... Args, typename A, typename B>
void WriteSorted(const std::map<const K *, V, Args...> &container, A sortFn, B writeFn)
{
	std::vector<const std::pair<const K *const, V> *> sorted;
	sorted.reserve(container.size());
	for(const auto &it : container)
		sorted.emplace_back(&it);
	std::sort(sorted.begin(), sorted.end(), sortFn);

	for(const auto &sit : sorted)
		writeFn(*sit);
}