Getting wrong result for FIND function of map - c++

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;
}

Related

Constant character pointer using unique_ptr

Let's say that I have a vector<string> input and I need to pass this to a function that takes a const char** argument. My thought had been to use a unique_ptr like this:
const auto output = make_unique<char*>(size(input));
But I can't seem to turn a const unique_ptr<const*> into a const char**. Is there a way to accomplish this, or perhaps a simpler alternative?
I would just build a vector of pointers to the c_str()'s of the strings and then get a pointer to that.
std:vector<const char*> pointers;
pointers.reserve(input.size());
for (const auto& e : input)
pointers.push_back(e.c_str()); // get const char *'s
auto argument = pointers.data(); // get pointer to const char*'s - const char**
Or using a unique_ptr
auto pointers = std::make_unique<const char*[]>(size(input))
for (size_t i = 0; i < input.size(); ++i)
pointers[i]= input[i].c_str(); // get const char *'s
auto argument = pointers.get(); // get pointer to const char*'s - const char**
I assume you need this to fit some interface you have no control of, otherwise I would consider adapting the interface in question to avoid unnecessary creation of temporary data just for the sake of fitting an ill-fitted interface…
Since you just need a temporary array of known size, the simplest solution would probably be to allocate an array of pointers and fill it with pointers to the strings in your vector:
auto pointers = std::make_unique<const char*[]>(size(v));
std::transform(begin(v), end(v), &pointers[0], [](const auto& s) { return s.c_str(); });
This array could also be placed on the stack to avoid dynamic memory allocation. But since you're working with strings here and are willing to copy data into a temporary array, I assume performance is not critical, so I guess there's no need for the added complexity…
two approaches, depending on whether the c interface requires null termination or not:
#include <vector>
#include <string>
#include <algorithm>
auto make_c_interface_null_terminated(std::vector<std::string> const &input) -> std::vector<const char*>
{
auto result = std::vector<const char*>(input.size() + 1);
auto to_c_str = [](auto&& str) { return str.c_str(); };
std::transform(begin(input), end(input), begin(result), to_c_str);
// implied: result[result.size() - 1] = nullptr
return result;
}
auto make_c_interface(std::vector<std::string> const &input) -> std::vector<const char*>
{
auto result = std::vector<const char*>(input.size());
auto to_c_str = [](auto&& str) { return str.c_str(); };
std::transform(begin(input), end(input), begin(result), to_c_str);
return result;
}
extern "C" void c_interface_requires_null(const char** argv);
extern "C" void c_interface_sized(size_t size, const char** args);
void test(std::vector<std::string> const &input)
{
auto output1 = make_c_interface_null_terminated(input);
c_interface_requires_null(output1.data());
auto output2 = make_c_interface(input);
c_interface_sized(output1.size(), output1.data());
}

Convert std::string to ci_string

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";
}

How to provide comparison function in std::set for unique element insertion?

I have following user defined class,
class simple
{
public:
simple(string str1, string str2)
: s1(str1)
, s2(str2)
{
}
const string& getS1() const {return s1;}
const string& getS2()const {return s2;}
bool operator<(const simple& s) {
return strcmp(this->getS1().c_str(), s.getS1().c_str()) < 0 && strcmp(this->getS2().c_str(), s.getS2().c_str()) < 0;
}
private:
const string s1;
const string s2;
}
And I'm storing this class in a std::set as below:
std::set<simple*> mySimplePtrSet;
simple* s1 = new simple("stack", "overflow");
simple* s2 = new simple("go", "ogle");
simple* s3 = new simple("stack", "overflow");
simple* s4 = new simple("go", "good");
simple* s5 = new simple("my", "overflow");
In above example set should only contains s1, s2, s4 and s5.
How to store unique class objects in set considering bothe the data members of class (i.e. s1 and s2)?
I tried with following compartor function but it's not working.
struct myCom {
bool operator()(const simple* a,const simple* b){
return *a < *b;
}
};
NOTE: Sorting is not important in my case, if the elements inserted unsorted is fine for me.
You're storing pointers so the comparator is simply comparing the memory addresses, not your custom operator<.
Stop using new and store the actual objects in the map:
std::set<simple> mySimpleSet;
mySimpleSet.emplace("stack", "overflow");
mySimpleSet.emplace("go", "ogle");
mySimpleSet.emplace("stack", "overflow");
mySimpleSet.emplace("go", "good");
mySimpleSet.emplace("my", "overflow");
Or if you really need to store pointers then you have to provide a custom comparator that doesn't compare the memory addresses but the objects at those memory addresses.
Also your operator< is flawed. You're using std::string so use std::string::compare instead of strcmp, and you're not using the parameter s. Something like this would work better:
bool operator<(const simple& s) {
return getS1() < s.getS1() && getS2() < s.getS2();
}

