Commit 5682c66a authored by Jakub Klinkovský's avatar Jakub Klinkovský
Browse files

Refactoring the configuration system in TNL::Config

parent 6a653029
Loading
Loading
Loading
Loading
+8 −7
Original line number Diff line number Diff line
@@ -2,12 +2,14 @@
                          ConfigDelimiter.h  -  description
                             -------------------
    begin                : Jul 5, 2014
    copyright            : (C) 2014 by Tomas Oberhuber
    copyright            : (C) 2014 by Tomas Oberhuber et al.
    email                : tomas.oberhuber@fjfi.cvut.cz
 ***************************************************************************/

/* See Copyright Notice in tnl/Copyright */

// Implemented by: Tomáš Oberhuber, Jakub Klinkovský

#pragma once

#include <TNL/Config/ConfigEntryBase.h>
@@ -15,17 +17,16 @@
namespace TNL {
namespace Config {

struct ConfigDelimiter : public ConfigEntryBase
class ConfigDelimiter : public ConfigEntryBase
{
   ConfigDelimiter( const String& delimiter )
public:
   ConfigDelimiter( const std::string& delimiter )
   : ConfigEntryBase( "", delimiter, false )
   {}

   bool isDelimiter() const { return true; };

   String getEntryType() const { return ""; };
   virtual bool isDelimiter() const override { return true; };

