GIT repositories

Index page of all the GIT repositories that are clonable form this server via HTTPS. Übersichtsseite aller GIT-Repositories, die von diesem Server aus über git clone (HTTPS) erreichbar sind.

Services

A bunch of service scripts to convert, analyse and generate data. Ein paar Services zum Konvertieren, Analysieren und Generieren von Daten.

GNU octave web interface

A web interface for GNU Octave, which allows to run scientific calculations from netbooks, tables or smartphones. The interface provides a web form generator for Octave script parameters with pre-validation, automatic script list generation, as well presenting of output text, figures and files in a output HTML page. Ein Webinterface für GNU-Octave, mit dem wissenschaftliche Berechnungen von Netbooks, Tablets oder Smartphones aus durchgeführt werden können. Die Schnittstelle beinhaltet einen Formulargenerator für Octave-Scriptparameter, mit Einheiten und Einfabevalidierung. Textausgabe, Abbildungen und generierte Dateien werden abgefangen und in einer HTML-Seite dem Nutzer als Ergebnis zur Verfügung gestellt.

STL basic_string - Funktionen

STL basic_string manipulation function templates

In Kürze gesagt string-Manipulation wie man es von anderen Sprachen kennt und des Öfteren bei c++ vermisst. Die Templates helfen lesbaren und kürzeren Code beim Umgang mit STL std::string/std::wstring/ std::u16string/std::u32string zu schreiben, und sind entweder <algorithm>-Wrappers oder für Strings optimiert. Die Funktionen arbeiten mit Argument move construction geben rvalues zurück.

Briefly and frankly some string manipulation functionality we know from other languages and that we are missing here and there in c++. These templates are useful to deal with STL std::string/std::wstring std::u16string/std::u32string and help writing shorted and more readable code. The functions are either <algorithm>-wrappers or optimised for particular purposes. In general they return rvalues and take take copies as arguments (move construction).

#include <string.hh>
#include <string>
#include <vector>
#include <deque>
#include <list>
#include <sstream>
#include <iostream>
#include <iomanip>
 
using namespace std;
using namespace sw;
 
template <typename Container>
typename sw::detail::basic_string_type<typename Container::value_type>::type to_string(const Container& v)
{
  using string_type = typename sw::detail::basic_string_type<typename Container::value_type>::type;
  using c = typename sw::detail::character_type_of<string_type>::type;
  return string_type()+c('[')+c(' ')+c('\"') + sw::join(v, (string_type()+c('\"')+c(',')+c(' ')+c('\"'))) + c('\"')+c(' ')+c(']');
}
 
/**
 * Main
 */
int main(int argc, char** argv)
{
  // This example transforms some user input from CLI args,
  // so we check this first ...
  if(argc < 2 || !argv[1]) {
    cerr << "Add some command line arguments to work with in this example." << endl;
    return 1;
  }
 
  // Some string variables and containers to be handled in the
  // example
  vector<string> args_vect;
  string args_str;
  const char* argv1_cstr = argv[1];
  wstring args_wstr;
 
  bool first = true;
  for(int i=1; i <= argc && argv[i]; ++i) {
    args_vect.emplace_back(argv[i]);
    if(first) first = false; else args_str += ' ';
    args_str += argv[i];
  }
 
  for(auto c:args_str) {
    args_wstr.push_back(wchar_t(c));
  }
 
  // Print the input variables as they are.
  cout << "Variable 'args_str' is of type std::string and contains all CLI arguments space separated." << endl;
  cout << "Variable 'args' is of type std::vector<std::string> and contains all CLI arguments." << endl;
  cout << "Variable 'argv1_cstr' is of type const char* and points to argv[1]." << endl;
  cout << "Variable 'args_wstr' is of type wstring and a character wise copy of args_str." << endl << endl;
 
  cout << "  -> args_str   = \"" << args_str << "\"  // std::string" << endl;
  cout << "  -> args_vect  = [\"" << sw::join(args_vect, "\", \"") << "\"]  // std::vector<std::string>" << endl;
  cout << "  -> argv1_cstr = \"" << argv1_cstr <<  "\"  // const char*" << endl;
  wcout << L"  -> args_wstr  = \"" << args_wstr <<  L"\"  // wstring" << endl << endl;
 
  //
  // std::string case conversions.
  // Note that the functions return string rvalues, means they implicitly
  // create a copy. That is easier to handle and due to forwarding/possible
  // move construction in combination with rvalue return optimisation basically
  // the same as by-ref algorithms or faster (depending on 'restrict' conditions),
  // but they use temporarily more memory depending how the compiler can optimise.
  //
  // String case conversions (default template, which uses std::locale)
  cout << "  -> to_lower(args_str)     = \"" << to_lower(args_str) << "\"" << endl;
  cout << "  -> to_upper(args_str)     = \"" << to_upper(args_str) << "\"" << endl;
  cout << "  -> to_proper(args_str)    = \"" << to_proper(args_str) << "\"" << endl;
  // C style (const char*) case conversions
  cout << "  -> to_lower(argv1_cstr)   = \"" << to_lower(argv1_cstr) << "\"" << endl;
  cout << "  -> to_upper(argv1_cstr)   = \"" << to_upper(argv1_cstr) << "\"" << endl;
  cout << "  -> to_proper(argv1_cstr)  = \"" << to_proper(argv1_cstr) << "\"" << endl;
  // wstring, u16/u32string function templates work identically
  wcout << L"  -> to_lower(args_wstr)    = \"" << to_lower(args_wstr) << L"\"" << endl;
  wcout << L"  -> to_upper(args_wstr)    = \"" << to_upper(args_wstr) << L"\"" << endl;
  wcout << L"  -> to_proper(args_wstr)   = \"" << to_proper(args_wstr) << L"\"" << endl;
  cout << endl;
 
  // Character type checks for ALL characters in the string:
  cout << "  -> is_alpha(args_str) = " << boolalpha << is_alpha(args_str) << endl;
  cout << "  -> is_alnum(args_str) = " << boolalpha << is_alnum(args_str) << endl;
  cout << "  -> is_digit(args_str) = " << boolalpha << is_digit(args_str) << endl;
  cout << "  -> is_xdigit(args_str)= " << boolalpha << is_xdigit(args_str) << endl;
  cout << "  -> is_cntrl(args_str) = " << boolalpha << is_cntrl(args_str) << endl;
  cout << "  -> is_graph(args_str) = " << boolalpha << is_graph(args_str) << endl;
  cout << "  -> is_print(args_str) = " << boolalpha << is_print(args_str) << endl;
  cout << "  -> is_punct(args_str) = " << boolalpha << is_punct(args_str) << endl;
  cout << "  -> is_lower(args_str) = " << boolalpha << is_lower(args_str) << endl;
  cout << "  -> is_upper(args_str) = " << boolalpha << is_upper(args_str) << endl;
 
  // Character type checks for ALL characters in the string. no locale
  cout << "  -> is_alpha<ctype_ascii>(args_str) = " << boolalpha << is_alpha<ctype_ascii>(args_str) << endl;
  cout << "  -> is_alnum<ctype_ascii>(args_str) = " << boolalpha << is_alnum<ctype_ascii>(args_str) << endl;
  cout << "  -> is_digit<ctype_ascii>(args_str) = " << boolalpha << is_digit<ctype_ascii>(args_str) << endl;
  cout << "  -> is_xdigit<ctype_ascii>(args_str)= " << boolalpha << is_xdigit<ctype_ascii>(args_str) << endl;
  cout << "  -> is_cntrl<ctype_ascii>(args_str) = " << boolalpha << is_cntrl<ctype_ascii>(args_str) << endl;
  cout << "  -> is_graph<ctype_ascii>(args_str) = " << boolalpha << is_graph<ctype_ascii>(args_str) << endl;
  cout << "  -> is_print<ctype_ascii>(args_str) = " << boolalpha << is_print<ctype_ascii>(args_str) << endl;
  cout << "  -> is_punct<ctype_ascii>(args_str) = " << boolalpha << is_punct<ctype_ascii>(args_str) << endl;
  cout << "  -> is_lower<ctype_ascii>(args_str) = " << boolalpha << is_lower<ctype_ascii>(args_str) << endl;
  cout << "  -> is_upper<ctype_ascii>(args_str) = " << boolalpha << is_upper<ctype_ascii>(args_str) << endl;
  cout << endl;
 
  // Trimming
  cout << "  -> trim(args_str) = \"" << trim(args_str) << "\"" << endl;
  cout << "  -> trim(args_str, \" \") = \"" << trim(args_str, " ") << "\"" << endl;
  cout << "  -> trim(args_str, ' ') = \"" << trim(args_str, ' ') << "\"" << endl;
  cout << "  -> trim(args_str, string(argv1_cstr)+\" \") = \"" << trim(args_str, string(argv1_cstr)+" ") << "\"" << endl;
  cout << "  -> ltrim(args_str, \" \") = \"" << ltrim(args_str, " ") << "\"" << endl;
  cout << "  -> ltrim(args_str, \" \") = \"" << ltrim(args_str, " ") << "\"" << endl;
  cout << "  -> ltrim(args_str, ' ') = \"" << ltrim(args_str, ' ') << "\"" << endl;
  cout << "  -> rtrim(args_str, \" \") = \"" << rtrim(args_str, " ") << "\"" << endl;
  cout << "  -> rtrim(args_str, \" \") = \"" << rtrim(args_str, " ") << "\"" << endl;
  cout << "  -> rtrim(args_str, ' ') = \"" << rtrim(args_str, ' ') << "\"" << endl;
  cout << "  -> trim<ctype_ascii>(args_str) = \"" << trim<ctype_ascii>(args_str) << "\"" << endl;
  cout << endl;
 
  // Replace
  cout << "  -> replace(args_str, ' ', '#') = \"" << replace(args_str, ' ', '#') << "\"" << endl;
  cout << "  -> replace(args_str, \"a\", 'Z') = \"" << replace(args_str, "a", 'Z') << "\"" << endl;
  cout << "  -> replace(args_str, \"abc\", \"XYZ\") = \"" << replace(args_str, "abc", "XYZ") << "\"" << endl;
  cout << "  -> replace(args_str, argv1_cstr, \"\") = \"" << replace(args_str, argv1_cstr, "") << "\"" << endl;
  cout << endl;
 
  // Split
  cout << "  -> split<vector<string>>(args_str, ' ') = " << to_string( split<vector<string>>(args_str, ' ') ) << endl;
  cout << "  -> split<deque<string>>(args_str, ' ') = " << to_string( split<deque<string>>(args_str, ' ') ) << endl;
  cout << "  -> split<list<string>>(args_str, ' ') = " << to_string( split<list<string>>(args_str, ' ') ) << endl;
  cout << "  -> split<vector<string>>(args_str, argv1_cstr) = " << to_string( split<vector<string>>(args_str, argv1_cstr) ) << endl;
  cout << endl;
 
  // Join
  cout << "  -> join(args_vect, ' ') = \"" << join(args_vect, ' ')  << "\"" << endl;
  cout << "  -> join(args_vect, argv1_cstr) = \"" << join(args_vect, argv1_cstr)  << "\"" << endl;
  cout << endl;
 
  // Length
  cout << "  -> length(args_str) = " << length(args_str) << endl;
  cout << "  -> length(argv1_cstr) = " << length(argv1_cstr) << endl;
  cout << "  -> length(args_str[0]) = " << length(args_str[0]) << endl;
  cout << "  -> length('a') = " << length('a') << endl;
  cout << "  -> length(\"abc\") = " << length("abc") << endl;
  cout << "  -> length(string(\"abc\")) = " << length(string("abc")) << endl;
  cout << "  -> length(wstring(L\"abc\")) = " << length(wstring(L"abc")) << endl;
  cout << "  -> length(u16string(u\"abc\")) = " << length(u16string(u"abc")) << endl;
  cout << "  -> length(u32string(u\"abc\")) = " << length(u32string(U"abc")) << endl;
  cout << endl;
 
  // to_hex
  cout << "  -> to_hex<string>(int(1024)) = \"" << to_hex<string>(int(1024))  << "\"" << endl;
  cout << "  -> to_hex<string>(long(0x0102030405)) = \"" << to_hex<string>(long(0x0102030405))  << "\"" << endl;
  cout << "  -> to_hex<string>(3.14157) = \"" << to_hex<string>(3.14157)  << "\"" << endl;
  struct A {short a; long b; char c; }; A a{1,2,3};
  cout << "  -> to_hex<string>(struct{short a; long b; char c;}{1,2,3}) = \"" << to_hex<string>(a)  << "\"" << endl;
 
  // textwrap
  cout << "  -> textwrap(args_str, 10) = \n----------\n" << textwrap(args_str, 10)  << "\n----------" << endl;
 
  return 0;
}

