AI 24/25 Project Software
Documentation for the AI 24/25 course programming project software
Loading...
Searching...
No Matches
options.h
1#ifndef PLUGINS_OPTIONS_H
2#define PLUGINS_OPTIONS_H
3
4#include "downward/utils/logging.h"
5#include "downward/utils/system.h"
6
7#include <any>
8#include <string>
9#include <typeinfo>
10#include <unordered_map>
11#include <vector>
12
13namespace downward::cli::plugins {
14/*
15 Elements in the storage of the class Options are stored as std::any. Normally,
16 the type stored inside the std::any objects matches the type we eventually use
17 to extract the element but there are two exceptions.
18
19 Lists are stored as vector<std::any> inside the options, so a normal
20 std::any_cast will not work for them. We instead have to create a new vector
21 and copy the casted elements into it. Since the new vector has to live
22 somewhere, we do not support this for pointer to such vectors. Those will also
23 not be created by the parser.
24
25 Enums are stored as ints, so we have to extract them as such and cast the
26 int into the enum types afterwards.
27*/
28template <typename ValueType, typename = void>
29struct OptionsAnyCaster {
30 static ValueType cast(const std::any& operand)
31 {
32 return std::any_cast<ValueType>(operand);
33 }
34};
35
36template <typename ValueType>
37struct OptionsAnyCaster<
38 ValueType,
39 typename std::enable_if<std::is_enum<ValueType>::value>::type> {
40 static ValueType cast(const std::any& operand)
41 {
42 // Enums set within the code (options.set()) are already the right
43 // ValueType...
44 if (operand.type() == typeid(ValueType)) {
45 return std::any_cast<ValueType>(operand);
46 }
47 // ... otherwise (Enums set over the command line) they are ints.
48 return static_cast<ValueType>(std::any_cast<int>(operand));
49 }
50};
51
52template <typename T>
53struct OptionsAnyCaster<std::vector<T>> {
54 static std::vector<T> cast(const std::any& operand)
55 {
56 if (operand.type() == typeid(std::vector<T>)) {
57 return std::any_cast<std::vector<T>>(operand);
58 }
59 // std::any_cast returns a copy here, not a reference.
60 const std::vector<std::any> any_elements =
61 std::any_cast<std::vector<std::any>>(operand);
62 std::vector<T> result;
63 result.reserve(any_elements.size());
64 for (const std::any& element : any_elements) {
65 result.push_back(OptionsAnyCaster<T>::cast(element));
66 }
67 return result;
68 }
69};
70
71// Wrapper for unordered_map<string, std::any>.
72class Options {
73 std::unordered_map<std::string, std::any> storage;
74 std::string unparsed_config;
75
76public:
77 explicit Options();
78 /*
79 TODO: we only need the copy constructor for cases where we need to modify
80 the options after parsing (see merge_and_shrink_heuristic.cc for an
81 example). This should no longer be necessary once we switch to builders.
82 At this time, the constructor can probably be deleted.
83 */
84 Options(const Options& other) = default;
85
86 template <typename T>
87 void set(const std::string& key, T value)
88 {
89 storage[key] = value;
90 }
91
92 template <typename T>
93 T get(const std::string& key) const
94 {
95 const auto it = storage.find(key);
96 if (it == storage.end()) {
97 ABORT(
98 "Attempt to retrieve nonexisting object of name " + key +
99 " (type: " + typeid(T).name() + ")");
100 }
101 try {
102 T result = OptionsAnyCaster<T, void>::cast(it->second);
103 return result;
104 } catch (const std::bad_any_cast&) {
105 ABORT(
106 "Invalid conversion while retrieving config options!\n" + key +
107 " is not of type " + typeid(T).name() + " but of type " +
108 it->second.type().name());
109 }
110 }
111
112 template <typename T>
113 T get(const std::string& key, const T& default_value) const
114 {
115 if (storage.count(key))
116 return get<T>(key);
117 else
118 return default_value;
119 }
120
121 template <typename T>
122 std::vector<T> get_list(const std::string& key) const
123 {
124 return get<std::vector<T>>(key);
125 }
126
127 bool contains(const std::string& key) const;
128 const std::string& get_unparsed_config() const;
129 void set_unparsed_config(const std::string& config);
130};
131
132template <typename T>
133void verify_list_non_empty(
134 const ::utils::Context& context,
135 const plugins::Options& opts,
136 const std::string& key)
137{
138 std::vector<T> list = opts.get_list<T>(key);
139 if (list.empty()) {
140 context.error("List argument '" + key + "' has to be non-empty.");
141 }
142}
143} // namespace downward::cli::plugins
144
145#endif
STL namespace.