std::find on vector of objects with overloaded == operator

I am trying to use std::find on the following vector:
std::vector<LoopDetectorData *> Vec_loopDetectors;
And this is how I am using it:
const LoopDetectorData *searchFor = new LoopDetectorData( (*it).c_str(), "", vehicleName.c_str() );
std::vector<LoopDetectorData *>::iterator counter = std::find(Vec_loopDetectors.begin(), Vec_loopDetectors.end(), searchFor);
This is the definition of LoopDetectorData class with operator == overloading.
class LoopDetectorData
{
public:
char detectorName[20];
char lane[20];
char vehicleName[20];
double entryTime;
double leaveTime;
double entrySpeed;
double leaveSpeed;
LoopDetectorData( const char *str1, const char *str2, const char *str3, double entryT=-1, double leaveT=-1, double entryS=-1, double leaveS=-1 )
{
strcpy(this->detectorName, str1);
strcpy(this->lane, str2);
strcpy(this->vehicleName, str3);
this->entryTime = entryT;
this->leaveTime = leaveT;
this->entrySpeed = entryS;
this->leaveSpeed = leaveS;
}
friend bool operator== (const LoopDetectorData &v1, const LoopDetectorData &v2);
};
It seems that std::find can not find an item even if the item exists in the vector.
std::find() searches by value. So it will compare pointers stored in your vector to the pointer which you just created to serve as search argument. This is dommed to fail: you compare pointers and not the values of the object poitned to.
You shall use std::find_if() instead:
auto counter = std::find_if (Vec_loopDetectors.begin(),
Vec_loopDetectors.end(),
[&searchFor](const LoopDetectorData *f)->bool
{ return *f == *searchFor; }
);
find_if uses a predicate which is here an ad-hoc lambda function that compares the values pointed to by dereferencing the pointers. If you're not comfortable with lambdas, you could use a function poitner instead.
Here a live demo of this alternative, with a comparison to you rinitial attempt.

How do I capture a smart pointer in a lambda?

What is best way to capture a smart pointer in a lambda? One attempt of mine lead to a use-after-free bug.
Example code:
#include <cstring>
#include <functional>
#include <memory>
#include <iostream>
std::function<const char *(const char *)> test(const char *input);
int main()
{
std::cout.sync_with_stdio(false);
std::function<const char *(const char *)> a = test("I love you");
const char *c;
while ((c = a(" "))){
std::cout << c << std::endl;
}
return 0;
}
std::function<const char *(const char *)> test(const char *input)
{
char* stored = strdup(input);
char *tmpstorage = nullptr;
std::shared_ptr<char> pointer = std::shared_ptr<char>(stored, free);
return [=](const char * delim) mutable -> const char *
{
const char *b = strtok_r(stored, delim, &tmpstorage);
stored = nullptr;
return b;
};
}
fails, as shown by AddressSanitizer.
A lambda (even one with a universal capture like [=]) only actually captures variables used within its definition. Since in your example, pointer is never used inside the lambda, it's not captured and thus when it goes out of scope, it's the last shared pointer referring to stored and free() is called.
If you want to capture pointer, you could force its use:
return [=](const char * delim) mutable -> const char *
{
pointer;
const char *b = strtok_r(stored, delim, &tmpstorage);
stored = nullptr;
return b;
};
However, this is rather hackish. You want your functor stateful and with nontrivial state management. To me, this is a strong indicator an actual named class (instead of a lambda) would be in order. So I would change it like this:
std::function<const char *(const char *)> test(const char *input)
{
struct Tokenizer
{
std::shared_ptr<char> pointer;
char* stored;
char* tmpstorage;
explicit Tokenizer(char* stored) : pointer(stored, free), stored(stored), tmpstorage(nullptr) {}
const char* operator() (const char * delim)
{
const char *b = strtok_r(stored, delim, &tmpstorage);
stored = nullptr;
return b;
}
};
return Tokenizer(strdup(input));
}
Just capture the variable by value and let the copy constructor and destructor worry about ownership semantics- that's what smart pointers are for.