Dateien

Files

string.hh microtest.cc example.cc

Quelltext

Source code

/**
 * @package de.atwillys.cc.swl
 * @license BSD (simplified)
 * @author Stefan Wilhelm (cerbero s@atwillys.de)
 *
 * @file string.hh
 * @ccflags
 * @ldflags
 * @platform linux, bsd, windows
 * @standard >= c++11
 *
 * -----------------------------------------------------------------------------------------------------------------
 *
 * Test and manipulation functions for std::string/std::wstring/std::u16string/std::u32string.
 *
 *  These function templates shall facilitate handling with STL strings with patterns and
 *  function names known also in other languages.
 *
 *  - NO Unicode support (matters for case conversions and character classes (is_space(),
 *    is_upper() ...). If you need e.g. `to_upper("ä") == "Ä"`, @see boost::locale.
 *
 *  - All functions return rvalues/rvalue references, even if non-const string references are
 *    passed. The compiler return value optimisations and the use of "universal references"
 *    (allowing move construction from rvalues).
 *    This makes the code more readable, also if the functions are cascaded, e.g.
 *
 *        //
 *        // Split a string separated by spaces after removing space characters at the end/beginning
 *        // and replacing all "w"s with "W"s. The container could also be a list, deque, etc.
 *        //
 *        string input = "  Hello world!  ";
 *        auto result = split<vector<string>>( replace( trim(input), "w", "W") , " ");
 *        // result == vector<string>{ "Hello", "World" }
 *
 *
 *  - The functions implement variations for strings, C-strings/literals and characters, so
 *    that no explicit conversions have to be made when invoking ("lean and mean feature").
 *    Note that only variations with compatible types are allowed (NOT trim(wstring(L"abc ", u" cb"))
 *    because the u"" literal is for u16string, not wstring.)
 *
 *
 * -----------------------------------------------------------------------------------------------------------------
 *
 *  - Testing:
 *      - bool is_space(str)       : Returns true if all characters are space characters.
 *      - bool is_alpha(str)       : Returns true if all characters are alpha.
 *      - bool is_alnum(str)       : Returns true if all characters are alphanumeric.
 *      - bool is_digit(str)       : Returns true if all characters are decimal digits.
 *      - bool is_xdigit(str)      : Returns true if all characters are hex digits.
 *      - bool is_lower(str)       : Returns true if all characters are lower case letters.
 *      - bool is_upper(str)       : Returns true if all characters are upper case letters.
 *      - bool is_print(str)       : Returns true if all characters are printable.
 *      - bool is_graph(str)       : Returns true if all characters have graphical representations.
 *      - bool is_cntrl(str)       : Returns true if all characters are control characters.
 *      - bool is_numeric(str)     : Returns true if The string represents a number.
 *
 *  - Case conversion
 *      - string to_upper(str)     : Converts all characters to upper case.
 *      - string to_lower(str)     : Converts all characters to lower case.
 *      - string to_proper(str)    : Converts to "proper case" (lower except first character of words).
 *
 *  - Trimming:
 *      - string trim(str)         : Remove space characters from the begin and end.
 *      - string trim(str, list)   : Remove specified character(s) from the begin and end.
 *      - string rtrim(str)        : Remove space characters from the end.
 *      - string rtrim(str, list)  : Remove specified character(s) from the end.
 *      - string ltrim(str)        : Remove space characters from the front/start.
 *      - string ltrim(str, list)  : Remove specified character(s) from the front/start.
 *                                            it with a replace term.
 *  - Manipulation:
 *      - string replace(str, find, replace): Finds a character/string and replaces all occurrences of
 *
 *  - To/from container:
 *      - string join(Container, separator) : Joins all elements of a vector into this string
 *      - Container split(str, separator)   : Splits the string (given separator) into a vector.
 *
 *  - Type checks (all input types T or S are treated stripped from const, volatile and references (decayed))
 *
 *      - detail::is_basic_string<T>::value   : True if the type T is a STL string type.
 *      - detail::is_c_string<T>::value       : True if the type T is a C-string type.
 *      - detail::is_character<T>::value      : True if the type T is a c++ character type.
 *      - detail::is_character_of<S,C>::value : True if the type C is the character type of the basic_string type S.
 *      - detail::character_type_of<T>::type  : The character type of a basic_string, c-string (or T if T itself is
 *                                              a character type)
 *      - detail::is_basic_string_constructible_from<T>::value :
 *                                              True if one of the STL basic_string specialisations is constructible
 *                                              from the type T.
 *
 * -----------------------------------------------------------------------------
 * +++ BSD license header +++
 * Copyright (c) 2008-2016, Stefan Wilhelm (stfwi, <cerbero s@atwilly s.de>)
 * All rights reserved.
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met: (1) Redistributions
 * of source code must retain the above copyright notice, this list of conditions
 * and the following disclaimer. (2) Redistributions in binary form must reproduce
 * the above copyright notice, this list of conditions and the following disclaimer
 * in the documentation and/or other materials provided with the distribution.
 * (3) Neither the name of atwillys.de nor the names of its contributors may be
 * used to endorse or promote products derived from this software without specific
 * prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS
 * AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER
 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
 * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
 * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
 * DAMAGE.
 * -----------------------------------------------------------------------------
 */
#ifndef SW_STRING_HH
#define SW_STRING_HH
// <editor-fold desc="preprocessor" defaultstate="collapsed">
#include <string>
#include <cctype>
#include <locale>
#include <limits>
#include <vector>
#include <cstdint>
#include <type_traits>
#include <utility>
// </editor-fold>
 
namespace sw {
 
  // <editor-fold desc="detail" defaultstate="collapsed">
  namespace detail {
 
    // Used to check if a type (or reference to) is equivalent or derived
    // from one of the STL std::basic_string<...> without using to_string().
    template <typename T, typename=void>
    struct is_basic_string : public std::false_type {};
 
    template <typename T>
    struct is_basic_string<T,
      typename std::enable_if<
        std::is_base_of<
          typename std::basic_string<typename std::decay<T>::type::value_type>,
          typename std::decay<T>::type
        >::value
      >::type
    > : public std::true_type {};
 
    // Used to check if a type (or reference to) is convertible to one
    // of the STL std::basic_string<...> without using to_string().
    template <typename T, typename=void>
    struct is_basic_string_constructible_from : public std::false_type {};
 
