std::map compare function and NULL - c++

I've written a compare function for a std::map so I can have custom key types.
std::map<GGString *, GGObject *, GGDictionaryMapCompare> _map;
...
class GGDictionaryMapCompare
{
public:
bool operator()(GGString * lhs, GGString * rhs)
{
return strcmp(lhs->str(), rhs->str()) < 0;
}
};
Code that adds elements:
GGObject *GGDictionary::addKeyObject(GGString *theKey, GGObject *theObject)
{
if (theKey == NULL || theObject == NULL)
return NULL;
_map.insert(std::pair<GGString *, GGObject *>(theKey, theObject));
return theObject;
}
Code that is causing the crash:
GGObject *GGDictionary::objectForKey(GGString *theKey)
{
if (theKey == NULL)
return NULL;
std::map<GGString *, GGObject *, GGDictionaryMapCompare>::iterator ii = _map.find(theKey);
if (ii == _map.end())
return NULL;
return GGAutoRelease(ii->second);
}
Stack trace:
#0 0x00009f15 in GGString::str()
#1 0x0004a4c4 in GGDictionaryMapCompare::operator()(GGString*, GGString*)
#2 0x0004a3d3 in std::_Rb_tree<GGString*, std::pair<GGString* const, GGObject*>, std::_Select1st<std::pair<GGString* const, GGObject*> >, GGDictionaryMapCompare, std::allocator<std::pair<GGString* const, GGObject*> > >::find(GGString* const&)
#3 0x00049b04 in std::map<GGString*, GGObject*, GGDictionaryMapCompare, std::allocator<std::pair<GGString* const, GGObject*> > >::find(GGString* const&)
#4 0x00048ec9 in GGDictionary::objectForKey(GGString*)
The issue is that the lhs is coming in NULL. I never insert a NULL into the map, so this should not be happening. Any idea why? Or am I just doing the compare function wrong? I can protect against getting NULL, but it seems like something is wrong and I don't want to cure a symptom and not the problem.
Thanks

In this code:
GGObject *GGDictionary::objectForKey(GGString *theKey)
{
std::map<GGString *, GGObject *, GGDictionaryMapCompare>::iterator ii = _map.find(theKey);
if (ii == _map.end())
return NULL;
return GGAutoRelease(ii->second);
}
You aren't checking whether theKey is NULL. Accordingly, when the comparator is invoked on theKey and any element of the map, you will dereference NULL.
To fix this, try adding in a NULL check:
GGObject *GGDictionary::objectForKey(GGString *theKey)
{
if (theKey == NULL) return NULL;
std::map<GGString *, GGObject *, GGDictionaryMapCompare>::iterator ii = _map.find(theKey);
if (ii == _map.end())
return NULL;
return GGAutoRelease(ii->second);
}
Hope this helps!

I wonder if the key compare function should be revised to something like this:
bool operator()(const GGString *&lhs, const GGString *&rhs)
{
if (lhs == NULL || rhs == NULL)
{
return false;
}
return strcmp(lhs->str(), rhs->str()) < 0;
}
Basically I'm thinking the parameters should be const references, and also that the function should protect against dereferencing NULL pointers

Are you sure the crash happens when accessing NULL? You are storing pointers in the map; is it possible that you deleted one of the pointers after storing it in the map? Something like this:
dict->addKeyObject( key1, obj1 );
delete key1; // now dict has a pointer to deleted key1
dict->addKeyObject( key2, obj2 ); // now dict will compare key2 to key1, causing crash

Related

How to use shared_pointer as value in a std::map structure?