   String getUIEntryType() const { return ""; };
   virtual std::string getUIEntryType() const override { return ""; };
};

} //namespace Config
+55 −263
Original line number Diff line number Diff line
/***************************************************************************
                          Config::ConfigDescription.h  -  description
                          ConfigDescription.h  -  description
                             -------------------
    begin                : 2007/06/09
    copyright            : (C) 2007 by Tomas Oberhuber
    copyright            : (C) 2007 by Tomas Oberhuber et al.
    email                : tomas.oberhuber@fjfi.cvut.cz
 ***************************************************************************/

/* See Copyright Notice in tnl/Copyright */

// Implemented by: Tomáš Oberhuber, Jakub Klinkovský

#pragma once

#include <iomanip>
#include <string>
#include <vector>
#include <memory>

#include <TNL/Assert.h>
#include <TNL/String.h>
#include <TNL/Config/ConfigEntryType.h>
#include <TNL/Config/ConfigEntry.h>
#include <TNL/Config/ConfigEntryList.h>
#include <TNL/Config/ConfigDelimiter.h>
#include <TNL/Config/ParameterContainer.h>
#include <TNL/Exceptions/ConfigError.h>

namespace TNL {
namespace Config {
@@ -37,10 +34,11 @@ public:
    * \param description More specific information about the entry.
    */
   template< typename EntryType >
   void addEntry( const String& name,
                  const String& description )
   void addEntry( const std::string& name,
                  const std::string& description )
   {
      entries.push_back( std::make_unique< ConfigEntry< EntryType > >( name, description, false ) );
      using CoercedEntryType = typename ParameterTypeCoercion< EntryType >::type;
      entries.push_back( std::make_unique< ConfigEntry< CoercedEntryType > >( name, description, false ) );
      currentEntry = entries.back().get();
      isCurrentEntryList = false;
   }
@@ -53,10 +51,11 @@ public:
    * \param description More specific information about the entry.
    */
   template< typename EntryType >
   void addRequiredEntry( const String& name,
                          const String& description )
   void addRequiredEntry( const std::string& name,
                          const std::string& description )
   {
      entries.push_back( std::make_unique< ConfigEntry< EntryType > >( name, description, true ) );
      using CoercedEntryType = typename ParameterTypeCoercion< EntryType >::type;
      entries.push_back( std::make_unique< ConfigEntry< CoercedEntryType > >( name, description, true ) );
      currentEntry = entries.back().get();
      isCurrentEntryList = false;
   }
@@ -70,11 +69,13 @@ public:
    * \param defaultValue Default value of the entry.
    */
   template< typename EntryType >
   void addEntry( const String& name,
                  const String& description,
   void addEntry( const std::string& name,
                  const std::string& description,
                  const EntryType& defaultValue )
   {
      entries.push_back( std::make_unique< ConfigEntry< EntryType > >( name, description, false, defaultValue ) );
      using CoercedEntryType = typename ParameterTypeCoercion< EntryType >::type;
      const auto convertedDefaultValue = ParameterTypeCoercion< EntryType >::convert( defaultValue );
      entries.push_back( std::make_unique< ConfigEntry< CoercedEntryType > >( name, description, false, convertedDefaultValue ) );
      currentEntry = entries.back().get();
      isCurrentEntryList = false;
   }
@@ -87,10 +88,11 @@ public:
    * \param description More specific information about the list.
    */
   template< typename EntryType >
   void addList( const String& name,
                 const String& description )
   void addList( const std::string& name,
                 const std::string& description )
   {
      entries.push_back( std::make_unique< ConfigEntryList< EntryType > >( name, description, false ) );
      using CoercedEntryType = typename ParameterTypeCoercion< EntryType >::type;
      entries.push_back( std::make_unique< ConfigEntryList< CoercedEntryType > >( name, description, false ) );
      currentEntry = entries.back().get();
      isCurrentEntryList = true;
   }
@@ -103,10 +105,11 @@ public:
    * \param description More specific information about the list.
    */
   template< typename EntryType >
   void addRequiredList( const String& name,
                         const String& description )
   void addRequiredList( const std::string& name,
                         const std::string& description )
   {
      entries.push_back( std::make_unique< ConfigEntryList< EntryType > >( name, description, true ) );
      using CoercedEntryType = typename ParameterTypeCoercion< EntryType >::type;
      entries.push_back( std::make_unique< ConfigEntryList< CoercedEntryType > >( name, description, true ) );
      currentEntry = entries.back().get();
      isCurrentEntryList = true;
   }
@@ -120,11 +123,13 @@ public:
    * \param defaultValue Default value of the list.
    */
   template< typename EntryType >
   void addList( const String& name,
                 const String& description,
                 const EntryType& defaultValue )
   void addList( const std::string& name,
                 const std::string& description,
                 const std::vector< EntryType >& defaultValue )
   {
      entries.push_back( std::make_unique< ConfigEntryList< EntryType > >( name, description, false, defaultValue ) );
      using CoercedEntryType = typename ParameterTypeCoercion< EntryType >::type;
      const auto convertedDefaultValue = ParameterTypeCoercion< std::vector< EntryType > >::convert( defaultValue );
      entries.push_back( std::make_unique< ConfigEntryList< CoercedEntryType > >( name, description, false, convertedDefaultValue ) );
      currentEntry = entries.back().get();
      isCurrentEntryList = true;
   }
@@ -136,31 +141,21 @@ public:
    * \tparam EntryType Type of the entry enumeration.
    * \param entryEnum Value of the entry enumeration.
    */
   template< typename EntryType >
   void addEntryEnum( const EntryType& entryEnum )
   template< typename EntryType = std::string >
   void addEntryEnum( const EntryType entryEnum = EntryType{} )
   {
      TNL_ASSERT_TRUE( this->currentEntry, "there is no current entry" );
      if( this->currentEntry == nullptr )
         throw Exceptions::ConfigError( "there is no current entry" );

      using CoercedEntryType = typename ParameterTypeCoercion< EntryType >::type;
      if( isCurrentEntryList ) {
         ConfigEntryList< EntryType >& entry = dynamic_cast< ConfigEntryList< EntryType >& >( *currentEntry );
         entry.getEnumValues().push_back( entryEnum );
         ConfigEntryList< CoercedEntryType >& entry = dynamic_cast< ConfigEntryList< CoercedEntryType >& >( *currentEntry );
         entry.getEnumValues().push_back( ParameterTypeCoercion< EntryType >::convert( entryEnum ) );
      }
      else {
         ConfigEntry< EntryType >& entry = dynamic_cast< ConfigEntry< EntryType >& >( *currentEntry );
         entry.getEnumValues().push_back( entryEnum );
      }
         ConfigEntry< CoercedEntryType >& entry = dynamic_cast< ConfigEntry< CoercedEntryType >& >( *currentEntry );
         entry.getEnumValues().push_back( ParameterTypeCoercion< EntryType >::convert( entryEnum ) );
      }

   /**
    * \brief Adds new entry enumeration of type \e char.
    *
    * Adds new option of setting an entry value.
    * \param entryEnum Value of the entry enumeration.
    */
   void addEntryEnum( const char* entryEnum )
   {
      TNL_ASSERT_TRUE( this->currentEntry, "there is no current entry" );
      ConfigEntry< String >& entry = dynamic_cast< ConfigEntry< String >& >( *currentEntry );
      entry.getEnumValues().push_back( String( entryEnum ) );
   }

   /**
@@ -168,7 +163,7 @@ public:
    *
    * \param delimeter String that defines how the delimeter looks like.
    */
   void addDelimiter( const String& delimiter )
   void addDelimiter( const std::string& delimiter )
   {
      entries.push_back( std::make_unique< ConfigDelimiter >( delimiter ) );
      currentEntry = nullptr;
@@ -179,227 +174,26 @@ public:
    *
    * \param name Name of the entry.
    */
   const ConfigEntryBase* getEntry( const String& name ) const
   const ConfigEntryBase* getEntry( const std::string& name ) const
   {
      // ConfigDelimiter has empty name
      if( ! name )
      if( name.empty() )
         return nullptr;

      const int entries_num = entries.size();
      for( int i = 0; i < entries_num; i++ )
         if( entries[ i ]->name == name )
         if( entries[ i ]->getName() == name )
            return entries[ i ].get();
      return nullptr;
   }


   //! Returns empty string if given entry does not exist
   //const String getEntryType( const char* name ) const;

   //! Returns zero pointer if there is no default value
   template< class T >
   const T* getDefaultValue( const String& name ) const
   {
      // ConfigDelimiter has empty name
      if( ! name )
         return nullptr;

      const int entries_num = entries.size();
      for( int i = 0; i < entries_num; i++ )
         if( entries[ i ]->name == name ) {
            if( entries[ i ]->hasDefaultValue ) {
               const ConfigEntry< T >& entry = dynamic_cast< ConfigEntry< T >& >( *entries[ i ] );
               return entry->default_value;
            }
            return nullptr;
         }
      std::cerr << "Asking for the default value of unknown parameter." << std::endl;
      return nullptr;
   }

   //! Returns zero pointer if there is no default value
   template< class T >
   T* getDefaultValue( const String& name )
   {
      // ConfigDelimiter has empty name
      if( ! name )
         return nullptr;

      const int entries_num = entries.size();
      for( int i = 0; i < entries_num; i++ )
         if( entries[ i ] -> name == name ) {
            if( entries[ i ] -> hasDefaultValue ) {
               ConfigEntry< T >& entry = dynamic_cast< ConfigEntry< T >& >( *entries[ i ] );
               return entry->default_value;
            }
            return nullptr;
         }
      std::cerr << "Asking for the default value of unknown parameter." << std::endl;
      return nullptr;
   }

   /**
    * \brief Fills in the parameters from the \e parameter_container.
    *
    * Parameters which were not defined in the command line by user but have their default value are added to the congiguration description.
    * If there is missing entry with defined default value in the Config::ParameterContainer it is going to be added.
    * \param parameter_container Name of the ParameterContainer object.
    */
   void addMissingEntries( Config::ParameterContainer& parameter_container ) const
   {
      const int size = entries.size();
      for( int i = 0; i < size; i++ )
      {
         const char* entry_name = entries[ i ]->name.getString();
         if( entries[ i ]->hasDefaultValue &&
             ! parameter_container.checkParameter( entry_name ) )
         {
            if( entries[ i ]->getEntryType() == "TNL::String" )
            {
               ConfigEntry< String >& entry = dynamic_cast< ConfigEntry< String >& >( *entries[ i ] );
               parameter_container.addParameter< String >( entry_name, entry.defaultValue );
               continue;
            }
            else if( entries[ i ]->getEntryType() == "bool" )
            {
               ConfigEntry< bool >& entry = dynamic_cast< ConfigEntry< bool >& >( *entries[ i ] );
               parameter_container.addParameter< bool >( entry_name, entry.defaultValue );
               continue;
            }
            else if( entries[ i ]->getEntryType() == "int" )
            {
               ConfigEntry< int >& entry = dynamic_cast< ConfigEntry< int >& >( *entries[ i ] );
               parameter_container.addParameter< int >( entry_name, entry.defaultValue );
               continue;
            }
            else if( entries[ i ]->getEntryType() == "double" )
            {
               ConfigEntry< double >& entry = dynamic_cast< ConfigEntry< double >& >( *entries[ i ] );
               parameter_container.addParameter< double >( entry_name, entry.defaultValue );
               continue;
            }
            else if( entries[ i ]->getEntryType() == "ConfigEntryList< TNL::String >" )
            {
               ConfigEntryList< String >& entry = dynamic_cast< ConfigEntryList< String >& >( *entries[ i ] );
               parameter_container.addList< String >( entry_name, entry.defaultValue );
               continue;
            }
            else if( entries[ i ]->getEntryType() == "ConfigEntryList< bool >" )
            {
               ConfigEntryList< bool >& entry = dynamic_cast< ConfigEntryList< bool >& >( *entries[ i ] );
               parameter_container.addList< bool >( entry_name, entry.defaultValue );
               continue;
            }
            else if( entries[ i ]->getEntryType() == "ConfigEntryList< int >" )
            {
               ConfigEntryList< int >& entry = dynamic_cast< ConfigEntryList< int >& >( *entries[ i ] );
               parameter_container.addList< int >( entry_name, entry.defaultValue );
               continue;
            }
            else if( entries[ i ]->getEntryType() == "ConfigEntryList< double >" )
            {
               ConfigEntryList< double >& entry = dynamic_cast< ConfigEntryList< double >& >( *entries[ i ] );
               parameter_container.addList< double >( entry_name, entry.defaultValue );
               continue;
            }
            else
            {
               throw std::runtime_error( "Method ConfigDescription::addMissingEntries encountered "
                                         "unsupported entry type: " + entries[ i ]->getEntryType() );
            }
         }
      }
   }

   //! Check for all entries with the flag 'required'.
   /*! Returns false if any parameter is missing.
    */
   bool checkMissingEntries( Config::ParameterContainer& parameter_container,
                             bool printUsage,
                             const char* programName ) const
   {
      const int size = entries.size();
      std::vector< std::string > missingParameters;
      for( int i = 0; i < size; i++ )
      {
         const char* entry_name = entries[ i ] -> name.getString();
         if( entries[ i ] -> required &&
             ! parameter_container.checkParameter( entry_name ) )
            missingParameters.push_back( entry_name );
      }
      if( missingParameters.size() > 0 )
      {
         std::cerr << "Some mandatory parameters are misssing. They are listed at the end. " << std::endl;
         if( printUsage )
            this->printUsage( programName );
         std::cerr << "Add the following missing parameters to the command line: " << std::endl << "   ";
         for( int i = 0; i < (int) missingParameters.size(); i++ )
            std::cerr << "--" << missingParameters[ i ] << " ... ";
         std::cerr << std::endl;
         return false;
      }
      return true;
   }

   /**
    * \brief Prints configuration description with the \e program_name at the top.
    *
    * \param program_name Name of the program
    */
   void printUsage( const char* program_name ) const
   {
      std::cout << "Usage of: " << program_name << std::endl << std::endl;
      const int entries_num = entries.size();
      int max_name_length( 0 );
      int max_type_length( 0 );
      for( int j = 0; j < entries_num; j++ )
         if( ! entries[ j ]->isDelimiter() )
         {
            max_name_length = std::max( max_name_length,
                        entries[ j ] -> name. getLength() );
            max_type_length = std::max( max_type_length,
                        entries[ j ] -> getUIEntryType().getLength() );
         }
      max_name_length += 2; // this is for '--'

      for( int j = 0; j < entries_num; j++ )
      {
         if( entries[ j ]->isDelimiter() )
         {
            std::cout << std::endl;
            std::cout << entries[ j ]->description;
            std::cout << std::endl << std::endl;
         }
         else
         {
            std::cout << std::setw( max_name_length + 3 ) << String( "--" ) + entries[ j ]->name
                 << std::setw( max_type_length + 5 ) << entries[ j ] -> getUIEntryType()
                 << "    " << entries[ j ]->description;
            if( entries[ j ] -> required )
               std::cout << " *** REQUIRED ***";
            if( entries[ j ]->hasEnumValues() )
            {
               std::cout << std::endl
                    << std::setw( max_name_length + 3 ) << ""
                    << std::setw( max_type_length + 5 ) << ""
                    << "    ";
               entries[ j ]->printEnumValues();
            }
            if( entries[ j ]->hasDefaultValue )
            {
               std::cout << std::endl
                    << std::setw( max_name_length + 3 ) << ""
                    << std::setw( max_type_length + 5 ) << ""
                    << "    ";
               std::cout << "- Default value is: " << entries[ j ]->printDefaultValue();
            }
            std::cout << std::endl;
         }
      }
      std::cout << std::endl;
   }

   //bool parseConfigDescription( const char* file_name );
   // iterators
   auto begin() noexcept { return entries.begin(); }
   auto begin() const noexcept { return entries.begin(); }
   auto cbegin() const noexcept { return entries.cbegin(); }
   auto end() noexcept { return entries.end(); }
   auto end() const noexcept { return entries.end(); }
   auto cend() const noexcept { return entries.cend(); }

protected:
   std::vector< std::unique_ptr< ConfigEntryBase > > entries;
@@ -409,5 +203,3 @@ protected:

} // namespace Config
} // namespace TNL

#include <TNL/Config/parseCommandLine.h>
+61 −52
Original line number Diff line number Diff line
@@ -2,110 +2,119 @@
                          ConfigEntry.h  -  description
                             -------------------
    begin                : Jul 5, 2014
    copyright            : (C) 2014 by Tomas Oberhuber
    copyright            : (C) 2014 by Tomas Oberhuber et al.
    email                : tomas.oberhuber@fjfi.cvut.cz
 ***************************************************************************/

/* See Copyright Notice in tnl/Copyright */

// Implemented by: Tomáš Oberhuber, Jakub Klinkovský

#pragma once

#include <vector>
#include <sstream>

#include <TNL/TypeInfo.h>
#include <TNL/Config/ConfigEntryBase.h>
#include <TNL/Config/ConfigEntryType.h>

namespace TNL {
namespace Config {

template< typename EntryType >
struct ConfigEntry : public ConfigEntryBase
template< typename EntryType, typename DefaultValueType = EntryType >
class ConfigEntry : public ConfigEntryBase
{
   EntryType defaultValue;
   static_assert( std::is_same< EntryType, DefaultValueType >::value || std::is_same< std::vector< EntryType >, DefaultValueType >::value,
                  "DefaultValueType must be the same as either EntryType or std::vector< EntryType >" );