    template <typename T>
    struct is_basic_string_constructible_from<T,
      typename std::enable_if<
           std::is_constructible<std::string,T>::value
        || std::is_constructible<std::wstring,T>::value
        || std::is_constructible<std::u16string,T>::value
        || std::is_constructible<std::u32string,T>::value
      >::type
    > : public std::true_type {};
 
    // Used to check if a type is a "regular" STL character.
    template <typename C, typename=void>
    struct is_character : public std::false_type {};
 
    template <typename C>
    struct is_character<C,
      typename std::enable_if<
           std::is_same<typename std::decay<C>::type, char>::value
        || std::is_same<typename std::decay<C>::type, wchar_t>::value
        || std::is_same<typename std::decay<C>::type, char16_t>::value
        || std::is_same<typename std::decay<C>::type, char32_t>::value
      >::type
    > : public std::true_type {};
 
    // Used to check if a type is a "regular" STL character to a corresponding string type.
    template <typename S, typename C, typename=void>
    struct is_character_of : public std::false_type {};
 
    template <typename S, typename C>
    struct is_character_of<S, C,
      typename std::enable_if<
        std::is_same<typename std::decay<S>::type::value_type, typename std::decay<C>::type>::value
      >::type
    > : public std::true_type {};
 
    // Used to check if a type is C-string corresponding to a STL basic_string<> type.
    template <typename C, typename=void>
    struct is_c_string : public std::false_type {};
 
    template <typename C>
    struct is_c_string<C,
      typename std::enable_if<
           std::is_same<typename std::decay<C>::type, char*>::value
        || std::is_same<typename std::decay<C>::type, wchar_t*>::value
        || std::is_same<typename std::decay<C>::type, char16_t*>::value
        || std::is_same<typename std::decay<C>::type, char32_t*>::value
        || std::is_same<typename std::decay<C>::type, const char*>::value
        || std::is_same<typename std::decay<C>::type, const wchar_t*>::value
        || std::is_same<typename std::decay<C>::type, const char16_t*>::value
        || std::is_same<typename std::decay<C>::type, const char32_t*>::value
      >::type
    > : public std::true_type {};
 
    // Evaluates to the default STL basic_string type that can be constructed from the given type (or reference).
    template <typename T, typename=void> struct basic_string_type {};
    template <typename T> struct basic_string_type<T, typename std::enable_if<is_basic_string<T>::value>::type> { using type = typename std::decay<T>::type; };
    template <typename T> struct basic_string_type<T, typename std::enable_if<std::is_same<typename std::decay<T>::type, const char*>::value>::type> { using type = std::string; };
    template <typename T> struct basic_string_type<T, typename std::enable_if<std::is_same<typename std::decay<T>::type, char*>::value>::type> { using type = std::string; };
    template <typename T> struct basic_string_type<T, typename std::enable_if<std::is_same<typename std::decay<T>::type, const wchar_t*>::value>::type> { using type = std::wstring; };
    template <typename T> struct basic_string_type<T, typename std::enable_if<std::is_same<typename std::decay<T>::type, wchar_t*>::value>::type> { using type = std::wstring; };
    template <typename T> struct basic_string_type<T, typename std::enable_if<std::is_same<typename std::decay<T>::type, const char16_t*>::value>::type> { using type = std::u16string; };
    template <typename T> struct basic_string_type<T, typename std::enable_if<std::is_same<typename std::decay<T>::type, char16_t*>::value>::type> { using type = std::u16string; };
    template <typename T> struct basic_string_type<T, typename std::enable_if<std::is_same<typename std::decay<T>::type, const char32_t*>::value>::type> { using type = std::u32string; };
    template <typename T> struct basic_string_type<T, typename std::enable_if<std::is_same<typename std::decay<T>::type, char32_t*>::value>::type> { using type = std::u32string; };
 
    // Evaluates to the character type (without const/volatile/ref) of a basic_string or C-string (or
    // for completeness the character type itself).
    template <typename T, typename=void> struct character_type_of {};
    template <typename T> struct character_type_of<T, typename std::enable_if<is_basic_string<T>::value>::type> { using type = typename std::remove_cv<typename std::decay<T>::type::value_type>::type; };
    template <typename T> struct character_type_of<T, typename std::enable_if<is_character<T>::value>::type>    { using type = typename std::remove_cv<typename std::decay<T>::type>::type; };
    template <typename T> struct character_type_of<T, typename std::enable_if<is_c_string<T>::value>::type>     { using type = typename std::remove_cv<typename std::remove_pointer<typename std::remove_cv<typename std::decay<T>::type>::type>::type>::type; };
 
    // Character class determination
    // These wrappers are mainly used to allow implicitly selecting the desired std::isxxxx() function
    // (without locale argument).
    enum class ctype_sel {
      space, upper, lower, alpha, alnum, digit, xdigit, graph, print, punct, cntrl,
      spacea, uppera, lowera, alphaa, alnuma, digita, xdigita, grapha, printa, puncta, cntrla
    };
    template <typename C, ctype_sel Sel> struct ctype { static bool is(C c) { (void)c; static_assert(sizeof(C)==0, "Invalid ctype::is selection"); return false; } };
    template <typename C> struct ctype<C, ctype_sel::upper> { static bool is(C c) { return std::isupper(c); } };
    template <typename C> struct ctype<C, ctype_sel::lower> { static bool is(C c) { return std::islower(c); } };
    template <typename C> struct ctype<C, ctype_sel::space> { static bool is(C c) { return std::isspace(c); } };
    template <typename C> struct ctype<C, ctype_sel::alpha> { static bool is(C c) { return std::isalpha(c); } };
    template <typename C> struct ctype<C, ctype_sel::alnum> { static bool is(C c) { return std::isalnum(c); } };
    template <typename C> struct ctype<C, ctype_sel::digit> { static bool is(C c) { return std::isdigit(c); } };
    template <typename C> struct ctype<C, ctype_sel::xdigit>{ static bool is(C c) { return std::isxdigit(c);} };
    template <typename C> struct ctype<C, ctype_sel::graph> { static bool is(C c) { return std::isgraph(c); } };
    template <typename C> struct ctype<C, ctype_sel::print> { static bool is(C c) { return std::isprint(c); } };
    template <typename C> struct ctype<C, ctype_sel::punct> { static bool is(C c) { return std::ispunct(c); } };
    template <typename C> struct ctype<C, ctype_sel::cntrl> { static bool is(C c) { return std::iscntrl(c); } };
    template <> struct ctype<wchar_t, ctype_sel::upper>     { static bool is(wchar_t c) { return std::iswupper(c) != 0; } };
    template <> struct ctype<wchar_t, ctype_sel::lower>     { static bool is(wchar_t c) { return std::iswlower(c) != 0; } };
    template <> struct ctype<wchar_t, ctype_sel::space>     { static bool is(wchar_t c) { return std::iswspace(c) != 0; } };
    template <> struct ctype<wchar_t, ctype_sel::alpha>     { static bool is(wchar_t c) { return std::iswalpha(c) != 0; } };
    template <> struct ctype<wchar_t, ctype_sel::alnum>     { static bool is(wchar_t c) { return std::iswalnum(c) != 0; } };
    template <> struct ctype<wchar_t, ctype_sel::digit>     { static bool is(wchar_t c) { return std::iswdigit(c) != 0; } };
    template <> struct ctype<wchar_t, ctype_sel::xdigit>    { static bool is(wchar_t c) { return std::iswxdigit(c) != 0; } };
    template <> struct ctype<wchar_t, ctype_sel::graph>     { static bool is(wchar_t c) { return std::iswgraph(c) != 0; } };
    template <> struct ctype<wchar_t, ctype_sel::print>     { static bool is(wchar_t c) { return std::iswprint(c) != 0; } };
    template <> struct ctype<wchar_t, ctype_sel::punct>     { static bool is(wchar_t c) { return std::iswpunct(c) != 0; } };
    template <> struct ctype<wchar_t, ctype_sel::cntrl>     { static bool is(wchar_t c) { return std::iswcntrl(c) != 0; } };
 
    // ASCII matching for fast locale-less character type tests
    template <typename C> struct ctype<C, ctype_sel::uppera> { static bool is(C c) { return (c <= C(0x5a) && c >= C(0x41)); }};
    template <typename C> struct ctype<C, ctype_sel::lowera> { static bool is(C c) { return (c <= C(0x7a) && c >= C(0x61)); }};
    template <typename C> struct ctype<C, ctype_sel::spacea> { static bool is(C c) { return (c <= C(0xd) && c >= C(0x9)) || (c==C(0x20)); }};
    template <typename C> struct ctype<C, ctype_sel::alphaa> { static bool is(C c) { c &= ~C(0x20); return (c <= C(0x5a) && c >= C(0x41)); }};
    template <typename C> struct ctype<C, ctype_sel::digita> { static bool is(C c) { return (c <= C(0x39) && c >= C(0x30)); }};
    template <typename C> struct ctype<C, ctype_sel::alnuma> { static bool is(C c) { return (ctype<C, ctype_sel::alphaa>::is(c)) || (ctype<C, ctype_sel::digita>::is(c)); }};
    template <typename C> struct ctype<C, ctype_sel::xdigita>{ static bool is(C c) { if(c <= C(0x39) && c >= C(0x30)) { return true; } c &= ~C(0x20); return (c <= C(0x46) && c >= C(0x41)); }};
    template <typename C> struct ctype<C, ctype_sel::grapha> { static bool is(C c) { return (c > C(0x20)) && (c < C(0x7f)); }};
    template <typename C> struct ctype<C, ctype_sel::printa> { static bool is(C c) { return (c >= C(0x20)) && (c < C(0x7f)); }};
    template <typename C> struct ctype<C, ctype_sel::puncta> { static bool is(C c) { return ((c >= C(0x21)) && (c < C(0x30))) || ((c >= C(0x3a)) && (c <= C(0x40))) || ((c >= C(0x5b)) && (c <= C(0x60))) || ((c >= C(0x7b)) && (c <= C(0x7e))); }};
    template <typename C> struct ctype<C, ctype_sel::cntrla> { static bool is(C c) { return (unsigned(c) < C(0x20)) || (c == C(0x7f)); }};
 
