stringstream unsigned input validation

后端 未结 2 1912
旧时难觅i
旧时难觅i 2021-01-13 03:13

I\'m writing part of program which parses and validates some user input in program console arguments. I choose to use stringstream for that purpose, but encounter a problem

2条回答
  •  暖寄归人
    2021-01-13 03:58

    A num_get facet to support the explicit check for signedness. Rejects any non-zero number beginning with a '-' (after white-spaces) for unsigned types and uses the default C locale's num_get to do the actual conversion.

    #include 
    #include 
    #include 
    #include 
    
    template  >
    class num_get_strictsignedness : public std::num_get 
    {
    public:
        typedef charT char_type;
        typedef InputIterator iter_type;
    
        explicit num_get_strictsignedness(std::size_t refs = 0)
            : std::num_get(refs)
        {}
        ~num_get_strictsignedness()
        {}
    
    private:
        #define DEFINE_DO_GET(TYPE) \
            virtual iter_type do_get(iter_type in, iter_type end,      \
                std::ios_base& str, std::ios_base::iostate& err,       \
                TYPE& val) const override                              \
            {  return do_get_templ(in, end, str, err, val);  }         // MACRO END
    
        DEFINE_DO_GET(unsigned short)
        DEFINE_DO_GET(unsigned int)
        DEFINE_DO_GET(unsigned long)
        DEFINE_DO_GET(unsigned long long)
    
        // not sure if a static locale::id is required..
    
        template 
        iter_type do_get_templ(iter_type in, iter_type end, std::ios_base& str,
                               std::ios_base::iostate& err, T& val) const
        {
            using namespace std;
    
            if(in == end)
            {
                err |= ios_base::eofbit;
                return in;
            }
    
            // leading white spaces have already been discarded by the
            // formatted input function (via sentry's constructor)
    
            // (assuming that) the sign, if present, has to be the first character
            // for the formatting required by the locale used for conversion
    
            // use the "C" locale; could use any locale, e.g. as a data member
    
            // note: the signedness check isn't actually required
            //       (because we only overload the unsigned versions)
            bool do_check = false;
            if(std::is_unsigned{} && *in == '-')
            {
                ++in;  // not required
                do_check = true;
            }
    
            in = use_facet< num_get >(locale::classic())
                     .get(in, end, str, err, val);
    
            if(do_check && 0 != val)
            {
                err |= ios_base::failbit;
                val = 0;
            }
    
            return in;
        }
    };
    

    Usage example:

    #include 
    #include 
    int main()
    {
        std::locale loc( std::locale::classic(),
                         new num_get_strictsignedness() );
        std::stringstream ss("-10");
        ss.imbue(loc);
        unsigned int ui = 42;
        ss >> ui;
        std::cout << "ui = "<

    Notes:

    • the allocation on the free store is not required, you could use e.g. a (static) local variable where you initialize the ref counter with 1 in the ctor
    • for every character type you want to support (like char, wchar_t, charXY_t), you need to add an own facet (can be different instantiations of the num_get_strictsignedness template)
    • "-0" is accepted

提交回复
热议问题