Convert std::string to ci_string - c++

I used this approach to create a case-insensitive typedef for string. Now, I'm trying to convert a std::string to ci_string. All of the following throw compiler errors:
std::string s {"a"};
ci_string cis {s};
ci_string cis (s);
ci_string cis {(ci_string)s};
ci_string cis ((ci_string)s);
ci_string cis = s;
I spent some time trying to figure out how to overload the = operator, and I attempted to use static_cast and dynamic_cast without success. How can I do this?

Your two types are different, so you cannot use the constructor with a regular std::string. But your string is still able to copy a C string, so this should work:
std::string s{"a"};
ci_string cis{ s.data() }; // or s.c_str(), they are the same

std::string and ci_string are unrelated types. Why would static_cast or dynamic_cast be able to convert them? Remember: Two different instantiations of the same template are unrelated types and are potentially completely incompatible.
Give up on the idea of overloading operator= or on some magic that performs the conversion automatically. You have two unrelated types. But they both offer member functions that can you can successfully use to copy the char elements from one to the other.
Just write a simple conversion function that takes advantage of the fact that both std::string and ci_string have their value_type defined as char, and appropriately use one of std::basic_string's constructors, either one which takes a pointer to raw data or one which takes two iterators which form a range.
Here is a complete example:
#include <string>
#include <iostream>
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) {
while( n-- != 0 ) {
if( toupper(*s1) < toupper(*s2) ) return -1;
if( toupper(*s1) > toupper(*s2) ) return 1;
++s1; ++s2;
}
return 0;
}
static const char* find(const char* s, int n, char a) {
while( n-- > 0 && toupper(*s) != toupper(a) ) {
++s;
}
return s;
}
};
typedef std::basic_string<char, ci_char_traits> ci_string;
ci_string to_ci_string(std::string const& src)
{
return ci_string(src.begin(), src.end());
// or:
// return ci_string(src.c_str());
}
int main()
{
std::string s {"a"};
auto cis = to_ci_string(s);
std::cout << cis.c_str() << "\n";
}

Related

How to write constructor/assignment operator overloads for custom string types?

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?
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.

Interchangeable use of hash_map and unordered_map (C++11)

From an earlier post, Difference between hash_map and unordered_map?, I understand that hash_map that existed in prior-C++-11 was kind of non-standard and that has been replaced by standard unordered_map as interchangeable one. However, I have some doubt.
Earlier (prior to C++11), we used hash_map as:
hash_map<string,string,shash,seq> some_hashmap;
where shash and seq could be defined as below:
class shash {
public:
size_t operator()(const string &a) const {
register size_t ret = 0;
register string::const_iterator a,e;
a = s.begin();
e = s.end();
for(;a != e;++a) {
ret = (ret << 5) + (ret >> 2) + *a;
}
return(ret);
}
};
class seq {
public:
bool operator()(const string &str1,const string &str2) const {
register const char *s1 = str1.c_str();
register const char *s2 = str2.c_str();
for(;*s1 && *s2 && *s1 == *s2;++s1,++s2);
return(!*s1 && !*s2);
}
};
Can unordered_map (C++11) be used interchangeably here? Or, is there any better alternative of shash and seq, or can they be omitted in unordered_map?

Getting wrong result for FIND function of map