    // String
    template <typename T, ctype_sel Sel>
    typename std::enable_if<is_basic_string<T>::value, bool>
    ::type is_all_cctype(T&& str)
    {
      if(str.empty()) return false;
      for(auto c:str) { if(!ctype<typename basic_string_type<T>::type::value_type, Sel>::is(c)) return false; }
      return true;
    }
 
    // Single character
    template <typename T, ctype_sel Sel>
    typename std::enable_if<is_character<T>::value, bool>
    ::type is_all_cctype(T&& ch)
    { return ctype<typename std::decay<T>::type, Sel>::is(ch); }
 
    // Literal / C-string
    template <typename T, ctype_sel Sel>
    typename std::enable_if<
         std::is_pointer<T>::value
      && is_character<typename std::remove_pointer<typename std::decay<T>::type>::type>::value, bool>
    ::type is_all_cctype(T&& str)
    { return (!str) ? false : is_all_cctype(*str); }
 
    template <typename T, ctype_sel Sel>
    typename std::enable_if<
         std::is_reference<T>::value
      && std::is_pointer<typename std::decay<T>::type>::value
      && !is_character<T>::value
      && is_character<typename std::remove_pointer<typename std::decay<T>::type>::type>::value, bool>
    ::type is_all_cctype(T&& str)
    {
      using char_type = typename std::remove_pointer<typename std::decay<T>::type>::type;
      if(!str[0]) return false;
      for(auto p = &str[0]; *p; ++p) { if(!ctype<char_type, Sel>::is(*p)) return false; }
      return true;
    }
 
    // Types from which std::basic_string's can be constructed
    template <typename T, ctype_sel Sel>
    typename std::enable_if<
         !is_basic_string<T>::value
      && is_basic_string_constructible_from<T>::value
      && !is_character<typename std::remove_pointer<typename std::decay<T>::type>::type >::value
    , bool>
    ::type is_all_cctype(T&& str)
    { return is_all_cctype<typename basic_string_type<T>::type, Sel>(typename basic_string_type<T>::type(str)); }
 
  } // namespace detail
  // </editor-fold>
 
  // <editor-fold desc="is_upper/lower/etc" defaultstate="collapsed">
  /**
   * Returns true if the string contains only space characters.
   * Note: Without multibyte/unicode interpretation.
   * Note: Uses global locale settings.
   *
   * @param String&& str
   * @return bool
   */
  template <typename T>
  bool is_space(T&& str)
  { return detail::is_all_cctype<T, detail::ctype_sel::space>(std::forward<T>(str)); }
 
  /**
   * Returns true if the string contains only upper case characters.
   * Note: Without multibyte/unicode interpretation.
   * Note: Uses global locale settings.
   *
   * @param String&& str
   * @return bool
   */
  template <typename T>
  bool is_upper(T&& str)
  { return detail::is_all_cctype<T, detail::ctype_sel::upper>(std::forward<T>(str)); }
 
  /**
   * Returns true if the string contains only upper case characters.
   * Note: Without multibyte/unicode interpretation.
   * Note: Uses global locale settings.
   *
   * @param String&& str
   * @return bool
   */
  template <typename T>
  bool is_lower(T&& str)
  { return detail::is_all_cctype<T, detail::ctype_sel::lower>(std::forward<T>(str)); }
 
  /**
   * Returns true if the string contains only alpha characters.
   * Note: Without multibyte/unicode interpretation.
   * Note: Uses global locale settings.
   *
   * @param String&& str
   * @return bool
   */
  template <typename T>
  bool is_alpha(T&& str)
  { return detail::is_all_cctype<T, detail::ctype_sel::alpha>(std::forward<T>(str)); }
 
  /**
   * Returns true if the string contains only alphanumeric characters.
   * Note: Without multibyte/unicode interpretation.
   * Note: Uses global locale settings.
   *
   * @param String&& str
   * @return bool
   */
  template <typename T>
  bool is_alnum(T&& str)
  { return detail::is_all_cctype<T, detail::ctype_sel::alnum>(std::forward<T>(str)); }
 
  /**
   * Returns true if the string contains only digit characters.
   * Note: Without multibyte/unicode interpretation.
   * Note: Uses global locale settings.
   *
   * @param String&& str
   * @return bool
   */
  template <typename T>
  bool is_digit(T&& str)
  { return detail::is_all_cctype<T, detail::ctype_sel::digit>(std::forward<T>(str)); }
 
  /**
   * Returns true if the string contains only hex digit characters.
   * Note: Without multibyte/unicode interpretation.
   * Note: Uses global locale settings.
   *
   * @param String&& str
   * @return bool
   */
  template <typename T>
  bool is_xdigit(T&& str)
  { return detail::is_all_cctype<T, detail::ctype_sel::xdigit>(std::forward<T>(str)); }
 
  /**
   * Returns true if the string contains only characters having a graphical representation,
   * (means that you can see it on the screen --> all printable (isprint(c)==true) characters
   * except space).
   * Note: Without multibyte/unicode interpretation.
   * Note: Uses global locale settings.
   *
   * @param String&& str
   * @return bool
   */
  template <typename T>
  bool is_graph(T&& str)
  { return detail::is_all_cctype<T, detail::ctype_sel::graph>(std::forward<T>(str)); }
 
  /**
   * Returns true if the string contains only printable characters.
   * Note: Without multibyte/unicode interpretation.
   * Note: Uses global locale settings.
   *
   * @param String&& str
   * @return bool
   */
  template <typename T>
  bool is_print(T&& str)
  { return detail::is_all_cctype<T, detail::ctype_sel::print>(std::forward<T>(str)); }
 
  /**
   * Returns true if the string contains only punctuation characters.
   * Note: Without multibyte/unicode interpretation.
   * Note: Uses global locale settings.
   *
   * @param String&& str
   * @return bool
   */
  template <typename T>
  bool is_punct(T&& str)
  { return detail::is_all_cctype<T, detail::ctype_sel::punct>(std::forward<T>(str)); }
 
  /**
   * Returns true if the string contains only control characters.
   * Note: Without multibyte/unicode interpretation.
   * Note: Uses global locale settings.
   *
   * @param String&& str
   * @return bool
   */
  template <typename T>
  bool is_cntrl(T&& str)
  { return detail::is_all_cctype<T, detail::ctype_sel::cntrl>(std::forward<T>(str)); }
 
  //
  // Types used to specify if character type operations shall be done using
  // ASCII matching or wrapping the C/C++ standard functions depending on
  // locale settings.
  //
  struct ctype_selection {};
  struct ctype_locale : ctype_selection {};
  struct ctype_ascii  : ctype_selection {};
 
  /**
   * Returns true if the string contains only space characters.
   * Note: Without multibyte/unicode interpretation.
   * Note: Interprets the character value as ASCII if CTypeSelection==ctype_ascii,
   *       otherwise uses global locale settings.
   *
   * @param String&& str
   * @return bool
   */
  template <typename CTypeSelection, typename T>
  bool is_space(T&& str)
  { return detail::is_all_cctype<T, std::is_same<CTypeSelection,ctype_ascii>::value ? detail::ctype_sel::spacea : detail::ctype_sel::space>(std::forward<T>(str)); }
 
  /**
   * Returns true if the string contains only upper case characters.
   * Note: Without multibyte/unicode interpretation.
   * Note: Interprets the character value as ASCII if CTypeSelection==ctype_ascii,
   *       otherwise uses global locale settings.
   *
   * @param String&& str
   * @return bool
   */
  template <typename CTypeSelection, typename T>
  bool is_upper(T&& str)
  { return detail::is_all_cctype<T, std::is_same<CTypeSelection,ctype_ascii>::value ? detail::ctype_sel::uppera : detail::ctype_sel::upper>(std::forward<T>(str)); }
 
  /**
   * Returns true if the string contains only upper case characters.
   * Note: Without multibyte/unicode interpretation.
   * Note: Interprets the character value as ASCII if CTypeSelection==ctype_ascii,
   *       otherwise uses global locale settings.
   *
   * @param String&& str
   * @return bool
   */
  template <typename CTypeSelection, typename T>
  bool is_lower(T&& str)
  { return detail::is_all_cctype<T, std::is_same<CTypeSelection,ctype_ascii>::value ? detail::ctype_sel::lowera : detail::ctype_sel::lower>(std::forward<T>(str)); }
 
  /**
   * Returns true if the string contains only alpha characters.
   * Note: Without multibyte/unicode interpretation.
   * Note: Interprets the character value as ASCII if CTypeSelection==ctype_ascii,
   *       otherwise uses global locale settings.
   *
   * @param String&& str
   * @return bool
   */
  template <typename CTypeSelection, typename T>
  bool is_alpha(T&& str)
  { return detail::is_all_cctype<T, std::is_same<CTypeSelection,ctype_ascii>::value ? detail::ctype_sel::alphaa : detail::ctype_sel::alpha>(std::forward<T>(str)); }
 