I have a map structure as below which has shared_pointer of a protobuf structure Stats:
map<string, shared_pointer<vol::proto::Stats> statsMap;
I am storing and retrieving the map entries through a LRU cache implementation
template class LRUCache<string,std::shared_ptr<vol::proto::Stats>>;
Protobuf structure:-
message Stats {
required string oid = 1;
required int64 logical_size = 2;
required int64 committed_size = 3;
}
message StatsRequest {
required string uuid = 1;
}
The function for storing entry into the map:-
template<class K, class V>
void
LRUCache<K, V>::Put(const K& key, V value)
{
Vmacore::System::WriteSynchronized lock(this);
auto val = refMap.find(key);
if (val == refMap.end()) {
if (cacheSize == refMap.size()) {
K leastUsedKey = lruList.back();
lruList.pop_back();
refMap.erase(leastUsedKey);
}
} else {
lruList.erase(val->second.lruKeyRef);
}
lruList.push_front(key);
refMap[key] = LRUValueReference<K, V>(lruList.begin(), value);
}
void PutS(const string& Id, const vol::proto::Stats& info)
{
shared_ptr<vol::proto::Stats> statsInfo = make_shared<vol::proto::Stats>(info);
_StatsCache.Put(Id, statsInfo);
}
void PutStats(vol::proto::StatsRequest &req) {
vol::proto::Stats *stats;
GetStats(stats); //stats gets populated here.
PutS(stats->oid(), *stats);
}
To Get the entry from the map:-
template<class K, class V>
bool
LRUCache<K, V>::Get(const K& key, V& value)
{
Vmacore::System::WriteSynchronized lock(this);
auto val = refMap.find(key);
if (val == refMap.end()) {
return false;
}
lruList.splice(lruList.begin(), lruList, val->second.lruKeyRef);
val->second.lruKeyRef = lruList.begin();
value = val->second.value;
return true;
}
void GetS(const string& Id, shared_ptr<vol::proto::Stats> info)
{
if (!_StatsCache.Get(Id, info))
return false;
return true;
}
void GetStats(vol::proto::StatsRequest &req) {
shared_ptr<vol::proto::Stats> stats;
if (GetS(req.uuid(), stats)) {
Log("object id is %s", stats.get()->oid()); // Crash at oid (this=0x0)
}
}
Now while doing GetStats, I am seeing a crash as to in GetStats's Log statement that is program terminated with signal SIGSEGV, Segmentation fault. Should I do a make_share for Stats in GetStats? Can you please describe as to what has caused this crash and how to fix the crash here?
You're passing a raw pointer from GetStats to GetS, which then wraps it into a shared_ptr. Now lets start ref-counting.
GetS -> increase on entry > 1
_StatsCache.Get -> pass by ref > 1
GetS -> decrease on exit > 0
Ref count 0 -> delete the pointer.
In GetStats you then try to de-reference the stats pointer by calling .get, but it was just deleted.
You should either always use smart pointers, or be very very very careful when you convert from raw pointer to smart pointer, and back.
Actually what I wrote is wrong. What you're doing is actually much worse.
You're wrapping a raw pointer in GetS into a smart pointer, and then pass it by ref to your _StatsCache.Get, which then assigns a different shared_ptr to yours. So you've overwritten the original raw pointer with something else.
But when GetS returns, that shared_ptr is now lost and destructed, but the original stats pointer is accessed in stats.get(). What's in it? Nothing from GetS, that's for sure.

Is it neccessary to use Json::value object=Json::objectValue to init?

To work with json using a library jsoncpp,is it neccessary to use Json::objectvalue?
Json::Value jsn_req;
jsn_req["header"] = Json::objectValue;
jsn_req["header"]["sid"] = 0;
or:
Json::Value jsn_req;
jsn_req["header"]["sid"] = 0;
What's the difference between this?
read the source code, may help. operator[] create null value if the key is not exist. Then a null value can add not exist key too.
diff:
the first just trans the value of "header" from null to object, the second doesn't do it.
the first find "header" twice, but the second only find it once.
Both of then could work, but the second must be quick, it's not necessary to trans it.
Value& Value::operator[](const char* key) {
return resolveReference(key, key + strlen(key));
}
Value& Value::resolveReference(char const* key, char const* end) {
JSON_ASSERT_MESSAGE(
type() == nullValue || type() == objectValue,
"in Json::Value::resolveReference(key, end): requires objectValue");
if (type() == nullValue)
*this = Value(objectValue);
CZString actualKey(key, static_cast<unsigned>(end - key),
CZString::duplicateOnCopy);
auto it = value_.map_->lower_bound(actualKey);
if (it != value_.map_->end() && (*it).first == actualKey)
return (*it).second;
ObjectValues::value_type defaultValue(actualKey, nullSingleton());
it = value_.map_->insert(it, defaultValue);
Value& value = (*it).second;
return value;
}

