diff --git a/src/colvar/ColvarShortcut.h b/src/colvar/ColvarShortcut.h index c25e9f61aa..0bbc799d33 100644 --- a/src/colvar/ColvarShortcut.h +++ b/src/colvar/ColvarShortcut.h @@ -38,10 +38,9 @@ template void ColvarShortcut::registerKeywords(Keywords& keys ) { T::registerKeywords( keys ); keys.remove("NO_ACTION_LOG"); - unsigned nkeys = keys.size(); - for(unsigned i=0; i::ColvarShortcut(const ActionOptions&ao): Action(ao), ActionShortcut(ao) { bool scalar=true; - unsigned nkeys = keywords.size(); if( getName()=="MASS" || getName()=="CHARGE" || getName()=="POSITION" ) { std::string inpt; parse("ATOMS",inpt); @@ -62,12 +60,12 @@ ColvarShortcut::ColvarShortcut(const ActionOptions&ao): scalar=false; } } - for(unsigned i=0; i0 ) { - readInputLine( getShortcutLabel() + ": " + getName() + "_VECTOR " + keywords.get(i) + "1=" + inpt + " " + convertInputLineToString() ); + readInputLine( getShortcutLabel() + ": " + getName() + "_VECTOR " + key + "1=" + inpt + " " + convertInputLineToString() ); scalar=false; break; } diff --git a/src/colvar/Distance.cpp b/src/colvar/Distance.cpp index f069d05f94..02c7a28f3a 100644 --- a/src/colvar/Distance.cpp +++ b/src/colvar/Distance.cpp @@ -152,17 +152,18 @@ PLUMED_REGISTER_ACTION(DistanceMulti,"DISTANCE_VECTOR") void Distance::registerKeywords( Keywords& keys ) { Colvar::registerKeywords( keys ); keys.setDisplayName("DISTANCE"); + constexpr auto scalarOrVector = Keywords::componentType::scalar | Keywords::componentType::vector; keys.add("atoms","ATOMS","the pair of atom that we are calculating the distance between"); keys.addFlag("COMPONENTS",false,"calculate the x, y and z components of the distance separately and store them as label.x, label.y and label.z"); keys.addFlag("SCALED_COMPONENTS",false,"calculate the a, b and c scaled components of the distance separately and store them as label.a, label.b and label.c"); - keys.addOutputComponent("x","COMPONENTS","scalar/vector","the x-component of the vector connecting the two atoms"); - keys.addOutputComponent("y","COMPONENTS","scalar/vector","the y-component of the vector connecting the two atoms"); - keys.addOutputComponent("z","COMPONENTS","scalar/vector","the z-component of the vector connecting the two atoms"); - keys.addOutputComponent("a","SCALED_COMPONENTS","scalar/vector","the normalized projection on the first lattice vector of the vector connecting the two atoms"); - keys.addOutputComponent("b","SCALED_COMPONENTS","scalar/vector","the normalized projection on the second lattice vector of the vector connecting the two atoms"); - keys.addOutputComponent("c","SCALED_COMPONENTS","scalar/vector","the normalized projection on the third lattice vector of the vector connecting the two atoms"); + keys.addOutputComponent("x","COMPONENTS",scalarOrVector,"the x-component of the vector connecting the two atoms"); + keys.addOutputComponent("y","COMPONENTS",scalarOrVector,"the y-component of the vector connecting the two atoms"); + keys.addOutputComponent("z","COMPONENTS",scalarOrVector,"the z-component of the vector connecting the two atoms"); + keys.addOutputComponent("a","SCALED_COMPONENTS",scalarOrVector,"the normalized projection on the first lattice vector of the vector connecting the two atoms"); + keys.addOutputComponent("b","SCALED_COMPONENTS",scalarOrVector,"the normalized projection on the second lattice vector of the vector connecting the two atoms"); + keys.addOutputComponent("c","SCALED_COMPONENTS",scalarOrVector,"the normalized projection on the third lattice vector of the vector connecting the two atoms"); keys.add("hidden","NO_ACTION_LOG","suppresses printing from action on the log"); - keys.setValueDescription("scalar/vector","the DISTANCE between this pair of atoms"); + keys.setValueDescription(scalarOrVector,"the DISTANCE between this pair of atoms"); } Distance::Distance(const ActionOptions&ao): diff --git a/src/colvar/MultiColvarTemplate.h b/src/colvar/MultiColvarTemplate.h index 5f95512b9b..8d076a30b6 100644 --- a/src/colvar/MultiColvarTemplate.h +++ b/src/colvar/MultiColvarTemplate.h @@ -54,8 +54,8 @@ void MultiColvarTemplate::registerKeywords(Keywords& keys ) { T::registerKeywords( keys ); unsigned nkeys = keys.size(); for(unsigned i=0; i ActionRegister::create(const std::vector & images auto content=get(images,ao.line[0]); Keywords keys; - keys.thisactname = ao.line[0]; + //the name of the function is not clear but does `keys.thisactname = ao.line[0];` + keys.setDisplayName(ao.line[0]); content.keys(keys); ActionOptions nao( ao,keys ); auto fullPath=getFullPath(images,ao.line[0]); @@ -85,7 +86,8 @@ bool ActionRegister::printTemplate(const std::string& action, bool include_optio //no need to insert the try/catch block: check will ensure that action is known if( check(action) ) { Keywords keys; - keys.thisactname = action; + //the name of the function is not clear but does `keys.thisactname = action;` + keys.setDisplayName(action); get(action).keys(keys); keys.print_template(action, include_optional); return true; @@ -110,7 +112,8 @@ ActionRegister::ID ActionRegister::add(std::string key,creator_pointer cp,keywor bool ActionRegister::getKeywords(const std::string& action, Keywords& keys) { //no need to insert the try/catch block: check will ensure that action is known if(check(action)) { - keys.thisactname = action; + //the name of the function is not clear but does `keys.thisactname = action;` + keys.setDisplayName(action); get(action).keys(keys); return true; } @@ -119,7 +122,8 @@ bool ActionRegister::getKeywords(const std::string& action, Keywords& keys) { void ActionRegister::getKeywords(const std::vector & images, const std::string& action, Keywords& keys) { auto content=get(images,action); - keys.thisactname = action; + //the name of the function is not clear but does `keys.thisactname = action;` + keys.setDisplayName(action); content.keys(keys); } diff --git a/src/core/ActionShortcut.cpp b/src/core/ActionShortcut.cpp index b3e4602650..5bb296125d 100644 --- a/src/core/ActionShortcut.cpp +++ b/src/core/ActionShortcut.cpp @@ -34,8 +34,8 @@ void ActionShortcut::registerKeywords( Keywords& keys ) { } void ActionShortcut::readShortcutKeywords( const Keywords& keys, std::map& keymap ) { - for(unsigned i=0; i0 ) { @@ -80,15 +80,11 @@ void ActionShortcut::readInputLine( const std::string& input, bool saveline ) { std::vector words=Tools::getWords(input); Tools::interpretLabel(words); // Check if this action name has been registered - bool founds=false, found = std::find(keywords.neededActions.begin(), keywords.neededActions.end(), words[0] )!=keywords.neededActions.end(); + bool founds=false; + bool found = keywords.isActionNeeded(words[0]); // Check if we are just calling something like SUM_VECTOR using just SUM. if( !found && words[0].find(getName())!=std::string::npos ) { - for(unsigned j=0 ; jgetLabel(); if( av_label == getShortcutLabel() && av->getNumberOfComponents()==1 ) { savedOutputs.push_back( av_label ); - plumed_massert( keywords.componentHasCorrectType(".#!value", (av->copyOutput(0))->getRank(), (av->copyOutput(0))->hasDerivatives() ), "documentation for type of value is incorrect"); + plumed_massert( keywords.componentHasCorrectType(".#!value", + (av->copyOutput(0))->getRank(), (av->copyOutput(0))->hasDerivatives() ), + "documentation for type of value is incorrect"); } else { - for(unsigned i=0; icopyOutput(0))->getRank(), (av->copyOutput(0))->hasDerivatives() ), "documentation for type of component " + keywords.cnames[i] + " is incorrect"); - } else if( keywords.getOutputComponentFlag(keywords.cnames[i])!="default" ) { - std::string thisflag = keywords.getOutputComponentFlag(keywords.cnames[i]); - if( keywords.numbered(thisflag) && av_label.find(getShortcutLabel() + "_" + keywords.cnames[i])!=std::string::npos ) { + plumed_massert( keywords.componentHasCorrectType(cname, + (av->copyOutput(0))->getRank(), + (av->copyOutput(0))->hasDerivatives() ), + "documentation for type of component " + cname + " is incorrect"); + } else if( keywords.getOutputComponentFlag(cname)!="default" ) { + std::string thisflag = keywords.getOutputComponentFlag(cname); + if( keywords.numbered(thisflag) && av_label.find(getShortcutLabel() + "_" + cname)!=std::string::npos ) { savedOutputs.push_back( av_label ); - plumed_massert( keywords.componentHasCorrectType(keywords.cnames[i], (av->copyOutput(0))->getRank(), (av->copyOutput(0))->hasDerivatives() ), "documentation for type of component " + keywords.cnames[i] + " is incorrect"); + plumed_massert( keywords.componentHasCorrectType(cname, + (av->copyOutput(0))->getRank(), + (av->copyOutput(0))->hasDerivatives() ), + "documentation for type of component " + cname + " is incorrect"); } } } @@ -186,9 +190,9 @@ void ActionShortcut::addToSavedInputLines( const std::string& line ) { Keywords thiskeys; actionRegister().getKeywords( actname, thiskeys ); std::vector numberedkeys; - for(unsigned i=0; i0 && actname!="CONCATENATE" ) { diff --git a/src/core/ActionWithArguments.cpp b/src/core/ActionWithArguments.cpp index 920e979c1e..06e156e0f3 100644 --- a/src/core/ActionWithArguments.cpp +++ b/src/core/ActionWithArguments.cpp @@ -49,7 +49,7 @@ void ActionWithArguments::registerKeywords(Keywords& keys) { void ActionWithArguments::parseArgumentList(const std::string&key,std::vector&arg) { if( keywords.getArgumentType(key).length()==0 ) { - warning("keyword " + key + " for reading arguments should is registered using Keyword::add rather than Keyword::addInputKeyword. The keyword will thus not appear in the correct place in the manual"); + warning("keyword " + key + " for reading arguments is registered using Keyword::add rather than Keyword::addInputKeyword. The keyword will thus not appear in the correct place in the manual"); } std::string def; std::vector c; @@ -67,7 +67,7 @@ void ActionWithArguments::parseArgumentList(const std::string&key,std::vector&arg) { if( keywords.getArgumentType(key).length()==0 ) { - warning("keyword " + key + " for reading argument should is registered using Keyword::add rather than Keyword::addInputKeyword. The keyword will thus not appear in the correct place in the manual"); + warning("keyword " + key + " for reading argument is registered using Keyword::add rather than Keyword::addInputKeyword. The keyword will thus not appear in the correct place in the manual"); } std::vector c; arg.clear(); diff --git a/src/core/CLTool.cpp b/src/core/CLTool.cpp index b2ab576ffd..76ae74e407 100644 --- a/src/core/CLTool.cpp +++ b/src/core/CLTool.cpp @@ -78,7 +78,7 @@ bool CLTool::readCommandLineArgs( int argc, char**argv, FILE*out ) { // Set all flags to default false for(unsigned k=0; k(thiskey,"false")); } @@ -96,7 +96,7 @@ bool CLTool::readCommandLineArgs( int argc, char**argv, FILE*out ) { } else { bool found=false; for(unsigned k=0; k. ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */ +#include "BitmaskEnum.h" diff --git a/src/tools/BitmaskEnum.h b/src/tools/BitmaskEnum.h new file mode 100644 index 0000000000..8c542d2ada --- /dev/null +++ b/src/tools/BitmaskEnum.h @@ -0,0 +1,167 @@ +/* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + Copyright (c) 2024 The plumed team + (see the PEOPLE file at the root of the distribution for a list of names) + + See http://www.plumed.org for more information. + + This file is part of plumed, version 2. + + plumed is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + plumed is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with plumed. If not, see . ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */ + +#ifndef __PLUMED_tools_BitmaskEnum_h +#define __PLUMED_tools_BitmaskEnum_h +#include + +namespace PLMD { +namespace enum_traits { +/** @brief struct for setting up bitmask operations on enum types + + example usage: specialize with extra traits (see it in action in tools/Keywords.h) + Example: + + Please note that in the example the `0` is not a named value (it is reserved + as a result of mask not matching masks) and the others values are implemented as single bit + @code{.cpp} + enum class argType {scalar=1,grid=1<<2,vector=1<<3,matrix=1<<4}; + template<> + struct BitmaskEnum< argType > { + static constexpr bool has_valid = true; + static constexpr bool has_bit_or = true; + static constexpr bool has_bit_and = true; + }; + @endcode + Currenlty we have implemented: + @code{.cpp} + static constexpr bool has_valid = true; + @endcode + that activates the ::valid(enumtype) function + @code{.cpp} + static constexpr bool has_bit_or = true; + @endcode + that activates the operator|(enumtype , enumtype ) + @code{.cpp} + static constexpr bool has_bit_and = true; + @endcode + that activates the operator&(enumtype , enumtype ) + + @see valid(enumtype) for a complete example + @see operator&(enumtype, enumtype) + @see operator|(enumtype, enumtype) +*/ +template< typename enum_type > +struct BitmaskEnum { +}; +} // namespace enum_traits + +/** + @brief Perform a bitwise AND between two enum values. + + @param a The first enum value. + @param b The second enum value. + @return The result of performing a bitwise AND between the two values. + + This operator is only available for enum types that have a specialization of +enum_traits::BitmaskEnum with the `has_bit_and` trait enabled. + +Useful for checking composed values agains masks. + +Note that the value may be a 0, and if you do not have defined the 0 as a named +value you should use valid(enumtype) to check it. + +@see valid(enumtype) for a complete example +@see operator|(enumtype, enumtype) +*/ +template< typename enumtype > // SFINAE makes function contingent on trait +constexpr typename std::enable_if_t< enum_traits::BitmaskEnum< enumtype >::has_bit_and,enumtype> +operator&( enumtype a, enumtype b ) { + return static_cast(static_cast>(a) & + static_cast>(b)); +} + +/** + @brief Perform a bitwise OR between two enum values. + + @param a The first enum value. + @param b The second enum value. + @return The result of performing a bitwise OR between the two values. + +This operator is only available for enum types that have a specialization of +enum_traits::BitmaskEnum with the `has_bit_or` trait enabled. + +The principal use is to compose named enum values into masks or combined options. + +@see valid(enumtype) for a complete example +@see operator&(enumtype, enumtype) +*/ +template< typename enumtype > // SFINAE makes function contingent on trait +constexpr typename std::enable_if_t< enum_traits::BitmaskEnum< enumtype >::has_bit_or,enumtype> +operator|( enumtype a, enumtype b ) { + return static_cast(static_cast>(a) | + static_cast>(b)); +} + +/** + @brief Test if an enum value is valid. + + @param a The enum value to test. + @return true if the enum value is not equal to zero, false otherwise. + + +This operator is only available for enum types that have a specialization of +enum_traits::BitmaskEnum with the `has_valid` trait enabled. + + @code + // Note: explicit declarations of the values, and + enum class myenum { A=1,B=1<<1,C=1<<2 }; + //then activate the functions `&`, `|` and `valid` + template<> + struct BitmaskEnum< myenum > { + static constexpr bool has_valid = true; + static constexpr bool has_bit_or = true; + static constexpr bool has_bit_and = true; + }; + //...code... + myenum val = myenum::A | myenum::C; + std::cout <<"val is "<< int(val) << "\n"; + if(PLMD::valid( val & myenum::A)) { + std::cout << "val has A\n"; + } + if(PLMD::valid(val & myenum::B)) { + std::cout << "val has B\n"; + } + if(PLMD::valid(val & myenum::C)) { + std::cout << "val has C\n"; + } + if(PLMD::valid(val & (myenum::A | myenum::C))) { + std::cout << "val has C and A\n"; + } + //will produce: + ///>val is 5 + ///>val has A + ///>val has C + ///>val has C and A + @endcode + +@see operator|(enumtype, enumtype) +@see operator&(enumtype, enumtype) +*/ +template< typename enumtype > // SFINAE makes function contingent on trait +constexpr typename std::enable_if_t< enum_traits::BitmaskEnum< enumtype >::has_valid,bool> +valid( enumtype a) { + return static_cast>(a)!=0; +} +} // namespace PLMD + +#endif //__PLUMED_tools_BitmaskEnum_h diff --git a/src/tools/Keywords.cpp b/src/tools/Keywords.cpp index d14eb195a8..4c9b2d48fb 100644 --- a/src/tools/Keywords.cpp +++ b/src/tools/Keywords.cpp @@ -24,315 +24,499 @@ #include "Tools.h" #include #include +#include + +template +void erase_remove(std::vector& vec, const T& value) { + vec.erase(std::remove(vec.begin(), vec.end(), value), vec.end()); +} + +void erase_remove(std::string& vec, const char value) { + vec.erase(std::remove(vec.begin(), vec.end(), value), vec.end()); +} + +//few definition to avoid rewriting the too many times the same docstring +#define NUMBERED_DOCSTRING(key) ". You can use multiple instances of this keyword i.e. " \ + + std::string(key) +"1, " + std::string(key) + "2, " + std::string(key) + "3..." + namespace PLMD { -Keywords::KeyType::KeyType( const std::string& type ) { - if( type=="compulsory" ) { - style=compulsory; - } else if( type=="flag" ) { - style=flag; - } else if( type=="optional" ) { - style=optional; - } else if( type.find("atoms")!=std::string::npos || type.find("residues")!=std::string::npos ) { - style=atoms; - } else if( type=="hidden" ) { - style=hidden; - } else if( type=="vessel" ) { - style=vessel; - } else { - plumed_massert(false,"invalid keyword specifier " + type); +std::string toString(Keywords::argType at) { + //the simple cases + switch (at) { + case Keywords::argType::scalar: + return "scalar"; + + case Keywords::argType::vector: + return "vector"; + + case Keywords::argType::matrix: + return "matrix"; + + case Keywords::argType::grid: + return "grid"; } + //the not simple cases + { + std::string ret=""; + std::string next=""; + if(valid(at & Keywords::argType::scalar)) { + ret+="scalar"; + next="/"; + } + if(valid(at & Keywords::argType::vector)) { + ret+=next+"vector"; + next="/"; + } + if(valid(at & Keywords::argType::matrix)) { + ret+=next+"matrix"; + next="/"; + } + if(valid(at & Keywords::argType::grid)) { + ret+=next+"grid"; + } + return ret; + } + //the return is outsids so the compile should block the compilation + //when expanding the enum without updating the toString + return ""; } -void Keywords::KeyType::setStyle( const std::string& type ) { +Keywords::argType stoat(std::string_view str) { + if(auto pos = str.find("/"); pos!=str.npos) { + //here we can express that we do not want certain combinations + auto val=stoat(str.substr(0,pos)); + return val | stoat(str.substr(pos+1)); + } + if (str == "scalar") { + return Keywords::argType::scalar; + } + if (str == "vector") { + return Keywords::argType::vector; + } + if (str == "matrix") { + return Keywords::argType::matrix; + } + if (str == "grid") { + return Keywords::argType::grid; + } + // Handle the case where the string does not match any enum value. + plumed_massert(false,"invalid argType specifier " + std::string(str)); +} + +std::string toString(Keywords::componentType at) { + switch (at) { + case Keywords::componentType::scalar: + return "scalar"; + + case Keywords::componentType::vector: + return "vector"; + + case Keywords::componentType::matrix: + return "matrix"; + + case Keywords::componentType::grid: + return "grid"; + + case Keywords::componentType::atom: + return "atom"; + + case Keywords::componentType::atoms: + return "atoms"; + } + //the not simple cases + { + std::string ret=""; + std::string next=""; + if(valid(at & Keywords::componentType::scalar)) { + ret+="scalar"; + next="/"; + } + if(valid(at & Keywords::componentType::vector)) { + ret+=next+"vector"; + next="/"; + } + if(valid(at & Keywords::componentType::matrix)) { + ret+=next+"matrix"; + next="/"; + } + if(valid(at & Keywords::componentType::grid)) { + ret+=next+"grid"; + next="/"; + } + //I do not think these two are necessary + if(valid(at & Keywords::componentType::atom)) { + ret+=next+"atom"; + next="/"; + } + if(valid(at & Keywords::componentType::atoms)) { + ret+=next+"atoms"; + } + return ret; + } + //the return is outsids so the compile should block the compilation + //when expanding the enum without updating the toString + return ""; +} + +inline Keywords::componentType stoct(std::string_view str) { + if(auto pos = str.find("/"); pos!=str.npos) { + //here we can express that we do not want certain combinations + auto val=stoct(str.substr(0,pos)); + return val | stoct(str.substr(pos+1)); + } + if (str == "scalar") { + return Keywords::componentType::scalar; + } + if (str == "grid") { + return Keywords::componentType::grid; + } + if (str == "vector") { + return Keywords::componentType::vector; + } + if (str == "matrix") { + return Keywords::componentType::matrix; + } + if (str == "atom") { + return Keywords::componentType::atom; + } + if (str == "atoms") { + return Keywords::componentType::atoms; + } + + plumed_massert(false,"invalid componentType specifier " + std::string(str)); +} + +Keywords::KeyType::keyStyle Keywords::KeyType::keyStyleFromString(std::string_view type ) { if( type=="compulsory" ) { - style=compulsory; + return keyStyle::compulsory; } else if( type=="flag" ) { - style=flag; + return keyStyle::flag; } else if( type=="optional" ) { - style=optional; - } else if( type.find("atoms")!=std::string::npos || type.find("residues")!=std::string::npos ) { - style=atoms; + return keyStyle::optional; + //this is special: some atoms keywords have extra characters usually a "-" followed by a number + } else if( type.find("atoms")!=type.npos || type.find("residues")!=type.npos) { + return keyStyle::atoms; } else if( type=="hidden" ) { - style=hidden; + return keyStyle::hidden; } else if( type=="vessel" ) { - style=vessel; + return keyStyle::vessel; } else { - plumed_massert(false,"invalid keyword specifier " + type); + plumed_massert(false,"invalid keyword specifier " + std::string(type)); } } -std::string Keywords::getStyle( const std::string & k ) const { - plumed_massert( types.count(k), "Did not find keyword " + k ); - return (types.find(k)->second).toString(); +Keywords::KeyType::KeyType( std::string_view type ) + : style(keyStyleFromString(type)) {} + +Keywords::KeyType::KeyType( Keywords::KeyType::keyStyle type ) + : style(type) {} + +void Keywords::KeyType::setStyle( std::string_view type ) { + style=keyStyleFromString(type); +} + +Keywords::keyInfo::keyInfo() + : type(Keywords::KeyType::keyStyle::unknown), + docstring(""), + defaultValue(std::monostate()), + allowmultiple(false) +{} + +Keywords::keyInfo& Keywords::keyInfo::setType(Keywords::KeyType t) { + type=t; + return *this; +} +Keywords::keyInfo& Keywords::keyInfo::setDocString(std::string_view d) { + docstring=d; + return *this; +} +Keywords::keyInfo& Keywords::keyInfo::setDefaultValue(std::string_view d) { + defaultValue=std::string(d); + return *this; +} +Keywords::keyInfo& Keywords::keyInfo::setDefaultFlag(bool a) { + defaultValue=a; + return *this; +} +Keywords::keyInfo& Keywords::keyInfo::setArgumentType(argType a) { + argument_type=a; + return *this; +} +Keywords::keyInfo& Keywords::keyInfo::setAllowMultiple(bool a) { + allowmultiple=a; + return *this; +} +bool Keywords::keyInfo::isArgument() const { + return std::holds_alternative(argument_type); +} + +Keywords::component::component(): +//the 0 ensures something that always fails unles explicitly set + type(static_cast(0)) {}; +Keywords::component& Keywords::component::setKey(std::string_view k) { + key=k; + return *this; +} +Keywords::component& Keywords::component::setDocstring(std::string_view d) { + docstring=d; + return *this; +} +Keywords::component& Keywords::component::setType(componentType t) { + type=t; + return *this; +} + +std::string Keywords::getStyle( const std::string& k ) const { + plumed_massert( exists(k)||reserved(k), "Did not find keyword " + k ); + return (keywords.at(k).type).toString(); } void Keywords::add( const Keywords& newkeys ) { - newkeys.copyData( keys, reserved_keys, types, allowmultiple, documentation, booldefs, numdefs, atomtags, cnames, ckey, cdocs ); -} - -void Keywords::copyData( std::vector& kk, std::vector& rk, std::map& tt, std::map& am, - std::map& docs, std::map& bools, std::map& nums, - std::map& atags, std::vector& cnam, std::map& ck, - std::map& cd ) const { - for(unsigned i=0; i( thiskey,types.find(thiskey)->second) ); - if( (types.find(thiskey)->second).isAtomList() ) { - atags.insert( std::pair( thiskey,atomtags.find(thiskey)->second) ); - } - plumed_massert( allowmultiple.count( thiskey ), "no numbered data on keyword " + thiskey + " to copy" ); - am.insert( std::pair(thiskey,allowmultiple.find(thiskey)->second) ); - plumed_massert( documentation.count( thiskey ), "no documentation for keyword " + thiskey + " to copy" ); - docs.insert( std::pair(thiskey,documentation.find(thiskey)->second) ); - if( booldefs.count( thiskey ) ) { - bools.insert( std::pair( thiskey,booldefs.find(thiskey)->second) ); - } - if( numdefs.count( thiskey ) ) { - nums.insert( std::pair( thiskey,numdefs.find(thiskey)->second) ); - } - } - for(unsigned i=0; i( thiskey,types.find(thiskey)->second) ); - if( (types.find(thiskey)->second).isAtomList() ) { - atags.insert( std::pair( thiskey,atomtags.find(thiskey)->second) ); - } - plumed_massert( allowmultiple.count( thiskey ), "no numbered data on keyword " + thiskey + " to copy" ); - am.insert( std::pair(thiskey,allowmultiple.find(thiskey)->second) ); - plumed_massert( documentation.count( thiskey ), "no documentation for keyword " + thiskey + " to copy" ); - docs.insert( std::pair(thiskey,documentation.find(thiskey)->second) ); - if( booldefs.count( thiskey ) ) { - bools.insert( std::pair( thiskey,booldefs.find(thiskey)->second) ); - } - if( numdefs.count( thiskey ) ) { - nums.insert( std::pair( thiskey,numdefs.find(thiskey)->second) ); - } - } - for(unsigned i=0; i( thisnam, ckey.find(thisnam)->second) ); - plumed_massert( cdocs.count( thisnam ), "no documentation on component " + thisnam + " to copy" ); - cd.insert( std::pair( thisnam, cdocs.find(thisnam)->second) ); - } -} - -void Keywords::reserve( const std::string & t, const std::string & k, const std::string & d ) { - plumed_assert( !exists(k) && !reserved(k) ); - std::string fd, lowkey=k; - // Convert to lower case - std::transform(lowkey.begin(),lowkey.end(),lowkey.begin(),[](unsigned char c) { - return std::tolower(c); - }); -// Remove any underscore characters - for(unsigned i=0;; ++i) { - std::size_t num=lowkey.find_first_of("_"); - if( num==std::string::npos ) { - break; - } - lowkey.erase( lowkey.begin() + num, lowkey.begin() + num + 1 ); - } - if( t=="vessel" ) { - fd = d + " The final value can be referenced using label." + lowkey; - if(d.find("flag")==std::string::npos) - fd += ". You can use multiple instances of this keyword i.e. " + - k +"1, " + k + "2, " + k + "3... The corresponding values are then " + //copies data from + //loop on the declared keys + for(const auto& thiskey:newkeys.keys) { + plumed_massert( !exists(thiskey), "keyword " + thiskey + " is in twice" ); + plumed_massert( !reserved(thiskey), "keyword " + thiskey + " is in twice" ); + keywords[thiskey] = newkeys.keywords.at(thiskey); + keys.emplace_back( thiskey ); + } + //loop on the reserved keys + for (const auto&thiskey : newkeys.reserved_keys) { + plumed_massert( !exists(thiskey), "keyword " + thiskey + " is in twice" ); + plumed_massert( !reserved(thiskey), "keyword " + thiskey + " is in twice" ); + + keywords[thiskey] = newkeys.keywords.at(thiskey); + reserved_keys.emplace_back( thiskey ); + } + for (const auto& thisnam : newkeys.cnames) { + plumed_massert( components.find(thisnam)!=components.end(), "keyword for component" + thisnam + " is in twice" ); + cnames.push_back( thisnam ); + components[thisnam]=newkeys.components.at(thisnam); + } +} + +void Keywords::addOrReserve( std::string_view keytype, + std::string_view key, + std::string_view docstring, + const bool reserve ) { + plumed_massert(!exists(key), "keyword " + std::string(key) + " has already been registered"); + plumed_massert(!reserved(key),"keyword " + std::string(key) + " has already been reserved"); + std::string t_type{keytype}; + bool isNumbered = keytype=="numbered"; + if( isNumbered ) { + t_type="optional"; + } + //let's fail asap in case of typo + auto type = KeyType(t_type); + if (!reserve) { + plumed_massert( !type.isFlag(), "use addFlag() to register a flag keyword (" + std::string(key) + ")"); + plumed_massert( !type.isVessel(), "use reserve() to register a vessel keyword (" + std::string(key) + ")"); + } + + std::string fd{docstring}; + bool allowMultiple= false; + if( type.isVessel() && reserve) { + // Convert to lower case + std::string lowkey{key}; + std::transform(lowkey.begin(),lowkey.end(),lowkey.begin(),[](unsigned char c) { + return std::tolower(c); + }); + // Remove any underscore characters + erase_remove(lowkey, '_'); + + fd += " The final value can be referenced using label." + lowkey; + if(docstring.find("flag")==std::string::npos) { + fd += NUMBERED_DOCSTRING(key) " The corresponding values are then " "referenced using label."+ lowkey +"-1, label." + lowkey + "-2, label." + lowkey + "-3..."; - allowmultiple.insert( std::pair(k,true) ); - types.insert( std::pair(k,KeyType("vessel")) ); - } else if( t=="numbered" ) { - fd = d + ". You can use multiple instances of this keyword i.e. " + k +"1, " + k + "2, " + k + "3..."; - allowmultiple.insert( std::pair(k,true) ); - types.insert( std::pair(k,KeyType("optional")) ); - } else { - fd = d; - if( t=="atoms" && isaction ) { - fd = d + ". For more information on how to specify lists of atoms see \\ref Group"; } - allowmultiple.insert( std::pair(k,false) ); - types.insert( std::pair(k,KeyType(t)) ); - if( (types.find(k)->second).isAtomList() ) { - atomtags.insert( std::pair(k,t) ); + allowMultiple = true; + } else if( isNumbered ) { + fd += NUMBERED_DOCSTRING(key); + allowMultiple = true; + } + + keywords[std::string(key)] = keyInfo() + .setType(type) + .setDocString(fd) + .setAllowMultiple(allowMultiple); + if( type.isAtomList() ) { + //keytype may be "residues" or something like "atoms-3" + keywords.find(key)->second.atomtag=keytype; + if (isaction && keytype == "atoms") { //this narrow the doctrstring ONLY to atoms + keywords.find(key)->second.docstring+= ". For more information on how to specify lists of atoms see \\ref Group"; } } - documentation.insert( std::pair(k,fd) ); - reserved_keys.push_back(k); + if (reserve) { + reserved_keys.emplace_back(key); + } else { + keys.emplace_back(key); + } +} + +void Keywords::reserve( std::string_view keytype, + std::string_view key, + std::string_view docstring ) { + //If you modify this function, please update also the add with three arguments + addOrReserve(keytype,key,docstring,true); } -void Keywords::reserveFlag( const std::string & k, const bool def, const std::string & d ) { - plumed_assert( !exists(k) && !reserved(k) ); +void Keywords::reserveFlag(const std::string & key, const bool defaultValue, const std::string & docstring ) { + plumed_assert( !exists(key) && !reserved(key) ); std::string defstr; - if( def ) { + if( defaultValue ) { defstr="( default=on ) "; } else { defstr="( default=off ) "; } - types.insert( std::pair(k,KeyType("flag")) ); - std::string fd,lowkey=k; - std::transform(lowkey.begin(),lowkey.end(),lowkey.begin(),[](unsigned char c) { - return std::tolower(c); - }); - fd=defstr + d; - documentation.insert( std::pair(k,fd) ); - allowmultiple.insert( std::pair(k,false) ); - booldefs.insert( std::pair(k,def) ); - reserved_keys.push_back(k); + + keywords[key] = keyInfo() + .setType(KeyType{KeyType::keyStyle::flag}) + .setDocString(defstr + docstring) + .setAllowMultiple(false) + .setDefaultFlag(defaultValue); + reserved_keys.emplace_back(key); } -void Keywords::use( const std::string & k ) { - plumed_massert( reserved(k), "the " + k + " keyword is not reserved"); - for(unsigned i=0; isecond).setStyle(style); - if( (types.find(k)->second).isVessel() ) { - allowmultiple[k]=true; + keywords.at(k).type.setStyle(style); + if( (keywords.at(k).type).isVessel() ) { + keywords.at(k).allowmultiple=true; } - if( (types.find(k)->second).isAtomList() ) { - atomtags.insert( std::pair(k,style) ); + if( (keywords.at(k).type).isAtomList() ) { + keywords.at(k).atomtag=style; } } -void Keywords::add( const std::string & t, const std::string & k, const std::string & d ) { - plumed_massert( !exists(k) && t!="flag" && !reserved(k) && t!="vessel", "keyword " + k + " has already been registered"); - std::string fd; - if( t=="numbered" ) { - fd=d + ". You can use multiple instances of this keyword i.e. " + k +"1, " + k + "2, " + k + "3..."; - allowmultiple.insert( std::pair(k,true) ); - types.insert( std::pair(k, KeyType("optional")) ); - } else { - fd=d; - allowmultiple.insert( std::pair(k,false) ); - types.insert( std::pair(k,KeyType(t)) ); - if( (types.find(k)->second).isAtomList() ) { - atomtags.insert( std::pair(k,t) ); - } - } - if( t=="atoms" && isaction ) { - fd = d + ". For more information on how to specify lists of atoms see \\ref Group"; - } - documentation.insert( std::pair(k,fd) ); - keys.push_back(k); +void Keywords::add(std::string_view keytype, + std::string_view key, + std::string_view docstring ) { + //the 'false' deactivates the "reserve mode" + addOrReserve(keytype,key,docstring,false); } -void Keywords::addInputKeyword( const std::string & t, const std::string & k, const std::string & ttt, const std::string & d ) { - if( exists(k) ) { - remove(k); - argument_types[k] = ttt; - } else { - argument_types.insert( std::pair(k,ttt) ); - } - add( t, k, d ); +void Keywords::addInputKeyword( const std::string & keyType, + const std::string & key, + const std::string & datatype, + const std::string & docstring ) { + addInputKeyword(keyType,key,stoat(datatype),docstring); } -void Keywords::addInputKeyword( const std::string & t, const std::string & k, const std::string & ttt, const std::string& def, const std::string & d ) { - if( exists(k) ) { - remove(k); - argument_types[k] = ttt; - } else { - argument_types.insert( std::pair(k,ttt) ); +void Keywords::addInputKeyword( const std::string & keyType, + const std::string & key, + argType datatype, + const std::string & docstring ) { + if( exists(key) ) { + remove(key); + } + //insert({k,datatype}) Inserts element(s) into the container, if the container doesn't already contain an element with an equivalent key.[cit.] + //operator[] inserts if the key doesn't exist, or overwrites if it does + add( keyType, key, docstring ); + keywords.at(key).setArgumentType(datatype); +} + +void Keywords::addInputKeyword( const std::string & keyType, + const std::string & key, + const std::string & datatype, + const std::string & defaultV, + const std::string & docstring ) { + addInputKeyword(keyType,key,stoat(datatype),defaultV,docstring); +} + +void Keywords::addInputKeyword( const std::string & keyType, + const std::string & key, + argType datatype, + const std::string & defaultV, + const std::string & docstring ) { + if( exists(key) ) { + remove(key); } - add( t, k, def, d ); + add( keyType, key, defaultV, docstring ); + keywords[key].setArgumentType(datatype); } -void Keywords::add( const std::string & t, const std::string & k, const std::string & def, const std::string & d ) { - plumed_massert( !exists(k) && !reserved(k) && (t=="compulsory" || t=="hidden" ), "failing on keyword " + k ); // An optional keyword can't have a default - types.insert( std::pair(k, KeyType(t)) ); - documentation.insert( std::pair(k,"( default=" + def + " ) " + d) ); - allowmultiple.insert( std::pair(k,false) ); - numdefs.insert( std::pair(k,def) ); - keys.push_back(k); +void Keywords::add( std::string_view keytype, + std::string_view key, + std::string_view defaultValue, + std::string_view docstring ) { + //let's fail asap in case of typo + auto type = KeyType(keytype); + + plumed_massert( !exists(key) && !reserved(key), "failing on keyword " + std::string(key) ); + // An optional keyword can't have a default + plumed_massert(type.isCompulsory() || type.isHidden(), "You can't set a default value for an optional keyword, failing on " + std::string(key)); + keywords[std::string(key)] = keyInfo() + .setType(type) + .setDefaultValue(defaultValue) + .setDocString("( default=" + std::string(defaultValue) + " ) " + std::string(docstring) ) + .setAllowMultiple(false); + + keys.emplace_back(key); } -void Keywords::addFlag( const std::string & k, const bool def, const std::string & d ) { - plumed_massert( !exists(k) && !reserved(k), "keyword " + k + " has already been registered"); - std::string defstr; - plumed_massert( !def, "the second argument to addFlag must be false " + k ); - defstr="( default=off ) "; - types.insert( std::pair(k,KeyType("flag")) ); - documentation.insert( std::pair(k,defstr + d) ); - allowmultiple.insert( std::pair(k,false) ); - booldefs.insert( std::pair(k,def) ); - keys.push_back(k); +void Keywords::addFlag(std::string_view key, bool defaultValue, std::string_view docstring) { + plumed_massert( !exists(key) && !reserved(key), "keyword " + std::string(key) + " has already been registered"); + plumed_massert( !defaultValue, "the second argument to addFlag must be false " + std::string(key) ); + std::string defstr="( default=off ) "; + keywords[std::string(key)] = keyInfo() + .setType(KeyType("flag")) + .setDefaultFlag(false) + .setDocString(std::string(defstr) + std::string(docstring)) + .setAllowMultiple(false); + + keys.emplace_back(key); } void Keywords::remove( const std::string & k ) { bool found=false; - unsigned j=0, n=0; - - while(true) { - for(j=0; j markForRemoval{}; + for(const auto& dkey : components ) { + if( dkey.second.key==k ) { + markForRemoval.push_back(dkey.first); } } - plumed_massert(found,"You are trying to forbid " + k + " a keyword that isn't there"); // You have tried to forbid a keyword that isn't there + for(const auto& toremove : markForRemoval ) { + removeOutputComponent( toremove ); + } } bool Keywords::numbered( const std::string & k ) const { if( style( k,"atoms") ) { return true; } - plumed_massert( allowmultiple.count(k), "Did not find keyword " + k ); - return allowmultiple.find(k)->second; + //should I add also the "reserved(k)" to the test? + plumed_massert( exists(k), "Did not find keyword " + k ); + return keywords.at(k).allowmultiple; } bool Keywords::style( const std::string & k, const std::string & t ) const { @@ -351,69 +535,47 @@ std::string Keywords::getKeyword( const unsigned i ) const { return keys[i]; } -bool Keywords::exists( const std::string & k ) const { - for(unsigned i=0; isecond).isAtomList() ) { - nkeys++; - } - } - if( nkeys>0 ) { + { std::string prevtag="start"; - for(unsigned i=0; isecond).isAtomList() ) { - plumed_massert( atomtags.count(keys[i]), "keyword " + keys[i] + " allegedly specifies atoms but no tag has been specified. Please email Gareth Tribello"); - if( prevtag!="start" && prevtag!=atomtags.find(keys[i])->second ) { + for(const auto& key : keys) { + if( keywords.at(key).type.isAtomList() ) { + plumed_massert( keywords.at(key).atomtag!="", "keyword " + key + " allegedly specifies atoms but no tag has been specified. Please email Gareth Tribello"); + const auto & currentTag=keywords.at(key).atomtag; + if( prevtag!="start" && prevtag!=currentTag ) { break; } - if( (atomtags.find(keys[i])->second).find("residues")!=std::string::npos) { - std::printf(" %s=", keys[i].c_str() ); + if( currentTag.find("residues")!=std::string::npos) { + std::printf(" %s=", key.c_str() ); } else { - std::printf(" %s=", keys[i].c_str() ); + std::printf(" %s=", key.c_str() ); } - prevtag=atomtags.find(keys[i])->second; + prevtag=currentTag; } } } - nkeys=0; - for(unsigned i=0; isecond).isCompulsory() ) { - nkeys++; - } - } - if( nkeys>0 ) { - for(unsigned i=0; isecond).isCompulsory() ) { - std::string def; - if( getDefaultValue( keys[i], def) ) { - std::printf(" %s=%s ", keys[i].c_str(), def.c_str() ); - } else { - std::printf(" %s= ", keys[i].c_str() ); - } - } else if (include_optional) { - // TG no defaults for optional keywords? - std::printf(" [%s]", keys[i].c_str() ); + + for(const auto& key : keys) { + if ( keywords.at(key).type.isCompulsory() ) { + std::string def; + if( getDefaultValue( key, def) ) { + std::printf(" %s=%s ", key.c_str(), def.c_str() ); + } else { + std::printf(" %s= ", key.c_str() ); } + } else if (include_optional) { + // TG no defaults for optional keywords? + std::printf(" [%s]", key.c_str() ); + } } std::printf("\n"); @@ -421,14 +583,14 @@ void Keywords::print_template(const std::string& actionname, bool include_option } void Keywords::print_vim() const { - for(unsigned i=0; isecond).isFlag() ) { - std::printf( ",flag:%s", keys[i].c_str() ); + for(const auto& key : keys) { + if( keywords.at(key).type.isFlag() ) { + std::printf( ",flag:%s", key.c_str() ); } else { - if( allowmultiple.find(keys[i])->second ) { - std::printf(",numbered:%s",keys[i].c_str() ); + if( keywords.at(key).allowmultiple ) { + std::printf(",numbered:%s",key.c_str() ); } else { - std::printf(",option:%s",keys[i].c_str() ); + std::printf(",option:%s",key.c_str() ); } } } @@ -436,12 +598,12 @@ void Keywords::print_vim() const { } void Keywords::print_html() const { - // This is the part that outputs the details of the components if( cnames.size()>0 ) { unsigned ndef=0; - for(unsigned i=0; isecond=="default") { + //running on the order of insertion + for(const auto& cname : cnames) { + if(components.at(cname).key=="default") { ndef++; } } @@ -452,15 +614,15 @@ void Keywords::print_html() const { std::cout<<" \n"; std::printf("\n"); unsigned nndef=0; - for(unsigned i=0; isecond=="default" ); - if( ckey.find(cnames[i])->second!="default" ) { + for(const auto& cname : cnames) { + //plumed_assert( components.at(cname).key=="default" ); + if( components.at(cname).key!="default" ) { nndef++; continue; } std::printf("\n"); - std::printf("\n",cnames[i].c_str() ); - std::printf("\n",(cdocs.find(cnames[i])->second).c_str() ); + std::printf("\n",cname.c_str() ); + std::printf("\n",(components.at(cname).docstring).c_str() ); std::printf("\n"); } std::cout<<"
Quantity Description
%s %s %s %s
\n\n"; @@ -469,12 +631,12 @@ void Keywords::print_html() const { std::cout<<"\n\n"; std::cout<<" \n"; std::printf("\n"); - for(unsigned i=0; isecond!="default") { + for(const auto& cname : cnames) { + if( components.at(cname).key!="default") { std::printf("\n"); std::printf(" \n", - cnames[i].c_str(),(ckey.find(cnames[i])->second).c_str() ); - std::printf("\n",(cdocs.find(cnames[i])->second).c_str() ); + cname.c_str(),(components.at(cname).key).c_str() ); + std::printf("\n",(components.at(cname).docstring).c_str() ); std::printf("\n"); } } @@ -482,8 +644,8 @@ void Keywords::print_html() const { } } else { unsigned nregs=0; - for(unsigned i=0; isecond) ) { + for(const auto& cname : cnames) { + if( exists(components.at(cname).key) ) { nregs++; } } @@ -492,12 +654,12 @@ void Keywords::print_html() const { std::cout< \n"; std::printf("\n"); - for(unsigned i=0; isecond) ) { + for(const auto& cname : cnames) { + if( exists(components.at(cname).key) ) { std::printf("\n"); std::printf(" \n", - cnames[i].c_str(),(ckey.find(cnames[i])->second).c_str() ); - std::printf("\n",(cdocs.find(cnames[i])->second).c_str() ); + cname.c_str(),(components.at(cname).key).c_str() ); + std::printf("\n",(components.at(cname).docstring).c_str() ); std::printf("\n"); } } @@ -507,8 +669,8 @@ void Keywords::print_html() const { } unsigned nkeys=0; - for(unsigned i=0; isecond).isAtomList() ) { + for(const auto& key : keys) { + if ( keywords.at(key).type.isAtomList() ) { nkeys++; } } @@ -523,10 +685,11 @@ void Keywords::print_html() const { std::cout<<"
Quantity Keyword Description
%s %s %s %s
Quantity Keyword Description
%s %s %s %s
\n"; std::string prevtag="start"; unsigned counter=0; - for(unsigned i=0; isecond).isAtomList() ) { - plumed_massert( atomtags.count(keys[i]), "keyword " + keys[i] + " allegedly specifies atoms but no tag has been specified. Please email Gareth Tribello"); - if( prevtag!="start" && prevtag!=atomtags.find(keys[i])->second && isaction ) { + for(const auto& key : keys) { + if ( keywords.at(key).type.isAtomList() ) { + const auto& currentTag = keywords.at(key).atomtag; + plumed_massert( currentTag!="", "keyword " + key + " allegedly specifies atoms but no tag has been specified. Please email Gareth Tribello"); + if( prevtag!="start" && prevtag!=currentTag && isaction ) { std::cout<<"
\n\n"; if( isatoms ) { std::cout<<"\\par Or alternatively by using\n\n"; @@ -538,15 +701,15 @@ void Keywords::print_html() const { } std::cout<<" \n"; } - print_html_item( keys[i] ); - prevtag=atomtags.find(keys[i])->second; + print_html_item( key ); + prevtag=currentTag; } } std::cout<<"
\n\n"; } nkeys=0; - for(unsigned i=0; isecond).isCompulsory() ) { + for(const auto& key : keys) { + if ( keywords.at(key).type.isCompulsory() ) { nkeys++; } } @@ -557,16 +720,16 @@ void Keywords::print_html() const { std::cout<<"\\par The following must be present\n\n"; } std::cout<<" \n"; - for(unsigned i=0; isecond).isCompulsory() ) { - print_html_item( keys[i] ); + for(const auto& key : keys) { + if ( keywords.at(key).type.isCompulsory() ) { + print_html_item( key ); } } std::cout<<"
\n\n"; } nkeys=0; - for(unsigned i=0; isecond).isFlag() || (types.find(keys[i])->second).isOptional() || (types.find(keys[i])->second).isVessel() ) { + for(const auto& key : keys) { + if ( keywords.at(key).type.isFlag() || keywords.at(key).type.isOptional() || keywords.at(key).type.isVessel() ) { nkeys++; } } @@ -577,23 +740,23 @@ void Keywords::print_html() const { std::cout<<"\\par The following options are available\n\n"; } std::cout<<" \n"; - for(unsigned i=0; isecond).isFlag() ) { - print_html_item( keys[i] ); + for(const auto& key : keys) { + if ( keywords.at(key).type.isFlag() ) { + print_html_item( key ); } } std::cout<<"\n"; } nkeys=0; - for(unsigned i=0; isecond).isOptional() || (types.find(keys[i])->second).isVessel() ) { + for(const auto& key : keys) { + if ( keywords.at(key).type.isOptional() || keywords.at(key).type.isVessel() ) { nkeys++; } } if( nkeys>0 ) { - for(unsigned i=0; isecond).isOptional() || (types.find(keys[i])->second).isVessel() ) { - print_html_item( keys[i] ); + for(const auto& key : keys) { + if ( keywords.at(key).type.isOptional() || keywords.at(key).type.isVessel() ) { + print_html_item( key ); } } } @@ -601,17 +764,17 @@ void Keywords::print_html() const { } void Keywords::print_spelling() const { - for(unsigned i=0; isecond).find("\\f$")!=std::string::npos ); // Check for latex - std::vector w=Tools::getWords( documentation.find(key)->second ); + bool killdot=( keywords.at(key).docstring.find("\\f$")!=std::string::npos ); // Check for latex + std::vector w=Tools::getWords( keywords.at(key).docstring ); std::stringstream sstr; sstr<second).isAtomList() ) { + for(const auto& key : keys) { + if ( keywords.at(key).type.isAtomList() ) { nkeys++; } } if( nkeys>0 ) { helpstr += "The input trajectory can be in any of the following formats: \n\n"; - for(unsigned i=0; isecond).isAtomList() ) { - helpstr += getKeywordDocs( keys[i] ); + for(const auto& key : keys) { + if ( keywords.at(key).type.isAtomList() ) { + helpstr += getKeywordDocs( key ); } } } - nkeys=0; - for(unsigned i=0; isecond).isCompulsory() ) { - nkeys++; + unsigned ncompulsory=0; + for(const auto& key : keys) { + if ( keywords.at(key).type.isCompulsory() ) { + ncompulsory++; } } - unsigned ncompulsory=nkeys; - if( nkeys>0 ) { + if( ncompulsory>0 ) { helpstr += "\nThe following arguments are compulsory: \n\n"; - for(unsigned i=0; isecond).isCompulsory() ) { - helpstr += getKeywordDocs( keys[i] ); + for(const auto& key : keys) { + if ( keywords.at(key).type.isCompulsory() ) { + helpstr += getKeywordDocs( key ); } } } nkeys=0; - for(unsigned i=0; isecond).isFlag() ) { + for(const auto& key : keys) { + if ( keywords.at(key).type.isFlag() ) { nkeys++; } } @@ -675,22 +837,22 @@ std::string Keywords::getHelpString() const { } else { helpstr += "\nThe following options are available\n\n"; } - for(unsigned i=0; isecond).isFlag() ) { - helpstr += getKeywordDocs( keys[i] ).c_str(); + for(const auto& key : keys) { + if ( keywords.at(key).type.isFlag() ) { + helpstr += getKeywordDocs( key ).c_str(); } } } nkeys=0; - for(unsigned i=0; isecond).isOptional() || (types.find(keys[i])->second).isVessel() ) { + for(const auto& key : keys) { + if ( keywords.at(key).type.isOptional() || keywords.at(key).type.isVessel() ) { nkeys++; } } if( nkeys>0 ) { - for(unsigned i=0; isecond).isOptional() || (types.find(keys[i])->second).isVessel() ) { - helpstr += getKeywordDocs( keys[i] ); + for(const auto& key : keys) { + if ( keywords.at(key).type.isOptional() || keywords.at(key).type.isVessel() ) { + helpstr += getKeywordDocs( key ); } } helpstr += "\n"; @@ -712,8 +874,9 @@ std::string Keywords::getTooltip( const std::string& name ) const { if( !exists(kname) ) { return " could not find this keyword "; } - std::string mystring, docstr = documentation.find(kname)->second; - if( types.find(kname)->second.isCompulsory() ) { + std::string mystring; + std::string docstr = keywords.at(kname).docstring; + if( keywords.at(kname).type.isCompulsory() ) { mystring += "compulsory keyword "; if( docstr.find("default")!=std::string::npos ) { std::size_t bra = docstr.find_first_of(")"); @@ -742,18 +905,14 @@ std::string Keywords::getTooltip( const std::string& name ) const { void Keywords::print_html_item( const std::string& key ) const { std::printf("\n"); std::printf("\n",key.c_str() ); - std::printf("\n",(documentation.find(key)->second).c_str() ); + std::printf("\n",(keywords.at(key).docstring).c_str() ); std::printf("\n"); } -std::string Keywords::get( const unsigned k ) const { - plumed_assert( ksecond; + // plumed_massert(exists(key)||reserved(key),"You can't ask for the default value of a keyword that doesn't exist("+key+")"); + if (std::holds_alternative(keywords.at(key).defaultValue)) { + def = std::get(keywords.at(key).defaultValue); return true; } else { return false; @@ -761,10 +920,9 @@ bool Keywords::getLogicalDefault(const std::string & key, bool& def ) const { } bool Keywords::getDefaultValue(const std::string & key, std::string& def ) const { - plumed_assert( style(key,"compulsory") || style(key,"hidden") ); - - if( numdefs.find(key)!=numdefs.end() ) { - def=numdefs.find(key)->second; + plumed_massert( style(key,"compulsory") || style(key,"hidden"),"You can't ask for the default value of a keyword that doesn't have one ("+key+")" ); + if (std::holds_alternative(keywords.at(key).defaultValue)) { + def = std::get(keywords.at(key).defaultValue); return true; } else { return false; @@ -774,15 +932,10 @@ bool Keywords::getDefaultValue(const std::string & key, std::string& def ) const void Keywords::destroyData() { keys.clear(); reserved_keys.clear(); - types.clear(); - allowmultiple.clear(); - documentation.clear(); - booldefs.clear(); - numdefs.clear(); - atomtags.clear(); - ckey.clear(); - cdocs.clear(); - ckey.clear(); + keywords.clear(); + components.clear(); + //cname was missing before, is it wanted or not? + cnames.clear(); } void Keywords::setComponentsIntroduction( const std::string& instr ) { @@ -794,6 +947,10 @@ void Keywords::addOutputComponent( const std::string& name, const std::string& k } void Keywords::addOutputComponent( const std::string& name, const std::string& key, const std::string& type, const std::string& descr ) { + addOutputComponent(name,key,stoct(type),descr); +} + +void Keywords::addOutputComponent( const std::string& name, const std::string& key, componentType type, const std::string& descr ) { plumed_assert( !outputComponentExists(name) ); plumed_massert( name!=".#!value", name + " is reserved for storing description of value" ); plumed_massert( name.find("-")==std::string::npos,"dash is reseved character in component names" ); @@ -809,38 +966,27 @@ void Keywords::addOutputComponent( const std::string& name, const std::string& k "be referenced elsewhere in the input by using this Action's label followed by a " "dot and the name of the quantity required from the list below."; } - - ckey.insert( std::pair(name,key) ); - cdocs.insert( std::pair(name,descr) ); - ctypes.insert( std::pair(name,type) ); - cnames.push_back(name); + components[name] = component() + .setKey(key) + .setDocstring(descr) + .setType(type); + cnames.emplace_back(name); } -void Keywords::removeOutputComponent( const std::string& name ) { - unsigned j=0; - while(true) { - for(j=0; j(".#!value","default") ); - cdocs.insert( std::pair(".#!value",descr) ); - ctypes.insert( std::pair(".#!value",type) ); - cnames.push_back(".#!value"); + components[".#!value"] =component() + .setKey("default") + .setDocstring(descr) + .setType(type); + cnames.emplace_back(".#!value"); } else { - cdocs[".#!value"] = descr; - ctypes[".#!value"] = type; + components[".#!value"].docstring = descr; + components[".#!value"].type = type; } } @@ -861,12 +1007,7 @@ bool Keywords::outputComponentExists( const std::string& name ) const { sname=name; } - for(unsigned i=0; isecond=="atom" ) { + // using valid(components.at(sname).type & (componentType::atom | componentType::atoms) will have a sligthly different flavour + // == means "is exactly", the valid(&) construct instead measn "can be different, but must contain the asked flag" + if( thisactname=="CENTER" && (components.at(sname).type == componentType::atom || components.at(sname).type == componentType::atoms)) { return true; } if( rank==0 ) { - return (ctypes.find(sname)->second.find("scalar")!=std::string::npos); + return (valid(components.at(sname).type | componentType::scalar)); } else if( hasderiv ) { - return (ctypes.find(sname)->second.find("grid")!=std::string::npos); + return (valid(components.at(sname).type | componentType::grid)); } else if( rank==1 ) { - return (ctypes.find(sname)->second.find("vector")!=std::string::npos); + return (valid(components.at(sname).type | componentType::vector)); } else if( rank==2 ) { - return (ctypes.find(sname)->second.find("matrix")!=std::string::npos); + return (valid(components.at(sname).type | componentType::matrix )); } return false; } +std::vector Keywords::getArgumentKeys() const { + std::vector arguments; + std::copy_if(keys.begin(), keys.end(),std::back_inserter(arguments), + [this](auto const& kw) { + return keywords.at(kw).isArgument(); + }); + return arguments; +} + bool Keywords::checkArgumentType( const std::size_t& rank, const bool& hasderiv ) const { - for(auto const& x : argument_types ) { - if( rank==0 && x.second.find("scalar")!=std::string::npos ) { - return true; + std::map arguments; + for(auto const& kw : getArgumentKeys() ) { + const auto & at = std::get(keywords.at(kw).argument_type); + arguments[kw] = false; + if( rank==0 && valid(at | argType::scalar)) { + arguments[kw] = true; + } + if( hasderiv && valid(at | argType::grid)) { + arguments[kw] = true; } - if( hasderiv && x.second.find("grid")!=std::string::npos ) { - return true; + if( rank==1 && valid(at | argType::vector)) { + arguments[kw] = true; } - if( rank==1 && x.second.find("vector")!=std::string::npos ) { - return true; + if( rank==2 && valid(at | argType::matrix)) { + arguments[kw] = true; } - if( rank==2 && x.second.find("matrix")!=std::string::npos ) { - return true; + } + if(std::all_of(arguments.begin(), arguments.end(), + [](auto const& arg) { + return arg.second; +})) { + return true; + } + ///@todo this plumed_merror breaks the check that is in the only place that + ///calls this function (at the end of ActionWithArguments::interpretArgumentList) + std::string errorMessage = "WARNING: type for the following arguments has not been specified\n" + "or dimensions are not compatible with rank "+std::to_string(rank) + +" and the "+ ((hasderiv)?"presence":"absence") +" of the derivative \n"; + for (auto const& arg : arguments) { + if (!arg.second) { + errorMessage += arg.first + + " ("+toString(std::get(keywords.at(arg.first).argument_type))+")" +"\n"; } } - plumed_merror("WARNING: type for input argument has not been specified"); + //the merror makes the return never executed!!! + plumed_merror(errorMessage); return false; } std::string Keywords::getArgumentType( const std::string& name ) const { - if( argument_types.find(name)==argument_types.end() ) { + auto argument_keys = getArgumentKeys(); + if( find(argument_keys.begin(),argument_keys.end(),name)==argument_keys.end() ) { return ""; } - return argument_types.find(name)->second; + return toString(std::get(keywords.at(name).argument_type)); } std::string Keywords::getOutputComponentFlag( const std::string& name ) const { - return ckey.find(name)->second; + return components.at(name).key; } std::string Keywords::getOutputComponentType( const std::string& name ) const { - return ctypes.find(name)->second; + //return toString( components.find(name)->second.type); brings to segfault in case name is ot present + //at at least throws + return toString( components.at(name).type); } std::string Keywords::getOutputComponentDescription( const std::string& name ) const { @@ -942,12 +1118,8 @@ std::string Keywords::getOutputComponentDescription( const std::string& name ) c checkname = name.substr(0,hyp); } - bool found=false; - for(unsigned i=0; isecond; + return components.at(checkname).docstring; } -void Keywords::removeComponent( const std::string& name ) { - bool found=false; - - while(true) { - unsigned j; - for(j=0; j Keywords::getOutputComponents() const { - return cnames; +///////////DUPLICATED??????????/////// +void Keywords::removeOutputComponent( const std::string& name ) { + components.erase(name); + erase_remove(cnames,name); } std::string Keywords::getKeywordDescription( const std::string& key ) const { plumed_assert( exists( key ) ); - return documentation.find(key)->second; + return keywords.at(key).docstring; } void Keywords::needsAction( const std::string& name ) { @@ -998,6 +1150,10 @@ void Keywords::needsAction( const std::string& name ) { neededActions.push_back( name ); } +bool Keywords::isActionNeeded( std::string_view name ) const { + return std::find(neededActions.begin(), neededActions.end(), name )!=neededActions.end(); +} + const std::vector& Keywords::getNeededKeywords() const { return neededActions; } @@ -1009,6 +1165,16 @@ void Keywords::addActionNameSuffix( const std::string& suffix ) { actionNameSuffixes.push_back( suffix ); } +bool Keywords::isActionSuffixed( std::string_view name, std::string_view basename) const { + std::string bname{basename}; + return std::any_of(actionNameSuffixes.begin(), + actionNameSuffixes.end(), + [name,&bname](const std::string& suffix)->bool{ + return (bname + suffix)==name ; + } + ); +} + void Keywords::setDisplayName( const std::string& name ) { thisactname = name; } @@ -1017,4 +1183,4 @@ std::string Keywords::getDisplayName() const { return thisactname; } -} +}// namespace PLMD diff --git a/src/tools/Keywords.h b/src/tools/Keywords.h index 6d40ff1863..755f3ad68e 100644 --- a/src/tools/Keywords.h +++ b/src/tools/Keywords.h @@ -23,10 +23,12 @@ #define __PLUMED_tools_Keywords_h #include #include -#include +#include #include +#include #include "Exception.h" +#include "BitmaskEnum.h" namespace PLMD { @@ -35,92 +37,124 @@ class Log; /// This class holds the keywords and their documentation class Keywords { /// This class lets me pass keyword types easily - class KeyType { - public: - enum {hidden,compulsory,flag,optional,atoms,vessel} style; - explicit KeyType( const std::string& type ); - void setStyle( const std::string& type ); + struct KeyType { + enum class keyStyle {hidden,compulsory,flag,optional,atoms,vessel,unknown}; + keyStyle style; + static keyStyle keyStyleFromString(std::string_view type ); + explicit KeyType( keyStyle type ); + explicit KeyType( std::string_view type ); + void setStyle( std::string_view type ); bool isCompulsory() const { - return (style==compulsory); + return (style==keyStyle::compulsory); } bool isFlag() const { - return (style==flag); + return (style==keyStyle::flag); } bool isOptional() const { - return (style==optional); + return (style==keyStyle::optional); } bool isAtomList() const { - return (style==atoms); + return (style==keyStyle::atoms); } bool isVessel() const { - return (style==vessel); + return (style==keyStyle::vessel); + } + bool isHidden() const { + return (style==keyStyle::hidden); } std::string toString() const { - if(style==compulsory) { + //if you add a style and you forget to update this function the compiler will refuse to compile + switch(style) { + case keyStyle::compulsory: return "compulsory"; - } else if(style==optional) { + case keyStyle::optional: return "optional"; - } else if(style==atoms) { + case keyStyle::atoms: return "atoms"; - } else if(style==flag) { + case keyStyle::flag: return "flag"; - } else if(style==hidden) { + case keyStyle::hidden: return "hidden"; - } else if(style==vessel) { + case keyStyle::vessel: return "vessel"; - } else { - plumed_assert(0); + default: + plumed_massert(false,"unknown keyword type"); } - return ""; + return "unknown"; } }; - friend class Action; - friend class ActionShortcut; - friend class ActionRegister; + +public: + enum class argType {scalar=1,vector=1<<2,matrix=1<<3,grid=1<<4}; + enum class componentType {scalar=1,vector=1<<2,matrix=1<<3,grid=1<<4,atoms=1<<5,atom=1<<6}; private: /// Is this an action or driver (this bool affects what style==atoms does in print) - bool isaction; + bool isaction=true; /// This allows us to overwrite the behavior of the atoms type in analysis actions - bool isatoms; + bool isatoms=true; /// The name of the action that has this set of keywords std::string thisactname; -/// The names of the allowed keywords + + struct keyInfo { + /// Whether the keyword is compulsory, optional... + KeyType type{KeyType::keyStyle::unknown}; + /// The documentation for the keyword + std::string docstring; + /// The default values (if there are default values) for compulsory keywords or flags + std::variant defaultValue; + ///The type of the argument if this keywords accepts arguments + std::variantargument_type; + /// The tags for atoms - we use this so the manual can differentiate between different ways of specifying atoms + std::string atomtag;//no noeed for optional, since the type will state if this is needed + /// Do we allow stuff like key1, key2 etc + bool allowmultiple; + keyInfo(); + //these functions are not neeeded (this is a struct), but are useful in constructing the key + keyInfo& setType(KeyType t); + keyInfo& setDocString(std::string_view d); + keyInfo& setDefaultValue(std::string_view d); + keyInfo& setDefaultFlag(bool a); + keyInfo& setArgumentType(argType a); + keyInfo& setAllowMultiple(bool a); + bool isArgument() const; + }; + ///Add o reserve a new keyword (internal tool) + void addOrReserve( std::string_view keytype, std::string_view key, std::string_view docstring, bool reserve ); + //std::less make some magic and makes find and [] work with string_view +/// Stores the keywords along with their settings + std::map> keywords; +/// The names of the allowed keywords, in order of declaration std::vector keys; -/// The names of the reserved keywords +/// The names of the reserved keywords, in order of declaration std::vector reserved_keys; -/// Whether the keyword is compulsory, optional... - std::map types; -/// Do we allow stuff like key1, key2 etc - std::map allowmultiple; -/// The documentation for the keywords - std::map documentation; -/// The type for the arguments in this action - std::map argument_types; -/// The default values for the flags (are they on or of) - std::map booldefs; -/// The default values (if there are default values) for compulsory keywords - std::map numdefs; -/// The tags for atoms - we use this so the manual can differentiate between different ways of specifying atoms - std::map atomtags; + struct component { + /// The keyword that turns on this component + std::string key; + /// The documentation for the component + std::string docstring; + /// The type of the component + componentType type; + component(); + //these functions are not neeeded (this is a struct), but are useful in constructing the component + component& setKey(std::string_view k); + component& setDocstring(std::string_view d); + component& setType(componentType t); + }; + //the "exists component" is stored in the map keys + std::map> components; /// The string that should be printed out to describe how the components work for this particular action std::string cstring; -/// The names of all the possible components for an action +/// The names of all the possible components for an action, in order of their (first) declaration std::vector cnames; -/// The keyword that turns on a particular component - std::map ckey; -/// The documentation for a particular component - std::map cdocs; -/// The type of a particular component - std::map ctypes; /// The list of actions that are needed by this action std::vector neededActions; /// List of suffixes that can be used with this action std::vector actionNameSuffixes; -/// Print the documentation for the jth keyword in html +/// Print the documentation for the named keyword in html void print_html_item( const std::string& ) const; public: /// Constructor - Keywords() : isaction(true), isatoms(true) {} + Keywords() {} /// void isDriver() { isaction=false; @@ -136,11 +170,17 @@ class Keywords { /// Return the number of defined keywords unsigned size() const; /// Check if numbered keywords are allowed for this action - bool numbered( const std::string & k ) const ; + bool numbered( const std::string & k ) const; + /// Get the ordered list of active keywords (not the reserved ones) + const std::vector& getKeys() const { + return keys; + } + //Get the ordered list of arguments + std::vector getArgumentKeys() const; /// Return the ith keyword - std::string getKeyword( const unsigned i ) const ; + std::string getKeyword( const unsigned i ) const; /// Get the documentation for a particular keyword - std::string getKeywordDocs( const std::string& key ) const ; + std::string getKeywordDocs( const std::string& key ) const; /// Print the documentation to the log file (used by PLMD::Action::error) void print( Log& log ) const ; /// Print the documentation to a file (use by PLUMED::CLTool::readCommandLineArgs) @@ -150,27 +190,27 @@ class Keywords { /// Print a file containing the list of keywords for a particular action (used for spell checking) void print_spelling() const ; /// Reserve a keyword - void reserve( const std::string & t, const std::string & k, const std::string & d ); + void reserve( std::string_view keytype, std::string_view key, std::string_view docstring ); /// Reserve a flag - void reserveFlag( const std::string & k, const bool def, const std::string & d ); + void reserveFlag( const std::string & key, bool defaultValue, const std::string & docstring ); /// Use one of the reserved keywords - void use( const std::string & k ); -/// Get the ith keyword - std::string get( const unsigned k ) const ; + void use( std::string_view k ); + /// append the data from another Keywords object (**only** keywords, reserved keywords and components) + void add( const Keywords& keys ); /// Add a new keyword of type t with name k and description d - void add( const std::string & t, const std::string & k, const std::string & d ); + void add( std::string_view keytype, std::string_view key, std::string_view docstring ); /// Add a new compulsory keyword (t must equal compulsory) with name k, default value def and description d - void add( const std::string & t, const std::string & k, const std::string & def, const std::string & d ); + void add( std::string_view keytype, std::string_view key, std::string_view defaultValue, std::string_view docstring ); /// Add a falg with name k that is by default on if def is true and off if def is false. d should provide a description of the flag - void addFlag( const std::string & k, const bool def, const std::string & d ); + void addFlag(std::string_view key, bool defaultValue, std::string_view docstring); /// Remove the keyword with name k void remove( const std::string & k ); /// Check if there is a keyword with name k - bool exists( const std::string & k ) const ; + bool exists( std::string_view k ) const ; /// Check the keyword k has been reserved - bool reserved( const std::string & k ) const ; + bool reserved( std::string_view k ) const ; /// Get the type for the keyword with string k - std::string getStyle( const std::string & k ) const ; + std::string getStyle(const std::string & k ) const ; /// Check if the keyword with name k has style t bool style( const std::string & k, const std::string & t ) const ; /// Print an html version of the documentation @@ -179,15 +219,27 @@ class Keywords { void print_vim() const ; /// Print the template version for the documentation void print_template( const std::string& actionname, bool include_optional) const ; -/// Change the style of a keyword +/// Change part of the style of a keyword +/// +/// The standard usecase for this method is creating a compulsory or an atom(s) numbered keyword. +/// For example: +/// @code{.cpp} +/// keys.add("numbered","ATOMS","the atoms involved in each of the contacts you wish to calculate. " +/// "Keywords like ATOMS1, ATOMS2, ATOMS3,... should be listed and one contact will be " +/// "calculated for each ATOM keyword you specify."); +/// keys.reset_style("ATOMS","atoms"); +/// keys.add("numbered","SWITCH","The switching functions to use for each of the contacts in your map. " +/// "You can either specify a global switching function using SWITCH or one " +/// "switching function for each contact. Details of the various switching " +/// "functions you can use are provided on \\ref switchingfunction."); +/// keys.reset_style("SWITCH","compulsory"); +/// @endcode +/// @note Note that some option of the selected keyword may not change. In particular: +/// - A numbered keyword will change the style but the keyInfo::allowmultiple +/// will remain set to `true` +/// - An eventual keyInfo::atomtag will not be changed unless the style is set to +/// an atomlist void reset_style( const std::string & k, const std::string & style ); -/// Add keywords from one keyword object to another - void add( const Keywords& keys ); -/// Copy the keywords data - void copyData( std::vector& kk, std::vector& rk, std::map& tt, std::map& am, - std::map& docs, std::map& bools, std::map& nums, - std::map& atags, std::vector& cnam, std::map& ck, - std::map& cd ) const ; /// Clear everything from the keywords object. /// Not actually needed if your Keywords object is going out of scope. void destroyData(); @@ -195,11 +247,15 @@ class Keywords { void setComponentsIntroduction( const std::string& instr ); /// Add the description of the value void setValueDescription( const std::string& type, const std::string& descr ); +/// @todo prepend[[deprecated("Please specify the data type for the argument from scalar/vector/matrix/grid with the Keywords::argType enum")]] + void setValueDescription( componentType type, const std::string& descr ); /// Add a potential component which can be output by this particular action [[deprecated("Use addOutputComponent with four argument and specify valid types for value from scalar/vector/matrix/grid")]] void addOutputComponent( const std::string& name, const std::string& key, const std::string& descr ); -/// Add a potential component which can be output by this particular action +/// @todo prepend[[deprecated("Please specify the data type for the argument from scalar/vector/matrix/grid with the Keywords::argType enum")]] void addOutputComponent( const std::string& name, const std::string& key, const std::string& type, const std::string& descr ); +/// Add a potential component which can be output by this particular action + void addOutputComponent( const std::string& name, const std::string& key, componentType type, const std::string& descr ); /// Remove a component that can be output by this particular action void removeOutputComponent( const std::string& name ); /// Has a component with this name been added? @@ -207,34 +263,45 @@ class Keywords { /// Check that type for component has been documented correctly bool componentHasCorrectType( const std::string& name, const std::size_t& rank, const bool& hasderiv ) const ; /// Create the documentation for a keyword that reads arguments - void addInputKeyword( const std::string & t, const std::string & k, const std::string & ttt, const std::string & d ); - void addInputKeyword( const std::string & t, const std::string & k, const std::string & ttt, const std::string& def, const std::string & d ); +/// @todo prepend [[deprecated("Please specify the data type for the argument from scalar/vector/matrix/grid with the Keywords::argType enum")]] + void addInputKeyword( const std::string & keyType, const std::string & key, const std::string & dataType, const std::string & docstring ); + /// Create the documentation for a keyword that reads arguments + void addInputKeyword( const std::string & keyType, const std::string & key, argType dataType, const std::string & docstring ); + /// Create the documentation for a keyword that reads arguments + /// @todo prepend[[deprecated("Please specify the data type for the argument from scalar/vector/matrix/grid with the Keywords::argType enum")]] + void addInputKeyword( const std::string & keyType, const std::string & key, const std::string & dataType, const std::string& defaultValue, const std::string & docstring ); + /// Create the documentation for a keyword that reads arguments + void addInputKeyword( const std::string & keyType, const std::string & key, argType dataType, const std::string& defaultValue, const std::string & docstring ); /// Check the documentation of the argument types bool checkArgumentType( const std::size_t& rank, const bool& hasderiv ) const ; /// Get the valid types that can be used as argument for this keyword std::string getArgumentType( const std::string& name ) const ; -/// Get the flag that forces this component to be calculated +/// Get the flag that forces thie named component to be calculated std::string getOutputComponentFlag( const std::string& name ) const ; -/// Get the type for this output component +/// Get the type for the named output component std::string getOutputComponentType( const std::string& name ) const ; -/// Get the description of this component +/// Get the description of the named component std::string getOutputComponentDescription( const std::string& name ) const ; -/// Get the full list of output components - std::vector getOutputComponents() const ; +/// Get the full ordered list of output components + const std::vector& getOutputComponents() const { + return cnames; + } /// Get the description of a particular keyword std::string getKeywordDescription( const std::string& name ) const ; -/// Remove a component with a particular name from the keywords - void removeComponent( const std::string& name ); -/// Reference to keys - std::vector getKeys() const { - return keys; - } /// Get the description of a particular keyword std::string getTooltip( const std::string& name ) const ; /// Note that another actions is required to create this shortcut void needsAction( const std::string& name ); -/// Add a suffix to the list of action name suffixes to test for +/// Check if the requested action is in the list of the needed actions + bool isActionNeeded( std::string_view name ) const ; +/// Add a suffix to the list of possible action name suffixes void addActionNameSuffix( const std::string& suffix ); + /** @brief checks that name is is a composition of basename and one of the possible suffixes + + Cycles on the registered suffixed and return true if the combination + `basename+suffix` equals to the passed name + */ + bool isActionSuffixed( std::string_view name, std::string_view basename) const ; /// Get the list of keywords that are needed by this action const std::vector& getNeededKeywords() const ; /// Return the name of the action that has this set of keywords @@ -243,6 +310,41 @@ class Keywords { void setDisplayName( const std::string& name ); }; -} +//the following templates specializations make the bitmask enum work with the +// bitwise operators `|`, `&` and the "valid" function (valid converts to bool the result of a "mask operation") +template<> +struct enum_traits::BitmaskEnum< Keywords::componentType > { + static constexpr bool has_valid = true; + static constexpr bool has_bit_or = true; + static constexpr bool has_bit_and = true; +}; + +template<> +struct enum_traits::BitmaskEnum< Keywords::argType > { + static constexpr bool has_valid = true; + static constexpr bool has_bit_or = true; + static constexpr bool has_bit_and = true; +}; + +std::string toString(Keywords::argType at); +/** + * Converts a string to the corresponding Keywords::argType. + * + * @param str The string to convert. + * @return The Keywords::argType corresponding to the string. + * @throws std::invalid_argument If the string does not match any enum value. + */ +Keywords::argType stoat(std::string_view str); +std::string toString(Keywords::componentType at); + +/** + * Converts a string to the corresponding Keywords::componentType. + * @param str The string to convert. + * @return The Keywords::componentType corresponding to the string. + * @throws std::invalid_argument if the string does not match any enum value. + */ +Keywords::componentType stoct(std::string_view str) ; + +} // namespace PLMD #endif
%s %s %s