Search code examples
c++parameter-passingcharacterc++17string-view

How to guarantee a specific set of characters as input to a string_view parameter?


I'm working on a design implementation where I want the input to be selected from a specific set of characters for its string literal representation.

Consider the following set of classes:

class enum BaseType {
    BINARY = 2,
    OCTAL = 8,
    DECIMAL = 10,
    HEXADECIMAL = 16
};

template<BaseType BASE = BaseType::DECIMAL> // Default Template
class Foo {
public:
    const uint16_t Base{BASE};
private: 
    std::string digits_;
    int64_t integral_value_;
    int64_t decimal_value_;
    size_t decimal_index_location_;
public:
    Foo() 
      : digits_{""}, 
        integral_value_{0}, 
        decimal_value_{0}
        decimal_index_location_{0}
    {}
    Foo(const std::string_view digit_sequence) 
      : digits_{digit_sequence}, 
        integral_value_{0}, 
        decimal_value_{0}
        decimal_index_location{0}
    {
        // set values according to the respective digits 
        // from the decimal point if one exists
        // and set the decimal index location if one exists...
    }
};

I may have to use specializations for the other non-default types which have yet to be decided. Regardless of that, I want to restrict each case to the following character set as follows:

  • BINARY : '0', '1', '.'
  • OCTAL : ['0' - '7'], '.'
  • DECIMAL : ['0' - '9'], '.'
  • HEXADECIMAL : ['0' - '9'], ['a' - 'f'], ['A' - 'F'], '.'

These would be acceptable inputs for each type:

  • BINARY :
    • "010", ".010", "01.0", "01." etc...
  • OCTAL :
    • "012345670", ".012345670", "01.2345670", "1.", etc...
  • DECIMAL :
    • "01234567890", ".01234567890", "01.234567890", "1.", etc...
  • HEXADECIMAL :
    • "0123456789abcdef0", ".0123456789abcdef0", "01.23456789abcdef0", "1.", etc...
    • "0123456789ABCDEF0", ".0123456789ABCDEF0", "01.23456789ABCDEF0", "1.", etc...

To be the only valid set of input characters for the string_view parameter of the class's constructor.


Is there a simple, elegant, and efficient way of doing this? If so, how? It doesn't really matter if this is handled by throwing exceptions, compile-time or run-time assertions... I just want to limit the possible set of valid characters for each templated version...


EDIT

For each of the cases even a single '.' is valid input. For example:

Foo a("."); 

Would be interpreted as 0 and later when I incorporate the exponent part, the exponent would evaluate to 1 so that the result would be 0 and not 1 due to power rules...


Solution

  • With <regex>, you might do:

    static const std::regex binary_regex(R"([01]*\.?[01]*)");
    static const std::regex octal_regex(R"([0-7]*\.?[0-7]*)");
    static const std::regex decimal_regex(R"([0-9]*\.?[0-9]*)");
    static const std::regex hex_regex(R"([0-9a-fA-F]*\.?[0-9a-fA-F]*)");
    
    bool do_match(const std::string& s, const std::regex& regex)
    {
        // if (s.empty()) { return false; }
        std::smatch base_match;
        
        return std::regex_match(s, base_match, regex);   
    }
    

    Demo

    You might even with grouping get the value before dot and after dot