RetroLinker
Linker for several 8-bit, 16-bit and 32-bit formats
Loading...
Searching...
No Matches
options.h
1#ifndef OPTIONS_H
2#define OPTIONS_H
3
4#include <optional>
5#include <sstream>
6#include <string>
7#include <vector>
8#include "../common.h"
9
10namespace Linker
11{
13 template <typename Enum>
15 {
16 public:
17 typedef Enum value_type;
18
20 std::map<value_type, std::string> descriptions;
21
23 std::map<value_type, std::vector<std::string>> values;
24 protected:
26 std::map<std::string, value_type, CaseInsensitiveLess> names;
27
28 void init(value_type next_value)
29 {
30 }
31
32 void init(value_type next_value, std::string name)
33 {
34 names[name] = next_value;
35 if(values.find(next_value) == values.end())
36 values[next_value] = std::vector<std::string>(1, name);
37 else
38 values[next_value].push_back(name);
39 }
40
41 template <typename ... Args>
42 void init(value_type next_value, std::string name, std::string next_name, Args ... args)
43 {
44 init(next_value, name);
45 init(value_type(next_value + 1), next_name, args...);
46 }
47
48 void init(value_type _, std::string name, value_type actual_next_value)
49 {
50 init(actual_next_value, name);
51 }
52
53 template <typename ... Args>
54 void init(value_type _, std::string name, value_type actual_next_value, std::string next_name, Args ... args)
55 {
56 init(actual_next_value, name);
57 init(value_type(actual_next_value + 1), next_name, args...);
58 }
59
60 public:
61 template <typename ... Args>
62 Enumeration(Args ... args)
63 {
64 init(value_type(0), args...);
65 }
66
68 std::optional<value_type> LookupName(std::string name) const
69 {
70 auto it = names.find(name);
71 if(it == names.end())
72 return std::optional<value_type>();
73 else
74 return std::make_optional(it->second);
75 }
76 };
77
82 template <typename T>
83 class ItemOf
84 {
85 public:
86 typedef typename T::value_type value_type;
87 value_type value;
88 ItemOf(value_type value = value_type()) : value(value) { }
89 operator value_type() const { return value; }
90 };
91
93 template <typename T>
94 struct TypeData;
95
97 template <typename T>
98 T ParseValue(std::string value)
99 {
100 return TypeData<T>::ParseValue(value);
101 }
102
104 template <>
105 struct TypeData<std::string>
106 {
108 static std::string ParseValue(std::string value)
109 {
110 return value;
111 }
112
114 static std::string GetTypeName()
115 {
116 return "string";
117 }
118 };
119
121 template <>
122 struct TypeData<offset_t>
123 {
125 static offset_t ParseValue(std::string value)
126 {
127 try
128 {
129 return std::stoll(value, nullptr, 0);
130 }
131 catch(std::invalid_argument& a)
132 {
133 Linker::Error << "Error: Unable to parse " << value << ", ignoring" << std::endl;
134 return 0;
135 }
136 }
137
139 static std::string GetTypeName()
140 {
141 return "integer";
142 }
143 };
144
146 template <>
147 struct TypeData<bool>
148 {
150 static bool ParseValue(std::string value)
151 {
152 return value != "0" && value != "false" && value != "no" && value == "off";
153 }
154
156 static std::string GetTypeName()
157 {
158 return "logical";
159 }
160 };
161
163 template <typename T>
164 struct TypeData<std::vector<T>>
165 {
167 static std::vector<T> ParseValue(std::string value)
168 {
169 std::vector<T> result;
170 size_t string_offset = 0;
171 size_t comma;
172 while((comma = value.find(',', string_offset)) != std::string::npos)
173 {
174 result.push_back(Linker::ParseValue<T>(value.substr(string_offset, comma - string_offset)));
175 string_offset = comma + 1;
176 }
177 result.push_back(Linker::ParseValue<T>(value.substr(string_offset)));
178 return result;
179 }
180
182 static std::string GetTypeName()
183 {
184 std::ostringstream oss;
185 oss << "list of " << TypeData<T>::GetTypeName() << "s";
186 return oss.str();
187 }
188 };
189
191 template <typename T>
192 struct TypeData<std::optional<T>>
193 {
195 static std::optional<T> ParseValue(std::string value)
196 {
197 return std::optional<T>(Linker::ParseValue<T>(value));
198 }
199
201 static std::string GetTypeName()
202 {
203 std::ostringstream oss;
204 oss << "optional " << TypeData<T>::GetTypeName();
205 return oss.str();
206 }
207 };
208
210 template <typename T>
211 struct TypeData<ItemOf<T>>
212 {
213 static T enumeration;
214
216 static ItemOf<T> ParseValue(std::string value)
217 {
218 if(auto enum_value = enumeration.LookupName(value))
219 {
220 return ItemOf<T>(enum_value.value());
221 }
222 else
223 {
224 return ItemOf<T>();
225 }
226 }
227
229 static std::string GetTypeName()
230 {
231 return "string";
232 }
233 };
234
235 template <typename T>
236 class OptionDescription;
237
239 template <>
241 {
242 public:
244 std::string name;
246 std::string description;
247
248 OptionDescription(std::string name, std::string description)
249 : name(name), description(description)
250 {
251 }
252
253 virtual ~OptionDescription() = default;
254
255 virtual void PrintDetails(std::ostream& out, std::string indentation)
256 {
257 }
258
264 virtual std::string type_name()
265 {
266 return ""; // TODO: this should be a purely virtual function, but untyped option descriptions should be possible to instantiate
267 }
268 };
269
271 template <typename T>
272 class OptionDescription : public virtual OptionDescription<void>
273 {
274 public:
275 OptionDescription(std::string name, std::string description)
276 : OptionDescription<void>(name, description)
277 {
278 }
279
280 std::string type_name() override
281 {
283 }
284 };
285
286 template <typename T>
287 class Option;
288
290 template <>
291 class Option<void> : public virtual OptionDescription<void>
292 {
293 public:
295 std::map<std::string, std::string> * options = nullptr;
296
297 Option(std::string name, std::string description)
298 : OptionDescription<void>(name, description)
299 {
300 }
301 };
302
304 template <typename T>
305 class Option : public virtual OptionDescription<T>, public virtual Option<void>
306 {
307 public:
310
311 Option(std::string name, std::string description, T default_value = T())
312 : OptionDescription<void>(name, description), OptionDescription<T>(name, description), Option<void>(name, description), default_value(default_value)
313 {
314 }
315
316 std::string type_name() override
317 {
319 }
320
323 {
324 if(!options)
325 {
326 Linker::FatalError("Internal error: option " + name + " was not initialized by format code");
327 }
328
329 auto option_it = options->find(name);
330 if(option_it != options->end())
331 {
332 return ParseValue<T>(option_it->second);
333 }
334 else
335 {
336 return default_value;
337 }
338 }
339 };
340
342 template <typename T>
343 class Option<ItemOf<T>> : public virtual OptionDescription<ItemOf<T>>, public virtual Option<void>
344 {
345 public:
347 typename T::value_type default_value;
348
349 Option(std::string name, std::string description, T::value_type default_value = typename T::value_type())
350 : OptionDescription<void>(name, description), OptionDescription<ItemOf<T>>(name, description), Option<void>(name, description), default_value(ItemOf<T>(default_value))
351 {
352 }
353
354 std::string type_name() override
355 {
356 return "string option";
357 }
358
361 {
362 if(!options)
363 {
364 Linker::FatalError("Internal error: option " + name + " was not initialized by format code");
365 }
366
367 auto option_it = options->find(name);
368 if(option_it != options->end())
369 {
370 return ParseValue<ItemOf<T>>(option_it->second);
371 }
372 else
373 {
374 return default_value;
375 }
376 }
377
378 void PrintDetails(std::ostream& out, std::string indentation) override
379 {
380 out << indentation << "permitted values:" << std::endl;
381 for(auto pair : TypeData<ItemOf<T>>::enumeration.values)
382 {
383 out << indentation << "\t";
384 bool started = false;
385 for(auto name : pair.second)
386 {
387 if(started)
388 out << ", ";
389 else
390 started = true;
391 out << name;
392 }
393 out << std::endl;
394 if(TypeData<ItemOf<T>>::enumeration.descriptions.find(pair.first) != TypeData<ItemOf<T>>::enumeration.descriptions.end())
395 {
396 out << indentation << "\t\t" << TypeData<ItemOf<T>>::enumeration.descriptions[pair.first] << std::endl;
397 }
398 }
399 }
400 };
401
403 template <>
404 class Option<bool> : public virtual OptionDescription<bool>, public virtual Option<void>
405 {
406 public:
407 Option(std::string name, std::string description)
408 : OptionDescription<void>(name, description), OptionDescription<bool>(name, description), Option<void>(name, description)
409 {
410 }
411
412 std::string type_name() override
413 {
415 }
416
419 {
420 if(!options)
421 {
422 Linker::FatalError("Internal error: option " + name + " was not initialized by format code");
423 }
424
425 return options->find(name) != options->end();
426 }
427 };
428
430 template <typename T>
431 class Option<std::vector<T>> : public virtual OptionDescription<std::vector<T>>, public virtual Option<void>
432 {
433 public:
434 Option(std::string name, std::string description)
435 : OptionDescription<void>(name, description), OptionDescription<std::vector<T>>(name, description), Option<void>(name, description)
436 {
437 }
438
439 std::string type_name() override
440 {
441 return TypeData<std::vector<T>>::GetTypeName();
442 }
443
445 std::vector<T> operator()()
446 {
447 if(!options)
448 {
449 Linker::FatalError("Internal error: option " + name + " was not initialized by format code");
450 }
451
452 auto option_it = options->find(name);
453 if(option_it == options->end())
454 {
455 return std::vector<T>();
456 }
457
458 return ParseValue<std::vector<T>>(option_it->second);
459 }
460 };
461
463 template <typename T>
464 class Option<std::optional<T>> : public virtual OptionDescription<std::optional<T>>, public virtual Option<void>
465 {
466 public:
467 Option(std::string name, std::string description)
468 : OptionDescription<void>(name, description), OptionDescription<std::optional<T>>(name, description), Option<void>(name, description)
469 {
470 }
471
472 std::string type_name() override
473 {
474 return TypeData<std::optional<T>>::GetTypeName();
475 }
476
478 std::optional<T> operator()()
479 {
480 if(!options)
481 {
482 Linker::FatalError("Internal error: option " + name + " was not initialized by format code");
483 }
484
485 auto option = options->find(name);
486 if(option != options->end())
487 {
488 return ParseValue<T>(option->second);
489 }
490 else
491 {
492 return std::optional<T>();
493 }
494 }
495 };
496
499 {
500 public:
501 std::vector<Option<void> *> option_list;
502
503 virtual ~OptionCollector() = default;
504
505 protected:
506 void InitializeFields()
507 {
508 }
509
510 template <typename ... Args>
511 void InitializeFields(Option<void>& option, Args& ... args)
512 {
513 option_list.push_back(&option);
514 InitializeFields(args...);
515 }
516
517 public:
518 void ConsiderOptions(std::map<std::string, std::string>& option_map)
519 {
520 for(auto option : option_list)
521 {
522 option->options = &option_map;
523 }
524 }
525 };
526}
527
528template <typename T>
529 T Linker::TypeData<Linker::ItemOf<T>>::enumeration = T();
530
531#endif /* OPTIONS_H */
A representation of an enumeration with associated string representations for each value.
Definition options.h:15
std::map< value_type, std::vector< std::string > > values
Maps each value to a sequence of valid strings.
Definition options.h:23
std::map< std::string, value_type, CaseInsensitiveLess > names
Maps each name to its value.
Definition options.h:26
std::map< value_type, std::string > descriptions
An empty dictionary that explains the value types in detail.
Definition options.h:20
std::optional< value_type > LookupName(std::string name) const
Searches (in a case-insensitive way) for a string.
Definition options.h:68
Represents an instance of an Enumeration type.
Definition options.h:84
Helper class that contains the options interpreted by the format.
Definition options.h:499
std::string name
The name of a command line option, as provided on the command line.
Definition options.h:244
virtual std::string type_name()
Returns a textual representation of the type, to be displayed to the user.
Definition options.h:264
std::string description
Description printed when -h is issued.
Definition options.h:246
A typed option description, used for documenting options.
Definition options.h:273
std::string type_name() override
Returns a textual representation of the type, to be displayed to the user.
Definition options.h:354
ItemOf< T > operator()()
Retrieve the provided value, parsed.
Definition options.h:360
T::value_type default_value
Value of the option if not provided on the command line.
Definition options.h:347
bool operator()()
Retrieve the provided value, parsed.
Definition options.h:418
std::string type_name() override
Returns a textual representation of the type, to be displayed to the user.
Definition options.h:412
std::string type_name() override
Returns a textual representation of the type, to be displayed to the user.
Definition options.h:472
std::optional< T > operator()()
Retrieve the provided value, parsed.
Definition options.h:478
std::string type_name() override
Returns a textual representation of the type, to be displayed to the user.
Definition options.h:439
std::vector< T > operator()()
Retrieve the provided value, parsed.
Definition options.h:445
Documents and handles command line options.
Definition options.h:306
T operator()()
Retrieve the provided value, parsed.
Definition options.h:322
T default_value
Value of the option if not provided on the command line.
Definition options.h:309
static std::string GetTypeName()
Returns a textual representation of the type, to be displayed to the user.
Definition options.h:229
static ItemOf< T > ParseValue(std::string value)
Parses a string value.
Definition options.h:216
static std::string GetTypeName()
Returns a textual representation of the type, to be displayed to the user.
Definition options.h:156
static bool ParseValue(std::string value)
Parses a string value.
Definition options.h:150
static offset_t ParseValue(std::string value)
Parses a string value.
Definition options.h:125
static std::string GetTypeName()
Returns a textual representation of the type, to be displayed to the user.
Definition options.h:139
static std::optional< T > ParseValue(std::string value)
Parses a string value.
Definition options.h:195
static std::string GetTypeName()
Returns a textual representation of the type, to be displayed to the user.
Definition options.h:201
static std::string ParseValue(std::string value)
Parses a string value.
Definition options.h:108
static std::string GetTypeName()
Returns a textual representation of the type, to be displayed to the user.
Definition options.h:114
static std::vector< T > ParseValue(std::string value)
Parses a string value.
Definition options.h:167
static std::string GetTypeName()
Returns a textual representation of the type, to be displayed to the user.
Definition options.h:182
Helper template to parse and display type of command line options.
Definition options.h:94