From d03c51f00809b458d913f1c47a7f383f5da1dcc7 Mon Sep 17 00:00:00 2001 From: Daniele Rapetti <5535617+Iximiel@users.noreply.github.com> Date: Wed, 18 Dec 2024 15:27:33 +0100 Subject: [PATCH 01/19] Setting typo-safe types for keyword type and datatype for keywords self documenting addInputKeyword and mking the types public applied the example to dumpforces making the enum bitmask makes everithing compile :D reworking "add" reserve and use --- src/generic/DumpForces.cpp | 2 +- src/tools/BitmaskEnum.h | 127 +++++++++++ src/tools/Keywords.cpp | 452 ++++++++++++++++++++++++++----------- src/tools/Keywords.h | 116 +++++++--- 4 files changed, 526 insertions(+), 171 deletions(-) create mode 100644 src/tools/BitmaskEnum.h diff --git a/src/generic/DumpForces.cpp b/src/generic/DumpForces.cpp index 704853fbb1..03bfebd4b0 100644 --- a/src/generic/DumpForces.cpp +++ b/src/generic/DumpForces.cpp @@ -71,7 +71,7 @@ void DumpForces::registerKeywords(Keywords& keys) { Action::registerKeywords(keys); ActionPilot::registerKeywords(keys); ActionWithArguments::registerKeywords(keys); - keys.addInputKeyword("compulsory","ARG","scalar","the labels of the values whose forces should be output"); + keys.addInputKeyword("compulsory","ARG",Keywords::argType::scalar,"the labels of the values whose forces should be output"); keys.add("compulsory","STRIDE","1","the frequency with which the forces should be output"); keys.add("compulsory","FILE","the name of the file on which to output the forces"); keys.add("compulsory","FMT","%15.10f","the format with which the derivatives should be output"); diff --git a/src/tools/BitmaskEnum.h b/src/tools/BitmaskEnum.h new file mode 100644 index 0000000000..6e6567c15c --- /dev/null +++ b/src/tools/BitmaskEnum.h @@ -0,0 +1,127 @@ +/* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + 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 { + +/// support struct for setting up some operations on enum types +template< typename enum_type > +struct BitmaskEnum { + //example traits + //Use: specialize with extra traits (see Keywords.h) + // Example: + /* + // Please note that the 0 value is not implemented (it is reserved as a result of mask not matching masks) + 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; + }; + */ + // Currenlty we have implemented: + // static constexpr bool has_valid = true; + // static constexpr bool has_bit_or = true; + // static constexpr bool has_bit_and = true; +}; + +/** + @brief Perform a bitwise OR between two enum values. + +This operator is only defined if in the declaration you define specialize BitmaskEnum +for the enum type in question with the `has_bit_or` trait. + + \param a The first enum value. + \param b The second enum value. + \return The result of performing a bitwise OR between the two values. +*/ +template< typename enumtype > // SFINAE makes function contingent on trait +typename std::enable_if_t< BitmaskEnum< enumtype >::has_bit_or,enumtype> +operator|( enumtype a, enumtype b ) { + return static_cast(static_cast>(a) | + static_cast>(b)); +} + +/** + @brief Perform a bitwise AND between two enum values. + +This operator is only defined if in the declaration you define specialize BitmaskEnum +for the enum type in question with the `has_bit_and` trait. + + \param a The first enum value. + \param b The second enum value. + \return The result of performing a bitwise AND between the two values. +*/ +template< typename enumtype > // SFINAE makes function contingent on trait +typename std::enable_if_t< BitmaskEnum< enumtype >::has_bit_and,enumtype> +operator&( enumtype a, enumtype b ) { + return static_cast(static_cast>(a) & + static_cast>(b)); +} + + +/** + @brief Test if an enum value is valid. + +This function is only defined if in the declaration you define specialize BitmaskEnum +for the enum type in question with the `has_valid` trait. + + + @param a The enum value to test. + @return true if the enum value is not equal to zero, false otherwise. + + @code + enum class myenum { A=1,B=1<<1,C=1<<2 }; + //then activate the functions `|` and `valid` + template<> + struct BitmaskEnum< Keywords::argType > { + static constexpr bool has_valid = true; + static constexpr bool has_bit_or = true; + }; + //...code... + myenum val = myenum::A | myenum::C; + if(PLMD::valid( val | myenum::A)) + std::cout << "Is A\n"; + if(PLMD::valid(val | myenum::B)) + std::cout << "Is B\n"; + if(PLMD::valid(val | myenum::C)) + std::cout << "Is C\n"; + //will produce: + //Is A + //Is C + @endcode + + @return true if the enum value is not equal to zero, false otherwise. +*/ +template< typename enumtype > // SFINAE makes function contingent on trait +typename std::enable_if_t< BitmaskEnum< enumtype >::has_valid,bool> +valid( enumtype a) { + return static_cast>(a)!=0; +} + +} // namespace PLMD + +#endif //__PLUMED_tools_BitmaskEnum_h \ No newline at end of file diff --git a/src/tools/Keywords.cpp b/src/tools/Keywords.cpp index d14eb195a8..65c9a96587 100644 --- a/src/tools/Keywords.cpp +++ b/src/tools/Keywords.cpp @@ -24,45 +24,185 @@ #include "Tools.h" #include #include - +#include 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::grid: + return "grid"; + + case Keywords::argType::vector: + return "vector"; + + case Keywords::argType::matrix: + return "matrix"; + } + //the not simple cases + { + std::string ret=""; + std::string next=""; + if(valid(at & Keywords::argType::scalar)) { + ret+="scalar"; + next="/"; + } + if(valid(at & Keywords::argType::grid)) { + ret+=next+"grid"; + next="/"; + } + if(valid(at & Keywords::argType::vector)) { + ret+=next+"vector"; + next="/"; + } + if(valid(at & Keywords::argType::matrix)) { + ret+=next+"matrix"; + } + return ret; + } + //the return is outsids so the compile should block the compilation + //when expanding the enum without updating the toString + return ""; +} + +Keywords::argType stoat(std::string_view str) { + using namespace std::literals; + if(auto pos = str.find("/"sv); 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 == "grid") { + return Keywords::argType::grid; + } + if (str == "vector") { + return Keywords::argType::vector; + } + if (str == "matrix") { + return Keywords::argType::matrix; + } + // 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::grid: + return "grid"; + + case Keywords::componentType::vector: + return "vector"; + + case Keywords::componentType::matrix: + return "matrix"; + + 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::grid)) { + ret+=next+"grid"; + next="/"; + } + if(valid(at & Keywords::componentType::vector)) { + ret+=next+"vector"; + next="/"; + } + if(valid(at & Keywords::componentType::matrix)) { + ret+=next+"matrix"; + next="/"; + } + //I do not think these two are necessary + if(valid(at & Keywords::componentType::atom)) { + ret+=next+"atom"; + } + 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) { + using namespace std::literals; + if(auto pos = str.find("/"sv); 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)); } -void Keywords::KeyType::setStyle( const std::string& type ) { +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)); } } +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); +} + 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(); @@ -72,7 +212,9 @@ 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, +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 { @@ -139,50 +281,57 @@ void Keywords::copyData( std::vector& kk, std::vector& } } -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 " +#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..." +void Keywords::reserve( const std::string & keytype, + const std::string & key, + const std::string & docstring ) { + plumed_assert( !exists(key) && !reserved(key) ); + 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); + plumed_assert( !exists(key) && !reserved(key) ); + + std::string fd{docstring}; + bool allowMultiple= false; + if( type.isVessel() ) { + // 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 + lowkey.erase(std::remove(lowkey.begin(), lowkey.end(), '_'), lowkey.end()); + + 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")) ); + } + allowMultiple = true; + } else if( isNumbered ) { + fd += NUMBERED_DOCSTRING(key); + allowMultiple = true; } else { - fd = d; - if( t=="atoms" && isaction ) { - fd = d + ". For more information on how to specify lists of atoms see \\ref Group"; + //if( type.isAtomList() && isaction ) {//<- why not this? atoms could also be "residues" or "atoms-n" + if( keytype=="atoms" && isaction ) { + fd += ". 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) ); + if( type.isAtomList() ) { + atomtags.insert( std::pair(key,keytype) ); } } - documentation.insert( std::pair(k,fd) ); - reserved_keys.push_back(k); + types.insert( std::pair(key,type) ); + allowmultiple.insert( std::pair(key,allowMultiple) ); + documentation.insert( std::pair(key,fd) ); + reserved_keys.emplace_back(key); } -void Keywords::reserveFlag( const std::string & k, const bool def, const std::string & d ) { +void Keywords::reserveFlag(const std::string & k, const bool def, const std::string & d ) { plumed_assert( !exists(k) && !reserved(k) ); std::string defstr; if( def ) { @@ -190,7 +339,7 @@ void Keywords::reserveFlag( const std::string & k, const bool def, const std::st } else { defstr="( default=off ) "; } - types.insert( std::pair(k,KeyType("flag")) ); + types.insert( std::pair(k,KeyType::keyStyle::flag) ); std::string fd,lowkey=k; std::transform(lowkey.begin(),lowkey.end(),lowkey.begin(),[](unsigned char c) { return std::tolower(c); @@ -202,13 +351,14 @@ void Keywords::reserveFlag( const std::string & k, const bool def, const std::st reserved_keys.push_back(k); } -void Keywords::use( const std::string & k ) { - plumed_massert( reserved(k), "the " + k + " keyword is not reserved"); - for(unsigned i=0; i(k,true) ); - types.insert( std::pair(k, KeyType("optional")) ); + fd=docstring; + if( isNumbered ) { + fd += NUMBERED_DOCSTRING(key); } 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( (types.find(key)->second).isAtomList() ) { + //keytype may be "residues" or something like "atoms-3" + atomtags.insert( std::pair(key,keytype) ); } } - if( t=="atoms" && isaction ) { - fd = d + ". For more information on how to specify lists of atoms see \\ref Group"; + allowmultiple.insert( std::pair(key,isNumbered) ); + types.insert( std::pair(key, type) ); + if( type.isAtomList() && isaction ) { + fd += ". For more information on how to specify lists of atoms see \\ref Group"; } - documentation.insert( std::pair(k,fd) ); - keys.push_back(k); + documentation.insert( std::pair(key,fd) ); + keys.emplace_back(key); } -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 & typekey, + const std::string & key, + const std::string & datatype, + const std::string & docstring ) { + addInputKeyword(typekey,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 & typekey, + 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 + argument_types[key] = datatype; + add( typekey, key, docstring ); +} + +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 ); + argument_types[key] = datatype; + add( keyType, key, defaultV, docstring ); } -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); + // An optional keyword can't have a default + plumed_massert( !exists(key) && !reserved(key) && + (type.isCompulsory() || type.isHidden() ), "failing on keyword " + std::string(key) ); + types.insert( std::pair {key, type} ); + documentation.insert( std::pair(key,"( default=" + std::string(defaultValue) + " ) " + std::string(docstring) )); + allowmultiple.insert( std::pair(key,false) ); + numdefs.insert( std::pair(key,defaultValue) ); + keys.emplace_back(key); } void Keywords::addFlag( const std::string & k, const bool def, const std::string & d ) { @@ -294,14 +480,16 @@ void Keywords::remove( const std::string & k ) { unsigned j=0, n=0; while(true) { - for(j=0; j(name,key) ); cdocs.insert( std::pair(name,descr) ); - ctypes.insert( std::pair(name,type) ); + ctypes.insert( std::pair(name,stoct(type)) ); cnames.push_back(name); } @@ -836,11 +1014,11 @@ void Keywords::setValueDescription( const std::string& type, const std::string& if( !outputComponentExists(".#!value") ) { ckey.insert( std::pair(".#!value","default") ); cdocs.insert( std::pair(".#!value",descr) ); - ctypes.insert( std::pair(".#!value",type) ); + ctypes.insert( std::pair(".#!value",stoct (type)) ); cnames.push_back(".#!value"); } else { cdocs[".#!value"] = descr; - ctypes[".#!value"] = type; + ctypes[".#!value"] = stoct(type); } } @@ -885,34 +1063,34 @@ bool Keywords::componentHasCorrectType( const std::string& name, const std::size sname=name; } - if( thisactname=="CENTER" && ctypes.find(sname)->second=="atom" ) { + 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(ctypes.find(sname)->second | componentType::scalar)); } else if( hasderiv ) { - return (ctypes.find(sname)->second.find("grid")!=std::string::npos); + return (valid(ctypes.find(sname)->second | componentType::grid)); } else if( rank==1 ) { - return (ctypes.find(sname)->second.find("vector")!=std::string::npos); + return (valid(ctypes.find(sname)->second | componentType::vector)); } else if( rank==2 ) { - return (ctypes.find(sname)->second.find("matrix")!=std::string::npos); + return (valid(ctypes.find(sname)->second | componentType::matrix )); } return false; } 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 ) { + if( rank==0 && valid(x.second | argType::scalar)) { return true; } - if( hasderiv && x.second.find("grid")!=std::string::npos ) { + if( hasderiv && valid(x.second | argType::grid)) { return true; } - if( rank==1 && x.second.find("vector")!=std::string::npos ) { + if( rank==1 && valid(x.second | argType::vector)) { return true; } - if( rank==2 && x.second.find("matrix")!=std::string::npos ) { + if( rank==2 && valid(x.second | argType::matrix)) { return true; } } @@ -924,7 +1102,7 @@ std::string Keywords::getArgumentType( const std::string& name ) const { if( argument_types.find(name)==argument_types.end() ) { return ""; } - return argument_types.find(name)->second; + return toString(argument_types.find(name)->second); } std::string Keywords::getOutputComponentFlag( const std::string& name ) const { @@ -932,7 +1110,7 @@ std::string Keywords::getOutputComponentFlag( const std::string& name ) const { } std::string Keywords::getOutputComponentType( const std::string& name ) const { - return ctypes.find(name)->second; + return toString( components.find(name)->second.type); } std::string Keywords::getOutputComponentDescription( const std::string& name ) const { diff --git a/src/tools/Keywords.h b/src/tools/Keywords.h index 6d40ff1863..6c93eb58aa 100644 --- a/src/tools/Keywords.h +++ b/src/tools/Keywords.h @@ -23,10 +23,11 @@ #define __PLUMED_tools_Keywords_h #include #include -#include +#include #include #include "Exception.h" +#include "BitmaskEnum.h" namespace PLMD { @@ -35,48 +36,56 @@ 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} 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); } return ""; } }; + friend class Action; friend class ActionShortcut; friend class ActionRegister; +public: + enum class argType {scalar=1,grid=1<<2,vector=1<<3,matrix=1<<4}; + enum class componentType {scalar=1,grid=1<<2,vector=1<<3,matrix=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; @@ -88,14 +97,15 @@ class Keywords { std::vector keys; /// The names of the reserved keywords std::vector reserved_keys; + //std::less make some magic and makes find and [] work with string_view /// Whether the keyword is compulsory, optional... - std::map types; + 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; + 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 @@ -111,7 +121,7 @@ class Keywords { /// The documentation for a particular component std::map cdocs; /// The type of a particular component - std::map ctypes; + 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 @@ -150,25 +160,25 @@ 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( const std::string & keytype, const std::string & key, const std::string & docstring ); /// Reserve a flag void reserveFlag( const std::string & k, const bool def, const std::string & d ); /// Use one of the reserved keywords - void use( const std::string & k ); -/// Get the ith keyword + void use( std::string_view k ); +/// Get the ith keyword std::string_view std::string get( const unsigned k ) const ; /// 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 ); /// 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 ; /// Check if the keyword with name k has style t @@ -184,7 +194,7 @@ class Keywords { /// 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, + 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 ; @@ -207,8 +217,15 @@ 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 ); + //[[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 + //[[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 @@ -243,6 +260,39 @@ class Keywords { void setDisplayName( const std::string& name ); }; -} +template<> +struct 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 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 From ff0140c071ffeff91eec37b9d8c0511ae83ec6ce Mon Sep 17 00:00:00 2001 From: Daniele Rapetti <5535617+Iximiel@users.noreply.github.com> Date: Mon, 20 Jan 2025 17:06:13 +0100 Subject: [PATCH 02/19] removed some friend declaration to hide implementation details --- src/colvar/ColvarShortcut.h | 16 +++++------- src/colvar/MultiColvarTemplate.h | 4 +-- src/core/ActionRegister.cpp | 12 ++++++--- src/core/ActionShortcut.cpp | 44 +++++++++++++++++--------------- src/core/CLTool.cpp | 8 +++--- src/tools/Keywords.cpp | 15 +++++++++++ src/tools/Keywords.h | 33 ++++++++++++++++-------- 7 files changed, 82 insertions(+), 50 deletions(-) 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/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/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& Keywords::getNeededKeywords() const { return neededActions; } @@ -1187,6 +1191,17 @@ 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}; + ///@todo convert into a find, or asses that this is more readable + for(auto const& suffix : actionNameSuffixes ) { + if( (bname + suffix)==name ) { + return true; + } + } + return false; +} + void Keywords::setDisplayName( const std::string& name ) { thisactname = name; } diff --git a/src/tools/Keywords.h b/src/tools/Keywords.h index 6c93eb58aa..c95b413e08 100644 --- a/src/tools/Keywords.h +++ b/src/tools/Keywords.h @@ -80,9 +80,6 @@ class Keywords { } }; - friend class Action; - friend class ActionShortcut; - friend class ActionRegister; public: enum class argType {scalar=1,grid=1<<2,vector=1<<3,matrix=1<<4}; enum class componentType {scalar=1,grid=1<<2,vector=1<<3,matrix=1<<4,atoms=1<<5,atom=1<<6}; @@ -146,7 +143,11 @@ 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; + /// Reference to keys + const std::vector& getKeys() const { + return keys; + } /// Return the ith keyword std::string getKeyword( const unsigned i ) const ; /// Get the documentation for a particular keyword @@ -165,8 +166,6 @@ class Keywords { void reserveFlag( const std::string & k, const bool def, const std::string & d ); /// Use one of the reserved keywords void use( std::string_view k ); -/// Get the ith keyword std::string_view - std::string get( const unsigned k ) const ; /// Add a new keyword of type t with name k and description 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 @@ -237,21 +236,33 @@ class Keywords { /// Get the description of this component std::string getOutputComponentDescription( const std::string& name ) const ; /// Get the full list of output components - std::vector getOutputComponents() const ; + 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 ); +/// 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 action name suffixes to test for void addActionNameSuffix( const std::string& suffix ); +// @todo: I need to confront someone about the readabilityof this +/// Check that 'name' is among the possible suffixes + /** more or less it does this: + ``` + for(unsigned j=0 ; j& getNeededKeywords() const ; /// Return the name of the action that has this set of keywords From 1b4f9383c0dbb4dc7e12e25a6c14663314a121ac Mon Sep 17 00:00:00 2001 From: Daniele Rapetti <5535617+Iximiel@users.noreply.github.com> Date: Mon, 20 Jan 2025 17:07:13 +0100 Subject: [PATCH 03/19] made compontne in a struct, rempoved copyData --- src/tools/Keywords.cpp | 496 +++++++++++++++++++---------------------- src/tools/Keywords.h | 46 ++-- 2 files changed, 264 insertions(+), 278 deletions(-) diff --git a/src/tools/Keywords.cpp b/src/tools/Keywords.cpp index 4eaa2682da..e90ad92bdd 100644 --- a/src/tools/Keywords.cpp +++ b/src/tools/Keywords.cpp @@ -25,6 +25,13 @@ #include #include #include + +//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..." +#define ATOM_DOCSTRING ". For more information on how to specify lists of atoms see \\ref Group" + + namespace PLMD { std::string toString(Keywords::argType at) { @@ -204,84 +211,70 @@ void Keywords::KeyType::setStyle( std::string_view type ) { } std::string Keywords::getStyle( const std::string & k ) const { - plumed_massert( types.count(k), "Did not find keyword " + k ); + plumed_massert( exists(k)||reserved(k), "Did not find keyword " + k ); return (types.find(k)->second).toString(); } void Keywords::add( const Keywords& newkeys ) { - newkeys.copyData( keys, reserved_keys, types, allowmultiple, documentation, booldefs, numdefs, atomtags, cnames, ckey, cdocs ); -} + for(std::string thiskey:newkeys.keys) { + plumed_massert( exists(thiskey), "keyword " + thiskey + " is in twice" ); + plumed_massert( reserved(thiskey), "keyword " + thiskey + " is in twice" ); -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( newkeys.types.count( thiskey )!=0, "no type data on keyword " + thiskey + " to copy" ); + plumed_massert( newkeys.allowmultiple.count( thiskey )!=0, "no numbered data on keyword " + thiskey + " to copy" ); + plumed_massert( newkeys.documentation.count( thiskey )!=0, "no documentation for keyword " + thiskey + " to copy" ); + + keys.emplace_back( thiskey ); + + types.insert( std::pair( thiskey,newkeys.types.at(thiskey)) ); + if( (types.at(thiskey)).isAtomList() ) { + atomtags.insert( std::pair( thiskey,newkeys.atomtags.at(thiskey)) ); } - 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) ); + allowmultiple.insert( std::pair(thiskey,newkeys.allowmultiple.at(thiskey)) ); + documentation.insert( std::pair(thiskey,newkeys.documentation.at(thiskey)) ); + if( newkeys.booldefs.count( thiskey ) ) { + booldefs.insert( std::pair( thiskey,newkeys.booldefs.at(thiskey)) ); } - if( numdefs.count( thiskey ) ) { - nums.insert( std::pair( thiskey,numdefs.find(thiskey)->second) ); + if( newkeys.numdefs.count( thiskey ) ) { + numdefs.insert( std::pair( thiskey,newkeys.numdefs.at(thiskey)) ); } } - 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) ); + for (std::string thiskey : newkeys.reserved_keys) { + //in c++20 we'll use .contains + plumed_massert( exists(thiskey), "keyword " + thiskey + " is in twice" ); + plumed_massert( reserved(thiskey), "keyword " + thiskey + " is in twice" ); + + plumed_massert( newkeys.types.count( thiskey )!=0, "no type data on keyword " + thiskey + " to copy" ); + plumed_massert( newkeys.allowmultiple.count( thiskey )!=0, "no numbered data on keyword " + thiskey + " to copy" ); + plumed_massert( newkeys.documentation.count( thiskey )!=0, "no documentation for keyword " + thiskey + " to copy" ); + + reserved_keys.emplace_back( thiskey ); + + types.insert( std::pair( thiskey,newkeys.types.at(thiskey)) ); + if( (types.at(thiskey)).isAtomList() ) { + atomtags.insert( std::pair( thiskey,newkeys.atomtags.at(thiskey)) ); } - 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) ); + + allowmultiple.insert( std::pair(thiskey,newkeys.allowmultiple.at(thiskey)) ); + + documentation.insert( std::pair(thiskey,newkeys.documentation.at(thiskey)) ); + if( newkeys.booldefs.count( thiskey ) ) { + booldefs.insert( std::pair( thiskey,newkeys.booldefs.at(thiskey)) ); } - if( numdefs.count( thiskey ) ) { - nums.insert( std::pair( thiskey,numdefs.find(thiskey)->second) ); + if( newkeys.numdefs.count( thiskey ) ) { + numdefs.insert( std::pair( thiskey,newkeys.numdefs.at(thiskey)) ); } } - 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) ); + for (std::string 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); + //these asserts now are obsolete due the way compontents are created now + // plumed_massert( ckey.count( thisnam ), "no keyword data on component " + thisnam + " to copy" ); + // plumed_massert( cdocs.count( thisnam ), "no documentation on component " + thisnam + " to copy" ); } } -#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..." void Keywords::reserve( const std::string & keytype, const std::string & key, const std::string & docstring ) { @@ -319,7 +312,7 @@ void Keywords::reserve( const std::string & keytype, } else { //if( type.isAtomList() && isaction ) {//<- why not this? atoms could also be "residues" or "atoms-n" if( keytype=="atoms" && isaction ) { - fd += ". For more information on how to specify lists of atoms see \\ref Group"; + fd += ATOM_DOCSTRING; } if( type.isAtomList() ) { atomtags.insert( std::pair(key,keytype) ); @@ -348,7 +341,7 @@ void Keywords::reserveFlag(const std::string & k, const bool def, const std::str documentation.insert( std::pair(k,fd) ); allowmultiple.insert( std::pair(k,false) ); booldefs.insert( std::pair(k,def) ); - reserved_keys.push_back(k); + reserved_keys.emplace_back(k); } void Keywords::use(std::string_view k ) { @@ -401,7 +394,7 @@ void Keywords::add(std::string_view keytype, allowmultiple.insert( std::pair(key,isNumbered) ); types.insert( std::pair(key, type) ); if( type.isAtomList() && isaction ) { - fd += ". For more information on how to specify lists of atoms see \\ref Group"; + fd += ATOM_DOCSTRING; } documentation.insert( std::pair(key,fd) ); keys.emplace_back(key); @@ -472,34 +465,19 @@ void Keywords::addFlag( const std::string & k, const bool def, const std::string documentation.insert( std::pair(k,defstr + d) ); allowmultiple.insert( std::pair(k,false) ); booldefs.insert( std::pair(k,def) ); - keys.push_back(k); + keys.emplace_back(k); } 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 ); + //should I add also the "reserved(k)" to the test? + plumed_massert( exists(k), "Did not find keyword " + k ); return allowmultiple.find(k)->second; } @@ -548,50 +531,37 @@ bool Keywords::reserved( std::string_view k ) const { } void Keywords::print_template(const std::string& actionname, bool include_optional) const { - unsigned nkeys=0; std::printf("%s",actionname.c_str()); - 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( (types.find(key)->second).isAtomList() ) { + plumed_massert( atomtags.count(key), "keyword " + key + " allegedly specifies atoms but no tag has been specified. Please email Gareth Tribello"); + if( prevtag!="start" && prevtag!=atomtags.find(key)->second ) { break; } - if( (atomtags.find(keys[i])->second).find("residues")!=std::string::npos) { - std::printf(" %s=", keys[i].c_str() ); + if( (atomtags.find(key)->second).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=atomtags.find(key)->second; } } } - 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 ( (types.find(key)->second).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"); @@ -599,14 +569,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( (types.find(key)->second).isFlag() ) { + std::printf( ",flag:%s", key.c_str() ); } else { - if( allowmultiple.find(keys[i])->second ) { - std::printf(",numbered:%s",keys[i].c_str() ); + if( allowmultiple.find(key)->second ) { + std::printf(",numbered:%s",key.c_str() ); } else { - std::printf(",option:%s",keys[i].c_str() ); + std::printf(",option:%s",key.c_str() ); } } } @@ -618,8 +588,9 @@ 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++; } } @@ -630,15 +601,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"; @@ -647,12 +618,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"); } } @@ -660,8 +631,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++; } } @@ -670,12 +641,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"); } } @@ -685,8 +656,8 @@ void Keywords::print_html() const { } unsigned nkeys=0; - for(unsigned i=0; isecond).isAtomList() ) { + for(const auto& key : keys) { + if ( (types.find(key)->second).isAtomList() ) { nkeys++; } } @@ -701,10 +672,10 @@ 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 ( (types.find(key)->second).isAtomList() ) { + plumed_massert( atomtags.count(key), "keyword " + key + " allegedly specifies atoms but no tag has been specified. Please email Gareth Tribello"); + if( prevtag!="start" && prevtag!=atomtags.find(key)->second && isaction ) { std::cout<<"
\n\n"; if( isatoms ) { std::cout<<"\\par Or alternatively by using\n\n"; @@ -716,15 +687,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=atomtags.find(key)->second; } } std::cout<<"
\n\n"; } nkeys=0; - for(unsigned i=0; isecond).isCompulsory() ) { + for(const auto& key : keys) { + if ( (types.find(key)->second).isCompulsory() ) { nkeys++; } } @@ -735,16 +706,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 ( (types.find(key)->second).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 ( (types.find(key)->second).isFlag() || (types.find(key)->second).isOptional() || (types.find(key)->second).isVessel() ) { nkeys++; } } @@ -755,23 +726,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 ( (types.find(key)->second).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 ( (types.find(key)->second).isOptional() || (types.find(key)->second).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 ( (types.find(key)->second).isOptional() || (types.find(key)->second).isVessel() ) { + print_html_item( key ); } } } @@ -779,11 +750,11 @@ void Keywords::print_html() const { } void Keywords::print_spelling() const { - for(unsigned i=0; isecond).isAtomList() ) { + for(const auto& key : keys) { + if ( (types.find(key)->second).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 ( (types.find(key)->second).isAtomList() ) { + helpstr += getKeywordDocs( key ); } } } nkeys=0; - for(unsigned i=0; isecond).isCompulsory() ) { + for(const auto& key : keys) { + if ( (types.find(key)->second).isCompulsory() ) { nkeys++; } } unsigned ncompulsory=nkeys; if( nkeys>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 ( (types.find(key)->second).isCompulsory() ) { + helpstr += getKeywordDocs( key ); } } } nkeys=0; - for(unsigned i=0; isecond).isFlag() ) { + for(const auto& key : keys) { + if ( (types.find(key)->second).isFlag() ) { nkeys++; } } @@ -853,22 +824,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 ( (types.find(key)->second).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 ( (types.find(key)->second).isOptional() || (types.find(key)->second).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 ( (types.find(key)->second).isOptional() || (types.find(key)->second).isVessel() ) { + helpstr += getKeywordDocs( key ); } } helpstr += "\n"; @@ -924,11 +895,6 @@ void Keywords::print_html_item( const std::string& key ) const { std::printf("\n"); } -std::string Keywords::get( const unsigned k ) const { - plumed_assert( ksecond; @@ -958,9 +924,9 @@ void Keywords::destroyData() { booldefs.clear(); numdefs.clear(); atomtags.clear(); - ckey.clear(); - cdocs.clear(); - ckey.clear(); + components.clear(); + //cname was missing before, it is wanted or not? + cnames.clear(); } void Keywords::setComponentsIntroduction( const std::string& instr ) { @@ -987,38 +953,23 @@ 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,stoct(type)) ); - cnames.push_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",stoct (type)) ); - cnames.push_back(".#!value"); + components[".#!value"] =component() + .setKey("default") + .setDocstring(descr) + .setType(stoct(type)); + cnames.emplace_back(".#!value"); } else { - cdocs[".#!value"] = descr; - ctypes[".#!value"] = stoct(type); + components[".#!value"].docstring = descr; + components[".#!value"].type = stoct(type); } } @@ -1039,12 +990,7 @@ bool Keywords::outputComponentExists( const std::string& name ) const { sname=name; } - for(unsigned i=0; isecond | componentType::scalar)); + return (valid(components.at(sname).type | componentType::scalar)); } else if( hasderiv ) { - return (valid(ctypes.find(sname)->second | componentType::grid)); + return (valid(components.at(sname).type | componentType::grid)); } else if( rank==1 ) { - return (valid(ctypes.find(sname)->second | componentType::vector)); + return (valid(components.at(sname).type | componentType::vector)); } else if( rank==2 ) { - return (valid(ctypes.find(sname)->second | componentType::matrix )); + return (valid(components.at(sname).type | componentType::matrix )); } return false; } @@ -1106,7 +1052,7 @@ std::string Keywords::getArgumentType( const std::string& name ) const { } std::string Keywords::getOutputComponentFlag( const std::string& name ) const { - return ckey.find(name)->second; + return components.find(name)->second.key; } std::string Keywords::getOutputComponentType( const std::string& name ) const { @@ -1120,12 +1066,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; +} + +///////////DUPLICATED??????????/////// +void Keywords::removeOutputComponent( const std::string& name ) { + if(components.find(name)!=components.end()) { + components.erase(name); + cnames.erase(std::remove(cnames.begin(), cnames.end(), name), cnames.end()); + } +} + +void Keywords::removeComponent( const std::string& name ) { + if(components.find(name)!=components.end()) { + components.erase(name); + cnames.erase(std::remove(cnames.begin(), cnames.end(), name), cnames.end()); + } else { + plumed_massert(false,"You are trying to remove " + name + " a component that isn't there"); + } +} + + +/* +//ORIGINALS +DIFFERENCES: +removeOutputComponent does not clean the keys and don't cares if you try remove something that doesn't exist +removeComponent does clean keys and cares if you try to remove something that doesn't exist + +void Keywords::removeOutputComponent( const std::string& name ) { + unsigned j=0; + while(true) { + for(j=0; j Keywords::getOutputComponents() const { - return cnames; -} +*/ std::string Keywords::getKeywordDescription( const std::string& key ) const { plumed_assert( exists( key ) ); @@ -1210,4 +1178,4 @@ std::string Keywords::getDisplayName() const { return thisactname; } -} +}// namespace PLMD diff --git a/src/tools/Keywords.h b/src/tools/Keywords.h index c95b413e08..68a1977e51 100644 --- a/src/tools/Keywords.h +++ b/src/tools/Keywords.h @@ -90,9 +90,16 @@ class Keywords { bool isatoms; /// The name of the action that has this set of keywords std::string thisactname; -/// The names of the allowed keywords +/// The names of the allowed keywords, in order of declaration std::vector keys; -/// The names of the reserved keywords + // struct key{ + // KeyType type; + // bool allowmultiple; + // std::string defaultValue; + // std::string docstring; + //} + +/// The names of the reserved keywords, in order of declaration std::vector reserved_keys; //std::less make some magic and makes find and [] work with string_view /// Whether the keyword is compulsory, optional... @@ -109,16 +116,32 @@ class 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 a particular component + std::string key; + /// The documentation for a particular component + std::string docstring; + /// The type of a particular component + componentType type; + component& setKey(std::string k) { + key=k; + return *this; + } + component& setDocstring(std::string d) { + docstring=d; + return *this; + } + component& setType(componentType t) { + type=t; + return *this; + } + }; + //the "exists component" is stored here + 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 @@ -192,11 +215,6 @@ class Keywords { 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(); From 3ca933adb342c87c81c9c4532a446ec264b6de44 Mon Sep 17 00:00:00 2001 From: Daniele Rapetti <5535617+Iximiel@users.noreply.github.com> Date: Mon, 20 Jan 2025 15:31:20 +0100 Subject: [PATCH 04/19] structured also the keyword informations --- src/tools/Keywords.cpp | 295 +++++++++++++++++++++-------------------- src/tools/Keywords.h | 58 ++++---- 2 files changed, 179 insertions(+), 174 deletions(-) diff --git a/src/tools/Keywords.cpp b/src/tools/Keywords.cpp index e90ad92bdd..48ac8163a3 100644 --- a/src/tools/Keywords.cpp +++ b/src/tools/Keywords.cpp @@ -210,68 +210,76 @@ 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::setAllowMultiple(bool a) { + allowmultiple=a; + return *this; +} +Keywords::keyInfo& Keywords::keyInfo::setDefaultFlag(bool a) { + defaultValue=a; + return *this; +} + +Keywords::component::component()=default; +Keywords::component& Keywords::component::setKey(std::string k) { + key=k; + return *this; +} +Keywords::component& Keywords::component::setDocstring(std::string 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 (types.find(k)->second).toString(); + return (keywords.find(k)->second.type).toString(); } void Keywords::add( const Keywords& newkeys ) { + //copies data from + //loop on the declared keys for(std::string thiskey:newkeys.keys) { - plumed_massert( exists(thiskey), "keyword " + thiskey + " is in twice" ); - plumed_massert( reserved(thiskey), "keyword " + thiskey + " is in twice" ); - - plumed_massert( newkeys.types.count( thiskey )!=0, "no type data on keyword " + thiskey + " to copy" ); - plumed_massert( newkeys.allowmultiple.count( thiskey )!=0, "no numbered data on keyword " + thiskey + " to copy" ); - plumed_massert( newkeys.documentation.count( thiskey )!=0, "no documentation for keyword " + thiskey + " to copy" ); - + 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 ); - - types.insert( std::pair( thiskey,newkeys.types.at(thiskey)) ); - if( (types.at(thiskey)).isAtomList() ) { + if( keywords.at(thiskey).type.isAtomList() ) { atomtags.insert( std::pair( thiskey,newkeys.atomtags.at(thiskey)) ); } - allowmultiple.insert( std::pair(thiskey,newkeys.allowmultiple.at(thiskey)) ); - documentation.insert( std::pair(thiskey,newkeys.documentation.at(thiskey)) ); - if( newkeys.booldefs.count( thiskey ) ) { - booldefs.insert( std::pair( thiskey,newkeys.booldefs.at(thiskey)) ); - } - if( newkeys.numdefs.count( thiskey ) ) { - numdefs.insert( std::pair( thiskey,newkeys.numdefs.at(thiskey)) ); - } } + //loop on the reserved keys for (std::string thiskey : newkeys.reserved_keys) { - //in c++20 we'll use .contains - plumed_massert( exists(thiskey), "keyword " + thiskey + " is in twice" ); - plumed_massert( reserved(thiskey), "keyword " + thiskey + " is in twice" ); - - plumed_massert( newkeys.types.count( thiskey )!=0, "no type data on keyword " + thiskey + " to copy" ); - plumed_massert( newkeys.allowmultiple.count( thiskey )!=0, "no numbered data on keyword " + thiskey + " to copy" ); - plumed_massert( newkeys.documentation.count( thiskey )!=0, "no documentation for keyword " + thiskey + " to copy" ); + 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 ); - - types.insert( std::pair( thiskey,newkeys.types.at(thiskey)) ); - if( (types.at(thiskey)).isAtomList() ) { - atomtags.insert( std::pair( thiskey,newkeys.atomtags.at(thiskey)) ); - } - - allowmultiple.insert( std::pair(thiskey,newkeys.allowmultiple.at(thiskey)) ); - - documentation.insert( std::pair(thiskey,newkeys.documentation.at(thiskey)) ); - if( newkeys.booldefs.count( thiskey ) ) { - booldefs.insert( std::pair( thiskey,newkeys.booldefs.at(thiskey)) ); - } - if( newkeys.numdefs.count( thiskey ) ) { - numdefs.insert( std::pair( thiskey,newkeys.numdefs.at(thiskey)) ); - } } for (std::string 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); - //these asserts now are obsolete due the way compontents are created now - // plumed_massert( ckey.count( thisnam ), "no keyword data on component " + thisnam + " to copy" ); - // plumed_massert( cdocs.count( thisnam ), "no documentation on component " + thisnam + " to copy" ); } } @@ -318,9 +326,11 @@ void Keywords::reserve( const std::string & keytype, atomtags.insert( std::pair(key,keytype) ); } } - types.insert( std::pair(key,type) ); - allowmultiple.insert( std::pair(key,allowMultiple) ); - documentation.insert( std::pair(key,fd) ); + + keywords[key] = keyInfo() + .setType(type) + .setDocString(fd) + .setAllowMultiple(allowMultiple); reserved_keys.emplace_back(key); } @@ -332,18 +342,19 @@ void Keywords::reserveFlag(const std::string & k, const bool def, const std::str } else { defstr="( default=off ) "; } - types.insert( std::pair(k,KeyType::keyStyle::flag) ); - std::string fd,lowkey=k; - std::transform(lowkey.begin(),lowkey.end(),lowkey.begin(),[](unsigned char c) { - return std::tolower(c); - }); + std::string fd; + // std::string 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) ); + keywords[k] = keyInfo() + .setType(KeyType(KeyType::keyStyle::flag)) + .setDocString(fd) + .setAllowMultiple(false) + .setDefaultFlag(def); reserved_keys.emplace_back(k); } +///this "copies" a reserved key into the keylist so it can be used void Keywords::use(std::string_view k ) { plumed_massert( reserved(k), "the " + std::string(k) + " keyword is not reserved"); keys.emplace_back(k); @@ -357,14 +368,14 @@ void Keywords::use(std::string_view k ) { void Keywords::reset_style( const std::string & k, const std::string & style ) { plumed_massert( exists(k) || reserved(k), "no " + k + " keyword" ); if( style=="numbered" ) { - allowmultiple[k]=true; + keywords.at(k).allowmultiple=true; return; } - (types.find(k)->second).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() ) { + if( (keywords.at(k).type).isAtomList() ) { atomtags.insert( std::pair(k,style) ); } } @@ -386,17 +397,18 @@ void Keywords::add(std::string_view keytype, if( isNumbered ) { fd += NUMBERED_DOCSTRING(key); } else { - if( (types.find(key)->second).isAtomList() ) { + if( type.isAtomList() ) { //keytype may be "residues" or something like "atoms-3" atomtags.insert( std::pair(key,keytype) ); } } - allowmultiple.insert( std::pair(key,isNumbered) ); - types.insert( std::pair(key, type) ); if( type.isAtomList() && isaction ) { fd += ATOM_DOCSTRING; } - documentation.insert( std::pair(key,fd) ); + keywords[std::string(key)] = keyInfo() + .setType(type) + .setDocString(fd) + .setAllowMultiple(isNumbered); keys.emplace_back(key); } @@ -442,30 +454,34 @@ void Keywords::addInputKeyword( const std::string & keyType, void Keywords::add( std::string_view keytype, std::string_view key, - std::string_view defaultValue, + 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( !exists(key) && !reserved(key) && - (type.isCompulsory() || type.isHidden() ), "failing on keyword " + std::string(key) ); - types.insert( std::pair {key, type} ); - documentation.insert( std::pair(key,"( default=" + std::string(defaultValue) + " ) " + std::string(docstring) )); - allowmultiple.insert( std::pair(key,false) ); - numdefs.insert( std::pair(key,defaultValue) ); + 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.emplace_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 ) { @@ -479,11 +495,9 @@ void Keywords::remove( const std::string & k ) { } plumed_massert(found,"You are trying to forbid " + k + " a keyword that isn't there"); // Delete documentation, type and so on from the description - types.erase(k); - documentation.erase(k); - allowmultiple.erase(k); - booldefs.erase(k); - numdefs.erase(k); + keywords.erase(k); + //and the atomtags? + // Remove any output comonents that this keyword creates //we need the double loop because we should not remove and iterate on the map at the same time std::vector markForRemoval{}; @@ -503,7 +517,7 @@ bool Keywords::numbered( const std::string & k ) const { } //should I add also the "reserved(k)" to the test? plumed_massert( exists(k), "Did not find keyword " + k ); - return allowmultiple.find(k)->second; + return keywords.at(k).allowmultiple; } bool Keywords::style( const std::string & k, const std::string & t ) const { @@ -535,7 +549,7 @@ void Keywords::print_template(const std::string& actionname, bool include_option { std::string prevtag="start"; for(const auto& key : keys) { - if( (types.find(key)->second).isAtomList() ) { + if( keywords.at(key).type.isAtomList() ) { plumed_massert( atomtags.count(key), "keyword " + key + " allegedly specifies atoms but no tag has been specified. Please email Gareth Tribello"); if( prevtag!="start" && prevtag!=atomtags.find(key)->second ) { break; @@ -551,7 +565,7 @@ void Keywords::print_template(const std::string& actionname, bool include_option } for(const auto& key : keys) { - if ( (types.find(key)->second).isCompulsory() ) { + if ( keywords.at(key).type.isCompulsory() ) { std::string def; if( getDefaultValue( key, def) ) { std::printf(" %s=%s ", key.c_str(), def.c_str() ); @@ -570,10 +584,10 @@ void Keywords::print_template(const std::string& actionname, bool include_option void Keywords::print_vim() const { for(const auto& key : keys) { - if( (types.find(key)->second).isFlag() ) { + if( keywords.at(key).type.isFlag() ) { std::printf( ",flag:%s", key.c_str() ); } else { - if( allowmultiple.find(key)->second ) { + if( keywords.at(key).allowmultiple ) { std::printf(",numbered:%s",key.c_str() ); } else { std::printf(",option:%s",key.c_str() ); @@ -657,7 +671,7 @@ void Keywords::print_html() const { unsigned nkeys=0; for(const auto& key : keys) { - if ( (types.find(key)->second).isAtomList() ) { + if ( keywords.at(key).type.isAtomList() ) { nkeys++; } } @@ -673,7 +687,7 @@ void Keywords::print_html() const { std::string prevtag="start"; unsigned counter=0; for(const auto& key : keys) { - if ( (types.find(key)->second).isAtomList() ) { + if ( keywords.at(key).type.isAtomList() ) { plumed_massert( atomtags.count(key), "keyword " + key + " allegedly specifies atoms but no tag has been specified. Please email Gareth Tribello"); if( prevtag!="start" && prevtag!=atomtags.find(key)->second && isaction ) { std::cout<<"
\n\n"; @@ -695,7 +709,7 @@ void Keywords::print_html() const { } nkeys=0; for(const auto& key : keys) { - if ( (types.find(key)->second).isCompulsory() ) { + if ( keywords.at(key).type.isCompulsory() ) { nkeys++; } } @@ -707,41 +721,35 @@ void Keywords::print_html() const { } std::cout<<" \n"; for(const auto& key : keys) { - if ( (types.find(key)->second).isCompulsory() ) { + if ( keywords.at(key).type.isCompulsory() ) { print_html_item( key ); } + std::cout<<"
\n\n"; } - std::cout<<"\n\n"; - } - nkeys=0; - for(const auto& key : keys) { - if ( (types.find(key)->second).isFlag() || (types.find(key)->second).isOptional() || (types.find(key)->second).isVessel() ) { - nkeys++; - } - } - if( nkeys>0 ) { - if(isaction) { - std::cout<<"\\par Options\n\n"; - } else { - std::cout<<"\\par The following options are available\n\n"; - } - std::cout<<" \n"; + nkeys=0; for(const auto& key : keys) { - if ( (types.find(key)->second).isFlag() ) { - print_html_item( key ); + if ( keywords.at(key).type.isFlag() || keywords.at(key).type.isOptional() || keywords.at(key).type.isVessel() ) { + nkeys++; } } - std::cout<<"\n"; - } - nkeys=0; - for(const auto& key : keys) { - if ( (types.find(key)->second).isOptional() || (types.find(key)->second).isVessel() ) { - nkeys++; + if( nkeys>0 ) { + if(isaction) { + std::cout<<"\\par Options\n\n"; + } else { + std::cout<<"\\par The following options are available\n\n"; + } + std::cout<<"
\n"; + for(const auto& key : keys) { + } + std::cout<<"\n"; + } + nkeys=0; + for(const auto& key : keys) { } } if( nkeys>0 ) { for(const auto& key : keys) { - if ( (types.find(key)->second).isOptional() || (types.find(key)->second).isVessel() ) { + if ( keywords.at(key).type.isOptional() || keywords.at(key).type.isVessel() ) { print_html_item( key ); } } @@ -751,7 +759,6 @@ void Keywords::print_html() const { void Keywords::print_spelling() const { for(const auto& key : keys) { - std::printf("%s\n", key.c_str() ); } for(const auto& cname : cnames) { std::printf("%s\n",cname.c_str() ); @@ -759,8 +766,8 @@ void Keywords::print_spelling() const { } std::string Keywords::getKeywordDocs( const std::string& key ) const { - bool killdot=( (documentation.find(key)->second).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() ) { + 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(const auto& key : keys) { - if ( (types.find(key)->second).isAtomList() ) { + if ( keywords.at(key).type.isAtomList() ) { helpstr += getKeywordDocs( key ); } } } - nkeys=0; for(const auto& key : keys) { - if ( (types.find(key)->second).isCompulsory() ) { + if ( keywords.at(key).type.isCompulsory() ) { nkeys++; } } @@ -807,14 +813,14 @@ std::string Keywords::getHelpString() const { if( nkeys>0 ) { helpstr += "\nThe following arguments are compulsory: \n\n"; for(const auto& key : keys) { - if ( (types.find(key)->second).isCompulsory() ) { + if ( keywords.at(key).type.isCompulsory() ) { helpstr += getKeywordDocs( key ); } } } nkeys=0; for(const auto& key : keys) { - if ( (types.find(key)->second).isFlag() ) { + if ( keywords.at(key).type.isFlag() ) { nkeys++; } } @@ -825,20 +831,20 @@ std::string Keywords::getHelpString() const { helpstr += "\nThe following options are available\n\n"; } for(const auto& key : keys) { - if ( (types.find(key)->second).isFlag() ) { + if ( keywords.at(key).type.isFlag() ) { helpstr += getKeywordDocs( key ).c_str(); } } } nkeys=0; for(const auto& key : keys) { - if ( (types.find(key)->second).isOptional() || (types.find(key)->second).isVessel() ) { + if ( keywords.at(key).type.isOptional() || keywords.at(key).type.isVessel() ) { nkeys++; } } if( nkeys>0 ) { for(const auto& key : keys) { - if ( (types.find(key)->second).isOptional() || (types.find(key)->second).isVessel() ) { + if ( keywords.at(key).type.isOptional() || keywords.at(key).type.isVessel() ) { helpstr += getKeywordDocs( key ); } } @@ -861,8 +867,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(")"); @@ -891,13 +898,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"); } bool Keywords::getLogicalDefault(const std::string & key, bool& def ) const { - if( booldefs.find(key)!=booldefs.end() ) { - def=booldefs.find(key)->second; + // 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; @@ -905,10 +913,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; @@ -918,11 +925,7 @@ 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(); + keywords.clear(); atomtags.clear(); components.clear(); //cname was missing before, it is wanted or not? @@ -1134,7 +1137,7 @@ void Keywords::removeComponent( const std::string& 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 ) { diff --git a/src/tools/Keywords.h b/src/tools/Keywords.h index 68a1977e51..9bbec91dc7 100644 --- a/src/tools/Keywords.h +++ b/src/tools/Keywords.h @@ -25,6 +25,7 @@ #include #include #include +#include #include "Exception.h" #include "BitmaskEnum.h" @@ -37,7 +38,7 @@ class Log; class Keywords { /// This class lets me pass keyword types easily struct KeyType { - enum class keyStyle {hidden,compulsory,flag,optional,atoms,vessel} style; + enum class keyStyle {hidden,compulsory,flag,optional,atoms,vessel,unknown} style; static keyStyle keyStyleFromString(std::string_view type ); explicit KeyType( keyStyle type ); explicit KeyType( std::string_view type ); @@ -75,8 +76,10 @@ class Keywords { return "hidden"; case keyStyle::vessel: return "vessel"; + default: + plumed_massert(false,"unknown keyword type"); } - return ""; + return "unknown"; } }; @@ -90,30 +93,37 @@ class Keywords { bool isatoms; /// The name of the action that has this set of keywords std::string thisactname; + + struct keyInfo { + KeyType type; + std::string docstring; + std::variant defaultValue; + bool allowmultiple; + keyInfo(); + keyInfo& setType(KeyType t); + keyInfo& setDocString(std::string_view d); + keyInfo& setDefaultValue(std::string_view d); + keyInfo& setAllowMultiple(bool a); + keyInfo& setDefaultFlag(bool a); + }; + std::map> keywords; /// The names of the allowed keywords, in order of declaration std::vector keys; - // struct key{ - // KeyType type; - // bool allowmultiple; - // std::string defaultValue; - // std::string docstring; - //} - /// The names of the reserved keywords, in order of declaration std::vector reserved_keys; //std::less make some magic and makes find and [] work with string_view /// Whether the keyword is compulsory, optional... - std::map> types; + // std::map> types; /// Do we allow stuff like key1, key2 etc - std::map allowmultiple; + // std::map allowmultiple; /// The documentation for the keywords - std::map documentation; + // 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; + // std::map booldefs; /// The default values (if there are default values) for compulsory keywords - std::map numdefs; + // 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 { @@ -123,21 +133,13 @@ class Keywords { std::string docstring; /// The type of a particular component componentType type; - component& setKey(std::string k) { - key=k; - return *this; - } - component& setDocstring(std::string d) { - docstring=d; - return *this; - } - component& setType(componentType t) { - type=t; - return *this; - } + component(); + component& setKey(std::string k); + component& setDocstring(std::string d); + component& setType(componentType t); }; //the "exists component" is stored here - std::map components; + 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, in order of their (first) declaration @@ -194,7 +196,7 @@ class Keywords { /// Add a new compulsory keyword (t must equal compulsory) with name k, default value def and description 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 From d2da9dc6218f4a7cff25fc098bb20634cd6b0577 Mon Sep 17 00:00:00 2001 From: Daniele Rapetti <5535617+Iximiel@users.noreply.github.com> Date: Mon, 20 Jan 2025 11:53:22 +0100 Subject: [PATCH 05/19] reorganization of some comments --- src/tools/Keywords.h | 35 +++++++++++++++++------------------ 1 file changed, 17 insertions(+), 18 deletions(-) diff --git a/src/tools/Keywords.h b/src/tools/Keywords.h index 9bbec91dc7..7e6c08c126 100644 --- a/src/tools/Keywords.h +++ b/src/tools/Keywords.h @@ -95,50 +95,47 @@ class Keywords { std::string thisactname; struct keyInfo { + /// Whether the keyword is compulsory, optional... KeyType type; + /// The documentation for the keyword std::string docstring; + /// The default values (if there are default values) for compulsory keywords or flags std::variant defaultValue; + /// 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& setAllowMultiple(bool a); keyInfo& setDefaultFlag(bool a); }; + //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, in order of declaration std::vector reserved_keys; - //std::less make some magic and makes find and [] work with string_view -/// 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; + std::map> argument_types; /// The tags for atoms - we use this so the manual can differentiate between different ways of specifying atoms - std::map atomtags; + std::map> atomtags; struct component { - /// The keyword that turns on a particular component + /// The keyword that turns on this component std::string key; - /// The documentation for a particular component + /// The documentation for the component std::string docstring; - /// The type of a particular component + /// 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 k); component& setDocstring(std::string d); component& setType(componentType t); }; - //the "exists component" is stored here + //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; @@ -148,7 +145,7 @@ class Keywords { 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 @@ -291,6 +288,8 @@ class Keywords { void setDisplayName( const std::string& name ); }; +//the follwoing templates psecializations make the bitmask enum work with the +// bitwise operators | & and the "valid" function (valid converts to bool a result of a "mask operation") template<> struct BitmaskEnum< Keywords::componentType > { static constexpr bool has_valid = true; From c9af3b2e3143f6cb3b3753edb6d7f08c55d6a453 Mon Sep 17 00:00:00 2001 From: Daniele Rapetti <5535617+Iximiel@users.noreply.github.com> Date: Tue, 21 Jan 2025 10:39:44 +0100 Subject: [PATCH 06/19] Documenting better the bitmask enabler --- src/tools/BitmaskEnum.cpp | 21 ++++++ src/tools/BitmaskEnum.h | 142 ++++++++++++++++++++++++-------------- src/tools/Keywords.h | 4 +- 3 files changed, 114 insertions(+), 53 deletions(-) create mode 100644 src/tools/BitmaskEnum.cpp diff --git a/src/tools/BitmaskEnum.cpp b/src/tools/BitmaskEnum.cpp new file mode 100644 index 0000000000..8a38cd0578 --- /dev/null +++ b/src/tools/BitmaskEnum.cpp @@ -0,0 +1,21 @@ +/* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + 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 . ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */ \ No newline at end of file diff --git a/src/tools/BitmaskEnum.h b/src/tools/BitmaskEnum.h index 6e6567c15c..a140d00c3a 100644 --- a/src/tools/BitmaskEnum.h +++ b/src/tools/BitmaskEnum.h @@ -25,15 +25,15 @@ #include namespace PLMD { +namespace enum_traits { +/** @brief struct for setting up bitmask operations on enum types -/// support struct for setting up some operations on enum types -template< typename enum_type > -struct BitmaskEnum { - //example traits - //Use: specialize with extra traits (see Keywords.h) - // Example: - /* - // Please note that the 0 value is not implemented (it is reserved as a result of mask not matching masks) + 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 > { @@ -41,87 +41,127 @@ struct BitmaskEnum { static constexpr bool has_bit_or = true; static constexpr bool has_bit_and = true; }; - */ - // Currenlty we have implemented: - // 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 OR between two enum values. + @brief Perform a bitwise AND between two enum values. -This operator is only defined if in the declaration you define specialize BitmaskEnum -for the enum type in question with the `has_bit_or` trait. + @param a The first enum value. + @param b The second enum value. + @return The result of performing a bitwise AND between the two 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_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 -typename std::enable_if_t< BitmaskEnum< enumtype >::has_bit_or,enumtype> -operator|( enumtype a, enumtype b ) { - return static_cast(static_cast>(a) | +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 AND between two enum values. + @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. -This operator is only defined if in the declaration you define specialize BitmaskEnum -for the enum type in question with the `has_bit_and` trait. +The principal use is to compose named enum values into masks or combined options. - \param a The first enum value. - \param b The second enum value. - \return The result of performing a bitwise AND between the two values. +@see valid(enumtype) for a complete example +@see operator&(enumtype, enumtype) */ template< typename enumtype > // SFINAE makes function contingent on trait -typename std::enable_if_t< BitmaskEnum< enumtype >::has_bit_and,enumtype> -operator&( enumtype a, enumtype b ) { - return static_cast(static_cast>(a) & +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. -This function is only defined if in the declaration you define specialize BitmaskEnum -for the enum type in question with the `has_valid` trait. - - @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` + //then activate the functions `&`, `|` and `valid` template<> - struct BitmaskEnum< Keywords::argType > { - static constexpr bool has_valid = true; - static constexpr bool has_bit_or = true; + 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; - if(PLMD::valid( val | myenum::A)) - std::cout << "Is A\n"; - if(PLMD::valid(val | myenum::B)) - std::cout << "Is B\n"; - if(PLMD::valid(val | myenum::C)) - std::cout << "Is C\n"; + 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: - //Is A - //Is C + ///>val is 5 + ///>val has A + ///>val has C + ///>val has C and A @endcode - @return true if the enum value is not equal to zero, false otherwise. +@see operator|(enumtype, enumtype) +@see operator&(enumtype, enumtype) */ template< typename enumtype > // SFINAE makes function contingent on trait -typename std::enable_if_t< BitmaskEnum< enumtype >::has_valid,bool> +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 \ No newline at end of file diff --git a/src/tools/Keywords.h b/src/tools/Keywords.h index 7e6c08c126..c7e8eae900 100644 --- a/src/tools/Keywords.h +++ b/src/tools/Keywords.h @@ -291,14 +291,14 @@ class Keywords { //the follwoing templates psecializations make the bitmask enum work with the // bitwise operators | & and the "valid" function (valid converts to bool a result of a "mask operation") template<> -struct BitmaskEnum< Keywords::componentType > { +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 BitmaskEnum< Keywords::argType > { +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; From eb66ba765e5527affc475217114e5909ade23921 Mon Sep 17 00:00:00 2001 From: Daniele Rapetti <5535617+Iximiel@users.noreply.github.com> Date: Tue, 21 Jan 2025 12:09:41 +0100 Subject: [PATCH 07/19] adjusting the documentation --- src/tools/Keywords.h | 40 +++++++++++++++++----------------------- 1 file changed, 17 insertions(+), 23 deletions(-) diff --git a/src/tools/Keywords.h b/src/tools/Keywords.h index c7e8eae900..b19671094c 100644 --- a/src/tools/Keywords.h +++ b/src/tools/Keywords.h @@ -166,7 +166,7 @@ class Keywords { unsigned size() const; /// Check if numbered keywords are allowed for this action bool numbered( const std::string & k ) const; - /// Reference to keys + /// Get the ordered list of active keywords (not the reserved ones) const std::vector& getKeys() const { return keys; } @@ -188,6 +188,8 @@ class Keywords { void reserveFlag( const std::string & k, const bool def, const std::string & d ); /// Use one of the reserved keywords 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( 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 @@ -212,8 +214,6 @@ class Keywords { void print_template( const std::string& actionname, bool include_optional) const ; /// Change the style of a keyword void reset_style( const std::string & k, const std::string & style ); -/// Add keywords from one keyword object to another - void add( const Keywords& keys ); /// Clear everything from the keywords object. /// Not actually needed if your Keywords object is going out of scope. void destroyData(); @@ -233,12 +233,12 @@ 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 - //[[deprecated("Please specify the data type for the argument from scalar/vector/matrix/grid with the Keywords::argType enum")]] +/// @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 - //[[deprecated("Please specify the data type for the argument from scalar/vector/matrix/grid with the Keywords::argType enum")]] + /// @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 ); @@ -246,13 +246,13 @@ class Keywords { 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 +/// Get the full ordered list of output components const std::vector& getOutputComponents() const { return cnames; } @@ -266,19 +266,13 @@ class Keywords { void needsAction( const std::string& name ); /// 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 action name suffixes to test for +/// Add a suffix to the list of possible action name suffixes void addActionNameSuffix( const std::string& suffix ); -// @todo: I need to confront someone about the readabilityof this -/// Check that 'name' is among the possible suffixes - /** more or less it does this: - ``` - for(unsigned j=0 ; j& getNeededKeywords() const ; @@ -288,8 +282,8 @@ class Keywords { void setDisplayName( const std::string& name ); }; -//the follwoing templates psecializations make the bitmask enum work with the -// bitwise operators | & and the "valid" function (valid converts to bool a result of a "mask operation") +//the following templates specializations make the bitmask enum work with the +// bitwise operators `|`, `&` and the "valid" function (valid converts to bool a result of a "mask operation") template<> struct enum_traits::BitmaskEnum< Keywords::componentType > { static constexpr bool has_valid = true; From 825abf735859896d915bc8cd2e4aa896deab742f Mon Sep 17 00:00:00 2001 From: Daniele Rapetti <5535617+Iximiel@users.noreply.github.com> Date: Tue, 21 Jan 2025 12:44:56 +0100 Subject: [PATCH 08/19] using algorithms for isActionSuffixed --- src/tools/BitmaskEnum.cpp | 2 +- src/tools/BitmaskEnum.h | 2 +- src/tools/Keywords.cpp | 11 +++++------ src/tools/Keywords.h | 2 +- 4 files changed, 8 insertions(+), 9 deletions(-) diff --git a/src/tools/BitmaskEnum.cpp b/src/tools/BitmaskEnum.cpp index 8a38cd0578..98a1789c82 100644 --- a/src/tools/BitmaskEnum.cpp +++ b/src/tools/BitmaskEnum.cpp @@ -18,4 +18,4 @@ You should have received a copy of the GNU Lesser General Public License along with plumed. If not, see . -+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */ \ No newline at end of file ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */ diff --git a/src/tools/BitmaskEnum.h b/src/tools/BitmaskEnum.h index a140d00c3a..dd9c0cd563 100644 --- a/src/tools/BitmaskEnum.h +++ b/src/tools/BitmaskEnum.h @@ -164,4 +164,4 @@ valid( enumtype a) { } } // namespace PLMD -#endif //__PLUMED_tools_BitmaskEnum_h \ No newline at end of file +#endif //__PLUMED_tools_BitmaskEnum_h diff --git a/src/tools/Keywords.cpp b/src/tools/Keywords.cpp index 48ac8163a3..9eb2490942 100644 --- a/src/tools/Keywords.cpp +++ b/src/tools/Keywords.cpp @@ -1164,13 +1164,12 @@ void Keywords::addActionNameSuffix( const std::string& suffix ) { bool Keywords::isActionSuffixed( std::string_view name, std::string_view basename) const { std::string bname{basename}; - ///@todo convert into a find, or asses that this is more readable - for(auto const& suffix : actionNameSuffixes ) { - if( (bname + suffix)==name ) { - return true; - } + return std::any_of(actionNameSuffixes.begin(), + actionNameSuffixes.end(), + [name,&bname](const std::string& suffix)->bool{ + return (bname + suffix)==name ; } - return false; + ); } void Keywords::setDisplayName( const std::string& name ) { diff --git a/src/tools/Keywords.h b/src/tools/Keywords.h index b19671094c..4929de586c 100644 --- a/src/tools/Keywords.h +++ b/src/tools/Keywords.h @@ -283,7 +283,7 @@ class Keywords { }; //the following templates specializations make the bitmask enum work with the -// bitwise operators `|`, `&` and the "valid" function (valid converts to bool a result of a "mask operation") +// 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; From 3670b27fee3050538a13375786c1df1ed4523a13 Mon Sep 17 00:00:00 2001 From: Daniele Rapetti <5535617+Iximiel@users.noreply.github.com> Date: Tue, 21 Jan 2025 15:33:28 +0100 Subject: [PATCH 09/19] adding constexpr to BitmaskEnum operations --- src/tools/BitmaskEnum.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/tools/BitmaskEnum.h b/src/tools/BitmaskEnum.h index dd9c0cd563..8c542d2ada 100644 --- a/src/tools/BitmaskEnum.h +++ b/src/tools/BitmaskEnum.h @@ -84,7 +84,7 @@ value you should use valid(enumtype) to check it. @see operator|(enumtype, enumtype) */ template< typename enumtype > // SFINAE makes function contingent on trait -typename std::enable_if_t< enum_traits::BitmaskEnum< enumtype >::has_bit_and,enumtype> +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)); @@ -106,7 +106,7 @@ The principal use is to compose named enum values into masks or combined options @see operator&(enumtype, enumtype) */ template< typename enumtype > // SFINAE makes function contingent on trait -typename std::enable_if_t< enum_traits::BitmaskEnum< enumtype >::has_bit_or,enumtype> +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)); @@ -158,7 +158,7 @@ enum_traits::BitmaskEnum with the `has_valid` trait enabled. @see operator&(enumtype, enumtype) */ template< typename enumtype > // SFINAE makes function contingent on trait -typename std::enable_if_t< enum_traits::BitmaskEnum< enumtype >::has_valid,bool> +constexpr typename std::enable_if_t< enum_traits::BitmaskEnum< enumtype >::has_valid,bool> valid( enumtype a) { return static_cast>(a)!=0; } From b0a44fda7a45e9998a54d76869226f3cd715c12c Mon Sep 17 00:00:00 2001 From: Daniele Rapetti <5535617+Iximiel@users.noreply.github.com> Date: Tue, 21 Jan 2025 16:07:03 +0100 Subject: [PATCH 10/19] added an example in Distance, modernized also Components declaration --- src/colvar/Distance.cpp | 15 ++++++++------- src/tools/Keywords.cpp | 17 +++++++++++++---- src/tools/Keywords.h | 6 +++++- 3 files changed, 26 insertions(+), 12 deletions(-) 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/tools/Keywords.cpp b/src/tools/Keywords.cpp index 9eb2490942..1948dd7581 100644 --- a/src/tools/Keywords.cpp +++ b/src/tools/Keywords.cpp @@ -215,7 +215,8 @@ Keywords::keyInfo::keyInfo() docstring(""), defaultValue(std::monostate()), allowmultiple(false) -{}; +{} + Keywords::keyInfo& Keywords::keyInfo::setType(Keywords::KeyType t) { type=t; return *this; @@ -941,6 +942,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" ); @@ -959,20 +964,24 @@ void Keywords::addOutputComponent( const std::string& name, const std::string& k components[name] = component() .setKey(key) .setDocstring(descr) - .setType(stoct(type)); + .setType(type); cnames.emplace_back(name); } void Keywords::setValueDescription( const std::string& type, const std::string& descr ) { + setValueDescription(stoct (type),descr); +} + +void Keywords::setValueDescription( componentType type, const std::string& descr ) { if( !outputComponentExists(".#!value") ) { components[".#!value"] =component() .setKey("default") .setDocstring(descr) - .setType(stoct(type)); + .setType(type); cnames.emplace_back(".#!value"); } else { components[".#!value"].docstring = descr; - components[".#!value"].type = stoct(type); + components[".#!value"].type = type; } } diff --git a/src/tools/Keywords.h b/src/tools/Keywords.h index 4929de586c..210018679a 100644 --- a/src/tools/Keywords.h +++ b/src/tools/Keywords.h @@ -221,11 +221,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? From c8e7997affe3619bfb36d9e035bcc5935b718c15 Mon Sep 17 00:00:00 2001 From: Daniele Rapetti <5535617+Iximiel@users.noreply.github.com> Date: Tue, 21 Jan 2025 16:33:26 +0100 Subject: [PATCH 11/19] restoring the flavour of removeOutputComponent with no existance checks --- src/tools/Keywords.cpp | 41 ++--------------------------------------- 1 file changed, 2 insertions(+), 39 deletions(-) diff --git a/src/tools/Keywords.cpp b/src/tools/Keywords.cpp index 1948dd7581..7708e08c77 100644 --- a/src/tools/Keywords.cpp +++ b/src/tools/Keywords.cpp @@ -1094,10 +1094,8 @@ std::string Keywords::getOutputComponentDescription( const std::string& name ) c ///////////DUPLICATED??????????/////// void Keywords::removeOutputComponent( const std::string& name ) { - if(components.find(name)!=components.end()) { - components.erase(name); - cnames.erase(std::remove(cnames.begin(), cnames.end(), name), cnames.end()); - } + components.erase(name); + cnames.erase(std::remove(cnames.begin(), cnames.end(), name), cnames.end()); } void Keywords::removeComponent( const std::string& name ) { @@ -1109,41 +1107,6 @@ void Keywords::removeComponent( const std::string& name ) { } } - -/* -//ORIGINALS -DIFFERENCES: -removeOutputComponent does not clean the keys and don't cares if you try remove something that doesn't exist -removeComponent does clean keys and cares if you try to remove something that doesn't exist - -void Keywords::removeOutputComponent( const std::string& name ) { - unsigned j=0; - while(true) { - for(j=0; j Date: Wed, 22 Jan 2025 09:19:50 +0100 Subject: [PATCH 12/19] found some merge errors --- src/tools/Keywords.cpp | 42 ++++++++++++++++++++++++------------------ 1 file changed, 24 insertions(+), 18 deletions(-) diff --git a/src/tools/Keywords.cpp b/src/tools/Keywords.cpp index 7708e08c77..192b68f96e 100644 --- a/src/tools/Keywords.cpp +++ b/src/tools/Keywords.cpp @@ -599,7 +599,6 @@ 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; @@ -725,27 +724,33 @@ void Keywords::print_html() const { if ( keywords.at(key).type.isCompulsory() ) { print_html_item( key ); } - std::cout<<"
%s %s %s
\n\n"; } - nkeys=0; - for(const auto& key : keys) { - if ( keywords.at(key).type.isFlag() || keywords.at(key).type.isOptional() || keywords.at(key).type.isVessel() ) { - nkeys++; - } + std::cout<<"\n\n"; + } + nkeys=0; + for(const auto& key : keys) { + if ( keywords.at(key).type.isFlag() || keywords.at(key).type.isOptional() || keywords.at(key).type.isVessel() ) { + nkeys++; } - if( nkeys>0 ) { - if(isaction) { - std::cout<<"\\par Options\n\n"; - } else { - std::cout<<"\\par The following options are available\n\n"; - } - std::cout<<" \n"; - for(const auto& key : keys) { - } - std::cout<<"\n"; + } + if( nkeys>0 ) { + if(isaction) { + std::cout<<"\\par Options\n\n"; + } else { + std::cout<<"\\par The following options are available\n\n"; } - nkeys=0; + std::cout<<"
\n"; for(const auto& key : keys) { + if ( keywords.at(key).type.isFlag() ) { + print_html_item( key ); + } + } + std::cout<<"\n"; + } + nkeys=0; + for(const auto& key : keys) { + if ( keywords.at(key).type.isOptional() || keywords.at(key).type.isVessel() ) { + nkeys++; } } if( nkeys>0 ) { @@ -760,6 +765,7 @@ void Keywords::print_html() const { void Keywords::print_spelling() const { for(const auto& key : keys) { + std::printf("%s\n", key.c_str() ); } for(const auto& cname : cnames) { std::printf("%s\n",cname.c_str() ); From 322d7caeb9dec2450dc3ee724c5be065a1d29151 Mon Sep 17 00:00:00 2001 From: Daniele Rapetti <5535617+Iximiel@users.noreply.github.com> Date: Wed, 22 Jan 2025 10:07:15 +0100 Subject: [PATCH 13/19] Some small adjustments for reserveFlag --- src/tools/Keywords.cpp | 62 +++++++++++++++++++----------------------- src/tools/Keywords.h | 4 +-- 2 files changed, 30 insertions(+), 36 deletions(-) diff --git a/src/tools/Keywords.cpp b/src/tools/Keywords.cpp index 192b68f96e..2342e62edd 100644 --- a/src/tools/Keywords.cpp +++ b/src/tools/Keywords.cpp @@ -252,15 +252,15 @@ Keywords::component& Keywords::component::setType(componentType t) { return *this; } -std::string Keywords::getStyle( const std::string & k ) const { +std::string Keywords::getStyle( const std::string& k ) const { plumed_massert( exists(k)||reserved(k), "Did not find keyword " + k ); - return (keywords.find(k)->second.type).toString(); + return (keywords.at(k).type).toString(); } void Keywords::add( const Keywords& newkeys ) { //copies data from //loop on the declared keys - for(std::string thiskey:newkeys.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); @@ -270,14 +270,14 @@ void Keywords::add( const Keywords& newkeys ) { } } //loop on the reserved keys - for (std::string thiskey : newkeys.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 (std::string thisnam : newkeys.cnames) { + 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); @@ -295,7 +295,6 @@ void Keywords::reserve( const std::string & keytype, } //let's fail asap in case of typo auto type = KeyType(t_type); - plumed_assert( !exists(key) && !reserved(key) ); std::string fd{docstring}; bool allowMultiple= false; @@ -335,35 +334,27 @@ void Keywords::reserve( const std::string & keytype, reserved_keys.emplace_back(key); } -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 ) "; } - std::string fd; - // std::string lowkey=k; - // std::transform(lowkey.begin(),lowkey.end(),lowkey.begin(),[](unsigned char c) { return std::tolower(c); }); - fd=defstr + d; - keywords[k] = keyInfo() - .setType(KeyType(KeyType::keyStyle::flag)) - .setDocString(fd) - .setAllowMultiple(false) - .setDefaultFlag(def); - reserved_keys.emplace_back(k); + + keywords[key] = keyInfo() + .setType(KeyType{KeyType::keyStyle::flag}) + .setDocString(defstr + docstring) + .setAllowMultiple(false) + .setDefaultFlag(defaultValue); + reserved_keys.emplace_back(key); } ///this "copies" a reserved key into the keylist so it can be used void Keywords::use(std::string_view k ) { plumed_massert( reserved(k), "the " + std::string(k) + " keyword is not reserved"); keys.emplace_back(k); - //reserved(k) verifies reserved_keys[i]==k, so no need to traverse again the reserved keys? - //since, from reserve(), the reserved keys are unique (shall we use a set?) - // for(unsigned i=0; isecond); + return toString(argument_types.at(name)); } std::string Keywords::getOutputComponentFlag( const std::string& name ) const { - return components.find(name)->second.key; + return components.at(name).key; } std::string Keywords::getOutputComponentType( const std::string& name ) const { - return toString( components.find(name)->second.type); + //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 { diff --git a/src/tools/Keywords.h b/src/tools/Keywords.h index 210018679a..347bcbe954 100644 --- a/src/tools/Keywords.h +++ b/src/tools/Keywords.h @@ -185,7 +185,7 @@ class Keywords { /// Reserve a keyword void reserve( const std::string & keytype, const std::string & key, const std::string & 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( std::string_view k ); /// append the data from another Keywords object (**only** keywords, reserved keywords and components) @@ -203,7 +203,7 @@ class Keywords { /// Check the keyword k has been reserved 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 From ae936cebeb67b1f8571fcd5567b2591740a08727 Mon Sep 17 00:00:00 2001 From: Daniele Rapetti <5535617+Iximiel@users.noreply.github.com> Date: Wed, 22 Jan 2025 12:48:10 +0100 Subject: [PATCH 14/19] adding the erase/remove shortcut --- src/tools/Keywords.cpp | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/src/tools/Keywords.cpp b/src/tools/Keywords.cpp index 2342e62edd..f02772df1e 100644 --- a/src/tools/Keywords.cpp +++ b/src/tools/Keywords.cpp @@ -26,6 +26,15 @@ #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..." @@ -305,7 +314,7 @@ void Keywords::reserve( const std::string & keytype, return std::tolower(c); }); // Remove any underscore characters - lowkey.erase(std::remove(lowkey.begin(), lowkey.end(), '_'), lowkey.end()); + erase_remove(lowkey, '_'); fd += " The final value can be referenced using label." + lowkey; if(docstring.find("flag")==std::string::npos) { @@ -478,10 +487,10 @@ void Keywords::addFlag(std::string_view key, bool defaultValue, std::string_view void Keywords::remove( const std::string & k ) { bool found=false; if(exists(k)) { - keys.erase(std::remove(keys.begin(), keys.end(), k), keys.end()); + erase_remove(keys,k); found=true; } else if(reserved(k)) { - reserved_keys.erase(std::remove(reserved_keys.begin(), reserved_keys.end(), k), reserved_keys.end()); + erase_remove(reserved_keys,k); found=true; } plumed_massert(found,"You are trying to forbid " + k + " a keyword that isn't there"); @@ -1095,13 +1104,13 @@ std::string Keywords::getOutputComponentDescription( const std::string& name ) c ///////////DUPLICATED??????????/////// void Keywords::removeOutputComponent( const std::string& name ) { components.erase(name); - cnames.erase(std::remove(cnames.begin(), cnames.end(), name), cnames.end()); + erase_remove(cnames,name); } void Keywords::removeComponent( const std::string& name ) { if(components.find(name)!=components.end()) { components.erase(name); - cnames.erase(std::remove(cnames.begin(), cnames.end(), name), cnames.end()); + erase_remove(cnames,name); } else { plumed_massert(false,"You are trying to remove " + name + " a component that isn't there"); } From 75d0f2026a56edb566c2dcd50053e9e07c934164 Mon Sep 17 00:00:00 2001 From: Daniele Rapetti <5535617+Iximiel@users.noreply.github.com> Date: Wed, 22 Jan 2025 12:48:29 +0100 Subject: [PATCH 15/19] grammar fix --- src/core/ActionWithArguments.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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(); From d877bf64a17e51ba34924437f12c303f733253cf Mon Sep 17 00:00:00 2001 From: Daniele Rapetti <5535617+Iximiel@users.noreply.github.com> Date: Wed, 22 Jan 2025 14:26:47 +0100 Subject: [PATCH 16/19] compacted the argument_type into the key struct --- src/tools/Keywords.cpp | 73 +++++++++++++++++++++++++++++++----------- src/tools/Keywords.h | 14 +++++--- 2 files changed, 64 insertions(+), 23 deletions(-) diff --git a/src/tools/Keywords.cpp b/src/tools/Keywords.cpp index f02772df1e..3bbd4f0fd5 100644 --- a/src/tools/Keywords.cpp +++ b/src/tools/Keywords.cpp @@ -238,13 +238,20 @@ 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; } -Keywords::keyInfo& Keywords::keyInfo::setDefaultFlag(bool a) { - defaultValue=a; - return *this; +bool Keywords::keyInfo::isArgument() const { + return std::holds_alternative(argument_type); } Keywords::component::component()=default; @@ -428,8 +435,8 @@ void Keywords::addInputKeyword( const std::string & keyType, } //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 - argument_types[key] = datatype; add( keyType, key, docstring ); + keywords.at(key).setArgumentType(datatype); } void Keywords::addInputKeyword( const std::string & keyType, @@ -448,8 +455,8 @@ void Keywords::addInputKeyword( const std::string & keyType, if( exists(key) ) { remove(key); } - argument_types[key] = datatype; add( keyType, key, defaultV, docstring ); + keywords[key].setArgumentType(datatype); } void Keywords::add( std::string_view keytype, @@ -498,7 +505,7 @@ void Keywords::remove( const std::string & k ) { keywords.erase(k); //and the atomtags? - // Remove any output comonents that this keyword creates + // Remove any output components that this keyword creates //we need the double loop because we should not remove and iterate on the map at the same time std::vector markForRemoval{}; for(const auto& dkey : components ) { @@ -1044,30 +1051,60 @@ bool Keywords::componentHasCorrectType( const std::string& name, const std::size 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 && valid(x.second | argType::scalar)) { - return true; + std::map arguments; + for(auto const& kw : getArgumentKeys() ) { + 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 && valid(x.second | argType::grid)) { - return true; + if( rank==1 && valid(at | argType::vector)) { + arguments[kw] = true; } - if( rank==1 && valid(x.second | argType::vector)) { - return true; + if( rank==2 && valid(at | argType::matrix)) { + arguments[kw] = true; } - if( rank==2 && valid(x.second | argType::matrix)) { - 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"); + 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 toString(argument_types.at(name)); + return toString(std::get(keywords.at(name).argument_type)); } std::string Keywords::getOutputComponentFlag( const std::string& name ) const { diff --git a/src/tools/Keywords.h b/src/tools/Keywords.h index 347bcbe954..a397fbcba9 100644 --- a/src/tools/Keywords.h +++ b/src/tools/Keywords.h @@ -101,6 +101,8 @@ class Keywords { 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; /// Do we allow stuff like key1, key2 etc bool allowmultiple; keyInfo(); @@ -108,8 +110,10 @@ class Keywords { keyInfo& setType(KeyType t); keyInfo& setDocString(std::string_view d); keyInfo& setDefaultValue(std::string_view d); - keyInfo& setAllowMultiple(bool a); keyInfo& setDefaultFlag(bool a); + keyInfo& setArgumentType(argType a); + keyInfo& setAllowMultiple(bool a); + bool isArgument() const; }; //std::less make some magic and makes find and [] work with string_view /// Stores the keywords along with their settings @@ -118,8 +122,6 @@ class Keywords { std::vector keys; /// The names of the reserved keywords, in order of declaration std::vector reserved_keys; -/// The type for the arguments in this action - std::map> argument_types; /// The tags for atoms - we use this so the manual can differentiate between different ways of specifying atoms std::map> atomtags; struct component { @@ -170,10 +172,12 @@ class Keywords { 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) From 8334044bdc9577114957639ab44fd938a8d77191 Mon Sep 17 00:00:00 2001 From: Daniele Rapetti <5535617+Iximiel@users.noreply.github.com> Date: Wed, 22 Jan 2025 15:53:50 +0100 Subject: [PATCH 17/19] compacted the add and reserve methods to simplify the life of future us --- src/tools/Keywords.cpp | 77 +++++++++++++++++++----------------------- src/tools/Keywords.h | 4 ++- 2 files changed, 37 insertions(+), 44 deletions(-) diff --git a/src/tools/Keywords.cpp b/src/tools/Keywords.cpp index 3bbd4f0fd5..66a4a51beb 100644 --- a/src/tools/Keywords.cpp +++ b/src/tools/Keywords.cpp @@ -300,10 +300,12 @@ void Keywords::add( const Keywords& newkeys ) { } } -void Keywords::reserve( const std::string & keytype, - const std::string & key, - const std::string & docstring ) { - plumed_assert( !exists(key) && !reserved(key) ); +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 ) { @@ -311,10 +313,14 @@ void Keywords::reserve( const std::string & keytype, } //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() ) { + if( type.isVessel() && reserve) { // Convert to lower case std::string lowkey{key}; std::transform(lowkey.begin(),lowkey.end(),lowkey.begin(),[](unsigned char c) { @@ -333,21 +339,30 @@ void Keywords::reserve( const std::string & keytype, } else if( isNumbered ) { fd += NUMBERED_DOCSTRING(key); allowMultiple = true; - } else { - //if( type.isAtomList() && isaction ) {//<- why not this? atoms could also be "residues" or "atoms-n" - if( keytype=="atoms" && isaction ) { + } else if( type.isAtomList() ) { + //keytype may be "residues" or something like "atoms-3" + atomtags.insert( std::pair(key,keytype) ); + if (isaction) { fd += ATOM_DOCSTRING; } - if( type.isAtomList() ) { - atomtags.insert( std::pair(key,keytype) ); - } } - keywords[key] = keyInfo() - .setType(type) - .setDocString(fd) - .setAllowMultiple(allowMultiple); - reserved_keys.emplace_back(key); + keywords[std::string(key)] = keyInfo() + .setType(type) + .setDocString(fd) + .setAllowMultiple(allowMultiple); + 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 & key, const bool defaultValue, const std::string & docstring ) { @@ -391,32 +406,8 @@ void Keywords::reset_style( const std::string & k, const std::string & style ) { void Keywords::add(std::string_view keytype, std::string_view key, std::string_view docstring ) { - 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); - plumed_massert( !exists(key) && (!type.isFlag()) && !reserved(key) && (!type.isVessel()), - "keyword " + std::string(key) + " has already been registered"); - std::string fd{docstring}; - if( isNumbered ) { - fd += NUMBERED_DOCSTRING(key); - } else { - if( type.isAtomList() ) { - //keytype may be "residues" or something like "atoms-3" - atomtags.insert( std::pair(key,keytype) ); - } - } - if( type.isAtomList() && isaction ) { - fd += ATOM_DOCSTRING; - } - keywords[std::string(key)] = keyInfo() - .setType(type) - .setDocString(fd) - .setAllowMultiple(isNumbered); - keys.emplace_back(key); + //the 'false' deactivates the "reserve mode" + addOrReserve(keytype,key,docstring,false); } void Keywords::addInputKeyword( const std::string & keyType, @@ -941,7 +932,7 @@ void Keywords::destroyData() { keywords.clear(); atomtags.clear(); components.clear(); - //cname was missing before, it is wanted or not? + //cname was missing before, is it wanted or not? cnames.clear(); } diff --git a/src/tools/Keywords.h b/src/tools/Keywords.h index a397fbcba9..e70fd1693d 100644 --- a/src/tools/Keywords.h +++ b/src/tools/Keywords.h @@ -115,6 +115,8 @@ class Keywords { 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; @@ -187,7 +189,7 @@ 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 & keytype, const std::string & key, const std::string & docstring ); + void reserve( std::string_view keytype, std::string_view key, std::string_view docstring ); /// Reserve a flag void reserveFlag( const std::string & key, bool defaultValue, const std::string & docstring ); /// Use one of the reserved keywords From 257cdc1e1b54954a8a1df3472a0757a2939a58ae Mon Sep 17 00:00:00 2001 From: Daniele Rapetti <5535617+Iximiel@users.noreply.github.com> Date: Wed, 22 Jan 2025 17:01:23 +0100 Subject: [PATCH 18/19] compacting also the atomtag into keyinfo --- src/tools/Keywords.cpp | 39 ++++++++++++++++++++------------------- src/tools/Keywords.h | 4 ++-- 2 files changed, 22 insertions(+), 21 deletions(-) diff --git a/src/tools/Keywords.cpp b/src/tools/Keywords.cpp index 66a4a51beb..a18e949f9d 100644 --- a/src/tools/Keywords.cpp +++ b/src/tools/Keywords.cpp @@ -281,9 +281,6 @@ void Keywords::add( const Keywords& newkeys ) { plumed_massert( !reserved(thiskey), "keyword " + thiskey + " is in twice" ); keywords[thiskey] = newkeys.keywords.at(thiskey); keys.emplace_back( thiskey ); - if( keywords.at(thiskey).type.isAtomList() ) { - atomtags.insert( std::pair( thiskey,newkeys.atomtags.at(thiskey)) ); - } } //loop on the reserved keys for (const auto&thiskey : newkeys.reserved_keys) { @@ -339,18 +336,19 @@ void Keywords::addOrReserve( std::string_view keytype, } else if( isNumbered ) { fd += NUMBERED_DOCSTRING(key); allowMultiple = true; - } else if( type.isAtomList() ) { - //keytype may be "residues" or something like "atoms-3" - atomtags.insert( std::pair(key,keytype) ); - if (isaction) { - fd += ATOM_DOCSTRING; - } } 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) { + fd += ATOM_DOCSTRING; + } + } if (reserve) { reserved_keys.emplace_back(key); } else { @@ -390,6 +388,9 @@ void Keywords::use(std::string_view k ) { void Keywords::reset_style( const std::string & k, const std::string & style ) { plumed_massert( exists(k) || reserved(k), "no " + k + " keyword" ); + keywords.at(k).atomtag=""; + //Adding this feels correct, but breacks some actions where a numbered keyword is changed to compulsory + //keywords.at(k).allowmultiple=false; if( style=="numbered" ) { keywords.at(k).allowmultiple=true; return; @@ -399,7 +400,7 @@ void Keywords::reset_style( const std::string & k, const std::string & style ) { keywords.at(k).allowmultiple=true; } if( (keywords.at(k).type).isAtomList() ) { - atomtags.insert( std::pair(k,style) ); + keywords.at(k).atomtag=style; } } @@ -494,7 +495,6 @@ void Keywords::remove( const std::string & k ) { plumed_massert(found,"You are trying to forbid " + k + " a keyword that isn't there"); // Delete documentation, type and so on from the description keywords.erase(k); - //and the atomtags? // Remove any output components that this keyword creates //we need the double loop because we should not remove and iterate on the map at the same time @@ -548,16 +548,17 @@ void Keywords::print_template(const std::string& actionname, bool include_option std::string prevtag="start"; for(const auto& key : keys) { if( keywords.at(key).type.isAtomList() ) { - plumed_massert( atomtags.count(key), "keyword " + key + " allegedly specifies atoms but no tag has been specified. Please email Gareth Tribello"); - if( prevtag!="start" && prevtag!=atomtags.find(key)->second ) { + 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(key)->second).find("residues")!=std::string::npos) { + if( currentTag.find("residues")!=std::string::npos) { std::printf(" %s=", key.c_str() ); } else { std::printf(" %s=", key.c_str() ); } - prevtag=atomtags.find(key)->second; + prevtag=currentTag; } } } @@ -685,8 +686,9 @@ void Keywords::print_html() const { unsigned counter=0; for(const auto& key : keys) { if ( keywords.at(key).type.isAtomList() ) { - plumed_massert( atomtags.count(key), "keyword " + key + " allegedly specifies atoms but no tag has been specified. Please email Gareth Tribello"); - if( prevtag!="start" && prevtag!=atomtags.find(key)->second && isaction ) { + 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"; @@ -699,7 +701,7 @@ void Keywords::print_html() const { std::cout<<" \n"; } print_html_item( key ); - prevtag=atomtags.find(key)->second; + prevtag=currentTag; } } std::cout<<"
\n\n"; @@ -930,7 +932,6 @@ void Keywords::destroyData() { keys.clear(); reserved_keys.clear(); keywords.clear(); - atomtags.clear(); components.clear(); //cname was missing before, is it wanted or not? cnames.clear(); diff --git a/src/tools/Keywords.h b/src/tools/Keywords.h index e70fd1693d..651d2b7b43 100644 --- a/src/tools/Keywords.h +++ b/src/tools/Keywords.h @@ -103,6 +103,8 @@ class Keywords { 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(); @@ -124,8 +126,6 @@ class Keywords { std::vector keys; /// The names of the reserved keywords, in order of declaration std::vector reserved_keys; -/// 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; From 33101da06acdeace97b8cefbdefd70683e92d564 Mon Sep 17 00:00:00 2001 From: Daniele Rapetti <5535617+Iximiel@users.noreply.github.com> Date: Thu, 23 Jan 2025 09:30:44 +0100 Subject: [PATCH 19/19] updating the documentation --- src/tools/Keywords.cpp | 5 +++-- src/tools/Keywords.h | 21 ++++++++++++++++++++- 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/src/tools/Keywords.cpp b/src/tools/Keywords.cpp index a18e949f9d..03cf9a3194 100644 --- a/src/tools/Keywords.cpp +++ b/src/tools/Keywords.cpp @@ -388,8 +388,9 @@ void Keywords::use(std::string_view k ) { void Keywords::reset_style( const std::string & k, const std::string & style ) { plumed_massert( exists(k) || reserved(k), "no " + k + " keyword" ); - keywords.at(k).atomtag=""; - //Adding this feels correct, but breacks some actions where a numbered keyword is changed to compulsory + //Adding this two feels correct, but breaks some actions where a numbered keyword is changed to compulsory + //So also the atomtag is removed + //keywords.at(k).atomtag=""; //keywords.at(k).allowmultiple=false; if( style=="numbered" ) { keywords.at(k).allowmultiple=true; diff --git a/src/tools/Keywords.h b/src/tools/Keywords.h index 651d2b7b43..a2afd2fbcc 100644 --- a/src/tools/Keywords.h +++ b/src/tools/Keywords.h @@ -218,7 +218,26 @@ 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 ); /// Clear everything from the keywords object. /// Not actually needed if your Keywords object is going out of scope.