  /**
   * Returns true if the string contains only alphanumeric characters.
   * Note: Without multibyte/unicode interpretation.
   * Note: Interprets the character value as ASCII if CTypeSelection==ctype_ascii,
   *       otherwise uses global locale settings.
   *
   * @param String&& str
   * @return bool
   */
  template <typename CTypeSelection, typename T>
  bool is_alnum(T&& str)
  { return detail::is_all_cctype<T, std::is_same<CTypeSelection,ctype_ascii>::value ? detail::ctype_sel::alnuma : detail::ctype_sel::alnum>(std::forward<T>(str)); }
 
  /**
   * Returns true if the string contains only digit characters.
   * Note: Without multibyte/unicode interpretation.
   * Note: Interprets the character value as ASCII if CTypeSelection==ctype_ascii,
   *       otherwise uses global locale settings.
   *
   * @param String&& str
   * @return bool
   */
  template <typename CTypeSelection, typename T>
  bool is_digit(T&& str)
  { return detail::is_all_cctype<T, std::is_same<CTypeSelection,ctype_ascii>::value ? detail::ctype_sel::digita : detail::ctype_sel::digit>(std::forward<T>(str)); }
 
  /**
   * Returns true if the string contains only hex digit characters.
   * Note: Without multibyte/unicode interpretation.
   * Note: Interprets the character value as ASCII if CTypeSelection==ctype_ascii,
   *       otherwise uses global locale settings.
   *
   * @param String&& str
   * @return bool
   */
  template <typename CTypeSelection, typename T>
  bool is_xdigit(T&& str)
  { return detail::is_all_cctype<T, std::is_same<CTypeSelection,ctype_ascii>::value ? detail::ctype_sel::xdigita : detail::ctype_sel::xdigit>(std::forward<T>(str)); }
 
  /**
   * Returns true if the string contains only characters having a graphical representation,
   * (means that you can see it on the screen --> all printable (isprint(c)==true) characters
   * except space).
   * Note: Without multibyte/unicode interpretation.
   * Note: Interprets the character value as ASCII if CTypeSelection==ctype_ascii,
   *       otherwise uses global locale settings.
   *
   * @param String&& str
   * @return bool
   */
  template <typename CTypeSelection, typename T>
  bool is_graph(T&& str)
  { return detail::is_all_cctype<T, std::is_same<CTypeSelection,ctype_ascii>::value ? detail::ctype_sel::grapha : detail::ctype_sel::graph>(std::forward<T>(str)); }
 
  /**
   * Returns true if the string contains only printable characters.
   * Note: Without multibyte/unicode interpretation.
   * Note: Interprets the character value as ASCII if CTypeSelection==ctype_ascii,
   *       otherwise uses global locale settings.
   *
   * @param String&& str
   * @return bool
   */
  template <typename CTypeSelection, typename T>
  bool is_print(T&& str)
  { return detail::is_all_cctype<T, std::is_same<CTypeSelection,ctype_ascii>::value ? detail::ctype_sel::printa : detail::ctype_sel::print>(std::forward<T>(str)); }
 
  /**
   * Returns true if the string contains only punctuation characters.
   * Note: Without multibyte/unicode interpretation.
   * Note: Interprets the character value as ASCII if CTypeSelection==ctype_ascii,
   *       otherwise uses global locale settings.
   *
   * @param String&& str
   * @return bool
   */
  template <typename CTypeSelection, typename T>
  bool is_punct(T&& str)
  { return detail::is_all_cctype<T, std::is_same<CTypeSelection,ctype_ascii>::value ? detail::ctype_sel::puncta : detail::ctype_sel::punct>(std::forward<T>(str)); }
 
  /**
   * Returns true if the string contains only control characters.
   * Note: Without multibyte/unicode interpretation.
   * Note: Interprets the character value as ASCII if CTypeSelection==ctype_ascii,
   *       otherwise uses global locale settings.
   *
   * @param String&& str
   * @return bool
   */
  template <typename CTypeSelection, typename T>
  bool is_cntrl(T&& str)
  { return detail::is_all_cctype<T, std::is_same<CTypeSelection,ctype_ascii>::value ? detail::ctype_sel::cntrla : detail::ctype_sel::cntrl>(std::forward<T>(str)); }
 
  // </editor-fold>
 
  // <editor-fold desc="rtrim" defaultstate="collapsed">
  /**
   * Removes the specified character or list of characters (given as
   * string or string literal) from end of a string.
   * The `str` can be a (const/volatile/rvalue/lvalue) reference to
   * a basic_string<T>, the `SpaceCharListType` can be a character
   * literal, string literal, or (const/volatile/rvalue/lvalue) string
   * reference corresponding to the type (T) of the given string..
   * Uses global locale settings to determine space characters.
   *
   * @param String&& str
   * @param SpaceCharListType&& space_characters
   * @return String
   */
  template <typename T, typename SpaceCharListType>
  typename std::enable_if<detail::is_basic_string_constructible_from<SpaceCharListType>::value || detail::is_character_of<T, SpaceCharListType>::value, typename detail::basic_string_type<T>::type>
  ::type rtrim(T&& str, SpaceCharListType&& space_characters)
  {
    auto n = str.find_last_not_of(space_characters);
    return (n == str.npos) ? typename std::decay<T>::type() : (n >= str.size()-1 ? str : str.substr(0, n+1));
  }
 
  /**
   * Removes trailing characters from a string according to
   * the return value of a predicate `bool predicate(T character)`.
   * Uses global locale settings to determine space characters.
   *
   * @param String&& str
   * @param Predicate predicate
   * @return String
   */
  template <typename T, typename Predicate>
  typename std::enable_if<!detail::is_basic_string_constructible_from<Predicate>::value && !detail::is_character_of<T, Predicate>::value, typename detail::basic_string_type<T>::type>
  ::type rtrim(T&& str, Predicate predicate)
  {
    using type = typename detail::basic_string_type<T>::type;
    auto pe = int(str.size());
    while((--pe >= 0) && (!!(predicate(static_cast<typename type::value_type>(str[pe]))))) {}
    return (pe < 0) ? (type()) : (str.substr(0, pe+1));
  }
 
  /**
   * Removes trailing whitespace from a string.
   * Uses global locale settings to determine space characters.
   *
   * @param String&& str
   * @return String
   */
  template <typename T>
  typename detail::basic_string_type<T>::type rtrim(T&& str)
  { return rtrim(std::forward<T>(str), is_space<ctype_locale, typename detail::basic_string_type<T>::type::value_type>); }
 
  /**
   * Removes trailing whitespace from a string.
   * Uses global locale settings to determine space characters, unless `CTypeSelection`
   * is `ctype_ascii`, in which case all char types of `T` are interpreted as 7bit ASCII.
   *
   * @param String&& str
   * @return String
   */
  template <typename CTypeSelection, typename T>
  typename std::enable_if<std::is_base_of<ctype_selection, CTypeSelection>::value, typename detail::basic_string_type<T>::type>
  ::type rtrim(T&& str)
  { return rtrim(std::forward<T>(str), is_space<CTypeSelection, typename detail::basic_string_type<T>::type::value_type>); }
 
  // </editor-fold>
 
  // <editor-fold desc="ltrim" defaultstate="collapsed">
  /**
   * Removes the specified character or list of characters (given as
   * string or string literal) from front of a string (leading characters).
   * The `str` can be a (const/volatile/rvalue/lvalue) reference to
   * a basic_string<T>, the `SpaceCharListType` can be a character
   * literal, string literal, or (const/volatile/rvalue/lvalue) string
   * reference corresponding to the type (T) of the given string..
   * Uses global locale settings to determine space characters.
   *
   * @param String&& str
   * @param SpaceCharListType&& space_characters
   * @return String
   */
  template <typename T, typename SpaceCharListType>
  typename std::enable_if<detail::is_basic_string_constructible_from<SpaceCharListType>::value || detail::is_character_of<T, SpaceCharListType>::value, typename detail::basic_string_type<T>::type>
  ::type ltrim(T&& str, SpaceCharListType&& space_characters)
  {
    auto n = str.find_first_not_of(space_characters);
    return (n == str.npos) ? typename std::decay<T>::type() : (n >= str.size() ? (str) : (str.substr(n, str.npos)));
  }
 
  /**
   * Removes leading and trailing characters from a string according to
   * the return value of a predicate `bool predicate(T character)`.
   * Uses global locale settings to determine space characters.
   *
   * @param String&& str
   * @param Predicate& predicate
   * @return String
   */
  template <typename T, typename Predicate>
  typename std::enable_if<!detail::is_basic_string_constructible_from<Predicate>::value && !detail::is_character_of<T, Predicate>::value, typename detail::basic_string_type<T>::type>
  ::type ltrim(T&& str, Predicate predicate)
  {
    using type = typename detail::basic_string_type<T>::type;
    typename type::size_type pb=0;
    while((pb < str.size()) && (!!(predicate(static_cast<typename type::value_type>(str[pb]))))) ++pb;
    return (pb >= str.size()) ? (type()) : str.substr(pb);
  }
 
  /**
   * Removes leading spaces from end of the string.
   * Uses global locale settings to determine space characters.
   *
   * @param String&& str
   * @return String
   */
  template <typename T>
  typename detail::basic_string_type<T>::type ltrim(T&& str)
  { return ltrim(std::forward<T>(str), is_space<ctype_locale, typename detail::basic_string_type<T>::type::value_type>); }
 
  /**
   * Removes leading spaces from end of the string.
   * Uses global locale settings to determine space characters, unless `CTypeSelection`
   * is `ctype_ascii`, in which case all char types of `T` are interpreted as 7bit ASCII.
   *
   * @param String&& str
   * @return String
   */
  template <typename CTypeSelection, typename T>
  typename std::enable_if<std::is_base_of<ctype_selection, CTypeSelection>::value, typename detail::basic_string_type<T>::type>
  ::type ltrim(T&& str)
  { return ltrim(std::forward<T>(str), is_space<CTypeSelection, typename detail::basic_string_type<T>::type::value_type>); }
 
