1 #pragma once 2 3 #include <functional> 4 #include <sstream> 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 } // namespace details 18 19 inline int alphanumComp(const std::string_view left, 20 const std::string_view right) 21 { 22 enum class ModeType 23 { 24 STRING, 25 NUMBER 26 } mode = ModeType::STRING; 27 28 std::string_view::const_iterator l = left.begin(); 29 std::string_view::const_iterator r = right.begin(); 30 31 while (l != left.end() && r != right.end()) 32 { 33 if (mode == ModeType::STRING) 34 { 35 while (l != left.end() && r != right.end()) 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 = ModeType::NUMBER; 44 break; 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 } 67 else // mode==NUMBER 68 { 69 // get the left number 70 int lInt = 0; 71 while (l != left.end() && details::simpleIsDigit(*l)) 72 { 73 lInt = lInt * 10 + static_cast<int>(*l) - '0'; 74 ++l; 75 } 76 77 // get the right number 78 int rInt = 0; 79 while (r != right.end() && details::simpleIsDigit(*r)) 80 { 81 rInt = rInt * 10 + static_cast<int>(*r) - '0'; 82 ++r; 83 } 84 85 // if the difference is not equal to zero, we have a comparison 86 // result 87 const int diff = lInt - rInt; 88 if (diff != 0) 89 { 90 return diff; 91 } 92 93 // otherwise we process the next substring in STRING mode 94 mode = ModeType::STRING; 95 } 96 } 97 if (r == right.end() && l == left.end()) 98 { 99 return 0; 100 } 101 if (r == right.end()) 102 { 103 return 1; 104 } 105 return -1; 106 } 107 108 // A generic template type compatible with std::less that can be used on generic 109 // containers (set, map, ect) 110 template <class Type> 111 struct AlphanumLess 112 { 113 bool operator()(const Type& left, const Type& right) const 114 { 115 return alphanumComp(left, right) < 0; 116 } 117 }; 118