1 /**
2 * @file core/util/param_checks_impl.hpp
3 * @author Ryan Curtin
4 *
5 * Utility function implementation for checking arguments, and so forth.
6 *
7 * mlpack is free software; you may redistribute it and/or modify it under the
8 * terms of the 3-clause BSD license. You should have received a copy of the
9 * 3-clause BSD license along with mlpack. If not, see
10 * http://www.opensource.org/licenses/BSD-3-Clause for more information.
11 */
12 #ifndef MLPACK_CORE_UTIL_PARAM_CHECKS_IMPL_HPP
13 #define MLPACK_CORE_UTIL_PARAM_CHECKS_IMPL_HPP
14
15 #include "param_checks.hpp"
16
17 namespace mlpack {
18 namespace util {
19
20 // Check that the arguments are given.
RequireOnlyOnePassed(const std::vector<std::string> & constraints,const bool fatal,const std::string & errorMessage)21 inline void RequireOnlyOnePassed(
22 const std::vector<std::string>& constraints,
23 const bool fatal,
24 const std::string& errorMessage)
25 {
26 if (BINDING_IGNORE_CHECK(constraints))
27 return;
28
29 size_t set = 0;
30 for (size_t i = 0; i < constraints.size(); ++i)
31 {
32 if (IO::HasParam(constraints[i]))
33 ++set;
34 }
35
36 util::PrefixedOutStream& stream = fatal ? Log::Fatal : Log::Warn;
37 if (set > 1)
38 {
39 // Give different output depending on whether 2 or more parameters are
40 // given.
41 if (constraints.size() == 2)
42 {
43 stream << "Can only pass one of " << PRINT_PARAM_STRING(constraints[0])
44 << " or " << PRINT_PARAM_STRING(constraints[1]);
45 }
46 else
47 {
48 stream << "Can only pass one of ";
49 for (size_t i = 0 ; i < constraints.size() - 1; ++i)
50 stream << PRINT_PARAM_STRING(constraints[i]) << ", ";
51 stream << "or "
52 << PRINT_PARAM_STRING(constraints[constraints.size() - 1]);
53 }
54
55 // Append a custom message.
56 if (!errorMessage.empty())
57 stream << "; " << errorMessage;
58 stream << "!" << std::endl;
59 }
60 else if (set == 0)
61 {
62 stream << (fatal ? "Must " : "Should ");
63
64 // Give different output depending on whether 1, 2, or more parameters are
65 // given.
66 if (constraints.size() == 1)
67 {
68 stream << "specify " << PRINT_PARAM_STRING(constraints[0]);
69 }
70 else if (constraints.size() == 2)
71 {
72 stream << "specify one of " << PRINT_PARAM_STRING(constraints[0])
73 << " or " << PRINT_PARAM_STRING(constraints[1]);
74 }
75 else
76 {
77 stream << "specify one of ";
78 for (size_t i = 0; i < constraints.size() - 1; ++i)
79 stream << PRINT_PARAM_STRING(constraints[i]) << ", ";
80 stream << "or "
81 << PRINT_PARAM_STRING(constraints[constraints.size() - 1]);
82 }
83
84 // Append a custom message.
85 if (!errorMessage.empty())
86 stream << "; " << errorMessage;
87 stream << "!" << std::endl;
88 }
89 }
90
RequireAtLeastOnePassed(const std::vector<std::string> & constraints,const bool fatal,const std::string & errorMessage)91 inline void RequireAtLeastOnePassed(
92 const std::vector<std::string>& constraints,
93 const bool fatal,
94 const std::string& errorMessage)
95 {
96 if (BINDING_IGNORE_CHECK(constraints))
97 return;
98
99 size_t set = 0;
100 for (size_t i = 0; i < constraints.size(); ++i)
101 {
102 if (IO::HasParam(constraints[i]))
103 ++set;
104 }
105
106 if (set == 0)
107 {
108 util::PrefixedOutStream& stream = fatal ? Log::Fatal : Log::Warn;
109 stream << (fatal ? "Must " : "Should ");
110 if (constraints.size() == 1)
111 {
112 // This shouldn't happen... just use PARAM_*_REQ()...
113 stream << "pass " << PRINT_PARAM_STRING(constraints[0]);
114 }
115 else if (constraints.size() == 2)
116 {
117 stream << "pass either " << PRINT_PARAM_STRING(constraints[0])
118 << " or " << PRINT_PARAM_STRING(constraints[1]) << " or both";
119 }
120 else
121 {
122 stream << "pass one of ";
123 for (size_t i = 0; i < constraints.size() - 1; ++i)
124 stream << PRINT_PARAM_STRING(constraints[i]) << ", ";
125 stream << "or "
126 << PRINT_PARAM_STRING(constraints[constraints.size() - 1]);
127 }
128
129 // Append custom error message.
130 if (!errorMessage.empty())
131 stream << "; " << errorMessage << "!" << std::endl;
132 else
133 stream << "!" << std::endl;
134 }
135 }
136
RequireNoneOrAllPassed(const std::vector<std::string> & constraints,const bool fatal,const std::string & errorMessage)137 inline void RequireNoneOrAllPassed(
138 const std::vector<std::string>& constraints,
139 const bool fatal,
140 const std::string& errorMessage)
141 {
142 if (BINDING_IGNORE_CHECK(constraints))
143 return;
144
145 size_t set = 0;
146 for (size_t i = 0; i < constraints.size(); ++i)
147 {
148 if (IO::HasParam(constraints[i]))
149 ++set;
150 }
151
152 if (set != 0 && set < constraints.size())
153 {
154 util::PrefixedOutStream& stream = fatal ? Log::Fatal : Log::Warn;
155 stream << (fatal ? "Must " : "Should ");
156 if (constraints.size() == 2)
157 {
158 stream << "pass none or both of " << PRINT_PARAM_STRING(constraints[0])
159 << " and " << PRINT_PARAM_STRING(constraints[1]);
160 }
161 else
162 {
163 // constraints.size() > 2.
164 stream << "pass none or all of ";
165 for (size_t i = 0; i < constraints.size() - 1; ++i)
166 stream << PRINT_PARAM_STRING(constraints[i]) << ", ";
167 stream << "and "
168 << PRINT_PARAM_STRING(constraints[constraints.size() - 1]);
169 }
170
171 // Append custom error message.
172 if (!errorMessage.empty())
173 stream << "; " << errorMessage << "!" << std::endl;
174 else
175 stream << "!" << std::endl;
176 }
177 }
178
179 template<typename T>
RequireParamInSet(const std::string & name,const std::vector<T> & set,const bool fatal,const std::string & errorMessage)180 void RequireParamInSet(const std::string& name,
181 const std::vector<T>& set,
182 const bool fatal,
183 const std::string& errorMessage)
184 {
185 if (BINDING_IGNORE_CHECK(name))
186 return;
187
188 if (std::find(set.begin(), set.end(), IO::GetParam<T>(name)) == set.end())
189 {
190 // The item was not found in the set.
191 util::PrefixedOutStream& stream = fatal ? Log::Fatal : Log::Warn;
192 stream << "Invalid value of " << PRINT_PARAM_STRING(name) << " specified ("
193 << PRINT_PARAM_VALUE(IO::GetParam<T>(name), true) << "); ";
194 if (!errorMessage.empty())
195 stream << errorMessage << "; ";
196 stream << "must be one of ";
197 for (size_t i = 0; i < set.size() - 1; ++i)
198 stream << PRINT_PARAM_VALUE(set[i], true) << ", ";
199 stream << "or " << PRINT_PARAM_VALUE(set[set.size() - 1], true) << "!"
200 << std::endl;
201 }
202 }
203
204 template<typename T>
RequireParamValue(const std::string & name,const std::function<bool (T)> & conditional,const bool fatal,const std::string & errorMessage)205 void RequireParamValue(const std::string& name,
206 const std::function<bool(T)>& conditional,
207 const bool fatal,
208 const std::string& errorMessage)
209 {
210 if (BINDING_IGNORE_CHECK(name))
211 return;
212
213 // We need to make sure that the condition holds.
214 bool condition = conditional(IO::GetParam<T>(name));
215 if (!condition)
216 {
217 // The condition failed.
218 util::PrefixedOutStream& stream = fatal ? Log::Fatal : Log::Warn;
219 stream << "Invalid value of " << PRINT_PARAM_STRING(name) << " specified ("
220 << PRINT_PARAM_VALUE(IO::GetParam<T>(name), false) << "); "
221 << errorMessage << "!" << std::endl;
222 }
223 }
224
ReportIgnoredParam(const std::vector<std::pair<std::string,bool>> & constraints,const std::string & paramName)225 inline void ReportIgnoredParam(
226 const std::vector<std::pair<std::string, bool>>& constraints,
227 const std::string& paramName)
228 {
229 if (BINDING_IGNORE_CHECK(paramName))
230 return;
231
232 // Determine whether or not the condition is true.
233 bool condition = true;
234 for (size_t i = 0; i < constraints.size(); ++i)
235 {
236 if (IO::HasParam(constraints[i].first) != constraints[i].second)
237 {
238 condition = false;
239 break;
240 }
241 }
242
243 // If the condition is satisfied, then report that the parameter is ignored
244 // (if the user passed it).
245 if (condition && IO::HasParam(paramName))
246 {
247 // The output will be different depending on whether there are 1, 2, or more
248 // constraints.
249 Log::Warn << PRINT_PARAM_STRING(paramName) << " ignored because ";
250 if (constraints.size() == 1)
251 {
252 Log::Warn << PRINT_PARAM_STRING(constraints[0].first)
253 << ((constraints[0].second) ? " is " : " is not ")
254 << "specified!" << std::endl;
255 }
256 else if (constraints.size() == 2)
257 {
258 if (constraints[0].second == constraints[1].second)
259 {
260 Log::Warn << ((constraints[0].second) ? "both " : "neither ")
261 << PRINT_PARAM_STRING(constraints[0].first)
262 << ((constraints[0].second) ? "or " : "nor ")
263 << PRINT_PARAM_STRING(constraints[1].first)
264 << " are specified!" << std::endl;
265 }
266 else
267 {
268 Log::Warn << PRINT_PARAM_STRING(constraints[0].first)
269 << ((constraints[0].second) ? " is " : " is not ")
270 << "specified and "
271 << ((constraints[1].second) ? " is " : " is not ")
272 << "specified!" << std::endl;
273 }
274 }
275 else
276 {
277 // List each constraint and whether or not it was or wasn't specified.
278 for (size_t i = 0; i < constraints.size(); ++i)
279 {
280 Log::Warn << PRINT_PARAM_STRING(constraints[i].first)
281 << ((constraints[i].second) ? " is " : " is not ")
282 << ((i == constraints.size() - 1) ? "specified!"
283 : "specified and ");
284 }
285 Log::Warn << std::endl;
286 }
287 }
288 }
289
ReportIgnoredParam(const std::string & paramName,const std::string & reason)290 inline void ReportIgnoredParam(const std::string& paramName,
291 const std::string& reason)
292 {
293 // If the argument was passed, we need to print the reason.
294 if (IO::HasParam(paramName))
295 {
296 Log::Warn << PRINT_PARAM_STRING(paramName) << " ignored because "
297 << reason << "!" << std::endl;
298 }
299 }
300
301 } // namespace util
302 } // namespace mlpack
303
304 #endif
305