  // </editor-fold>
 
  // <editor-fold desc="trim" defaultstate="collapsed">
  /**
   * Removes the specified character or list of characters (given as
   * string or string literal) from front and end of a string (leading characters).
   * The `str` can be a (const/volatile/rvalue/lvalue) reference to
   * a basic_string<T>, the `SpaceCharListType` can be a character
   * literal, string literal, or (const/volatile/rvalue/lvalue) string
   * reference corresponding to the type (T) of the given string..
   * Uses global locale settings to determine space characters.
   *
   * @param String&& str
   * @param SpaceCharListType&& space_characters
   * @return String
   */
  template <typename T, typename SpaceCharListType>
  typename std::enable_if<detail::is_basic_string_constructible_from<SpaceCharListType>::value || detail::is_character_of<T, SpaceCharListType>::value, typename detail::basic_string_type<T>::type>
  ::type trim(T&& str, SpaceCharListType&& space_characters)
  {
    if(str.empty()) return str;
    auto n1 = str.find_last_not_of(space_characters);
    if(n1 == str.npos) return typename std::decay<T>::type();
    auto n0 = str.find_first_not_of(space_characters);
    return str.substr(n0, n1-n0+1);
  }
 
  /**
   * Removes leading and trailing characters from a string according to
   * the return value of a predicate `bool predicate(T character)`.
   * Uses global locale settings to determine space characters.
   *
   * @param String&& str
   * @param Predicate predicate
   * @return String
   */
  template <typename T, typename Predicate>
  typename std::enable_if<!detail::is_basic_string_constructible_from<Predicate>::value && !detail::is_character_of<T, Predicate>::value, typename detail::basic_string_type<T>::type>
  ::type trim(T&& str, Predicate predicate)
  {
    using type = typename detail::basic_string_type<T>::type;
    auto pb=int(0), pe=int(str.size()); // explicitly not "typename type::size_type" to ensure that this is not uint
    while((pb < pe) && (!!(predicate(static_cast<typename type::value_type>(str[pb]))))) ++pb;
    while((--pe >= pb) && (!!(predicate(static_cast<typename type::value_type>(str[pe])))));
    return (pe < pb) ? (type()) : (str.substr(pb, pe-pb+1));
  }
 
  /**
   * Removes leading and trailing whitespace from a string.
   * Uses global locale settings to determine space characters.
   *
   * @param String&& str
   * @return String
   */
  template <typename T>
  typename detail::basic_string_type<T>::type trim(T&& str)
  { return trim(std::forward<T>(str), is_space<ctype_locale, typename detail::basic_string_type<T>::type::value_type>); }
 
  /**
   * Removes leading and trailing whitespace from a string.
   * Uses global locale settings to determine space characters, unless `CTypeSelection`
   * is `ctype_ascii`, in which case all char types of `T` are interpreted as 7bit ASCII.
   *
   * @param String&& str
   * @return String
   */
  template <typename CTypeSelection, typename T>
  typename std::enable_if<std::is_base_of<ctype_selection, CTypeSelection>::value, typename detail::basic_string_type<T>::type>
  ::type trim(T&& str)
  { return trim(std::forward<T>(str), is_space<CTypeSelection, typename detail::basic_string_type<T>::type::value_type>); }
 
  // </editor-fold>
 
  // <editor-fold desc="join" defaultstate="collapsed">
  /**
   * Concatenates a container of strings or values that are convertible
   * to basic_string<..> (without using to_string()).
   *
   * @param ContainerType container
   * @return ContainerType::value_type
   */
  template <typename ContainerType>
  typename detail::basic_string_type<typename std::decay<ContainerType>::type::value_type>::type
  join(ContainerType&& container)
  {
    auto s = typename detail::basic_string_type<typename std::decay<ContainerType>::type::value_type>::type();
    for(auto& e:container) s += e;
    return s;
  }
 
  /**
   * Concatenates a container of strings, separated with a
   * specified separator.
   *
   * @param ContainerType container
   * @param SeparatorType separator
   * @return ContainerType::value_type
   */
  template <typename ContainerType, typename SeparatorType>
  typename detail::basic_string_type<typename std::decay<ContainerType>::type::value_type>::type
  join(ContainerType&& container, SeparatorType&& separator)
  {
    // I suppose the compiler will manage the optimisation of the loop (add first element,
    // then loop with separator-element-pairs -- means: removes the if() from the loop).
    auto s = typename detail::basic_string_type<typename std::decay<ContainerType>::type::value_type>::type();
    bool has_sep = false;
    for(auto& e:container) {
      if(has_sep) s += separator; else has_sep = true;
      s += e;
    }
    return s;
  }
  // </editor-fold>
 
  // <editor-fold desc="to_upper/to_lower/to_proper" defaultstate="collapsed">
  /**
   * Returns the upper case transformed string of a given string, literal or C-string.
   * The function is affected by the global `locale()` settings.
   *
   * @param String&& s
   * @return String
   */
  template <typename T>
  typename detail::basic_string_type<T>::type to_upper(T&& str)
  { auto s = typename detail::basic_string_type<T>::type(str); for(auto& c:s) { c = std::toupper(c); } return s; }
 
  /**
   * Returns the lower case transformed string of a given string, literal or C-string.
   * The function is affected by the global `locale()` settings.
   *
   * @param String&& s
   * @return String
   */
  template <typename T>
  typename detail::basic_string_type<T>::type to_lower(T&& str)
  { auto s = typename detail::basic_string_type<T>::type(str); for(auto& c:s) { c = std::tolower(c); } return s; }
 
  /**
   * Returns the "proper case" transformed string of a given string, literal or C-string.
   * (the first character of words upper case, other lower case).
   * The function is affected by the global `locale()` settings.
   *
   * @param String&& s
   * @return String
   */
  template <typename T>
  typename detail::basic_string_type<T>::type to_proper(T&& str)
  {
    auto s = typename detail::basic_string_type<T>::type(str);
    bool sp = true;
    for(auto& c:s) {
      if(std::isspace(c) || (!std::isprint(c)) || std::iscntrl(c) || std::ispunct(c)) {
        sp = true;
      } else if(sp) {
        sp = false;
        c = std::toupper(c);
      } else {
        c = std::tolower(c);
      }
    }
    return s;
  }
  // </editor-fold>
 
  // <editor-fold desc="length" defaultstate="collapsed">
  /**
   * Returns the length of a string in string value_type elements
   * (not unicode code points, not visible text characters.
   *  This function returns basically string.length() or string.size().)
   * Template matches: basic_string<CharType, Alloc etc>
   *
   * @param String&& str
   * @return String::size_type
   */
  template <typename T>
  typename std::enable_if<detail::is_basic_string<T>::value, typename detail::basic_string_type<T>::type::size_type>
  ::type length(T&& str)
  { return str.length(); }
 
  /**
   * Returns the length of a string character in value_type elements of
   * the corresponding string (not in unicode code points, not in visible
   * text characters. This function returns always the value one.)
   * Template matches: character
   *
   * @param Character&&
   * @return String::size_type
   */
  template <typename T>
  constexpr typename std::enable_if<detail::is_character<T>::value, typename std::basic_string<T>::size_type>
  ::type length(T)
  { return typename std::basic_string<T>::size_type(1); }
 
  /**
   * Returns the length of a C-string in value_type elements of
   * the corresponding string (not in unicode code points, not in visible
   * text characters. This function returns a value like ::strlen(cstr).)
   * Template matches: C-string
   *
   * @param C_String&&
   * @return String::size_type
   */
  template <typename T>
  typename std::enable_if<detail::is_c_string<T>::value, typename detail::basic_string_type<T>::type::size_type>
  ::type length(T&& str)
  {
    auto p = static_cast<typename std::decay<T>::type>(str);
    if(!p) return 0;
    auto size = typename detail::basic_string_type<T>::type::size_type(0);
    for(; *p; ++p) ++size;
    return size;
  }
  // </editor-fold>
 
  // <editor-fold desc="split" defaultstate="collapsed">
  template <typename ContainerType, typename String, typename Separator>
  ContainerType split(String&& str, Separator&& sep, int max_number_of_chunks=-1)
  {
    static_assert(std::is_same<typename ContainerType::value_type, typename std::decay<String>::type>::value, "Your container does not have the right string type as value_type.");
    using container_type = ContainerType;
    using string_type = typename detail::basic_string_type<String>::type;
    if(!max_number_of_chunks) return container_type();
    const auto str_len = length(str);
    if(!str_len) return container_type();
    const auto sep_len = length(sep);
    if(!sep_len || (max_number_of_chunks == 1)) return container_type(1, str);
    if(max_number_of_chunks < 0) max_number_of_chunks = -1;
    container_type container;
    auto start = typename string_type::size_type(0);
    auto next  = typename string_type::size_type(0);
    constexpr auto npos = string_type::npos;
    if((next = str.find(sep)) == 0) {
      start += sep_len;
      if(!(--max_number_of_chunks)) {
        container.push_back(str.substr(start));
        return container;
      } else {
        container.push_back(string_type());
      }
    }
    while(start < str_len) {
      if((next = str.find(sep, start)) == npos) {
        container.push_back(str.substr(start, str_len - start));
        break;
      } else {
        if(!(--max_number_of_chunks)) {
          container.push_back(str.substr(start));
          break;
        }
        container.push_back(str.substr(start, next - start));
        start = next + sep_len;
        if(start >= str_len) {
          container.push_back(string_type());
        }
      }
    }
    return container;
  }
  // </editor-fold>
 
