1 /* This file is part of the Spring engine (GPL v2 or later), see LICENSE.html */
2 
3 #ifndef CONFIG_VALUE_H
4 #define CONFIG_VALUE_H
5 
6 #include <boost/utility.hpp>
7 #include <map>
8 #include <sstream>
9 #include <string>
10 #include "System/Util.h"
11 
12 
13 
14 /**
15  * @brief Untyped configuration variable meta data.
16  *
17  * That is, meta data of a type that does not depend on the declared type
18  * of the config variable.
19  */
20 class ConfigVariableMetaData : public boost::noncopyable
21 {
22 public:
23 	typedef TypedStringConvertibleOptionalValue<std::string> OptionalString;
24 	typedef TypedStringConvertibleOptionalValue<int> OptionalInt;
25 
~ConfigVariableMetaData()26 	virtual ~ConfigVariableMetaData() {}
27 
28 	/// @brief Get the default value of this config variable.
29 	virtual const StringConvertibleOptionalValue& GetDefaultValue() const = 0;
30 
31 	/// @brief Get the minimum value of this config variable.
32 	virtual const StringConvertibleOptionalValue& GetMinimumValue() const = 0;
33 
34 	/// @brief Get the maximum value of this config variable.
35 	virtual const StringConvertibleOptionalValue& GetMaximumValue() const = 0;
36 
37 	/// @brief Get the safemode value of this config variable.
38 	virtual const StringConvertibleOptionalValue& GetSafemodeValue() const = 0;
39 
40 	/// @brief Clamp a value using the declared minimum and maximum value.
41 	virtual std::string Clamp(const std::string& value) const = 0;
42 
GetKey()43 	std::string GetKey() const { return key; }
GetType()44 	std::string GetType() const { return type; }
45 
GetDeclarationFile()46 	const OptionalString& GetDeclarationFile() const { return declarationFile; }
GetDeclarationLine()47 	const OptionalInt& GetDeclarationLine() const { return declarationLine; }
GetDescription()48 	const OptionalString& GetDescription() const { return description; }
GetReadOnly()49 	const OptionalInt& GetReadOnly() const { return readOnly; }
50 
51 protected:
52 	const char* key;
53 	const char* type;
54 	OptionalString declarationFile;
55 	OptionalInt declarationLine;
56 	OptionalString description;
57 	OptionalInt readOnly;
58 
59 	template<typename F> friend class ConfigVariableBuilder;
60 };
61 
62 /**
63  * @brief Typed configuration variable meta data.
64  *
65  * That is, meta data of the same type as the declared type
66  * of the config variable.
67  */
68 template<typename T>
69 class ConfigVariableTypedMetaData : public ConfigVariableMetaData
70 {
71 public:
ConfigVariableTypedMetaData(const char * k,const char * t)72 	ConfigVariableTypedMetaData(const char* k, const char* t) { key = k; type = t; }
73 
GetDefaultValue()74 	const StringConvertibleOptionalValue& GetDefaultValue() const { return defaultValue; }
GetMinimumValue()75 	const StringConvertibleOptionalValue& GetMinimumValue() const { return minimumValue; }
GetMaximumValue()76 	const StringConvertibleOptionalValue& GetMaximumValue() const { return maximumValue; }
GetSafemodeValue()77 	const StringConvertibleOptionalValue& GetSafemodeValue() const { return safemodeValue; }
78 
79 	/**
80 	 * @brief Clamp a value using the declared minimum and maximum value.
81 	 *
82 	 * Even though the input and the output is passed as string, the clamping
83 	 * happens in the domain of the declared type of the config variable.
84 	 * This may be a different type than what is read from the ConfigHandler.
85 	 *
86 	 * For example: a config variable declared as int will be clamped in the
87 	 * int domain even if ConfigHandler::GetString is used to fetch it.
88 	 *
89 	 * This guarantees the value will always be in range, even if Lua, a client
90 	 * of unitsync or erroneous Spring code uses the wrong getter.
91 	 */
Clamp(const std::string & value)92 	std::string Clamp(const std::string& value) const
93 	{
94 		TypedStringConvertibleOptionalValue<T> temp;
95 		temp = TypedStringConvertibleOptionalValue<T>::FromString(value);
96 		if (minimumValue.IsSet()) {
97 			temp = std::max(temp.Get(), minimumValue.Get());
98 		}
99 		if (maximumValue.IsSet()) {
100 			temp = std::min(temp.Get(), maximumValue.Get());
101 		}
102 		return temp.ToString();
103 	}
104 
105 protected:
106 	TypedStringConvertibleOptionalValue<T> defaultValue;
107 	TypedStringConvertibleOptionalValue<T> minimumValue;
108 	TypedStringConvertibleOptionalValue<T> maximumValue;
109 	TypedStringConvertibleOptionalValue<T> safemodeValue;
110 
111 	template<typename F> friend class ConfigVariableBuilder;
112 };
113 
114 /**
115  * @brief Fluent interface to declare meta data of config variables
116  *
117  * Uses method chaining so that a config variable can be declared like this:
118  *
119  * CONFIG(int, Example)
120  *   .defaultValue(6)
121  *   .minimumValue(1)
122  *   .maximumValue(10)
123  *   .description("This is an example")
124  *   .readOnly(true);
125  */
126 template<typename T>
127 class ConfigVariableBuilder : public boost::noncopyable
128 {
129 public:
ConfigVariableBuilder(ConfigVariableTypedMetaData<T> & data)130 	ConfigVariableBuilder(ConfigVariableTypedMetaData<T>& data) : data(&data) {}
GetData()131 	const ConfigVariableMetaData* GetData() const { return data; }
132 
133 #define MAKE_CHAIN_METHOD(property, type) \
134 	ConfigVariableBuilder& property(type const& x) { \
135 		data->property = x; \
136 		return *this; \
137 	}
138 
139 	MAKE_CHAIN_METHOD(declarationFile, const char*);
140 	MAKE_CHAIN_METHOD(declarationLine, int);
141 	MAKE_CHAIN_METHOD(description, std::string);
142 	MAKE_CHAIN_METHOD(readOnly, bool);
143 	MAKE_CHAIN_METHOD(defaultValue, T);
144 	MAKE_CHAIN_METHOD(minimumValue, T);
145 	MAKE_CHAIN_METHOD(maximumValue, T);
146 	MAKE_CHAIN_METHOD(safemodeValue, T);
147 
148 #undef MAKE_CHAIN_METHOD
149 
150 private:
151 	ConfigVariableTypedMetaData<T>* data;
152 };
153 
154 /**
155  * @brief Configuration variable declaration
156  *
157  * The only purpose of this class is to store the meta data created by a
158  * ConfigVariableBuilder in a global map of meta data as it is assigned to
159  * an instance of this class.
160  */
161 class ConfigVariable
162 {
163 public:
164 	typedef std::map<std::string, const ConfigVariableMetaData*> MetaDataMap;
165 
166 	static const MetaDataMap& GetMetaDataMap();
167 	static const ConfigVariableMetaData* GetMetaData(const std::string& key);
168 	static void OutputMetaDataMap();
169 
170 private:
171 	static MetaDataMap& GetMutableMetaDataMap();
172 	static void AddMetaData(const ConfigVariableMetaData* data);
173 
174 public:
175 	/// @brief Implicit conversion from ConfigVariableBuilder<T>.
176 	template<typename T>
ConfigVariable(const ConfigVariableBuilder<T> & builder)177 	ConfigVariable(const ConfigVariableBuilder<T>& builder)
178 	{
179 		AddMetaData(builder.GetData());
180 	}
181 };
182 
183 /**
184  * @brief Macro to start the method chain used to declare a config variable.
185  * @see ConfigVariableBuilder
186  */
187 #define CONFIG(T, name) \
188 	static ConfigVariableTypedMetaData<T> cfgdata##name(#name, #T); \
189 	static ConfigVariable cfg##name = ConfigVariableBuilder<T>(cfgdata##name) \
190 		.declarationFile(__FILE__).declarationLine(__LINE__)
191 
192 #endif // CONFIG_VALUE_H
193