/* * Copyright (c) 2002-2005 The Regents of The University of Michigan * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer; * redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution; * neither the name of the copyright holders nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include #include #include "base/inifile.hh" #include "base/misc.hh" #include "base/range.hh" #include "base/str.hh" #include "base/trace.hh" #include "sim/config_node.hh" #include "sim/configfile.hh" #include "sim/param.hh" #include "sim/sim_object.hh" using namespace std; //////////////////////////////////////////////////////////////////////// // // BaseParam member definitions // //////////////////////////////////////////////////////////////////////// void BaseParam::die(const string &err) const { context->printErrorProlog(cerr); cerr << " parameter '" << name << "': " << err << endl; abort(); } //////////////////////////////////////////////////////////////////////// // // Param and VectorParam member definitions // // We implement parsing & displaying values for various parameter // types T using a set of overloaded functions: // // - parseParam(string s, T &value) parses s into value // - showParam(ostream &os, T &value) displays value on os // // By making these independent functions, we can reuse the same code // for type T in both Param and VectorParam. // // For enum types, the parseParam function requires additional // arguments, in which case we must specialize the Param::parse and // VectorParam::parse calls as well. // // Type-specific instances come first, followed by more generic // templated versions and their instantiations. // //////////////////////////////////////////////////////////////////////// // // The base implementations use to_number for parsing and '<<' for // displaying, suitable for integer types. // template bool parseParam(const string &s, T &value) { return to_number(s, value); } template void showParam(ostream &os, T const &value) { os << value; } // // Template specializations: // - char (8-bit integer) // - floating-point types // - bool // - string // // Treat 8-bit ints (chars) as ints on output, not as chars template <> void showParam(ostream &os, const char &value) { os << (int)value; } template <> void showParam(ostream &os, const unsigned char &value) { os << (unsigned int)value; } // Use sscanf() for FP types as to_number() only handles integers template <> bool parseParam(const string &s, float &value) { return (sscanf(s.c_str(), "%f", &value) == 1); } template <> bool parseParam(const string &s, double &value) { return (sscanf(s.c_str(), "%lf", &value) == 1); } // Be flexible about what we take for bool template <> bool parseParam(const string &s, bool &value) { const string &ls = to_lower(s); if (ls == "true" || ls == "t" || ls == "yes" || ls == "y" || ls == "1") { value = true; return true; } if (ls == "false" || ls == "f" || ls == "no" || ls == "n" || ls == "0") { value = false; return true; } return false; } // Display bools as strings template <> void showParam(ostream &os, const bool &value) { os << (value ? "true" : "false"); } // String requires no processing to speak of template <> bool parseParam(const string &s, string &value) { value = s; return true; } template <> bool parseParam(const string &s, Range &value) { value = s; return value.valid(); } template <> bool parseParam(const string &s, Range &value) { value = s; return value.valid(); } // // End of parseParam/showParam definitions. Now we move on to // incorporate them into the Param/VectorParam parse() and showValue() // methods. // // These definitions for Param::parse and VectorParam::parse // work for any type for which parseParam() takes only two arguments // (i.e., all the fundamental types like int, bool, etc.), thanks to // overloading. template void Param::parse(const string &s) { if (parseParam(s, value)) { wasSet = true; } else { string err("could not parse \""); err += s; err += "\""; die(err); } } template void VectorParam::parse(const string &s) { if (s.empty()) { wasSet = true; return; } vector tokens; tokenize(tokens, s, ' '); value.resize(tokens.size()); for (int i = 0; i < tokens.size(); i++) { // need to parse into local variable to handle vector, // for which operator[] returns a special reference class // that's not the same as 'bool&', (since it's a packed // vector) T scalar_value; if (!parseParam(tokens[i], scalar_value)) { string err("could not parse \""); err += s; err += "\""; die(err); } // assign parsed value to vector value[i] = scalar_value; } wasSet = true; } // These definitions for Param::showValue() and // VectorParam::showValue() work for any type where showParam() // takes only two arguments (i.e., everything but the SimpleEnum and // MappedEnum classes). template void Param::showValue(ostream &os) const { showParam(os, value); } template void VectorParam::showValue(ostream &os) const { for (int i = 0; i < value.size(); i++) { if (i != 0) { os << " "; } showParam(os, value[i]); } } #ifdef INSURE_BUILD #define INSTANTIATE_PARAM_TEMPLATES(type, typestr) \ void Param::showType(ostream &os) const { os << typestr; } \ void VectorParam::showType(ostream &os) const { \ os << "vector of " << typestr; \ } \ template Param; \ template VectorParam; #else // instantiate all four methods (parse/show, scalar/vector) for basic // types that can use the above templates #define INSTANTIATE_PARAM_TEMPLATES(type, typestr) \ template bool parseParam(const string &s, type &value); \ template void showParam(ostream &os, type const &value); \ template void Param::parse(const string &); \ template void VectorParam::parse(const string &); \ template void Param::showValue(ostream &) const; \ template void VectorParam::showValue(ostream &) const; \ template <> void Param::showType(ostream &os) const { os << typestr; } \ template <> void VectorParam::showType(ostream &os) const { \ os << "vector of " << typestr; \ } #endif INSTANTIATE_PARAM_TEMPLATES(unsigned long long, "ull") INSTANTIATE_PARAM_TEMPLATES(signed long long, "sll") INSTANTIATE_PARAM_TEMPLATES(unsigned long, "uns long") INSTANTIATE_PARAM_TEMPLATES(signed long, "long") INSTANTIATE_PARAM_TEMPLATES(unsigned int, "uns") INSTANTIATE_PARAM_TEMPLATES(signed int, "int") INSTANTIATE_PARAM_TEMPLATES(unsigned short, "uns short") INSTANTIATE_PARAM_TEMPLATES(signed short, "short") INSTANTIATE_PARAM_TEMPLATES(unsigned char, "uns char") INSTANTIATE_PARAM_TEMPLATES(signed char, "char") INSTANTIATE_PARAM_TEMPLATES(float, "float") INSTANTIATE_PARAM_TEMPLATES(double, "double") INSTANTIATE_PARAM_TEMPLATES(bool, "bool") INSTANTIATE_PARAM_TEMPLATES(string, "string") INSTANTIATE_PARAM_TEMPLATES(Range, "uint64 range") INSTANTIATE_PARAM_TEMPLATES(Range, "uint32 range") #undef INSTANTIATE_PARAM_TEMPLATES // // SimpleEnumParam & MappedEnumParam must specialize their parse(), // showValue(), and showType() methods. // // // SimpleEnumParam & SimpleEnumVectorParam // bool parseEnumParam(const char *const *map, const int num_values, const string &s, int &value) { for (int i = 0; i < num_values; ++i) { if (s == map[i]) { value = i; return true; } } return false; } void showEnumParam(ostream &os, const char *const *map, const int num_values, int value) { assert(0 <= value && value < num_values); os << map[value]; } void showEnumType(ostream &os, const char *const *map, const int num_values) { os << "{" << map[0]; for (int i = 1; i < num_values; ++i) os << "," << map[i]; os << "}"; } // // MappedEnumParam & MappedEnumVectorParam // bool parseEnumParam(const EnumParamMap *map, const int num_values, const string &s, int &value) { for (int i = 0; i < num_values; ++i) { if (s == map[i].name) { value = map[i].value; return true; } } return false; } void showEnumParam(ostream &os, const EnumParamMap *map, const int num_values, int value) { for (int i = 0; i < num_values; ++i) { if (value == map[i].value) { os << map[i].name; return; } } // if we can't find a reverse mapping just print the int value os << value; } void showEnumType(ostream &os, const EnumParamMap *map, const int num_values) { os << "{" << map[0].name; for (int i = 1; i < num_values; ++i) os << "," << map[i].name; os << "}"; } template void EnumParam::parse(const string &s) { if (parseEnumParam(map, num_values, s, value)) { wasSet = true; } else { string err("no match for enum string \""); err += s; err += "\""; die(err); } } template void EnumVectorParam::parse(const string &s) { vector tokens; if (s.empty()) { wasSet = true; return; } tokenize(tokens, s, ' '); value.resize(tokens.size()); for (int i = 0; i < tokens.size(); i++) { if (!parseEnumParam(map, num_values, tokens[i], value[i])) { string err("no match for enum string \""); err += s; err += "\""; die(err); } } wasSet = true; } template void EnumParam::showValue(ostream &os) const { showEnumParam(os, map, num_values, value); } template void EnumVectorParam::showValue(ostream &os) const { for (int i = 0; i < value.size(); i++) { if (i != 0) { os << " "; } showEnumParam(os, map, num_values, value[i]); } } template void EnumParam::showType(ostream &os) const { showEnumType(os, map, num_values); } template void EnumVectorParam::showType(ostream &os) const { os << "vector of"; showEnumType(os, map, num_values); } template class EnumParam; template class EnumVectorParam; template class EnumParam; template class EnumVectorParam; //////////////////////////////////////////////////////////////////////// // // SimObjectBaseParam methods // //////////////////////////////////////////////////////////////////////// bool parseSimObjectParam(ParamContext *context, const string &s, SimObject *&value) { SimObject *obj; if (to_lower(s) == "null") { // explicitly set to null by user; assume that's OK obj = NULL; } else { obj = context->resolveSimObject(s); if (obj == NULL) return false; } value = obj; return true; } void SimObjectBaseParam::showValue(ostream &os, SimObject *value) const { os << (value ? value->name() : "null"); } void SimObjectBaseParam::parse(const string &s, SimObject *&value) { if (parseSimObjectParam(context, s, value)) { wasSet = true; } else { string err("could not resolve object name \""); err += s; err += "\""; die(err); } } void SimObjectBaseParam::parse(const string &s, vector&value) { vector tokens; tokenize(tokens, s, ' '); value.resize(tokens.size()); for (int i = 0; i < tokens.size(); i++) { if (!parseSimObjectParam(context, tokens[i], value[i])) { string err("could not resolve object name \""); err += s; err += "\""; die(err); } } wasSet = true; } //////////////////////////////////////////////////////////////////////// // // ParamContext member definitions // //////////////////////////////////////////////////////////////////////// list *ParamContext::ctxList = NULL; ParamContext::ParamContext(const string &_iniSection, InitPhase _initPhase) : iniFilePtr(NULL), // initialized on call to parseParams() iniSection(_iniSection), paramList(NULL), initPhase(_initPhase) { // Put this context on global list for initialization if (initPhase != NoAutoInit) { if (ctxList == NULL) ctxList = new list(); // keep list sorted by ascending initPhase values list::iterator i = ctxList->begin(); list::iterator end = ctxList->end(); for (; i != end; ++i) { if (initPhase <= (*i)->initPhase) { // found where we want to insert break; } } // (fall through case: insert at end) ctxList->insert(i, this); } } void ParamContext::addParam(BaseParam *param) { getParamList()->push_back(param); } void ParamContext::parseParams(IniFile &iniFile) { iniFilePtr = &iniFile; // set object member ParamList::iterator i; for (i = getParamList()->begin(); i != getParamList()->end(); ++i) { string string_value; if (iniFile.find(iniSection, (*i)->name, string_value)) (*i)->parse(string_value); } } // Check parameter values for validity & consistency. Default // implementation is no-op; derive subclass & override to add // actual functionality here. void ParamContext::checkParams() { // nada } // Clean up context-related objects at end of execution. Default // implementation is no-op; derive subclass & override to add actual // functionality here. void ParamContext::cleanup() { // nada } void ParamContext::describeParams(ostream &os) { ParamList::iterator i; for (i = getParamList()->begin(); i != getParamList()->end(); ++i) { BaseParam *p = *i; os << p->name << " ("; p->showType(os); os << "): " << p->description << "\n"; } } void ParamContext::showParams(ostream &os) { ParamList::iterator i; for (i = getParamList()->begin(); i != getParamList()->end(); ++i) { BaseParam *p = *i; if (p->isValid()) { os << p->name << "="; p->showValue(os); os << endl; } else { os << "// "<< p->name << " not specified" << endl; } } } void ParamContext::printErrorProlog(ostream &os) { os << "Parameter error in section [" << iniSection << "]: " << endl; } // // Resolve an object name to a SimObject pointer. The object will be // created as a side-effect if necessary. If the name contains a // colon (e.g., "iq:IQ"), then the object is local (invisible to // outside this context). If there is no colon, the name needs to be // resolved through the configuration hierarchy (only possible for // SimObjectBuilder objects, which return non-NULL for configNode()). // SimObject * ParamContext::resolveSimObject(const string &name) { ConfigNode *n = getConfigNode(); return n ? n->resolveSimObject(name) : NULL; } // // static method: call parseParams() on all registered contexts // void ParamContext::parseAllContexts(IniFile &iniFile) { list::iterator iter; for (iter = ctxList->begin(); iter != ctxList->end(); ++iter) { ParamContext *pc = *iter; pc->parseParams(iniFile); } } // // static method: call checkParams() on all registered contexts // void ParamContext::checkAllContexts() { list::iterator iter; for (iter = ctxList->begin(); iter != ctxList->end(); ++iter) { ParamContext *pc = *iter; pc->checkParams(); } } // // static method: call showParams() on all registered contexts // void ParamContext::showAllContexts(ostream &os) { list::iterator iter; for (iter = ctxList->begin(); iter != ctxList->end(); ++iter) { ParamContext *pc = *iter; os << "[" << pc->iniSection << "]" << endl; pc->showParams(os); os << endl; } } // // static method: call cleanup() on all registered contexts // void ParamContext::cleanupAllContexts() { list::iterator iter; for (iter = ctxList->begin(); iter != ctxList->end(); ++iter) { ParamContext *pc = *iter; pc->cleanup(); } } // // static method: call describeParams() on all registered contexts // void ParamContext::describeAllContexts(ostream &os) { list::iterator iter; for (iter = ctxList->begin(); iter != ctxList->end(); ++iter) { ParamContext *pc = *iter; os << "[" << pc->iniSection << "]\n"; pc->describeParams(os); os << endl; } }