  // <editor-fold desc="replace" defaultstate="collapsed">
  /**
   * Replaces all occurrences of a search-substring with another given string.
   *
   * (Template match: string, character, character) --> character replace
   *
   * @param String&& str
   * @param Character search
   * @param Character replace
   * @param int max_num_substitutions=-1
   * @return String
   */
  template <typename String>
  typename std::enable_if<detail::is_basic_string<String>::value, typename detail::basic_string_type<String>::type>
  ::type replace(
    String&& str,
    typename detail::character_type_of<String>::type search,
    typename detail::character_type_of<String>::type repl,
    int max_num_substitutions=-1
  )
  {
    auto result = typename detail::basic_string_type<String>::type(str); // assure copying if a non-const reference is passed
    if(!max_num_substitutions) {
      return result;
    } else if(max_num_substitutions < 0) {
      // replace all
      for(auto& c:result) {
        if(c == search) c = repl;
      }
    } else {
      // replace max N ...
      for(auto& c:result) {
        if(c == search) {
          c = repl;
          if(!(--max_num_substitutions)) return result;
        }
      }
    }
    return result;
  }
 
  /**
   * Replaces all occurrences of a search-substring with another given string.
   *
   * (Template match: string, string/c-string, string/c-string)
   *
   * @param String&& str
   * @param String&& search
   * @param String&& replace
   * @param int max_num_substitutions=-1
   * @return String
   */
  template <typename String, typename Search, typename Replace>
  typename std::enable_if<
      (std::is_same<typename detail::basic_string_type<String>::type, typename detail::basic_string_type<Search>::type>::value) &&
      (std::is_same<typename detail::basic_string_type<String>::type, typename detail::basic_string_type<Replace>::type>::value)
    , typename detail::basic_string_type<String>::type
  >::type
  replace(String&& str, Search&& search, Replace&& repl, int max_num_substitutions=-1)
  {
    using namespace detail;
    using string_type = typename detail::basic_string_type<String>::type;
    const auto str_len = length(str);
    if(!str_len) return string_type(); // empty haystack
    const auto search_len = length(search);
    if((!search_len) || (search_len > str_len) || (!max_num_substitutions)) return string_type(std::forward<String>(str)); // allow move construction, ensure not to return a writable reference to str
    const auto repl_len = length(repl);
    if((search_len == 1) && (repl_len == 1)) return replace(std::forward<string_type>(str), search[0], repl[0], max_num_substitutions);
 
    // @todo make fast version
    return join(split<std::vector<string_type>>(std::forward<string_type>(str), search, (max_num_substitutions < 0) ? (-1) : (max_num_substitutions+1)), repl);
  }
 
  /**
   * Replaces all occurrences of a search-substring with another given string.
   *
   * (Template match: string, string/c-string, character)
   *
   * @param String&& str
   * @param String&& search
   * @param String&& replace
   * @param int max_num_substitutions=-1
   * @return String
   */
  template <typename String, typename Search, typename Replace>
  typename std::enable_if<
      (std::is_same<typename detail::basic_string_type<String>::type, typename detail::basic_string_type<Search>::type>::value) &&
      (detail::is_character_of<String, Replace>::value)
    , typename detail::basic_string_type<String>::type
  >::type
  replace(String&& str, Search&& search, Replace&& repl, int max_num_substitutions=-1)
  {
    typename detail::character_type_of<Replace>::type repl_cstr[2] { repl, typename detail::character_type_of<Replace>::type(0) };
    return  replace(std::forward<String>(str), std::forward<Search>(search), repl_cstr, max_num_substitutions);
  }
 
  /**
   * Replaces all occurrences of a search-substring with another given string.
   *
   * (Template match: string, character, string/c-string)
   *
   * @param String&& str
   * @param String&& search
   * @param String&& replace
   * @param int max_num_substitutions=-1
   * @return String
   */
  template <typename String, typename Search, typename Replace>
  typename std::enable_if<
    (std::is_same<typename detail::basic_string_type<String>::type, typename detail::basic_string_type<Replace>::type>::value) &&
    (detail::is_character_of<String, Search>::value)
    , typename detail::basic_string_type<String>::type
  >::type
  replace(String&& str, Search&& search, Replace&& repl, int max_num_substitutions=-1)
  {
    typename detail::character_type_of<Search>::type search_cstr[2] { search, typename detail::character_type_of<Search>::type(0) };
    return replace(std::forward<String>(str), search_cstr, std::forward<Replace>(repl), max_num_substitutions);
  }
  // </editor-fold>
 
  // <editor-fold desc="to_hex" defaultstate="collapsed">
  /**
   * Convert a numeric (integral) value to a to a lowercase big endian
   * hex string representation. The size of the hex string corresponds to
   * the size of the input data type (filled with leading zeros).
   *
   * @tparam String
   * @tparam Number
   * @param Number val
   * @return String
   */
  template <typename String, typename Number>
  typename std::enable_if<std::is_integral<Number>::value, String>::type to_hex(Number val)
  {
    const char tab[17] = "0123456789abcdef";
    String s(sizeof(Number)*2, typename String::value_type('0'));
    int i = static_cast<int>(sizeof(Number)*2);
    while(i && val) {
      s[--i] = typename String::value_type(tab[val & 0xf]);
      val >>=4;
    }
    return s;
  }
 
  /**
   * Convert a numeric (double) value to a to a lowercase big endian
   * hex string representation. The size of the hex string corresponds to
   * the size of the input data type (filled with leading zeros).
   *
   * @tparam String
   * @param double val
   * @return String
   */
  template <typename String>
  String to_hex(double val)
  {
    union { double f; uint64_t i; } u;
    static_assert(sizeof(u) == sizeof(uint64_t), "union size assumption failed.");
    u.f = val; return to_hex<String, uint64_t>(u.i);
  }
 
  /**
   * Convert a numeric (float) value to a to a lowercase big endian
   * hex string representation. The size of the hex string corresponds to
   * the size of the input data type (filled with leading zeros).
   *
   * @tparam String
   * @param double val
   * @return String
   */
  template <typename String>
  String to_hex(float val)
  {
    union { float f; uint32_t i; } u;
    static_assert(sizeof(u) == sizeof(uint32_t), "union size assumption failed.");
    u.f = val; return to_hex<String, uint32_t>(u.i);
  }
 
  /**
   * Convert plain old data (POD) to a lowercase
   * HEX string representation.
   *
   * Warning: Use prefer other `to_hex()` functions
   *          this functions before using this C-style
   *          overload.
   *
   * @tparam String
   * @param const void *data
   * @param size_t size
   * @return String
   */
  template <typename String>
  String to_hex(const void *data, size_t size)
  {
    const char tab[18] = "0123456789abcdef\0";
    if((size <= 0) || (!data)) return String();
    const unsigned char* p = reinterpret_cast<const unsigned char*>(data);
    String s(size*2, typename String::value_type(0));
    auto it = s.begin();
    for(--it; size--; ++p) {
      unsigned char c = *p;
      *++it = tab[(c >> 4) & 0xf ]; // value range checked, not using at()
      *++it = tab[c & 0xf];         // value range checked, not using at()
    }
    return s;
  }
 
  /**
   * Convert plain old data (POD) to a lowercase
   * HEX string representation.
   *
   * @tparam String
   * @tparam POD
   * @param const void *data
   * @param size_t size
   * @return String
   */
  template <typename String, typename POD>
  typename std::enable_if<(std::is_pod<POD>::value) && (!std::is_arithmetic<POD>::value) && (!std::is_pointer<POD>::value), String>
  ::type to_hex(const POD& pod)
  { return to_hex<String>(&pod, sizeof(pod)); }
  // </editor-fold>
 
  // <editor-fold desc="is_numeric" defaultstate="collapsed">
  /**
   * Returns true if the given string suffices the convention of a
   * LOCALE INDEPENDENT floating point notation. That is:
   *
   *  - optional sign
   *  - integral part (optional as ".1e10" is also allowed)
   *  - optional period ('.') and fraction part
   *  - optional 'e' or 'E' and exponent part with optional +/- sign and leading zeros
   *
   * Again, intentionally NOT affected by `locale`.
   *
   * @param String&& s
   * @return bool
   */
  template <typename String>
  typename std::enable_if<detail::is_basic_string<String>::value || detail::is_c_string<String>::value, bool>
  ::type is_numeric(String&& str)
  {
    using char_type = typename detail::character_type_of<String>::type;
    if(!length(str)) return false;
    auto is_blank_ = [](char_type c) -> bool { return (c==char_type('\t')) || (c==char_type(' ')); };
    auto is_digit_ = [](char_type c) -> bool { return (c>=char_type('0' )) && (c<=char_type('9')); };
    const auto *p = &str[0];
    {
      bool had_valid_value = false;
      while(is_blank_(*p)) ++p; // leading "blanks"
      if((*p == char_type('-')) || (*p == char_type('+'))) ++p; // sign
      if(*p == char_type('0')) { had_valid_value = true; ++p; }
      while(*p == char_type('0')) ++p; // ignore leading zeros
      if(is_digit_(*p)) { had_valid_value = true; ++p; }
      while(is_digit_(*p)) ++p; // digit part
      if(*p == char_type('.')) { // fraction
        if(is_digit_(*(++p))) { had_valid_value = true; ++p; }
        while(is_digit_(*p)) ++p; // '\0' is no digit
      }
      if(!had_valid_value) return false; // can abort here, can't get better ...
    }
    {
      if((*p == char_type('e')) || (*p == char_type('E'))) { // exponent part
        bool had_valid_value = false;
        if((*(++p) == char_type('+')) || (*p == char_type('-'))) ++p;
        if(is_digit_(*p)) had_valid_value = true;
        while(*p == char_type('0')) ++p; // ignore leading zeros
        if(is_digit_(*p)) ++p;
        if(!had_valid_value) return false;
      }
    }
    while(is_blank_(*p)) ++p; // ignore trailing blanks
    return !(*p); // must be end of string, otherwise there was a mismatch somewhere before.
  }
 
