0
struct ci_char_traits : public std::char_traits<char>
{
  static bool eq( char c1, char c2 ) { return toupper( c1 ) == toupper( c2 ); }
  static bool ne( char c1, char c2 ) { return toupper( c1 ) != toupper( c2 ); }
  static bool lt( char c1, char c2 ) { return toupper( c1 ) <  toupper( c2 ); }

  static int compare( const char* s1, const char* s2, size_t n );
  static const char* find( const char* s, int n, char a );
};

using ci_string = std::basic_string<char, ci_char_traits>;

I'm working with this char_traits derivative which should help me dealing with case-insensitive string comparisons. It works perfectly fine when constructing ci_strings from character literals, however I'm often faced with the situation where I have one or two std::strings and would like to compare them case-insensitively. Is there some way to write a custom constructor or assignment/conversion operator to convert from std::string to ci_string or is there no other possibility than to iterate the std::strings and call tolower on each character?

0

1 Answer 1

2

To construct a ci_string from a std::string, you can use the constructor that takes an iterator range. This will iterate through the std::string and store a copy of its characters into the ci_string:

std::string foo = "test";
ci_string bar(foo.begin(), foo.end());

This will work for any basic_string specialization as long as the underlying type (char in this case) is the same. You would not want to do this though with a std::wstring, as that uses wchar_t, which would give you an incorrect conversion.

Also, as pointed out in comments, you can also construct the ci_string using the constructor that takes a const char* and count:

std::string foo = "test";
ci_string bar(foo.c_str(), foo.length());

This has the advantage that you could not use any "string" that uses something other than char under the hood, like std::wstring.

Sign up to request clarification or add additional context in comments.

6 Comments

Alternatively, you can use the constructor that takes a char* and a length as input: ci_string bar(foo.c_str(), foo.size()); That would also prevent trying to construct a ci_string from a std::wstring since a wchar_t* cannot be assigned to a char*.
@RemyLebeau Good point. We don't even need the size as it has a constructor for just a const char*
Yes, but that constructor has to manually count the char elements. Since the source string already knows how many are present, it is better to pass that value to the constructor when it is known.
@RemyLebeau I am always hesitant with that one as I can never remember if the size needs the null terminated size or not. Just using the pointer only one stops me from having to worry.
The null terminator is never counted, not in the constructor, not in size() or length(). If the string contains embedded nulls, those are counted, though, since they are character data. In which case, you must use the constructor that takes a count as input, otherwise they will be treated as a null terminator and won't be copied correctly.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.