CString used on static method

I am stuck into a problem, and I will be very grateful if you help me.
I have a MDI, and in CDocument class, I have a struct:
CMyDoc.h
class CMyDoc : public CDocument
{
...
struct SRecord
{
SRecord(){}
virtual ~SRecord(){}
CString sName;
CString sState;
CString sDateu;
CString sDatec;
};
CTypedPtrArray<CPtrArray, SRecord*> m_arrRecord;
and somewhere I load this struct with data:
SRecord* pItem = new SRecord;
pItem->sName = saItem.GetAt(ML_ASSETNAME);
pItem->sState = saItem.GetAt(ML_STATE);
pItem->sDateu = saItem.GetAt(ML_DATEU;
pItem->sDatec = saItem.GetAt(ML_DATEC);
m_arrRecord.Add(pItem);
ok. I am trying to sort data:
void CMyDoc::SortData(int nColumn, BOOL bAscending)
{
switch(nColumn)
{
case 9:
if(bAscending)qsort((void*)m_arrRecord.GetData(), m_arrRecord.GetSize(), sizeof(SRecord), CompareDateUAscending);
else qsort((void*)m_arrRecord.GetData(), m_arrRecord.GetSize(), sizeof(SRecord), CompareDateUDescending);
break;
...
}
but the problem become when data is access in static method:
int CMyDoc::CompareDateUDescending(const void* arg1, const void* arg2)
{
SRecord* Record1 = (SRecord*)arg1;
SRecord* Record2 = (SRecord*)arg2;
if(Record1->sDateu.IsEmpty() || Record2->sDateu.IsEmpty()) // <--- crash !
return 0;
COleDateTime dL, dR;
dL.ParseDateTime(Record1->sDateu);
dR.ParseDateTime(Record2->sDateu);
return (dL == dR ? 0 : (dL < dR ? 1 : -1));
}
and the crash take me here (atlsimpstr.h):
CStringData* GetData() const throw()
{
return( reinterpret_cast< CStringData* >( m_pszData )-1 ); // the crash lead me on this line
}
what I am doing wrong ?
Any help will be very appreciated !
Update:
I have tried this:
int CMyDoc::CompareDateUDescending(const void* arg1, const void* arg2)
{
SRecord* Record1 = *(SRecord**)arg1; // <-- OK
SRecord* Record2 = *(SRecord**)arg2; // <-- Unhandled exception* see note below
if(Record1->sDateu.IsEmpty() || Record2->sDateu.IsEmpty())
return 0;
COleDateTime dL, dR;
dL.ParseDateTime(Record1->sDateu);
dR.ParseDateTime(Record2->sDateu);
return (dL == dR ? 0 : (dL < dR ? 1 : -1));
}
and the crash told me:
"An unhandled exception was encountered during a user callback." strange ...
The qsort comparison function receives pointers to the elements in the array. But since the elements in the array are themselves pointers what your specific function receives as arguments are pointers to pointers to SRecord, i.e. SRecord**.
You can solve it by doing e.g.
const SRecord* Record1 = *reinterpret_cast<const SRecord**>(arg1);
That is, you cast arg1 to SRecord** and then dereference that pointer to get a SRecord*.
Example on how to use the C++ standard sort function.
First you need to update your comparison function a little:
// The comparison function should return true if Record1 is *smaller* than Record2,
// and return false otherwise
bool CMyDoc::CompareDateUDescending(const SRecord* Record1, const SRecord* Record2)
{
return Record1->sDateu < Record2->sDateu;
}
Then to call sort:
std::sort(m_arrRecord.GetData(), m_arrRecord.GetData() + m_arrRecord.GetSize(), CompareDateUDescending);
Much simpler!

how to sort a std::set using more than one key

I need to copy a set to another one based on more than one key.
the keys are used to -collectively- maintain the uniqueness as well as the order of elements in the set.
My class:
class LaneConnector {
public:
const Lane* getLaneFrom() const {
return From;
}
const Lane* getLaneTo() const {
return To;
}
private:
Lane* From;
Lane* To;
}
my functor:
struct MyLaneConectorSorter {
bool operator() (const LaneConnector* rhs, const LaneConnector* lhs) const
{
const Lane* a = lhs->getLaneFrom();
const Lane* b = rhs->getLaneFrom();
bool key1 = a->getLaneID() < b->getLaneID();
bool key2 = a->getLaneParent->ID() < b->getLaneParent->ID();
bool key2 = a->getLaneParent->getParent->ID() < b->getLaneParent->getParent->ID();
//remind you that I NEED the elements to be in ascending order of
//getLaneParent->getParent->ID() ,a->getLaneParent->ID() and then a->getLaneID()
//duplicate elements are the ones which have all three keys same and need to be discarded
return (key1 && key2 && key3); //which dont seem to be working
}
};
and my source and origin sets:
const std::set<LaneConnector*> src = ..... ; //the getter give me a const version
std::set<sim_mob::LaneConnector *, MyLaneConectorSorter> dest;
and how I fill it up:
for(std::set<sim_mob::LaneConnector*>::iterator it = tempLC.begin(); it != tempLC.end(); it++)
{
dest.insert(*it);//I know I can insert it right at the time of declaration, but keep it like this for now...please
}
your kind help would be highly appreciated.
Since getting operator< for multiple tests right is rather hard, I advocate my way of doing this with tuple (in this case with make_tuple instead of tie since we're dealing with temporaries returned from functions):
#include <tuple>
struct MyLaneConectorSorter {
bool operator() (const LaneConnector* lhs, const LaneConnector* rhs) const
{
const Lane* a = lhs->getLaneFrom();
const Lane* b = rhs->getLaneFrom();
auto const* pa = a->getLaneParent();
auto const* pb = b->getLaneParent();
return std::make_tuple(a->getLaneID(), pa->ID(), pa->getParent()->ID()) <
std::make_tuple(b->getLaneID(), pb->ID(), pb->getParent()->ID())
}
This should work and you can get tuple and make_tuple from Boost too, if your compiler doesn't offer them yet.
You need to prioritorise your key field comparisons... only if the most important field is equal, then you compare the second most important - if that's equal then you compare the third most important etc.. As soon as there's an inequality, you return true or false as appropriate. So, it's not a && operation, it should be ? : or an if-else chain, as in:
return lhs.key1 < rhs.key1 ? true :
rhs.key1 < lhs.key1 ? false :
lhs.key2 < rhs.key2 ? true :
rhs.key2 < lhs.key2 ? false :
...
false;
For the set to operate correctly, you must ensure the keys are never equal - so that last false is never actually used.
If you have three member foo, bar and baz to compare on, this is a common way to compare them:
return lhs.foo < rhs.foo
|| lhs.foo == rhs.foo && (lhs.bar < rhs.bar
|| lhs.bar == rhs.bar && lhs.baz < rhs.baz);
Do you see the pattern? ;)
I have problem understanding your sorting rules, but if the relation is a simple sub-sort than the code should look like this:
if (a->getLaneID() < b->getLaneID())
return true;
else if (a->getLaneID() == b->getLaneID())
{
if (a->getLaneParent->ID() < b->getLaneParent->ID())
return true;
// etc...
}
return false;
Your class MyLaneConnectionSorter has a flaw.
std::set expects a comparison class that can order elements. So your comparison function must provide behaviour similar to less functor or operator<, i.e. either a < b or a > b (which is b < a) or a == b (which is !(a < b) && !(a > b))
If we take your comparison function, it will consider Lanes (6, 5, 4) and (7, 3, 4) (in format (PPID, PID, ID)) to be equal, because neither one is less than another. So you need to compare like this:
if (a->getLaneParent->getParent->ID() < b->getLaneParent->getParent->ID()) return true;
else if (a->getLaneParent->getParent->ID() > b->getLaneParent->getParent->ID()) return false;
else {
if (a->getLaneParent->ID() < b->getLaneParent->ID()) return true;
else if (a->getLaneParent->ID() > b->getLaneParent->ID()) return false;
else {
return (a->getLaneID() < b->getLaneID());
}
}

Valgrind reports "Invalid free() / delete / delete[]"

I'm not sure what could be causing this.
==18270== Invalid free() / delete / delete[]
==18270== at 0x400576A: operator delete(void*) (vg_replace_malloc.c:342)
==18270== by 0x80537F7: LCD::LCDControl::~LCDControl() (LCDControl.cpp:23)
==18270== by 0x806C055: main (Main.cpp:22)
==18270== Address 0x59e92c4 is 388 bytes inside a block of size 712 alloc'd
==18270== at 0x40068AD: operator new(unsigned int) (vg_replace_malloc.c:224)
==18270== by 0x8078033: LCD::DrvCrystalfontz::Get(std::string, LCD::LCDControl*, Json::Value*, std::string) (DrvCrystalfontz.cpp:652)
==18270== by 0x8053F50: LCD::LCDControl::ConfigSetup() (LCDControl.cpp:71)
==18270== by 0x80539F7: LCD::LCDControl::Start(int, char**) (LCDControl.cpp:31)
==18270== by 0x806C025: main (Main.cpp:21)
Here's LCDControl's destructor where delete is called.
LCDControl::~LCDControl() {
Shutdown();
for(std::vector<std::string>::iterator it = display_keys_.begin();
it != display_keys_.end(); it++) {
Error("Deleting %s %p", (*it).c_str(), devices_text_[*it]);
if(devices_text_.find(*it) != devices_text_.end() && devices_text_[*it])
delete devices_text_[*it]; // line 23
}
//delete app_;
}
Here's Crystalfontz::Get()
switch(m->GetProtocol()) {
case 1:
return new Protocol1(name, m, v, config);
break;
case 2:
return new Protocol2(name, m, v, config); // line 652
break;
case 3:
return new Protocol3(name, m, v, config, scab);
break;
default:
Error("Internal error. Model has bad protocol: <%s>",
m->GetName().c_str());
break;
devices_text_:
std::map<std::string, Generic <LCDText>*> devices_text_;
LCDControl::ConfigSetup(),
void LCDControl::ConfigSetup() {
if(!CFG_Get_Root()) return;
Json::Value::Members keys = CFG_Get_Root()->getMemberNames();
for(std::vector<std::string>::iterator it = keys.begin(); it != keys.end(); it++ ) {
if(it->find("display_", 0) != std::string::npos) {
Json::Value *display = CFG_Fetch_Raw(CFG_Get_Root(), it->c_str());
Json::Value *driver = CFG_Fetch_Raw(display, "driver");
if(!driver) {
Error("CFG: Must specify driver <%s>", it->c_str());
continue;
}
Json::Value *rows = CFG_Fetch_Raw(display, "rows", new Json::Value(-1));
/*if(!rows->isNumeric() || rows->asInt() == -1) {
Error("Display <%s> requires number of rows to initialize.", it->c_str());
delete display;
delete driver;
continue;
}*/
Json::Value *cols = CFG_Fetch_Raw(display, "cols", new Json::Value(-1));
/*if(!cols->isNumeric() || rows->asInt() == -1) {
Error("Display <%s> requires number of columns to initialize.", it->c_str());
delete display;
delete driver;
delete rows;
continue;
}*/
Json::Value *model = CFG_Fetch_Raw(display, "model");
if(driver->asString() == "crystalfontz") {
if(model) {
devices_text_[*it] = DrvCrystalfontz::Get(*it, this,
CFG_Get_Root(), model->asString()); // line 71
} else {
Error("Device <%s> requires a model.", it->c_str());
delete display;
delete driver;
delete rows;
delete cols;
continue;
}
} else if(driver->asString() == "qt") {
devices_text_[*it] = new DrvQt(*it, this, CFG_Get_Root(),
rows->asInt(), cols->asInt());
} else if(driver->asString() == "pertelian") {
//devices_text_[*it] = new DrvPertelian(this, CFG_Get_Root(), rows->asInt(), cols->asInt());
} else
continue;
if(model) delete model;
delete display;
delete driver;
delete rows;
delete cols;
}
}
for(std::map<std::string, Generic<LCDText> *>::iterator it =
devices_text_.begin(); it != devices_text_.end(); it++) {
display_keys_.push_back(it->first);
Error("Starting <%s> %p", it->first.c_str(), it->second);
Generic<LCDText> *device = it->second;
device->CFGSetup(it->first);
device->Connect();
device->SetupDevice();
device->BuildLayouts();
device->StartLayout();
}
}
I'm guessing that Protocol1 et al. are subclasses of some SuperProtocol?
devices_text_[*it] goes through assuming that it contains something of type SuperProtocol, so delete devices_text_[*it] calls SuperProtocol::~ SuperProtocol().
However, what you really want to call is Protocol1::~Protocol1() if you're destructing a Protocol1; this only works if you mark SuperProtocol::~ SuperProtocol() as virtual.
Instead of going through the keys, finding it in the map, then deleting it, why not just iterate through the map deleting as you go? I'd make a functor and use for_each (this isn't a guideline or anything, just my opinion),
typedef Generic<LCDText> GenericLCDText;
typedef std::map<std::string, GenericLCDText*> GenericLCDTextMap;
typedef GenericLCDTextMap::value_type GenericLCDTextPair;
struct device_text_deleter : std::unary_function<const GenericLCDTextPair&, void>
{
void operator()(const GenericLCDTextPair& pPair)
{
Error("Deleting %s %p", pPair.first.c_str(), pPair.second);
delete pPair.second;
}
}
std::for_each(devices_text_.begin(), devices_text_.end(), device_text_deleter());
_devices.text_.clear(); // optional, removes the deleted pointers. unnecessary
// if this is run in the destructor, since map goes away
// shortly after
That said, you're code would be improved by the following:
// typedef's for readability (would be in header, maybe private)
typedef std::vector<std::string> StringVector;
typedef Generic<LCDText> GenericLCDText;
typedef std::map<std::string, GenericLCDText*> GenericLCDTextMap;
for(StringVector::iterator it = display_keys_.begin();
it != display_keys_.end(); it++)
{
// find first! otherwise you're looking up the pair every time
GenericLCDTextMap::iterator pair = devices_text_.find(*it);
if (p != devices_text_.end())
{
// operator-> is overloaded for iterators but might as well use
// the pair now.
Error("Deleting %s %p", pair->first.c_str(), pair->second);
// no need to check for null, delete null is a-ok
delete pair->second;
}
}
Hopefully this will make it easier to spot the errors. Make sure any base classes you use have virtual destructors.
Check you haven't added a string in the vector twice (this would be "fixed" buy just iterating through the map, though you'll want to find out why duplicates exist in the first place), etc.
I've never tried this before, but maybe add a double delete macro thing (totally untested):
#define DOUBLE_DELETE_GAURD static bool x = false; assert(x == false); x = true;
Then just add it to your destructor. If you double delete, and the static bool is still around, the assertion will fail. This is completely in undefined la-la land, though.
Could it be that
display_keys_ contains the same string more than once and
this string has an associated Generic <LCDText>* in devices_text_?
In this case the pointer would be given to delete twice and this isn't legal...