The method fails to find the char* array passed to it even though it is present in map.
When I replaced char* with std::string in map. Code works fine.
static void CreateTranslationMap();
static UString FindTranslatedString(char* propertyName);
static std::map<char*,UString> TranslationMap ;
static void CreateTranslationMap()
{
UString engString("TextAlignmentPosition");
char* transString= MSGTXT("TextAlignmentPosition");
TranslationMap.insert(std::pair<char*,UString>(transString,engString));
}
UString FindTranslatedString(char* propertyName)
{
UString NotFound("CannotFind");
std::map<char*, UString>::iterator itr;
itr = TranslationMap.begin();
itr = TranslationMap.find(propertyName);
if(itr!= TranslationMap.end())
{
return itr->second;
}
else if(itr== TranslationMap.end())
{
return NotFound;
}
}
You need to use your own custom comparator for comparing pointer to char
Use:
struct cmp_c_string
{
bool operator()(char const *lhs, char const *rhs) const
{
return std::strcmp(lhs, rhs) < 0;
}
};
std::map<char*,UString, cmp_c_string > TranslationMap ;
That's because when doing comparison for equality the map uses <.
When the Key of the map is char* you are doing comparisons of pointers (not the C-String). So you are testing to see if one pointer is less than the other pointer (ie comparing the address).
When the Key of the map is std::string you using the operator< that is defined for std::string, which actually compares the characters in the string to determine which is less than the other.
As the std::map is a template it actually takes more parameters to define how it works. The third parameters is the comparison function (which defaults to less std::less<K> which is usually operator<).
So you can use char* you just need a custom comparator operator.
bool comparitor(char const* lhs, char const* rhs)
{
return (test for less than or some other strict weak function);
}
std::map<char*, UString, comparitor> myMap;
when using char *, it just compare address.
char a[] = "hi";
char b[] = "hi";
char *c = a;
char *d = b;
c & d are different.(c != d) If you want to compare string, you should use strcmp.
But when using string, it overwrites "==" operation.
So you can just compare using "=="
string a = "hi";
string b = "hi";
a & b are same. (a == b)
You have this behavior because you use pointer to string literal which is different every time you create such a pointer. So, for example, you create 2 pointers:
char* p1 = "Hello world!";
char* p2 = "Hello world!";
While content to which p1 and p2 point is identical the pointers, themselves, are different. So p1 != p2, and you trying to store pointer in the map. You should use std::string instead or have global constants pointers which you'd use everywhere; something like:
const char* const c_transString = MSGTXT("TextAlignmentPosition");
...
TranslationMap.insert(std::pair<char*,UString>(c_transString, engString));
...
FindTranslatedString(c_transString)
Just replace char* to const char* because the map data type always take the string in const form . I took your example and it is running in my terminal. So the new code is :
#include<iostream>
using namespace std;
static void CreateTranslationMap();
static string FindTranslatedString(const char* propertyName);
static std::map<const char*,string> TranslationMap ;
static void CreateTranslationMap()
{
string engString("TextAlignmentPosition");
const char* transString= ("1");
TranslationMap.insert(std::pair<const char*,string>(transString,engString));
}
string FindTranslatedString(const char* propertyName)
{
string NotFound("CannotFind");
std::map<const char*, string>::iterator itr;
itr = TranslationMap.begin();
itr = TranslationMap.find(propertyName);
if(itr!= TranslationMap.end())
{
return itr->second;
}
else if(itr== TranslationMap.end())
{
return NotFound;
}
}
int main()
{
CreateTranslationMap();
string s =FindTranslatedString("1");
cout<<s<<endl;
return 0;
}

C++ comparison using strings for operators