  /**
   * Returns true if the given character is LOCALE INDEPENDENT convertible
   * into a number (means character range '0' to '9'.
   *
   * @param Character&& s
   * @return bool
   */
  template <typename Character>
  typename std::enable_if<detail::is_character<Character>::value, bool>
  ::type is_numeric(Character&& str)
  { return (str >= Character('0' )) && (str <= Character('9'));; }
  // </editor-fold>
 
  // <editor-fold desc="textwrap" defaultstate="collapsed">
  /**
   * Wraps a given text `str` so that the length of each line (separated
   * with '\n' is not longer than `width` characters. If possible the
   * function wraps at the last possible whitespace or punctuation of a
   * line. If this is not possible the line is cut at the `width`th
   * character.
   *
   * @return String
   */
  template <typename String, typename WrapIndicator=typename detail::basic_string_type<String>::type>
  typename detail::basic_string_type<String>
  ::type textwrap(String&& str, int width, WrapIndicator&& wrap_indicator="")
  {
    using string_type = typename detail::basic_string_type<String>::type;
    using char_type = typename detail::character_type_of<String>::type;
    constexpr auto newline = char_type('\n');
    auto len = int(length(str));
    if(!len || (width <= 0)) return str;
    width -= length(wrap_indicator);
    if(width < 1) width = 1;
    auto s = string_type();
    s.reserve(len + (len/width) + 1);
    // We iterate over the full length, even if zero characters are in between,
    // for c_str() `length(str)` will stop at the first '\0', for string types
    // zeros are allowed in the string, and `str.length()` specifies how big the
    // memory block is - this is also covered in `length(str)`.
    // Assumption: Wrap separators generally occur more than 2 times in a line,
    // so that back searching when exceeding the maximum line length (width)
    // has higher performance than checking each character on the go.
    auto pstart = &str[0]; // because s can be literal/c_string or string. ...
    auto pend = &str[len];
    auto nleft = width;
    for(auto p = pstart; p < pend; ++p) {
      if((pend-pstart) <= width) {
        while(pstart < pend) { s.push_back(*pstart); ++pstart; }
        break;
      } else if(*p == newline) {
        while(pstart <= p) { s.push_back(*pstart); ++pstart; }
        nleft = width;
      } else if(!--nleft) {
        if(is_space(*(p+1))) {
          ++nleft; // can't be newline, postpone wrap
        } else {
          auto pwrap = p;
          for(; pwrap >= pstart; --pwrap) {
            if(is_punct(*pwrap) || is_space(*pwrap) || is_cntrl(*pwrap)) break;
          }
          // no wrappable character found, do hard wrap.
          if(pwrap > pstart) p=pwrap;
          auto pend = p;
          while((pend > pstart) && is_space(*pend)) --pend;
          while(pstart <= pend) { s.push_back(*pstart); ++pstart; }
          s.push_back(newline);
          nleft = width;
          if((p < pend) && is_space(*(p+1))) ++p; // peek one ahead, one whitespace after a break has to be removed.
          pstart = p+1; // p will be incremented
        }
      }
    }
    while(pstart < pend) { s.push_back(*pstart); ++pstart; }
    s.shrink_to_fit();
    return s;
  }
  // </editor-fold>
}
 
// <editor-fold desc="preprocessor" defaultstate="collapsed">
#include <sstream> /* until floating point conversion improved */
// </editor-fold>
namespace sw {
  // <editor-fold desc="to_floating_point" defaultstate="collapsed">
  /**
   * Converts the string to double/float/long double with a given locale,
   * returns NaN if not entirely convertible.
   *
   * @param String&& str
   * @return FloatingPoint
   */
  template <typename FloatingPoint, typename String>
  typename std::enable_if<
    (detail::is_basic_string<String>::value || detail::is_c_string<String>::value) &&
    (std::is_floating_point<FloatingPoint>::value)
    , FloatingPoint
  >
  ::type to_floating_point(String&& str, const std::locale& locale=std::locale("C")) // until locale independent version implemented
  {
    using fp_type = typename std::decay<FloatingPoint>::type;
    constexpr fp_type nan = std::numeric_limits<fp_type>::quiet_NaN();
    int len = length(str);
    if(!len) return nan;
    // u16/u32 strings might not be installed on the system, causing a bad cast.
    // As the floating point conversion does anyway only encompass Arabic digits,
    // an implicit cast to std::string appears to be a possible solution, even
    // if (absolutely) not a high-performance one.
    // Anyway, looking up the locale for each character is neither.
    std::string s(len, 0);
    for(int i=0; i<len; ++i) { unsigned c = unsigned(str[i]); s[i] = char(c > 0x7f ? 0xff : c); };
    std::stringstream ins(std::move(s));
    ins.imbue(locale);
    auto d = fp_type(0);
    if(!(ins >> d)) return nan;
    for(char c=ins.get(); !!ins; c=ins.get()) {
      if(!is_space(c)) return nan;
    }
    return d;
  }
  // </editor-fold>
}
 
// <editor-fold desc="old swlib string functionality forwards" defaultstate="collapsed">
namespace sw {
  //
  // Old c++98 string.hh forwards (with deprecated marks)
  //
  #define basicstr std::basic_string<C> /* temporary macro use, undefined directly below */
  #if defined(__GNUC__) && !defined(NO_DEPRECATED_WARNING)
    #define swdepr(X)  __attribute__ ((deprecated(X)))
  #else
    #define swdepr(X)
  #endif
  // Cases where new interface is compatible
  //template <typename C> basicstr rtrim(basicstr s, const ch_t* spacechars = (const ch_t*)" ")
  //template <typename C> basicstr ltrim(basicstr s, const ch_t* spacechars = (const ch_t*)" ")
  //template <typename C> basicstr trim(basicstr s, const ch_t* spacechars = (const ch_t*)" ")
  //template <typename C> basicstr replace(basicstr s, ch_t search, ch_t repl)
  //template <typename C> basicstr replace(basicstr s, const basicstr & search, const basicstr & repl)
  //template <typename C> basicstr replace(basicstr s, const ch_t* search, const ch_t* repl)
  //template <typename StringType> to_hex(const void *data, size_t size)
  //template <typename C> basicstr join(const std::vector<basicstr> & v, const basicstr & sp) { return <std::vector<basicstr>, basicstr>join(); }
  //template <typename C> basicstr join(const std::vector<basicstr> & v, const C* sp) { return join(); }
  //template <typename C> basicstr join(const std::vector<basicstr> & v, C sp) { return join<std::vector<basicstr>, const C*>(); }
 
  // Renamed
  template <typename C> swdepr("Use to_upper(str)") basicstr toupper(basicstr s) { return to_upper(s); }
  template <typename C> swdepr("Use to_lower(str)") basicstr tolower(basicstr s) { return to_lower(s); }
  template <typename C> swdepr("Use to_proper(str)") basicstr toproper(basicstr s) { return to_proper(s); }
  template <typename C> swdepr("Use is_upper(str)") bool isupper(const basicstr & s) { return is_upper(s); }
  template <typename C> swdepr("Use is_lower(str)") bool islower(const basicstr & s) { return is_lower(s); }
  template <typename C> swdepr("Use is_alpha(str)") bool isalpha(const basicstr & s) { return is_alpha(s); }
  template <typename C> swdepr("Use is_digit(str)") bool isdigit(const basicstr & s) { return is_digit(s); }
  template <typename C> swdepr("Use is_alnum(str)") bool isalnum(const basicstr & s) { return is_alnum(s); }
  template <typename C> swdepr("Use is_alnum(str)") bool isisalnum(const basicstr & s) { return is_alnum(s); } // wow that was bad
  template <typename C> swdepr("Use is_xdigit(str)") bool ishex(const basicstr & s) { return is_xdigit(s); }
  template <typename C> swdepr("Use is_numeric(str)") bool isnumeric(const basicstr & s) { return is_numeric(s); }
 
  template <typename C> double swdepr("Use to_floating_point<double>(str)") to_double(const basicstr & s) { return ::sw::to_floating_point<double>(s); }
  template <typename C> double swdepr("Use to_floating_point<double>(str)") str2dbl(const basicstr & s) { return ::sw::to_floating_point<double>(s); }
  template <typename C> swdepr("Use to_upper(str)") basicstr uppercase(basicstr s) { return toupper(s); }
  template <typename C> swdepr("Use to_lower(str)") basicstr lowercase(basicstr s) { return tolower(s); }
  template <typename C> swdepr("Use to_proper(str)") basicstr propercase(basicstr s) { return toproper(s); }
 
  template <typename C> std::vector<basicstr> swdepr("Use split<Container>(str, separator)") split(const basicstr& s, basicstr sp) { return split<std::vector<basicstr>>(s,sp,-1); }
  template <typename C> std::vector<basicstr> swdepr("Use split<Container>(str, separator)") split(const basicstr& s, const C* sp) { return split<std::vector<basicstr>>(s,sp,-1); }
  template <typename C> std::vector<basicstr> swdepr("Use split<Container>(str, separator)") split(const basicstr& s, C sp) { return split<std::vector<basicstr>>(s,sp,-1); }
 
  #undef basicstr
}
// </editor-fold>
 
#endif