00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029 #include <rosmatlab/options.h>
00030 #include <rosmatlab/log.h>
00031 #include <rosmatlab/exception.h>
00032
00033 #include <mex.h>
00034
00035 #include <boost/algorithm/string.hpp>
00036 #include <limits>
00037
00038 namespace rosmatlab {
00039
00040 template <typename T>
00041 static T getScalar(const mxArray *value)
00042 {
00043 if (value && mxGetNumberOfElements(value) == 1) {
00044 switch(mxGetClassID(value)) {
00045 case mxSINGLE_CLASS:
00046 return *static_cast<float *>(mxGetData(value));
00047 case mxDOUBLE_CLASS:
00048 return *static_cast<double *>(mxGetData(value));
00049 case mxINT8_CLASS:
00050 return *static_cast<int8_T *>(mxGetData(value));
00051 case mxUINT8_CLASS:
00052 return *static_cast<uint8_T *>(mxGetData(value));
00053 case mxINT16_CLASS:
00054 return *static_cast<int16_T *>(mxGetData(value));
00055 case mxUINT16_CLASS:
00056 return *static_cast<uint16_T *>(mxGetData(value));
00057 case mxINT32_CLASS:
00058 return *static_cast<int32_T *>(mxGetData(value));
00059 case mxUINT32_CLASS:
00060 return *static_cast<uint32_T *>(mxGetData(value));
00061 case mxINT64_CLASS:
00062 return *static_cast<int64_T *>(mxGetData(value));
00063 case mxUINT64_CLASS:
00064 return *static_cast<uint64_T *>(mxGetData(value));
00065
00066 default: break;
00067 }
00068 }
00069
00070 return std::numeric_limits<T>::quiet_NaN();
00071 }
00072
00073 bool Options::isScalar(const mxArray *value)
00074 {
00075 return value && mxGetNumberOfElements(value) == 1;
00076 }
00077
00078 bool Options::isString(const mxArray *value)
00079 {
00080 return value && mxIsChar(value);
00081 }
00082
00083 std::string Options::getString(const mxArray *value)
00084 {
00085 if (!isString(value)) return std::string();
00086 std::size_t len = mxGetNumberOfElements(value);
00087 char temp[len + 1];
00088 mxGetString(value, temp, sizeof(temp));
00089 return std::string(temp);
00090 }
00091
00092 bool Options::isDoubleScalar(const mxArray *value)
00093 {
00094 if (!isScalar(value)) return false;
00095
00096 switch(mxGetClassID(value)) {
00097 case mxSINGLE_CLASS:
00098 case mxDOUBLE_CLASS:
00099 return true;
00100 default: break;
00101 }
00102
00103 return false;
00104 }
00105
00106 double Options::getDoubleScalar(const mxArray *value)
00107 {
00108 return getScalar<double>(value);
00109 }
00110
00111 bool Options::isIntegerScalar(const mxArray *value)
00112 {
00113 if (!isScalar(value)) return false;
00114
00115 switch(mxGetClassID(value)) {
00116 case mxINT8_CLASS:
00117 case mxUINT8_CLASS:
00118 case mxINT16_CLASS:
00119 case mxUINT16_CLASS:
00120 case mxINT32_CLASS:
00121 case mxUINT32_CLASS:
00122 case mxINT64_CLASS:
00123 case mxUINT64_CLASS:
00124 return true;
00125
00126 default: break;
00127 }
00128
00129 return false;
00130 }
00131
00132 int Options::getIntegerScalar(const mxArray *value)
00133 {
00134 return getScalar<int>(value);
00135 }
00136
00137 bool Options::isLogicalScalar(const mxArray *value)
00138 {
00139 return value && mxIsLogicalScalar(value);
00140 }
00141
00142 bool Options::getLogicalScalar(const mxArray *value)
00143 {
00144 if (isLogicalScalar(value)) return mxIsLogicalScalarTrue(value);
00145 if (isIntegerScalar(value)) return getIntegerScalar(value);
00146 if (isDoubleScalar(value)) return getDoubleScalar(value);
00147 return false;
00148 }
00149
00150 Options::Options()
00151 {
00152 }
00153
00154 Options::Options(int nrhs, const mxArray *prhs[], bool lowerCaseKeys)
00155 {
00156 init(nrhs, prhs, lowerCaseKeys);
00157 }
00158
00159 Options::~Options()
00160 {
00161 }
00162
00163 void Options::init(int nrhs, const mxArray *prhs[], bool lowerCaseKeys)
00164 {
00165
00166 if (nrhs == 1 && mxIsStruct(prhs[0])) {
00167 int n = mxGetNumberOfFields(prhs[0]);
00168 for(int i = 0; i < n; ++i) {
00169 std::string key = mxGetFieldNameByNumber(prhs[0], i);
00170 if (lowerCaseKeys) boost::algorithm::to_lower(key);
00171 set(key, mxGetFieldByNumber(prhs[0], 0, i));
00172 }
00173 return;
00174 }
00175
00176
00177 if (nrhs == 1 && mxIsCell(prhs[0])) {
00178 std::vector<const mxArray *> cell(mxGetNumberOfElements(prhs[0]));
00179 for(int i = 0; i < cell.size(); ++i) {
00180 cell[i] = mxGetCell(prhs[0], i);
00181 }
00182 init(cell.size(), cell.data());
00183 return;
00184 }
00185
00186
00187 if (nrhs % 2 != 0) {
00188 set(std::string(), *prhs);
00189 nrhs--; prhs++;
00190 }
00191
00192
00193 for(; nrhs > 0; nrhs -= 2, prhs += 2) {
00194 if (!isString(prhs[0])) continue;
00195 std::string key = getString(prhs[0]);
00196 if (lowerCaseKeys) boost::algorithm::to_lower(key);
00197 set(key, prhs[1]);
00198 }
00199 }
00200
00201 void Options::merge(const Options& other)
00202 {
00203 for(ArrayMap::const_iterator it = other.arrays_.begin(); it != other.arrays_.end(); ++it) {
00204
00205 arrays_[it->first] = it->second;
00206 used_.erase(it->first);
00207 }
00208 for(StringMap::const_iterator it = other.strings_.begin(); it != other.strings_.end(); ++it) {
00209
00210 strings_[it->first] = it->second;
00211 used_.erase(it->first);
00212 }
00213 for(DoubleMap::const_iterator it = other.doubles_.begin(); it != other.doubles_.end(); ++it) {
00214
00215 doubles_[it->first] = it->second;
00216 used_.erase(it->first);
00217 }
00218 for(IntegerMap::const_iterator it = other.integers_.begin(); it != other.integers_.end(); ++it) {
00219
00220 integers_[it->first] = it->second;
00221 used_.erase(it->first);
00222 }
00223 for(BoolMap::const_iterator it = other.bools_.begin(); it != other.bools_.end(); ++it) {
00224
00225 bools_[it->first] = it->second;
00226 used_.erase(it->first);
00227 }
00228 }
00229
00230 void Options::clear()
00231 {
00232 arrays_.clear();
00233 strings_.clear();
00234 doubles_.clear();
00235 integers_.clear();
00236 bools_.clear();
00237 used_.clear();
00238 }
00239
00240 bool Options::hasKey(const std::string& key) const
00241 {
00242 return arrays_.count(key) || strings_.count(key) || doubles_.count(key) || integers_.count(key) || bools_.count(key);
00243 }
00244
00245 const mxArray *Options::getArray(const std::string &key) const
00246 {
00247 if (arrays_.count(key)) {
00248 used_.insert(key);
00249 return arrays_.at(key);
00250 }
00251 return 0;
00252 }
00253
00254 const std::string& Options::getString(const std::string& key, const std::string& default_value) const
00255 {
00256 if (strings_.count(key) && strings_.at(key).size()) {
00257 used_.insert(key);
00258 return strings_.at(key).front();
00259 }
00260 return default_value;
00261 }
00262
00263 const Options::Strings& Options::getStrings(const std::string &key) const
00264 {
00265 if (strings_.count(key)) {
00266 used_.insert(key);
00267 return strings_.at(key);
00268 }
00269
00270 static const Strings empty;
00271 return empty;
00272 }
00273
00274 double Options::getDouble(const std::string& key, double default_value) const
00275 {
00276 if (doubles_.count(key) && doubles_.at(key).size()) {
00277 used_.insert(key);
00278 return doubles_.at(key).front();
00279 }
00280 return default_value;
00281 }
00282
00283 const Options::Doubles& Options::getDoubles(const std::string &key) const
00284 {
00285 if (doubles_.count(key)) {
00286 used_.insert(key);
00287 return doubles_.at(key);
00288 }
00289 static const Doubles empty;
00290 return empty;
00291 }
00292
00293 int Options::getInteger(const std::string& key, int default_value) const
00294 {
00295 if (integers_.count(key) && integers_.at(key).size()) {
00296 used_.insert(key);
00297 return integers_.at(key).front();
00298 }
00299 if (doubles_.count(key) && doubles_.at(key).size()) {
00300 used_.insert(key);
00301 return doubles_.at(key).front();
00302 }
00303 return default_value;
00304 }
00305
00306 const Options::Integers& Options::getIntegers(const std::string &key) const
00307 {
00308 if (integers_.count(key)) {
00309 used_.insert(key);
00310 return integers_.at(key);
00311 }
00312 static const Integers empty;
00313 return empty;
00314 }
00315
00316 bool Options::getBool(const std::string& key, bool default_value) const
00317 {
00318 if (bools_.count(key) && bools_.at(key).size()) {
00319 used_.insert(key);
00320 return bools_.at(key).front();
00321 }
00322 if (doubles_.count(key) && doubles_.at(key).size()) {
00323 used_.insert(key);
00324 return doubles_.at(key).front();
00325 }
00326 return default_value;
00327 }
00328
00329 const Options::Bools& Options::getBools(const std::string &key) const
00330 {
00331 if (bools_.count(key)) {
00332 used_.insert(key);
00333 return bools_.at(key);
00334 }
00335
00336 static const Bools empty;
00337 return empty;
00338 }
00339
00340 Options &Options::set(const std::string& key, const std::string& value)
00341 {
00342 strings_[key] = Strings(1, value);
00343 return *this;
00344 }
00345
00346 Options &Options::set(const std::string& key, double value)
00347 {
00348 doubles_[key] = Doubles(1, value);
00349 return *this;
00350 }
00351
00352 Options &Options::set(const std::string& key, int value)
00353 {
00354 integers_[key] = Integers(1, value);
00355 return *this;
00356 }
00357
00358 Options &Options::set(const std::string& key, bool value)
00359 {
00360 bools_[key] = Bools(1, value);
00361 return *this;
00362 }
00363
00364 Options &Options::add(const std::string& key, const std::string& value)
00365 {
00366 strings_[key].push_back(value);
00367 return *this;
00368 }
00369
00370 Options &Options::add(const std::string& key, double value)
00371 {
00372 doubles_[key].push_back(value);
00373 return *this;
00374 }
00375
00376 Options &Options::add(const std::string& key, int value)
00377 {
00378 integers_[key].push_back(value);
00379 return *this;
00380 }
00381
00382 Options &Options::add(const std::string& key, bool value)
00383 {
00384 bools_[key].push_back(value);
00385 return *this;
00386 }
00387
00388 Options &Options::set(const std::string &key, const mxArray *value)
00389 {
00390 arrays_[key] = value;
00391
00392 if (isString(value)) {
00393 std::string str = getString(value);
00394 add(key, str);
00395
00396 if (boost::algorithm::iequals(str, "on")) add(key, true);
00397 if (boost::algorithm::iequals(str, "off")) add(key, false);
00398 }
00399
00400 if (isDoubleScalar(value)) add(key, getDoubleScalar(value));
00401 if (isIntegerScalar(value)) add(key, getIntegerScalar(value));
00402 if (isLogicalScalar(value)) add(key, getLogicalScalar(value));
00403
00404 if (mxIsCell(value)) {
00405 std::size_t n = mxGetNumberOfElements(value);
00406 for(mwIndex i = 0; i < n; ++i) {
00407 set(key, mxGetCell(value, i));
00408 }
00409 }
00410 }
00411
00412 void Options::warnUnused() const
00413 {
00414 for(StringMap::const_iterator it = strings_.begin(); it != strings_.end(); ++it) {
00415 if (!used_.count(it->first)) ROSMATLAB_PRINTF("WARNING: unused string argument '%s'", it->first.c_str());
00416 }
00417 for(DoubleMap::const_iterator it = doubles_.begin(); it != doubles_.end(); ++it) {
00418 if (!used_.count(it->first)) ROSMATLAB_PRINTF("WARNING: unused double argument '%s'", it->first.c_str());
00419 }
00420 for(IntegerMap::const_iterator it = integers_.begin(); it != integers_.end(); ++it) {
00421 if (!used_.count(it->first)) ROSMATLAB_PRINTF("WARNING: unused integer argument '%s'", it->first.c_str());
00422 }
00423 for(BoolMap::const_iterator it = bools_.begin(); it != bools_.end(); ++it) {
00424 if (!used_.count(it->first)) ROSMATLAB_PRINTF("WARNING: unused logical argument '%s'", it->first.c_str());
00425 }
00426 for(ArrayMap::const_iterator it = arrays_.begin(); it != arrays_.end(); ++it) {
00427 if (!used_.count(it->first)) ROSMATLAB_PRINTF("WARNING: unused argument '%s'", it->first.c_str());
00428 }
00429 }
00430
00431 void Options::throwOnUnused() const
00432 {
00433 for(StringMap::const_iterator it = strings_.begin(); it != strings_.end(); ++it) {
00434 if (!used_.count(it->first)) throw Exception("unknown string argument '" + it->first + "'");
00435 }
00436 for(DoubleMap::const_iterator it = doubles_.begin(); it != doubles_.end(); ++it) {
00437 if (!used_.count(it->first)) throw Exception("unknown double argument '" + it->first + "'");
00438 }
00439 for(IntegerMap::const_iterator it = integers_.begin(); it != integers_.end(); ++it) {
00440 if (!used_.count(it->first)) throw Exception("unknown integer argument '" + it->first + "'");
00441 }
00442 for(BoolMap::const_iterator it = bools_.begin(); it != bools_.end(); ++it) {
00443 if (!used_.count(it->first)) throw Exception("unknown logical argument '" + it->first + "'");
00444 }
00445 for(ArrayMap::const_iterator it = arrays_.begin(); it != arrays_.end(); ++it) {
00446 if (!used_.count(it->first)) throw Exception("unknown argument '" + it->first + "'");
00447 }
00448 }
00449
00450 }