All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
StringBuilder.h
Go to the documentation of this file.
1 #ifndef STRING_BUILDER_H_
2 #define STRING_BUILDER_H_
3 
4 #include <algorithm>
5 #include <cassert>
6 #include <string>
7 #include <sstream>
8 #include <stdexcept>
9 #include <iterator>
10 #include <type_traits>
11 #include <tuple>
12 #include <utility>
13 
14 #include "TemplateUtils.h"
15 
16 namespace Aux {
17 
18 template<typename...T>
19 std::string toString(const T&...args);
20 
21 template<typename...T>
22 std::ostream& printToStream(std::ostream& stream, const T&...args);
23 
24 template<typename...T>
25 std::string toStringF(const std::string& format, const T&...args);
26 
27 template<typename...T>
28 std::ostream& printToStreamF(std::ostream& stream, const std::string& format, const T&...args);
29 
30 // Implementation
32 
33 namespace Impl {
34 
35 // Categories of how a type might be printable
36 enum class PrintableCategory {
38  Iteratable,
39  Pair,
40  Tuple,
41  Streamable,
42 };
43 
44 template<typename T> constexpr bool isStreamable();
45 template<typename T> constexpr bool isPair();
46 template<typename T> constexpr bool isTuple();
47 template<typename T> constexpr bool isIteratable();
48 
49 template<typename T>
51  return
52  isStreamable<T>() ? PrintableCategory::Streamable :
53  isPair<T>() ? PrintableCategory::Pair :
54  isTuple<T>() ? PrintableCategory::Tuple :
55  isIteratable<T>() ? PrintableCategory::Iteratable :
56  /* else: */ PrintableCategory::Unprintable ;
57 }
58 template<PrintableCategory Tag> struct PrintableCategoryTag{};
64 
65 template<typename T, typename...Args> void printToStream(std::ostream& stream, const T&, const Args&...);
66 inline void printToStream(std::ostream&) {}
67 
68 template<typename T> void printToStreamTagged(std::ostream& stream, const T&, IteratableTag);
69 template<typename T> void printToStreamTagged(std::ostream& stream, const T&, PairTag);
70 template<typename T> void printToStreamTagged(std::ostream& stream, const T&, TupleTag);
71 template<typename T> void printToStreamTagged(std::ostream& stream, const T&, StreamableTag);
72 
73 // Claim that this function exists somewhere else to keep the errors clean
74 // (calling this function is not a problem since the error is already caught earlier
75 // in printToStream, and calling it directly will result in a linker-error)
76 template<typename T> extern void printToStreamTagged(std::ostream& , const T&, UnprintableTag);
77 
78 template<typename T, typename...Args>
79 void printToStream(std::ostream& stream, const T& arg, const Args&...args) {
80  static_assert(getPrintableCategory<T>() != PrintableCategory::Unprintable,
81  "printToStream must not be called with an unprintable argument");
82  printToStreamTagged(stream, arg, PrintableCategoryTag<getPrintableCategory<T>()>{});
83  printToStream(stream, args...);
84 }
85 
86 inline std::tuple<std::string::const_iterator, bool> printFormatPartToStream(std::ostream& stream,
87  std::string::const_iterator begin, std::string::const_iterator end);
88 
89 inline void printToStreamF(std::ostream& stream, std::string::const_iterator format_begin,
90  std::string::const_iterator format_end) {
91  bool printArgument;
92  using iterator = std::string::const_iterator;
93  iterator it;
94  std::tie(it, printArgument) = printFormatPartToStream(stream, format_begin, format_end);
95  if (printArgument) {
96  throw std::invalid_argument{"formatstring requests more arguments then provided"};
97  }
98 }
99 template<typename T, typename...Args>
100 void printToStreamF(std::ostream& stream, std::string::const_iterator format_begin,
101  std::string::const_iterator format_end, const T& arg, const Args&...args) {
102  bool printArgument;
103  using iterator = std::string::const_iterator;
104  iterator it;
105  std::tie(it, printArgument) = printFormatPartToStream(stream, format_begin, format_end);
106  if(printArgument) {
107  printToStream(stream, arg);
108  printToStreamF(stream, it, format_end, args...);
109  } else {
110  assert(it == format_end);
111  return;
112  }
113 }
114 
115 
116 
124 inline auto printFormatPartToStream(std::ostream& stream, std::string::const_iterator begin,
125  std::string::const_iterator end)
126  -> std::tuple<std::string::const_iterator, bool>
127 {
128  auto it = begin;
129  while (it != end) {
130  auto nextPercent = std::find(it, end, '%');
131  stream.write(&*it, std::distance(it, nextPercent));
132 
133  if(nextPercent == end) {
134  it = nextPercent;
135  break;
136  }
137 
138  it = ++nextPercent;
139 
140  if(it == end) {
141  throw std::invalid_argument{"formatstrings must not end on unmatched '%'"};
142  } else if (*it == '%') {
143  stream.put('%');
144  ++it;
145  } else if (*it == 's') {
146  ++it;
147  return std::make_tuple(it, true);
148  } else {
149  throw std::invalid_argument{"formatstring contains illegal format-specifier"};
150  }
151  }
152  assert(it == end);
153  return std::make_tuple(end, false);
154 }
155 
156 // Brace Yourself: Templatemetaprogramming is coming
158 
159 // Iteratable
161  static std::false_type isIteratable(...);
162 
163  template<typename T,
164  class Iterator = decltype(std::begin(std::declval<T>())),
165  AUX_REQUIRE(EndIteratorValid, isSame<Iterator, decltype(std::end(std::declval<T>()))>()),
166  AUX_REQUIRE(HasInputIterator, isBaseOrSame<std::input_iterator_tag,
167  typename std::iterator_traits<Iterator>::iterator_category>())
168  > static std::true_type isIteratable(const T&);
169 };
170 template<typename T> constexpr bool isIteratable() {
171  return decltype(IsIteratableHelper::isIteratable(std::declval<T>()))::value;
172 }
173 
174 // Pair
175 template<typename T > struct IsPairHelper : std::false_type {};
176 template<typename T1, typename T2> struct IsPairHelper<std::pair<T1, T2>> : std::true_type {};
177 template<typename T> constexpr bool isPair() {
178  return IsPairHelper<T>::value;
179 }
180 
181 // Tuple
182 template<typename T> struct IsTupleHelper : std::false_type {};
183 template<typename...T> struct IsTupleHelper<std::tuple<T...>> : std::true_type {};
184 template<typename T> constexpr bool isTuple() {
186 }
187 
188 // Streamable
190  static std::false_type isStreamable(...);
191 
192  template<typename T,
193  AUX_REQUIRE(Streamable, isBaseOrSame<std::ostream,
194  decay_t<decltype(std::declval<std::ostream&>() << std::declval<const T&>())>>())
195  >
196  static std::true_type isStreamable(const T&);
197 
198 };
199 template<typename T> constexpr bool isStreamable() {
200  return decltype(IsStreamableHelper::isStreamable(std::declval<T>()))::value;
201 }
202 
203 
204 // And now: implement the actual printing:
206 
207 // Streamable
208 template<typename T> void printToStreamTagged(std::ostream& stream, const T& arg, StreamableTag) {
209  stream << arg;
210 }
211 
212 // Pair
213 template<typename T> void printToStreamTagged(std::ostream& stream, const T& arg, PairTag) {
214  stream << '(';
215  printToStream(stream, arg.first);
216  stream << ", ";
217  printToStream(stream, arg.second);
218  stream << ')';
219 }
220 
221 // Tuple
222 template<typename Tuple, int I, int TupleSize> struct printTupleHelper {
223  static void print(std::ostream& stream, const Tuple& arg) {
224  printToStream(stream, std::get<I-1>(arg));
225  stream << ", ";
227  }
228 };
229 template<typename Tuple, int I> struct printTupleHelper<Tuple, I, I>{
230  static void print(std::ostream& stream, const Tuple& arg) {
231  printToStream(stream, std::get<I-1>(arg));
232  }
233 };
234 template<typename T> void printToStreamTagged(std::ostream& stream, const T& arg, TupleTag) {
235  stream << '(';
236  printTupleHelper<T, 1, std::tuple_size<T>::value>::print(stream, arg);
237  stream << ')';
238 }
239 
240 // Iteratable
241 template<typename T> void printToStreamTagged(std::ostream& stream, const T& arg, IteratableTag) {
242  auto it = std::begin(arg);
243  auto end = std::end(arg);
244  bool firstpass = true;
245  stream << '[';
246  while(it != end) {
247  if(firstpass) {
248  firstpass = false;
249  }
250  else {
251  stream << ", ";
252  }
253  printToStream(stream, *it);
254  ++it;
255  }
256  stream << ']';
257 }
258 
259 
260 } // namespace Impl
261 
262 // Finally: put together the public interface:
264 
265 template<typename...T>
266 std::string toString(const T&...args) {
267  std::stringstream stream;
268  printToStream(stream, args...);
269  return stream.str();
270 }
271 
272 template<typename...T>
273 std::ostream& printToStream(std::ostream& stream, const T&...args) {
274  Impl::printToStream(stream, args...);
275  return stream;
276 }
277 
278 template<typename...T>
279 std::string toStringF(const std::string& format, const T&...args) {
280  std::stringstream stream;
281  printToStreamF(stream, format, args...);
282  return stream.str();
283 }
284 
285 template<typename...T>
286 std::ostream& printToStreamF(std::ostream& stream, const std::string& format, const T&...args) {
287  Impl::printToStreamF(stream, format.begin(), format.end(), args...);
288  return stream;
289 }
290 
291 } //namespace Aux
292 
293 
294 #endif
std::tuple< std::string::const_iterator, bool > printFormatPartToStream(std::ostream &stream, std::string::const_iterator begin, std::string::const_iterator end)
Write the formatstring until the first occurance of "%s" to stream, '%' will be replaced by ''...
Definition: StringBuilder.h:124
constexpr bool isTuple()
Definition: StringBuilder.h:184
std::ostream & printToStream(std::ostream &stream, const T &...args)
Definition: StringBuilder.h:273
PrintableCategoryTag< PrintableCategory::Unprintable > UnprintableTag
Definition: StringBuilder.h:63
static void print(std::ostream &stream, const Tuple &arg)
Definition: StringBuilder.h:223
PrintableCategoryTag< PrintableCategory::Streamable > StreamableTag
Definition: StringBuilder.h:62
Definition: StringBuilder.h:182
std::string toString(const T &...args)
Definition: StringBuilder.h:266
void printToStreamF(std::ostream &stream, std::string::const_iterator format_begin, std::string::const_iterator format_end)
Definition: StringBuilder.h:89
constexpr bool isStreamable()
Definition: StringBuilder.h:199
PrintableCategoryTag< PrintableCategory::Pair > PairTag
Definition: StringBuilder.h:60
Definition: StringBuilder.h:175
std::string toStringF(const std::string &format, const T &...args)
Definition: StringBuilder.h:279
constexpr bool isBaseOrSame()
Checks whether Derived is either a type that derives from Base or is identical to Base...
Definition: TemplateUtils.h:68
void printToStream(std::ostream &stream, const T &, const Args &...)
constexpr bool isIteratable()
Definition: StringBuilder.h:170
PrintableCategoryTag< PrintableCategory::Tuple > TupleTag
Definition: StringBuilder.h:61
PrintableCategory
Definition: StringBuilder.h:36
Definition: StringBuilder.h:222
void printToStreamTagged(std::ostream &stream, const T &, IteratableTag)
Definition: StringBuilder.h:208
typename std::decay< T >::type decay_t
This is a backport of C++14 std::decay_t.
Definition: TemplateUtils.h:44
constexpr bool isPair()
Definition: StringBuilder.h:177
constexpr bool isSame()
This is just a wrapper around std::is_same in order to provide a nicer interface. ...
Definition: TemplateUtils.h:58
Definition: StringBuilder.h:58
static std::false_type isStreamable(...)
float distance
Definition: PubWebGenerator.h:25
Definition: StringBuilder.h:189
std::ostream & printToStreamF(std::ostream &stream, const std::string &format, const T &...args)
Definition: StringBuilder.h:286
static std::false_type isIteratable(...)
PrintableCategoryTag< PrintableCategory::Iteratable > IteratableTag
Definition: StringBuilder.h:59
static void print(std::ostream &stream, const Tuple &arg)
Definition: StringBuilder.h:230
Definition: StringBuilder.h:160
constexpr PrintableCategory getPrintableCategory()
Definition: StringBuilder.h:50
#define AUX_REQUIRE(what,...)
The macro makes the use of std::enable_if much easier by removing all the boilerplate.
Definition: TemplateUtils.h:17