1 #pragma once 2 3 #include <charconv> 4 #include <iterator> 5 #include <string_view> 6 7 namespace details 8 { 9 10 // This implementation avoids the complexity of using std::isdigit, which pulls 11 // in all of <locale>, and likely has other consequences. 12 inline bool simpleIsDigit(const char c) 13 { 14 return c >= '0' && c <= '9'; 15 } 16 17 enum class ModeType 18 { 19 STRING, 20 NUMBER 21 }; 22 23 } // namespace details 24 25 inline int alphanumComp(const std::string_view left, 26 const std::string_view right) 27 { 28 29 std::string_view::const_iterator l = left.begin(); 30 std::string_view::const_iterator r = right.begin(); 31 32 details::ModeType mode = details::ModeType::STRING; 33 34 while (l != left.end() && r != right.end()) 35 { 36 if (mode == details::ModeType::STRING) 37 { 38 // check if this are digit characters 39 const bool lDigit = details::simpleIsDigit(*l); 40 const bool rDigit = details::simpleIsDigit(*r); 41 // if both characters are digits, we continue in NUMBER mode 42 if (lDigit && rDigit) 43 { 44 mode = details::ModeType::NUMBER; 45 continue; 46 } 47 // if only the left character is a digit, we have a result 48 if (lDigit) 49 { 50 return -1; 51 } // if only the right character is a digit, we have a result 52 if (rDigit) 53 { 54 return +1; 55 } 56 // compute the difference of both characters 57 const int diff = *l - *r; 58 // if they differ we have a result 59 if (diff != 0) 60 { 61 return diff; 62 } 63 // otherwise process the next characters 64 l++; 65 r++; 66 } 67 else // mode==NUMBER 68 { 69 // get the left number 70 int lInt = 0; 71 auto fc = std::from_chars(&(*l), &(*left.end()), lInt); 72 l += std::distance(l, fc.ptr); 73 74 // get the right number 75 int rInt = 0; 76 fc = std::from_chars(&(*r), &(*right.end()), rInt); 77 r += std::distance(r, fc.ptr); 78 79 // if the difference is not equal to zero, we have a comparison 80 // result 81 const int diff = lInt - rInt; 82 if (diff != 0) 83 { 84 return diff; 85 } 86 87 // otherwise we process the next substring in STRING mode 88 mode = details::ModeType::STRING; 89 } 90 } 91 if (r == right.end() && l == left.end()) 92 { 93 return 0; 94 } 95 if (r == right.end()) 96 { 97 return 1; 98 } 99 return -1; 100 } 101 102 // A generic template type compatible with std::less that can be used on generic 103 // containers (set, map, ect) 104 template <class Type> 105 struct AlphanumLess 106 { 107 bool operator()(const Type& left, const Type& right) const 108 { 109 return alphanumComp(left, right) < 0; 110 } 111 }; 112