I was writing out the function below, and started to think that there's probably a better way to go about it; however Google isn't turning up much, so any insight would be appreciated. I also have a very similar situation involving integers.
bool compare_strs (std::string operator_, std::string str_0, std::string str_1)
{
if (operator_ == ">")
{
return str_0 > str1;
}
else if (operator_ == "<")
{
return str_0 < str1;
}
else if (operator_ == "<=")
{
return str_0 <= str1;
}
else
{
return str_0 >= str1;
}
}
You can use a map to store operators and related functors. In C++11, something along these lines should work, though there might be a couple subtle errors. In C++03, you'll have to change a couple things, including changing std::function to boost::function or function pointers, as well as using std::make_pair to store the map values.
#include <functional> //for std::function and std::less et al.
#include <map> //for std::map
#include <stdexcept> //for std::invalid_argument
#include <string> //for std::string
struct StringComparer {
static bool compare( //split up to fit width
const std::string &oper,
const std::string &str0, const std::string &str1
) {
MapType::const_iterator iter = operations.find(oper);
if (iter == std::end(operations)) //check if operator is found
throw std::invalid_argument("No match for provided operator.");
return iter->second(str0, str1); //call the appropriate functor
}
private:
using MapType = std::map< //makes life easier, same as typedef
std::string,
std::function<bool(const std::string &, const std::string &)>
>;
static const MapType operations; //a map of operators to functors
};
const StringComparer::MapType StringComparer::operations = { //define the map
{"<", std::less<std::string>()}, //std::less is a functor version of <
{"<=", std::less_equal<std::string>()},
{">", std::greater<std::string>()},
{">=", std::greater_equal<std::string>()}
};
You can also see it in action. The nice thing about an approach like this is that it's very easy to include more operators, as all you have to do is add them to the map.
As others have mentioned, you should first ask yourself why you are doing this - there is likely a better solution. Going with this though, I might do something like:
template <typename T1, typename T2>
bool mycompare(std::string operator_, const T1 & _lhs, const T2 & _rhs)
{
if (operator_ == ">")
{
return _lhs > _rhs;
}
else if (operator_ == "<")
{
return _lhs < _rhs;
}
//etc.
else
{
throw new exception("Invalid operator");
}
}

std::string and multiple concatenations

