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(std::string_view left, std::string_view right) 26 { 27 28 std::string_view::const_iterator l = left.begin(); 29 std::string_view::const_iterator r = right.begin(); 30 31 details::ModeType mode = details::ModeType::STRING; 32 33 while (l != left.end() && r != right.end()) 34 { 35 if (mode == details::ModeType::STRING) 36 { 37 // check if this are digit characters 38 const bool lDigit = details::simpleIsDigit(*l); 39 const bool rDigit = details::simpleIsDigit(*r); 40 // if both characters are digits, we continue in NUMBER mode 41 if (lDigit && rDigit) 42 { 43 mode = details::ModeType::NUMBER; 44 continue; 45 } 46 // if only the left character is a digit, we have a result 47 if (lDigit) 48 { 49 return -1; 50 } // if only the right character is a digit, we have a result 51 if (rDigit) 52 { 53 return +1; 54 } 55 // compute the difference of both characters 56 const int diff = *l - *r; 57 // if they differ we have a result 58 if (diff != 0) 59 { 60 return diff; 61 } 62 // otherwise process the next characters 63 l++; 64 r++; 65 } 66 else // mode==NUMBER 67 { 68 // get the left number 69 int lInt = 0; 70 auto fc = std::from_chars(&(*l), &(*left.end()), lInt); 71 l += std::distance(l, fc.ptr); 72 73 // get the right number 74 int rInt = 0; 75 fc = std::from_chars(&(*r), &(*right.end()), rInt); 76 r += std::distance(r, fc.ptr); 77 78 // if the difference is not equal to zero, we have a comparison 79 // result 80 const int diff = lInt - rInt; 81 if (diff != 0) 82 { 83 return diff; 84 } 85 86 // otherwise we process the next substring in STRING mode 87 mode = details::ModeType::STRING; 88 } 89 } 90 if (r == right.end() && l == left.end()) 91 { 92 return 0; 93 } 94 if (r == right.end()) 95 { 96 return 1; 97 } 98 return -1; 99 } 100 101 // A generic template type compatible with std::less that can be used on generic 102 // containers (set, map, ect) 103 template <class Type> 104 struct AlphanumLess 105 { 106 bool operator()(const Type& left, const Type& right) const 107 { 108 return alphanumComp(left, right) < 0; 109 } 110 }; 111