From 602ee2daa667895ffdee24f30cd3b4751b339e0b Mon Sep 17 00:00:00 2001 From: Luca Heltai Date: Thu, 20 Jul 2017 19:07:43 +0200 Subject: [PATCH] Moved pattern_tools.h inside patterns.h --- include/deal.II/base/pattern_tools.h | 802 --------------------------- include/deal.II/base/patterns.h | 743 ++++++++++++++++++++++++- tests/base/pattern_tools_01.cc | 4 +- tests/base/pattern_tools_02.cc | 4 +- tests/base/pattern_tools_03.cc | 4 +- tests/base/pattern_tools_04.cc | 4 +- tests/base/pattern_tools_05.cc | 4 +- tests/base/pattern_tools_06.cc | 4 +- tests/base/pattern_tools_07.cc | 2 +- 9 files changed, 752 insertions(+), 819 deletions(-) delete mode 100644 include/deal.II/base/pattern_tools.h diff --git a/include/deal.II/base/pattern_tools.h b/include/deal.II/base/pattern_tools.h deleted file mode 100644 index 80945534a7..0000000000 --- a/include/deal.II/base/pattern_tools.h +++ /dev/null @@ -1,802 +0,0 @@ -// --------------------------------------------------------------------- -// -// Copyright (C) 1998 - 2017 by the deal.II authors -// -// This file is part of the deal.II library. -// -// The deal.II library is free software; you can use it, 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 2.1 of the License, or (at your option) any later version. -// The full text of the license can be found in the file LICENSE at -// the top level of the deal.II distribution. -// -// --------------------------------------------------------------------- - -#ifndef dealii__pattern_tools_h -#define dealii__pattern_tools_h - -#include -#include -#include -#include -#include -#include -#include - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -DEAL_II_NAMESPACE_OPEN - -/** - * Namespace for a few classes and functions that act on values and patterns, - * and allow to convert from non elementary types to strings and vice versa. - * - * A typical usage of these tools is in the following example: - * - * @code - * typedef std::vector T; - * - * T vec(3); - * vec[0] = 1; - * vec[1] = 3; - * vec[2] = 5; - * - * auto pattern = PatternTools::Convert::to_pattern(); - * - * std::cout << pattern->description() << std::endl; - * // [List of <[Integer]> of length 0...4294967295 (inclusive)] - * - * auto s = PatternTools::Convert::to_string(vec); - * std::cout << s << std::endl; - * // 1, 2, 3 - * - * auto vec = PatternTools::Convert::to_value("2,3,4,5"); - * // now vec has size 4, and contains the elements 2,3,4,5 - * - * std::cout << internal::RankInfo::list_rank << std::endl; // Outputs 1 - * std::cout << internal::RankInfo::map_rank << std::endl; // Outputs 0 - * @endcode - * - * Convert is used by the function PatternTools::add_parameter() in this - * namespace. Internally it uses the internal::RankInfo class to decide how - * many different separators are required to convert the given type to a string. - * - * For example, to write vectors of vectors, the default is to use "," for the - * first (inner) separator, and ";" for the second (outer) separator, i.e. - * - * @code - * std::vector> vec; - * vec = Convert::to_value("1,2,3 ; 4,5,6"); - * - * s = convert::to_string(vec[0]); - * // s now contains the string "1,2,3" - * @endcode - * - * Separators for Patterns::List and Patterns::Map compatible types are - * selected according to the - * rank of the list and map objects, using the arrays - * PatternTools::internal::default_list_separator and - * PatternTools::internal::default_map_separator. - * - * They are currently set to: - * - * @code - * default_list_separator{{"," , ";" , "|" , "%"}}; - * default_map_separator {{":" , "=" , "@" , "#"}}; - * @endcode - * - * When one needs a mixture of Patterns::List and Patterns::Map types, their - * RankInfo is computed by taking the maximum of the vector_rank of the Key and - * of the Value type, so that, for example, it is possible to have the following - * @code - * ... // Build compare class - * std::map, std::vector, compare> map; - * - * map = convert::to_value("1,2,3 : 5.0,6.0,7.0 ; 8,9,10 : - * 11.0,12.0,13.0"); - * - * @endcode - * - * Some non elementary types are supported, like Point(), or - * std::complex. If you wish to support more types, you have to - * specialize the Convert struct as well as the RankInfo struct. - * - * @ingroup input - * @author Luca Heltai, 2017 - */ -namespace PatternTools -{ - /** - * Converter class. This class is used to generate strings and Patterns - * associated to the given type, and to convert from a string to the given - * type and viceversa. - * - * The second template parameter is used internally to allow for advanced - * SFINAE (substitution failure is not an error) tricks used to specialise - * this class for arbitrary STL containers and maps. - * - * @author Luca Heltai, 2017 - */ - template - struct Convert - { - - /** - * Return a std::unique_ptr to a Pattern that can be used to interpret a - * string as the type of the template argument, and the other way around. - * - * While the current function (in the general Convert template) is deleted, - * it is implemented and available in the specializations of the Convert - * class template for particular kinds of template arguments @p T. - */ - static std::unique_ptr to_pattern() = delete; - - /** - * Return a string containing a textual version of the variable s. Use the - * pattern passed to perform the conversion, or create and use a default - * one. - * - * While the current function (in the general Convert template) is deleted, - * it is implemented and available in the specializations of the Convert - * class template for particular kinds of template arguments @p T. - */ - static std::string to_string(const T &s, - const std::unique_ptr - &p = Convert::to_pattern()) = delete; - - /** - * Convert a string to a value, using the given pattern. Use the pattern - * passed to perform the conversion, or create and use a default one. - * - * While the current function (in the general Convert template) is deleted, - * it is implemented and available in the specializations of the Convert - * class template for particular kinds of template arguments @p T. - */ - static T to_value(const std::string &s, - const std::unique_ptr &p = - Convert::to_pattern()) = delete; - }; - - /** - * Declare a new entry in @p prm with name @p entry, set its default value - * to the content of the variable @p parameter, and create an action - * that will fill @p parameter with updated values when a file is parsed, - * or the entry is set to a new value. - * - * By default, the pattern to use is obtained by calling the function - * PatternTools::Convert::to_pattern(), but a custom one can be used. - */ - template - void add_parameter(const std::string &entry, - ParameterType ¶meter, - ParameterHandler &prm, - const std::string &documentation = std::string(), - const Patterns::PatternBase &pattern = - *Convert::to_pattern()); - - /** - * @addtogroup Exceptions - * @{ - */ - - /** - * Exception. - */ - DeclException2(ExcNoMatch, - std::string, - Patterns::PatternBase &, - << "The string " << arg1 << " does not match the pattern \"" - << arg2.description() - << "\""); - //@} -} - -// ---------------------- inline and template functions -------------------- -namespace PatternTools -{ - namespace internal - { - /** - * Store information about the rank types of the given class. - * - * A class has Rank equal to the number of different separators - * that are required to uniquely identify its element(s) in a string. - * - * This class is used to detect wether the class T is compatible - * with a Patterns::List pattern or with a Patterns::Map pattern. - * - * Objects like Point() or std::complex are vector-likes, and - * have vector_rank 1. Elementary types, like `int`, `unsigned int`, - * `double`, etc. have vector_rank 0. `std::vector`, `std::list` and in - * general containers have rank equal to 1 + vector_rank of the contained - * type. Similarly for map types. - * - * A class with list_rank::value = 0 is either elementary or a - * map. A class with map_rank::value = 0 is either a List compatible - * class, or an elementary type. - * - * Elementary types are not compatible with Patterns::List, but non - * elementary types, like Point(), or std::complex, are compatible - * with the List type. Adding more compatible types is a matter of adding a - * specialization of this struct for the given type. - * - * @author Luca Heltai, 2017 - */ - template - struct RankInfo - { - static constexpr int list_rank = 0; - static constexpr int map_rank = 0; - }; - } - - // Arithmetic types - template - struct Convert::value>::type> - { - - static std::unique_ptr to_pattern() - { - if (std::is_integral::value) - return std_cxx14::make_unique( - std::numeric_limits::min(), std::numeric_limits::max()); - else if (std::is_floating_point::value) - return std_cxx14::make_unique( - -std::numeric_limits::max(), std::numeric_limits::max()); - } - - static std::string to_string(const T &value, - const std::unique_ptr - &p = Convert::to_pattern()) - { - std::string str; - if (std::is_same() || std::is_same()) - str = std::to_string((int)value); - else - str = std::to_string(value); - AssertThrow(p->match(str), ExcNoMatch(str, *p)); - return str; - } - - static T to_value(const std::string &s, - const std::unique_ptr &p = - Convert::to_pattern()) - { - AssertThrow(p->match(s), ExcNoMatch(s, *p)); - std::istringstream is(s); - T value; - if (std::is_same::value || std::is_same::value) - { - int i; - is >> i; - value = i; - } - else - is >> value; - - // If someone passes "123 abc" to the function, the method yelds an - // integer 123 alright, but the space terminates the read from the string - // although there is more to come. This case, however, is checked for in - // the call p->match(s) at the beginning of this function, and would - // throw earlier. Here it is safe to assume that if we didn't fail the - // conversion with the operator >>, then we are good to go. - AssertThrow(!is.fail(), - ExcMessage("Failed to convert from \"" + s + - "\" to the type \"" + - boost::core::demangle(typeid(T).name()) + "\"")); - return value; - } - }; - - namespace internal - { - const std::array default_list_separator {{"," , ";" , "|" , "%"}}; - const std::array default_map_separator {{":" , "=" , "@" , "#"}}; - - //specialize a type for all of the STL containers and maps - template struct is_stl_container : std::false_type {}; - template struct is_stl_container> : std::true_type {}; - template struct is_stl_container> : std::true_type {}; - template struct is_stl_container> : std::true_type {}; - template struct is_stl_container> : std::true_type {}; - template struct is_stl_container> : std::true_type {}; - template struct is_stl_container> : std::true_type {}; - template struct is_stl_container> : std::true_type {}; - template struct is_stl_container> : std::true_type {}; - - template struct is_stl_map : std::false_type {}; - template struct is_stl_map> : std::true_type {}; - template struct is_stl_map> : std::true_type {}; - template struct is_stl_map> : std::true_type {}; - template struct is_stl_map> : std::true_type {}; - } - - // type trait to use the implementation type traits as well as decay the type - template - struct is_stl_container - { - static constexpr bool const value = - internal::is_stl_container::type>::value; - }; - - template - struct is_stl_map - { - static constexpr bool const value = - internal::is_stl_map::type>::value; - }; - - namespace internal - { - // Rank of vector types - template - struct RankInfo::value>::type> - { - static constexpr int list_rank = - RankInfo::list_rank + 1; - static constexpr int map_rank = - RankInfo::map_rank; - }; - - // Rank of map types - template - struct RankInfo::value>::type> - { - static constexpr int list_rank = - std::max(internal::RankInfo::list_rank, - RankInfo::list_rank) + - 1; - static constexpr int map_rank = - std::max(internal::RankInfo::map_rank, - RankInfo::map_rank) + - 1; - }; - - // Rank of Tensor types - template - struct RankInfo> - { - static constexpr int list_rank = rank + RankInfo::list_rank; - static constexpr int map_rank = RankInfo::map_rank; - }; - - template - struct RankInfo> : RankInfo> - { - }; - - // Rank of complex types - template - struct RankInfo> - { - static constexpr int list_rank = RankInfo::list_rank + 1; - static constexpr int map_rank = RankInfo::map_rank; - }; - - template - struct RankInfo> - { - static constexpr int list_rank = std::max(RankInfo::list_rank, RankInfo::list_rank); - static constexpr int map_rank = std::max(RankInfo::map_rank, RankInfo::map_rank)+1; - }; - } - - // stl containers - template - struct Convert::value>::type> - { - static std::unique_ptr to_pattern() - { - static_assert(internal::RankInfo::list_rank > 0, - "Cannot use this class for non List-compatible types."); - return std_cxx14::make_unique( - *Convert::to_pattern(), - 0, - std::numeric_limits::max(), - internal::default_list_separator[internal::RankInfo::list_rank - 1]); - } - - static std::string to_string(const T &t, - const std::unique_ptr - &pattern = Convert::to_pattern()) - { - - auto p = dynamic_cast(pattern.get()); - AssertThrow( - p, - ExcMessage( - "I need a List pattern to convert a string to a List type.")); - auto base_p = p->get_base_pattern().clone(); - std::vector vec(t.size()); - - unsigned int i = 0; - for (const auto &ti : t) - vec[i++] = Convert::to_string(ti, base_p); - - std::string s; - if (vec.size() > 0) - s = vec[0]; - for (unsigned int i = 1; i < vec.size(); ++i) - s += p->get_separator() + " " + vec[i]; - - AssertThrow(pattern->match(s), ExcNoMatch(s, *p)); - return s; - } - - static T to_value(const std::string &s, - const std::unique_ptr &pattern = - Convert::to_pattern()) - { - - AssertThrow(pattern->match(s), ExcNoMatch(s, *pattern)); - - auto p = dynamic_cast(pattern.get()); - AssertThrow( - p, - ExcMessage( - "I need a List pattern to convert a string to a List type.")); - - auto base_p = p->get_base_pattern().clone(); - T t; - - auto v = Utilities::split_string_list(s, p->get_separator()); - for (const auto &str : v) - t.insert(t.end(), - Convert::to_value(str, base_p)); - - return t; - } - }; - - // stl maps - template - struct Convert::value>::type> - { - static std::unique_ptr to_pattern() - { - static_assert(internal::RankInfo::list_rank > 0, - "Cannot use this class for non List-compatible types."); - static_assert(internal::RankInfo::map_rank > 0, - "Cannot use this class for non Map-compatible types."); - return std_cxx14::make_unique( - *Convert::to_pattern(), - *Convert::to_pattern(), - 0, - std::numeric_limits::max(), - internal::default_list_separator[internal::RankInfo::list_rank - 1], - internal::default_map_separator[internal::RankInfo::map_rank - 1]); - } - - static std::string to_string(const T &t, - const std::unique_ptr - &pattern = Convert::to_pattern()) - { - auto p = dynamic_cast(pattern.get()); - AssertThrow( - p, - ExcMessage("I need a Map pattern to convert a string to a List type.")); - auto key_p = p->get_key_pattern().clone(); - auto val_p = p->get_value_pattern().clone(); - std::vector vec(t.size()); - - unsigned int i = 0; - for (const auto &ti : t) - vec[i++] = - Convert::to_string(ti.first, key_p) + - p->get_key_value_separator() + - Convert::to_string(ti.second, val_p); - - std::string s; - if (vec.size() > 0) - s = vec[0]; - for (unsigned int i = 1; i < vec.size(); ++i) - s += p->get_separator() + " " + vec[i]; - - AssertThrow(p->match(s), ExcNoMatch(s, *p)); - return s; - } - - static T to_value(const std::string &s, - const std::unique_ptr &pattern = - Convert::to_pattern()) - { - - AssertThrow(pattern->match(s), ExcNoMatch(s, *pattern)); - - auto p = dynamic_cast(pattern.get()); - AssertThrow( - p, - ExcMessage("I need a Map pattern to convert a string to a List type.")); - - auto key_p = p->get_key_pattern().clone(); - auto val_p = p->get_value_pattern().clone(); - T t; - - auto v = Utilities::split_string_list(s, p->get_separator()); - for (const auto &str : v) - { - auto key_val = - Utilities::split_string_list(str, p->get_key_value_separator()); - AssertDimension(key_val.size(), 2); - t.insert(std::make_pair( - Convert::to_value(key_val[0], key_p), - Convert::to_value(key_val[1]))); - } - - return t; - } - }; - - // Tensors - template - struct Convert> - { - typedef Tensor T; - static std::unique_ptr to_pattern() - { - static_assert(internal::RankInfo::list_rank > 0, - "Cannot use this class for non List-compatible types."); - return std_cxx14::make_unique( - *Convert::to_pattern(), - dim, - dim, - internal::default_list_separator[internal::RankInfo::list_rank - 1]); - } - - static std::string to_string(const T &t, - const std::unique_ptr - &pattern = Convert::to_pattern()) - { - - auto p = dynamic_cast(pattern.get()); - AssertThrow( - p, - ExcMessage( - "I need a List pattern to convert a string to a List type.")); - auto base_p = p->get_base_pattern().clone(); - std::vector vec(dim); - - for (unsigned int i = 0; i < dim; ++i) - vec[i] = Convert::to_string(t[i], base_p); - - std::string s; - if (vec.size() > 0) - s = vec[0]; - for (unsigned int i = 1; i < vec.size(); ++i) - s += p->get_separator() + " " + vec[i]; - - AssertThrow(p->match(s), ExcNoMatch(s, *p)); - return s; - } - - static T to_value(const std::string &s, - const std::unique_ptr &pattern = - Convert::to_pattern()) - { - - AssertThrow(pattern->match(s), ExcNoMatch(s, *pattern)); - - auto p = dynamic_cast(pattern.get()); - AssertThrow( - p, - ExcMessage( - "I need a List pattern to convert a string to a List type.")); - - auto base_p = p->get_base_pattern().clone(); - T t; - - auto v = Utilities::split_string_list(s, p->get_separator()); - unsigned int i = 0; - for (const auto &str : v) - t[i++] = Convert::to_value(str, base_p); - - return t; - } - }; - - // Points - template - struct Convert> - { - - typedef Point T; - - static std::unique_ptr to_pattern() - { - return Convert>::to_pattern(); - } - - static std::string to_string(const T &t, - const std::unique_ptr - &pattern = Convert::to_pattern()) - { - return Convert>::to_string( - Tensor<1, dim, Number>(t), pattern); - } - - static T to_value(const std::string &s, - const std::unique_ptr &pattern = - Convert::to_pattern()) - { - return T(Convert>::to_value(s, pattern)); - } - }; - - // Complex numbers - template - struct Convert> - { - typedef std::complex T; - - static std::unique_ptr to_pattern() - { - static_assert(internal::RankInfo::list_rank > 0, - "Cannot use this class for non List-compatible types."); - return std_cxx14::make_unique( - *Convert::to_pattern(), - 2, - 2, - internal::default_list_separator[internal::RankInfo::list_rank - 1]); - } - - static std::string to_string(const T &t, - const std::unique_ptr - &pattern = Convert::to_pattern()) - { - - auto p = dynamic_cast(pattern.get()); - AssertThrow( - p, - ExcMessage( - "I need a List pattern to convert a string to a List type.")); - auto base_p = p->get_base_pattern().clone(); - std::string s = - Convert::to_string(t.real(), base_p) + - p->get_separator() + " " + - Convert::to_string(t.imag(), base_p); - - AssertThrow(pattern->match(s), ExcNoMatch(s, *p)); - return s; - } - - /** - * Convert a string to a value, using the given pattern, or a default one. - */ - static T to_value(const std::string &s, - const std::unique_ptr &pattern = - Convert::to_pattern()) - { - - AssertThrow(pattern->match(s), ExcNoMatch(s, *pattern)); - - auto p = dynamic_cast(pattern.get()); - AssertThrow( - p, - ExcMessage( - "I need a List pattern to convert a string to a List type.")); - - auto base_p = p->get_base_pattern().clone(); - - auto v = Utilities::split_string_list(s, p->get_separator()); - AssertDimension(v.size(), 2); - T t(Convert::to_value(v[0], base_p), - Convert::to_value(v[1], base_p)); - return t; - } - }; - - // Strings - template <> - struct Convert - { - typedef std::string T; - - static std::unique_ptr to_pattern() - { - return std_cxx14::make_unique(); - } - - static std::string to_string(const T &t, - const std::unique_ptr - &pattern = Convert::to_pattern()) - { - AssertThrow(pattern->match(t), ExcNoMatch(t, *pattern)); - return t; - } - - static T to_value(const std::string &s, - const std::unique_ptr &pattern = - Convert::to_pattern()) - { - AssertThrow(pattern->match(s), ExcNoMatch(s, *pattern)); - return s; - } - }; - - - // Pairs - template - struct Convert> - { - typedef std::pair T; - - static std::unique_ptr to_pattern() - { - static_assert(internal::RankInfo::map_rank > 0, - "Cannot use this class for non Map-compatible types."); - return std_cxx14::make_unique( - *Convert::to_pattern(), - *Convert::to_pattern(), - 1, 1, - // We keep the same list separator of the previous level, as this is - // a map with only 1 possible entry - internal::default_list_separator[internal::RankInfo::list_rank], - internal::default_map_separator[internal::RankInfo::map_rank - 1]); - } - - static std::string to_string(const T &t, - const std::unique_ptr - &pattern = Convert::to_pattern()) - { - std::unordered_map m; - m.insert(t); - std:: string s = Convert::to_string(m, pattern); - AssertThrow(pattern->match(s), ExcNoMatch(s, *pattern)); - return s; - } - - static T to_value(const std::string &s, - const std::unique_ptr &pattern = - Convert::to_pattern()) - { - std::unordered_map m; - m = Convert::to_value(s, pattern); - return *m.begin(); - } - }; - - template - void add_parameter(const std::string &entry, - ParameterType ¶meter, - ParameterHandler &prm, - const std::string &documentation, - const Patterns::PatternBase &pattern) - { - - static_assert(std::is_const::value == false, - "You tried to add a parameter using a type " - "that is const. Use a non-const type."); - - prm.declare_entry(entry, - PatternTools::Convert::to_string( - parameter, pattern.clone()), - pattern, - documentation); - - auto action = [&](const std::string &val) - { - parameter = - PatternTools::Convert::to_value(val, pattern.clone()); - }; - prm.add_action(entry, action); - } -} - -DEAL_II_NAMESPACE_CLOSE - -#endif diff --git a/include/deal.II/base/patterns.h b/include/deal.II/base/patterns.h index 905d8e7206..28f43f625e 100644 --- a/include/deal.II/base/patterns.h +++ b/include/deal.II/base/patterns.h @@ -20,16 +20,30 @@ #include #include #include +#include +#include +#include +#include + -#include -#include #include +#include +#include #include +#include +#include +#include +#include #include -#include +#include +#include #include -#include +#include +#include +#include +#include +#include DEAL_II_NAMESPACE_OPEN @@ -939,8 +953,729 @@ namespace Patterns */ static const char *description_init; }; + + + /** + * Namespace for a few classes and functions that act on values and patterns, + * and allow to convert from non elementary types to strings and vice versa. + * + * A typical usage of these tools is in the following example: + * + * @code + * typedef std::vector T; + * + * T vec(3); + * vec[0] = 1; + * vec[1] = 3; + * vec[2] = 5; + * + * auto pattern = Patterns::Tools::Convert::to_pattern(); + * + * std::cout << pattern->description() << std::endl; + * // [List of <[Integer]> of length 0...4294967295 (inclusive)] + * + * auto s = Patterns::Tools::Convert::to_string(vec); + * std::cout << s << std::endl; + * // 1, 2, 3 + * + * auto vec = Patterns::Tools::Convert::to_value("2,3,4,5"); + * // now vec has size 4, and contains the elements 2,3,4,5 + * + * std::cout << internal::RankInfo::list_rank << std::endl; // Outputs 1 + * std::cout << internal::RankInfo::map_rank << std::endl; // Outputs 0 + * @endcode + * + * Convert is used by the function Patterns::Tools::add_parameter() in this + * namespace. Internally it uses the internal::RankInfo class to decide how + * many different separators are required to convert the given type to a string. + * + * For example, to write vectors of vectors, the default is to use "," for the + * first (inner) separator, and ";" for the second (outer) separator, i.e. + * + * @code + * std::vector> vec; + * vec = Convert::to_value("1,2,3 ; 4,5,6"); + * + * s = convert::to_string(vec[0]); + * // s now contains the string "1,2,3" + * @endcode + * + * Separators for Patterns::List and Patterns::Map compatible types are + * selected according to the + * rank of the list and map objects, using the arrays + * Patterns::Tools::internal::default_list_separator and + * Patterns::Tools::internal::default_map_separator. + * + * They are currently set to: + * + * @code + * default_list_separator{{"," , ";" , "|" , "%"}}; + * default_map_separator {{":" , "=" , "@" , "#"}}; + * @endcode + * + * When one needs a mixture of Patterns::List and Patterns::Map types, their + * RankInfo is computed by taking the maximum of the vector_rank of the Key and + * of the Value type, so that, for example, it is possible to have the following + * @code + * ... // Build compare class + * std::map, std::vector, compare> map; + * + * map = convert::to_value("1,2,3 : 5.0,6.0,7.0 ; 8,9,10 : + * 11.0,12.0,13.0"); + * + * @endcode + * + * Some non elementary types are supported, like Point(), or + * std::complex. If you wish to support more types, you have to + * specialize the Convert struct as well as the RankInfo struct. + * + * @ingroup input + * @author Luca Heltai, 2017 + */ + namespace Tools + { + /** + * Converter class. This class is used to generate strings and Patterns + * associated to the given type, and to convert from a string to the given + * type and viceversa. + * + * The second template parameter is used internally to allow for advanced + * SFINAE (substitution failure is not an error) tricks used to specialise + * this class for arbitrary STL containers and maps. + * + * @author Luca Heltai, 2017 + */ + template + struct Convert + { + + /** + * Return a std::unique_ptr to a Pattern that can be used to interpret a + * string as the type of the template argument, and the other way around. + * + * While the current function (in the general Convert template) is deleted, + * it is implemented and available in the specializations of the Convert + * class template for particular kinds of template arguments @p T. + */ + static std::unique_ptr to_pattern() = delete; + + /** + * Return a string containing a textual version of the variable s. Use the + * pattern passed to perform the conversion, or create and use a default + * one. + * + * While the current function (in the general Convert template) is deleted, + * it is implemented and available in the specializations of the Convert + * class template for particular kinds of template arguments @p T. + */ + static std::string to_string(const T &s, + const std::unique_ptr + &p = Convert::to_pattern()) = delete; + + /** + * Convert a string to a value, using the given pattern. Use the pattern + * passed to perform the conversion, or create and use a default one. + * + * While the current function (in the general Convert template) is deleted, + * it is implemented and available in the specializations of the Convert + * class template for particular kinds of template arguments @p T. + */ + static T to_value(const std::string &s, + const std::unique_ptr &p = + Convert::to_pattern()) = delete; + }; + + + + /** + * @addtogroup Exceptions + * @{ + */ + + /** + * Exception. + */ + DeclException2(ExcNoMatch, + std::string, + Patterns::PatternBase &, + << "The string " << arg1 << " does not match the pattern \"" + << arg2.description() + << "\""); + //@} + } +} + + +// ---------------------- inline and template functions -------------------- +namespace Patterns +{ + namespace Tools + { + namespace internal + { + /** + * Store information about the rank types of the given class. + * + * A class has Rank equal to the number of different separators + * that are required to uniquely identify its element(s) in a string. + * + * This class is used to detect wether the class T is compatible + * with a Patterns::List pattern or with a Patterns::Map pattern. + * + * Objects like Point() or std::complex are vector-likes, and + * have vector_rank 1. Elementary types, like `int`, `unsigned int`, + * `double`, etc. have vector_rank 0. `std::vector`, `std::list` and in + * general containers have rank equal to 1 + vector_rank of the contained + * type. Similarly for map types. + * + * A class with list_rank::value = 0 is either elementary or a + * map. A class with map_rank::value = 0 is either a List compatible + * class, or an elementary type. + * + * Elementary types are not compatible with Patterns::List, but non + * elementary types, like Point(), or std::complex, are compatible + * with the List type. Adding more compatible types is a matter of adding a + * specialization of this struct for the given type. + * + * @author Luca Heltai, 2017 + */ + template + struct RankInfo + { + static constexpr int list_rank = 0; + static constexpr int map_rank = 0; + }; + } + + // Arithmetic types + template + struct Convert::value>::type> + { + + static std::unique_ptr to_pattern() + { + if (std::is_integral::value) + return std_cxx14::make_unique( + std::numeric_limits::min(), std::numeric_limits::max()); + else if (std::is_floating_point::value) + return std_cxx14::make_unique( + -std::numeric_limits::max(), std::numeric_limits::max()); + } + + static std::string to_string(const T &value, + const std::unique_ptr + &p = Convert::to_pattern()) + { + std::string str; + if (std::is_same() || std::is_same()) + str = std::to_string((int)value); + else + str = std::to_string(value); + AssertThrow(p->match(str), ExcNoMatch(str, *p)); + return str; + } + + static T to_value(const std::string &s, + const std::unique_ptr &p = + Convert::to_pattern()) + { + AssertThrow(p->match(s), ExcNoMatch(s, *p)); + std::istringstream is(s); + T value; + if (std::is_same::value || std::is_same::value) + { + int i; + is >> i; + value = i; + } + else + is >> value; + + // If someone passes "123 abc" to the function, the method yelds an + // integer 123 alright, but the space terminates the read from the string + // although there is more to come. This case, however, is checked for in + // the call p->match(s) at the beginning of this function, and would + // throw earlier. Here it is safe to assume that if we didn't fail the + // conversion with the operator >>, then we are good to go. + AssertThrow(!is.fail(), + ExcMessage("Failed to convert from \"" + s + + "\" to the type \"" + + boost::core::demangle(typeid(T).name()) + "\"")); + return value; + } + }; + + namespace internal + { + const std::array default_list_separator {{"," , ";" , "|" , "%"}}; + const std::array default_map_separator {{":" , "=" , "@" , "#"}}; + + //specialize a type for all of the STL containers and maps + template struct is_stl_container : std::false_type {}; + template struct is_stl_container> : std::true_type {}; + template struct is_stl_container> : std::true_type {}; + template struct is_stl_container> : std::true_type {}; + template struct is_stl_container> : std::true_type {}; + template struct is_stl_container> : std::true_type {}; + template struct is_stl_container> : std::true_type {}; + template struct is_stl_container> : std::true_type {}; + template struct is_stl_container> : std::true_type {}; + + template struct is_stl_map : std::false_type {}; + template struct is_stl_map> : std::true_type {}; + template struct is_stl_map> : std::true_type {}; + template struct is_stl_map> : std::true_type {}; + template struct is_stl_map> : std::true_type {}; + } + + // type trait to use the implementation type traits as well as decay the type + template + struct is_stl_container + { + static constexpr bool const value = + internal::is_stl_container::type>::value; + }; + + template + struct is_stl_map + { + static constexpr bool const value = + internal::is_stl_map::type>::value; + }; + + namespace internal + { + // Rank of vector types + template + struct RankInfo::value>::type> + { + static constexpr int list_rank = + RankInfo::list_rank + 1; + static constexpr int map_rank = + RankInfo::map_rank; + }; + + // Rank of map types + template + struct RankInfo::value>::type> + { + static constexpr int list_rank = + std::max(internal::RankInfo::list_rank, + RankInfo::list_rank) + + 1; + static constexpr int map_rank = + std::max(internal::RankInfo::map_rank, + RankInfo::map_rank) + + 1; + }; + + // Rank of Tensor types + template + struct RankInfo> + { + static constexpr int list_rank = rank + RankInfo::list_rank; + static constexpr int map_rank = RankInfo::map_rank; + }; + + template + struct RankInfo> : RankInfo> + { + }; + + // Rank of complex types + template + struct RankInfo> + { + static constexpr int list_rank = RankInfo::list_rank + 1; + static constexpr int map_rank = RankInfo::map_rank; + }; + + template + struct RankInfo> + { + static constexpr int list_rank = std::max(RankInfo::list_rank, RankInfo::list_rank); + static constexpr int map_rank = std::max(RankInfo::map_rank, RankInfo::map_rank)+1; + }; + } + + // stl containers + template + struct Convert::value>::type> + { + static std::unique_ptr to_pattern() + { + static_assert(internal::RankInfo::list_rank > 0, + "Cannot use this class for non List-compatible types."); + return std_cxx14::make_unique( + *Convert::to_pattern(), + 0, + std::numeric_limits::max(), + internal::default_list_separator[internal::RankInfo::list_rank - 1]); + } + + static std::string to_string(const T &t, + const std::unique_ptr + &pattern = Convert::to_pattern()) + { + + auto p = dynamic_cast(pattern.get()); + AssertThrow( + p, + ExcMessage( + "I need a List pattern to convert a string to a List type.")); + auto base_p = p->get_base_pattern().clone(); + std::vector vec(t.size()); + + unsigned int i = 0; + for (const auto &ti : t) + vec[i++] = Convert::to_string(ti, base_p); + + std::string s; + if (vec.size() > 0) + s = vec[0]; + for (unsigned int i = 1; i < vec.size(); ++i) + s += p->get_separator() + " " + vec[i]; + + AssertThrow(pattern->match(s), ExcNoMatch(s, *p)); + return s; + } + + static T to_value(const std::string &s, + const std::unique_ptr &pattern = + Convert::to_pattern()) + { + + AssertThrow(pattern->match(s), ExcNoMatch(s, *pattern)); + + auto p = dynamic_cast(pattern.get()); + AssertThrow( + p, + ExcMessage( + "I need a List pattern to convert a string to a List type.")); + + auto base_p = p->get_base_pattern().clone(); + T t; + + auto v = Utilities::split_string_list(s, p->get_separator()); + for (const auto &str : v) + t.insert(t.end(), + Convert::to_value(str, base_p)); + + return t; + } + }; + + // stl maps + template + struct Convert::value>::type> + { + static std::unique_ptr to_pattern() + { + static_assert(internal::RankInfo::list_rank > 0, + "Cannot use this class for non List-compatible types."); + static_assert(internal::RankInfo::map_rank > 0, + "Cannot use this class for non Map-compatible types."); + return std_cxx14::make_unique( + *Convert::to_pattern(), + *Convert::to_pattern(), + 0, + std::numeric_limits::max(), + internal::default_list_separator[internal::RankInfo::list_rank - 1], + internal::default_map_separator[internal::RankInfo::map_rank - 1]); + } + + static std::string to_string(const T &t, + const std::unique_ptr + &pattern = Convert::to_pattern()) + { + auto p = dynamic_cast(pattern.get()); + AssertThrow( + p, + ExcMessage("I need a Map pattern to convert a string to a List type.")); + auto key_p = p->get_key_pattern().clone(); + auto val_p = p->get_value_pattern().clone(); + std::vector vec(t.size()); + + unsigned int i = 0; + for (const auto &ti : t) + vec[i++] = + Convert::to_string(ti.first, key_p) + + p->get_key_value_separator() + + Convert::to_string(ti.second, val_p); + + std::string s; + if (vec.size() > 0) + s = vec[0]; + for (unsigned int i = 1; i < vec.size(); ++i) + s += p->get_separator() + " " + vec[i]; + + AssertThrow(p->match(s), ExcNoMatch(s, *p)); + return s; + } + + static T to_value(const std::string &s, + const std::unique_ptr &pattern = + Convert::to_pattern()) + { + + AssertThrow(pattern->match(s), ExcNoMatch(s, *pattern)); + + auto p = dynamic_cast(pattern.get()); + AssertThrow( + p, + ExcMessage("I need a Map pattern to convert a string to a List type.")); + + auto key_p = p->get_key_pattern().clone(); + auto val_p = p->get_value_pattern().clone(); + T t; + + auto v = Utilities::split_string_list(s, p->get_separator()); + for (const auto &str : v) + { + auto key_val = + Utilities::split_string_list(str, p->get_key_value_separator()); + AssertDimension(key_val.size(), 2); + t.insert(std::make_pair( + Convert::to_value(key_val[0], key_p), + Convert::to_value(key_val[1]))); + } + + return t; + } + }; + + // Tensors + template + struct Convert> + { + typedef Tensor T; + static std::unique_ptr to_pattern() + { + static_assert(internal::RankInfo::list_rank > 0, + "Cannot use this class for non List-compatible types."); + return std_cxx14::make_unique( + *Convert::to_pattern(), + dim, + dim, + internal::default_list_separator[internal::RankInfo::list_rank - 1]); + } + + static std::string to_string(const T &t, + const std::unique_ptr + &pattern = Convert::to_pattern()) + { + + auto p = dynamic_cast(pattern.get()); + AssertThrow( + p, + ExcMessage( + "I need a List pattern to convert a string to a List type.")); + auto base_p = p->get_base_pattern().clone(); + std::vector vec(dim); + + for (unsigned int i = 0; i < dim; ++i) + vec[i] = Convert::to_string(t[i], base_p); + + std::string s; + if (vec.size() > 0) + s = vec[0]; + for (unsigned int i = 1; i < vec.size(); ++i) + s += p->get_separator() + " " + vec[i]; + + AssertThrow(p->match(s), ExcNoMatch(s, *p)); + return s; + } + + static T to_value(const std::string &s, + const std::unique_ptr &pattern = + Convert::to_pattern()) + { + + AssertThrow(pattern->match(s), ExcNoMatch(s, *pattern)); + + auto p = dynamic_cast(pattern.get()); + AssertThrow( + p, + ExcMessage( + "I need a List pattern to convert a string to a List type.")); + + auto base_p = p->get_base_pattern().clone(); + T t; + + auto v = Utilities::split_string_list(s, p->get_separator()); + unsigned int i = 0; + for (const auto &str : v) + t[i++] = Convert::to_value(str, base_p); + + return t; + } + }; + + // Points + template + struct Convert> + { + + typedef Point T; + + static std::unique_ptr to_pattern() + { + return Convert>::to_pattern(); + } + + static std::string to_string(const T &t, + const std::unique_ptr + &pattern = Convert::to_pattern()) + { + return Convert>::to_string( + Tensor<1, dim, Number>(t), pattern); + } + + static T to_value(const std::string &s, + const std::unique_ptr &pattern = + Convert::to_pattern()) + { + return T(Convert>::to_value(s, pattern)); + } + }; + + // Complex numbers + template + struct Convert> + { + typedef std::complex T; + + static std::unique_ptr to_pattern() + { + static_assert(internal::RankInfo::list_rank > 0, + "Cannot use this class for non List-compatible types."); + return std_cxx14::make_unique( + *Convert::to_pattern(), + 2, + 2, + internal::default_list_separator[internal::RankInfo::list_rank - 1]); + } + + static std::string to_string(const T &t, + const std::unique_ptr + &pattern = Convert::to_pattern()) + { + + auto p = dynamic_cast(pattern.get()); + AssertThrow( + p, + ExcMessage( + "I need a List pattern to convert a string to a List type.")); + auto base_p = p->get_base_pattern().clone(); + std::string s = + Convert::to_string(t.real(), base_p) + + p->get_separator() + " " + + Convert::to_string(t.imag(), base_p); + + AssertThrow(pattern->match(s), ExcNoMatch(s, *p)); + return s; + } + + /** + * Convert a string to a value, using the given pattern, or a default one. + */ + static T to_value(const std::string &s, + const std::unique_ptr &pattern = + Convert::to_pattern()) + { + + AssertThrow(pattern->match(s), ExcNoMatch(s, *pattern)); + + auto p = dynamic_cast(pattern.get()); + AssertThrow( + p, + ExcMessage( + "I need a List pattern to convert a string to a List type.")); + + auto base_p = p->get_base_pattern().clone(); + + auto v = Utilities::split_string_list(s, p->get_separator()); + AssertDimension(v.size(), 2); + T t(Convert::to_value(v[0], base_p), + Convert::to_value(v[1], base_p)); + return t; + } + }; + + // Strings + template <> + struct Convert + { + typedef std::string T; + + static std::unique_ptr to_pattern() + { + return std_cxx14::make_unique(); + } + + static std::string to_string(const T &t, + const std::unique_ptr + &pattern = Convert::to_pattern()) + { + AssertThrow(pattern->match(t), ExcNoMatch(t, *pattern)); + return t; + } + + static T to_value(const std::string &s, + const std::unique_ptr &pattern = + Convert::to_pattern()) + { + AssertThrow(pattern->match(s), ExcNoMatch(s, *pattern)); + return s; + } + }; + + + // Pairs + template + struct Convert> + { + typedef std::pair T; + + static std::unique_ptr to_pattern() + { + static_assert(internal::RankInfo::map_rank > 0, + "Cannot use this class for non Map-compatible types."); + return std_cxx14::make_unique( + *Convert::to_pattern(), + *Convert::to_pattern(), + 1, 1, + // We keep the same list separator of the previous level, as this is + // a map with only 1 possible entry + internal::default_list_separator[internal::RankInfo::list_rank], + internal::default_map_separator[internal::RankInfo::map_rank - 1]); + } + + static std::string to_string(const T &t, + const std::unique_ptr + &pattern = Convert::to_pattern()) + { + std::unordered_map m; + m.insert(t); + std:: string s = Convert::to_string(m, pattern); + AssertThrow(pattern->match(s), ExcNoMatch(s, *pattern)); + return s; + } + + static T to_value(const std::string &s, + const std::unique_ptr &pattern = + Convert::to_pattern()) + { + std::unordered_map m; + m = Convert::to_value(s, pattern); + return *m.begin(); + } + }; + } } + DEAL_II_NAMESPACE_CLOSE #endif diff --git a/tests/base/pattern_tools_01.cc b/tests/base/pattern_tools_01.cc index 042a554398..8737715a5c 100644 --- a/tests/base/pattern_tools_01.cc +++ b/tests/base/pattern_tools_01.cc @@ -15,7 +15,7 @@ #include "../tests.h" -#include +#include #include #include #include @@ -24,7 +24,7 @@ #include using namespace dealii; -using namespace PatternTools; +using namespace Patterns::Tools; // Try conversion on elementary types diff --git a/tests/base/pattern_tools_02.cc b/tests/base/pattern_tools_02.cc index 4c9ee96621..4cc37c28d1 100644 --- a/tests/base/pattern_tools_02.cc +++ b/tests/base/pattern_tools_02.cc @@ -15,7 +15,7 @@ #include "../tests.h" -#include +#include #include #include #include @@ -24,7 +24,7 @@ #include using namespace dealii; -using namespace PatternTools; +using namespace Patterns::Tools; // Try conversion on non elementary types diff --git a/tests/base/pattern_tools_03.cc b/tests/base/pattern_tools_03.cc index 22d1c0746c..f12dea24ca 100644 --- a/tests/base/pattern_tools_03.cc +++ b/tests/base/pattern_tools_03.cc @@ -15,7 +15,7 @@ #include "../tests.h" -#include +#include #include #include #include @@ -24,7 +24,7 @@ #include using namespace dealii; -using namespace PatternTools; +using namespace Patterns::Tools; // Try conversion on container types diff --git a/tests/base/pattern_tools_04.cc b/tests/base/pattern_tools_04.cc index 700e58b014..073b2b4d5b 100644 --- a/tests/base/pattern_tools_04.cc +++ b/tests/base/pattern_tools_04.cc @@ -15,7 +15,7 @@ #include "../tests.h" -#include +#include #include #include #include @@ -24,7 +24,7 @@ #include using namespace dealii; -using namespace PatternTools; +using namespace Patterns::Tools; // Try conversion on map types diff --git a/tests/base/pattern_tools_05.cc b/tests/base/pattern_tools_05.cc index d3729bd7f6..77ec88562b 100644 --- a/tests/base/pattern_tools_05.cc +++ b/tests/base/pattern_tools_05.cc @@ -15,7 +15,7 @@ #include "../tests.h" -#include +#include #include #include #include @@ -24,7 +24,7 @@ #include using namespace dealii; -using namespace PatternTools; +using namespace Patterns::Tools; // Try conversion on complex map types diff --git a/tests/base/pattern_tools_06.cc b/tests/base/pattern_tools_06.cc index 0915ebc934..b1811577b2 100644 --- a/tests/base/pattern_tools_06.cc +++ b/tests/base/pattern_tools_06.cc @@ -15,7 +15,7 @@ #include "../tests.h" -#include +#include #include #include #include @@ -24,7 +24,7 @@ #include using namespace dealii; -using namespace PatternTools; +using namespace Patterns::Tools; // Try conversion on complex map types diff --git a/tests/base/pattern_tools_07.cc b/tests/base/pattern_tools_07.cc index 718f1a464d..c43e570511 100644 --- a/tests/base/pattern_tools_07.cc +++ b/tests/base/pattern_tools_07.cc @@ -24,7 +24,7 @@ #include using namespace dealii; -using namespace PatternTools; +using namespace Patterns::Tools; // Try conversion on arbitrary container types -- 2.39.5