Let’s consider that snippet, and please suppose that a, b, c and d are non-empty strings.
std::string a, b, c, d;
d = a + b + c;
When computing the sum of those 3 std::string instances, the standard library implementations create a first temporary std::string object, copy in its internal buffer the concatenated buffers of a and b, then perform the same operations between the temporary string and the c.
A fellow programmer was stressing that instead of this behaviour, operator+(std::string, std::string) could be defined to return a std::string_helper.
This object’s very role would be to defer the actual concatenations to the moment where it’s casted into a std::string. Obviously, operator+(std::string_helper, std::string) would be defined to return the same helper, which would "keep in mind" the fact that it has an additional concatenation to carry out.
Such a behavior would save the CPU cost of creating n-1 temporary objects, allocating their buffer, copying them, etc. So my question is: why doesn’t it already work like that ?I can’t think of any drawback or limitation.
why doesn’t it already work like that?
I can only speculate about why it was originally designed like that. Perhaps the designers of the string library simply didn't think of it; perhaps they thought the extra type conversion (see below) might make the behaviour too surprising in some situations. It is one of the oldest C++ libraries, and a lot of wisdom that we take for granted simply didn't exist in past decades.
As to why it hasn't been changed to work like that: it could break existing code, by adding an extra user-defined type conversion. Implicit conversions can only involve at most one user-defined conversion. This is specified by C++11, 13.3.3.1.2/1:
A user-defined conversion sequence consists of an initial standard conversion sequence followed by a user-defined conversion followed by a second standard conversion sequence.
Consider the following:
struct thingy {
thingy(std::string);
};
void f(thingy);
f(some_string + another_string);
This code is fine if the type of some_string + another_string is std::string. That can be implicitly converted to thingy via the conversion constructor. However, if we were to change the definition of operator+ to give another type, then it would need two conversions (string_helper to string to thingy), and so would fail to compile.
So, if the speed of string building is important, you'll need to use alternative methods like concatenation with +=. Or, according to Matthieu's answer, don't worry about it because C++11 fixes the inefficiency in a different way.
The obvious answer: because the standard doesn't allow it. It impacts code by introducing an additional user defined conversion in some cases: if C is a type having a user defined constructor taking an std::string, then it would make:
C obj = stringA + stringB;
illegal.
It depends.
In C++03, it is exact that there may be a slight inefficiency there (comparable to Java and C# as they use string interning by the way). This can be alleviated using:
d = std::string("") += a += b +=c;
which is not really... idiomatic.
In C++11, operator+ is overloaded for rvalue references. Meaning that:
d = a + b + c;
is transformed into:
d.assign(std::move(operator+(a, b).append(c)));
which is (nearly) as efficient as you can get.
The only inefficiency left in the C++11 version is that the memory is not reserved once and for all at the beginning, so there might be reallocation and copies up to 2 times (for each new string). Still, because appending is amortized O(1), unless C is quite longer than B, then at worst a single reallocation + copy should take place. And of course, we are talking POD copy here (so a memcpy call).
Sounds to me like something like this already exists: std::stringstream.
Only you have << instead of +. Just because std::string::operator + exists, it doesn't make it the most efficient option.
I think if you use +=, then it will be little faster:
d += a;
d += b;
d += c;
It should be faster, as it doesn't create temporary objects.Or simply this,
d.append(a).append(b).append(c); //same as above: i.e using '+=' 3 times.
The main reason for not doing a string of individual + concatenations, and especially not doing that in a loop, is that is has O(n2) complexity.
A reasonable alternative with O(n) complexity is to use a simple string builder, like
template< class Char >
class ConversionToString
{
public:
// Visual C++ 10.0 has some DLL linking problem with other types:
CPP_STATIC_ASSERT((
std::is_same< Char, char >::value || std::is_same< Char, wchar_t >::value
));
typedef std::basic_string< Char > String;
typedef std::basic_ostringstream< Char > OutStringStream;
// Just a default implementation, not particularly efficient.
template< class Type >
static String from( Type const& v )
{
OutStringStream stream;
stream << v;
return stream.str();
}
static String const& from( String const& s )
{
return s;
}
};
template< class Char, class RawChar = Char >
class StringBuilder;
template< class Char, class RawChar >
class StringBuilder
{
private:
typedef std::basic_string< Char > String;
typedef std::basic_string< RawChar > RawString;
RawString s_;
template< class Type >
static RawString fastStringFrom( Type const& v )
{
return ConversionToString< RawChar >::from( v );
}
static RawChar const* fastStringFrom( RawChar const* s )
{
assert( s != 0 );
return s;
}
static RawChar const* fastStringFrom( Char const* s )
{
assert( s != 0 );
CPP_STATIC_ASSERT( sizeof( RawChar ) == sizeof( Char ) );
return reinterpret_cast< RawChar const* >( s );
}
public:
enum ToString { toString };
enum ToPointer { toPointer };
String const& str() const { return reinterpret_cast< String const& >( s_ ); }
operator String const& () const { return str(); }
String const& operator<<( ToString ) { return str(); }
RawChar const* ptr() const { return s_.c_str(); }
operator RawChar const* () const { return ptr(); }
RawChar const* operator<<( ToPointer ) { return ptr(); }
template< class Type >
StringBuilder& operator<<( Type const& v )
{
s_ += fastStringFrom( v );
return *this;
}
};
template< class Char >
class StringBuilder< Char, Char >
{
private:
typedef std::basic_string< Char > String;
String s_;
template< class Type >
static String fastStringFrom( Type const& v )
{
return ConversionToString< Char >::from( v );
}
static Char const* fastStringFrom( Char const* s )
{
assert( s != 0 );
return s;
}
public:
enum ToString { toString };
enum ToPointer { toPointer };
String const& str() const { return s_; }
operator String const& () const { return str(); }
String const& operator<<( ToString ) { return str(); }
Char const* ptr() const { return s_.c_str(); }
operator Char const* () const { return ptr(); }
Char const* operator<<( ToPointer ) { return ptr(); }
template< class Type >
StringBuilder& operator<<( Type const& v )
{
s_ += fastStringFrom( v );
return *this;
}
};
namespace narrow {
typedef StringBuilder<char> S;
} // namespace narrow
namespace wide {
typedef StringBuilder<wchar_t> S;
} // namespace wide
Then you can write efficient and clear things like …
using narrow::S;
std::string a = S() << "The answer is " << 6*7;
foo( S() << "Hi, " << username << "!" );