   DefaultValueType defaultValue;

   std::vector< EntryType > enumValues;

public:

   ConfigEntry( const String& name,
                const String& description,
   ConfigEntry( const std::string& name,
                const std::string& description,
                bool required )
   : ConfigEntryBase( name,
                      description,
                      required )
   {
      hasDefaultValue = false;
      _hasDefaultValue = false;
   }

   ConfigEntry( const String& name,
                const String& description,
   ConfigEntry( const std::string& name,
                const std::string& description,
                bool required,
                const EntryType& defaultValue)
                const DefaultValueType& defaultValue)
   : ConfigEntryBase( name,
                      description,
                      required ),
     defaultValue( defaultValue )
   {
      hasDefaultValue = true;
   }

   String getEntryType() const
   {
      return TNL::getType< EntryType >();
      _hasDefaultValue = true;
   }

   String getUIEntryType() const
   virtual std::string getUIEntryType() const override
   {
      return TNL::Config::getUIEntryType< EntryType >();
      return Config::getUIEntryType< DefaultValueType >();
   }

   String printDefaultValue() const
   virtual std::string printDefaultValue() const override
   {
      return convertToString( defaultValue );
      // printDefaultValue must be compilable even if DefaultValueType is std::vector,
      // so we can't override the method in ConfigEntryList
      return _print_value( defaultValue );
   }

   std::vector< EntryType >& getEnumValues()
   {
      return this->enumValues;
   }

   bool hasEnumValues() const
   virtual bool hasEnumValues() const override
   {
      if( enumValues.size() > 0 )
         return true;
      return false;
   }

   void printEnumValues() const
   virtual void printEnumValues( std::ostream& str ) const override
   {
      std::cout << "- Can be:           ";
      str << "- Can be:           ";
      int i;
      for( i = 0; i < (int) enumValues.size() - 1; i++ )
         std::cout << enumValues[ i ] << ", ";
      std::cout << enumValues[ i ];
      std::cout << " ";
         str << enumValues[ i ] << ", ";
      str << enumValues[ i ];
      str << " ";
   }

   bool checkValue( const EntryType& value ) const
   {
      if( this->enumValues.size() > 0 )
   virtual DefaultValueType getDefaultValue() const
   {
         bool found( false );
         for( int i = 0; i < (int) this->enumValues.size(); i++ )
            if( value == this->enumValues[ i ] )
      return defaultValue;
   }

   virtual std::vector< EntryType >& getEnumValues()
   {
               found = true;
               break;
      return enumValues;
   }
         if( ! found )

   virtual const std::vector< EntryType >& getEnumValues() const
   {
            std::cerr << "The value " << value << " is not allowed for the config entry " << this->name << "." << std::endl;
            this->printEnumValues();
            std::cerr << std::endl;
            return false;
      return enumValues;
   }

private:
   static std::string _print_value( const EntryType& value )
   {
      std::stringstream str;
      str << value;
      return str.str();
   }
      return true;

   static std::string _print_value( const std::vector< EntryType >& vec )
   {
      std::stringstream str;
      str << "[ ";
      for( std::size_t i = 0; i < vec.size() - 1; i++ )
         str << vec[ i ] << ", ";
      str << vec[ vec.size() - 1 ];
      str << " ]";
      return str.str();
   }
};

+24 −12

File changed.

Preview size limit exceeded, changes collapsed.

+8 −96

File changed.

Preview size limit exceeded, changes collapsed.

Loading