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!
Related
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.
So trying to hook a OpenVR method "ITrackedDeviceServerDriver::GetPose" but i'm getting access violations when my custom hooked method returns out (I think something is wrong with calling convention but idk enough about what x64 asm expects).
All other methods I hook in "ITrackedDeviceServerDriver" work fine. Only "GetPose" fails.
Class thats being hooked looks like:
class ITrackedDeviceServerDriver
{
public:
virtual EVRInitError Activate( uint32_t unObjectId ) = 0;
virtual void Deactivate() = 0;
virtual void EnterStandby() = 0;
virtual void *GetComponent( const char *pchComponentNameAndVersion ) = 0;
virtual void DebugRequest( const char *pchRequest, char *pchResponseBuffer, uint32_t unResponseBufferSize ) = 0;
virtual DriverPose_t GetPose() = 0;
};
Object thats larger than ptr & gets returned from method that crashes
struct DriverPose_t
{
double poseTimeOffset;
vr::HmdQuaternion_t qWorldFromDriverRotation;
double vecWorldFromDriverTranslation[ 3 ];
vr::HmdQuaternion_t qDriverFromHeadRotation;
double vecDriverFromHeadTranslation[ 3 ];
double vecPosition[ 3 ];
double vecVelocity[ 3 ];
double vecAcceleration[ 3 ];
vr::HmdQuaternion_t qRotation;
double vecAngularVelocity[ 3 ];
double vecAngularAcceleration[ 3 ];
ETrackingResult result;
bool poseIsValid;
bool willDriftInYaw;
bool shouldApplyHeadModel;
bool deviceIsConnected;
};
Example of how I'm hooking the virtual methods:
typedef DriverPose_t(__thiscall* GetPose_Org)(ITrackedDeviceServerDriver* thisptr);
GetPose_Org GetPose_Ptr = nullptr;
DriverPose_t __fastcall GetPose_Hook(ITrackedDeviceServerDriver* thisptr)
{
DriverPose_t result = GetPose_Ptr(thisptr);// works
//result.deviceIsConnected = true;
return result;// after return I get access violation crash
}
void TestHook(ITrackedDeviceServerDriver *pDriver)
{
MEMORY_BASIC_INFORMATION mbi;
ZeroMemory(&mbi, sizeof(MEMORY_BASIC_INFORMATION));
void** vTable = *(void***)(pDriver);
VirtualQuery((LPCVOID)vTable, &mbi, sizeof(MEMORY_BASIC_INFORMATION));
VirtualProtect(mbi.BaseAddress, mbi.RegionSize, PAGE_EXECUTE_READWRITE, &mbi.Protect);// unlock
Activate_Ptr = (Activate_Org)vTable[0];
vTable[0] = &Activate_Hook;// Hook!
Deactivate_Ptr = (Deactivate_Org)vTable[1];
vTable[1] = &Deactivate_Hook;// Hook!
EnterStandby_Ptr = (EnterStandby_Org)vTable[2];
vTable[2] = &EnterStandby_Hook;// Hook!
GetPose_Ptr = (GetPose_Org)vTable[5];
vTable[5] = &GetPose_Hook;// Hook!
VirtualProtect(mbi.BaseAddress, mbi.RegionSize, mbi.Protect, &mbi.Protect);// lock
}
So why is "GetPose" the only method that fails after it returns out?
How can I fix the calling convention on "GetPose" to work with registers correctly?
as already noted in comments
__thiscall and __fastcall are not the same calling convention
even for x64. for x86 this is obvious - __thiscall - pass first parameter (this) via Ecx register, and next parameters via stack (i skip now float/double parameters case). __fastcall - 1-first via Ecx, 2-second via Edx, and next via stack
in case x64 (again let assume that no float/double parameters) - first 4 parameters via Rcx, Rdx, R8, R9 and next in stack. so look like x64 have single and common calling convention (no difference between __stdcall, __fastcall, __cdecl, __thiscall)
but exist question about implicit parameters. say in case __thiscall - the first parameter - this is implicit parameter, always passed as first parameter via Ecx/Rcx
and now case when function try "return" object. really on any platform function can return something only via cpu registers. it finite count, and only several of it can be used for return value store.
say on x86(x64) - for return value can be used: AL, AX, EAX, RAX, DAX:EAX, RDX:RAX
so function can return only something that have size - 1, 2, 4, 8 (and 16 for x64)
if function try "return" object with another size - this is impossible do direct, compiler need transform your code. for example if you write function:
DriverPose_t GetPose(ITrackedDeviceServerDriver* thisptr)
{
DriverPose_t pos = ...;
return pos;
}
and call
DriverPose_t pos = GetPose(thisptr);
compiler (of course this already implementation details, different compilers can do this in different ways !) can transform this to
void GetPose(DriverPose_t *ppos, ITrackedDeviceServerDriver* thisptr)
{
DriverPose_t pos = ...;
*ppos = pos;
}
and call to
DriverPose_t pos;
GetPose(&pos, thisptr);
so here pointer to object passed to function as first ,hidden/implicit, parameter. but in case member function, still exist another hidden/implicit, parameter - this, which always passed as first, as result hidden/implicit, parameter for return value moved to second place !
struct ITrackedDeviceServerDriver
{
DriverPose_t GetPose()
{
DriverPose_t pos = ...;
return pos;
}
};
and call
ITrackedDeviceServerDriver obj;
DriverPose_t pos = obj.GetPose();
can be transformed to
struct ITrackedDeviceServerDriver
{
void GetPose(DriverPose_t *ppos)
{
DriverPose_t pos = ...;
*ppos = pos;
}
};
and call to
ITrackedDeviceServerDriver obj;
DriverPose_t pos;
obj.GetPose(&pos);
so real signature of function, if "uncover" this parameter is
void ITrackedDeviceServerDriver::GetPose(ITrackedDeviceServerDriver* this, DriverPose_t *ppos)
{
DriverPose_t pos = ...;
*ppos = pos;
}
and call
ITrackedDeviceServerDriver obj;
DriverPose_t pos;
GetPose(&obj, &pos);
so compare
void ITrackedDeviceServerDriver::GetPose(ITrackedDeviceServerDriver* this, DriverPose_t *ppos);
and
void GetPose(DriverPose_t *ppos, ITrackedDeviceServerDriver* thisptr);
you implement
DriverPose_t GetPose_Hook(ITrackedDeviceServerDriver* thisptr);
which will be transformed by compiler to
void GetPose_Hook(DriverPose_t* ,ITrackedDeviceServerDriver* thisptr);
but really you need implement next hook:
void GetPose_Hook(ITrackedDeviceServerDriver* thisptr, DriverPose_t* );
you confuse first and second parameters. however i think not need change object vtable at all, instead need return proxy object to client. possible and very generic implementation for COM interface hooks, but it too long for post here. for your case can be done next example of hook:
struct DriverPose_t
{
int x, y, z;
};
struct __declspec(novtable) IDemoInterface
{
virtual DriverPose_t GetPose() = 0;
virtual ULONG GetComponent( const char * pchComponentNameAndVersion ) = 0;
virtual void Delete() = 0;
};
class DemoObject : public IDemoInterface
{
ULONG v;
DriverPose_t pos;
virtual DriverPose_t GetPose()
{
DbgPrint("%s<%p>\n", __FUNCTION__, this);
return pos;
}
virtual ULONG GetComponent( const char * pchComponentNameAndVersion )
{
DbgPrint("%s<%p>(%s)\n", __FUNCTION__, this, pchComponentNameAndVersion);
return v;
}
virtual void Delete()
{
delete this;
}
public:
DemoObject(ULONG v, int x, int y, int z) : v(v)
{
pos.x = x, pos.y = y, pos.z = z;
DbgPrint("%s<%p>\n", __FUNCTION__, this);
}
virtual ~DemoObject()
{
DbgPrint("%s<%p>\n", __FUNCTION__, this);
}
};
class Hook_DemoInterface : public IDemoInterface
{
IDemoInterface* pItf;
virtual DriverPose_t GetPose()
{
DriverPose_t pos = pItf->GetPose();
DbgPrint("%s<%p>=<%d, %d, %d>\n", __FUNCTION__, this, pos.x, pos.y, pos.z);
return pos;
}
virtual ULONG GetComponent( const char * pchComponentNameAndVersion )
{
ULONG v = pItf->GetComponent(pchComponentNameAndVersion);
DbgPrint("%s<%p>(%s)=%u\n", __FUNCTION__, this, pchComponentNameAndVersion);
return v;
}
virtual void Delete()
{
delete this;
}
public:
Hook_DemoInterface(IDemoInterface* pItf) : pItf(pItf)
{
DbgPrint("%s<%p>\n", __FUNCTION__, this);
}
~Hook_DemoInterface()
{
pItf->Delete();
DbgPrint("%s<%p>\n", __FUNCTION__, this);
}
};
BOOL CreateDemoIface(IDemoInterface** ppItf)
{
if (DemoObject* pObj = new DemoObject (1, -1, 2, 3))
{
*ppItf = pObj;
return TRUE;
}
return FALSE;
}
BOOL Hook_CreateDemoIface(IDemoInterface** ppItf)
{
IDemoInterface* pItf;
if (CreateDemoIface(&pItf))
{
if (Hook_DemoInterface* pObj = new Hook_DemoInterface(pItf))
{
*ppItf = pObj;
return TRUE;
}
*ppItf = pItf;
return TRUE;
}
return FALSE;
}
void UseIface(IDemoInterface* pItf)
{
ULONG v = pItf->GetComponent("some string");
DriverPose_t pos = pItf->GetPose();
DbgPrint("v=%u, pos<%d, %d, %d>\n", v, pos.x, pos.y, pos.z);
}
void test()
{
IDemoInterface* pItf;
if (CreateDemoIface(&pItf))
{
UseIface(pItf);
pItf->Delete();
}
if (Hook_CreateDemoIface(&pItf))
{
UseIface(pItf);
pItf->Delete();
}
}
instead hook object vtable - hook (if possible) object creation CreateDemoIface - replace it to self Hook_CreateDemoIface which return proxy object.
debug output
DemoObject::DemoObject<0000012C31E08F70>
DemoObject::GetComponent<0000012C31E08F70>(some string)
DemoObject::GetPose<0000012C31E08F70>
v=1, pos<-1, 2, 3>
DemoObject::~DemoObject<0000012C31E08F70>
DemoObject::DemoObject<0000012C31E08F70>
Hook_DemoInterface::Hook_DemoInterface<0000012C31DFAA10>
DemoObject::GetComponent<0000012C31E08F70>(some string)
Hook_DemoInterface::GetComponent<0000012C31DFAA10>(some string)=1
DemoObject::GetPose<0000012C31E08F70>
Hook_DemoInterface::GetPose<0000012C31DFAA10>=<-1, 2, 3>
v=1, pos<-1, 2, 3>
DemoObject::~DemoObject<0000012C31E08F70>
Hook_DemoInterface::~Hook_DemoInterface<0000012C31DFAA10>
Got into an interesting problem while tried to call the overloaded function using conditional operator (just to avoid multiple if else condition)
class VirtualGpio
{
typedef enum
{
OUTPUT = 0xC7,
INPUT ,
DIRINVALID
}GpioDirection;
struct pinconfig
{
struct pinmap pin;
GpioPolarity plrty;
bool IsPullupCfgValid;
bool IsTriStCfgValid;
bool IsInputFilterValid;
GpioDirection dic;
gpiolistner fptr; // Callback function pointer on event change
};
};
class factory
{
public:
VirtualGpio *GetGpiofactory(VirtualGpio::pinconfig *cfg,VirtualGpio::GpioAccessTyp acc=VirtualGpio::Pin);
private:
int setCfgSetting(VirtualGpio::pinmap * const getpin, VirtualGpio::GpioDirection const data);
int setCfgSetting(VirtualGpio::pinmap * const getpin, bool const data);
};
int factory::setCfgSetting(VirtualGpio::pinmap * const getpin, VirtualGpio::GpioDirection const data)
{
cout << "It is a Direction overloaded" << endl;
}
int factory::setCfgSetting(VirtualGpio::pinmap * const getpin, bool const data)
{
cout << "It is a bool overloaded" << endl;
}
VirtualGpio* factory::GetGpiofactory(VirtualGpio::pinconfig *cfg,VirtualGpio::GpioAccessTyp acc)
{
VirtualGpio * io = new VirtualGpio();
printf("acc : 0x%X, pin : 0x%x, port : 0x%x\n",acc, cfg->pin.pinno, cfg->pin.portno);
printf("value of expression : 0x%x\n",((acc == VirtualGpio::Pin)? cfg->dic : ((cfg->dic == VirtualGpio::INPUT)?true :false))); <= this prints the right value
if(acc == VirtualGpio::Pin)
setCfgSetting(&cfg->pin,cfg->dic);
else if(cfg->dic == VirtualGpio::INPUT)
setCfgSetting(&cfg->pin,true);
else
setCfgSetting(&cfg->pin,false);
#if 0
if(setCfgSetting(&cfg->pin, ((acc == VirtualGpio::Pin)? cfg->dic : ((cfg->dic == VirtualGpio::INPUT)?true :false))) == ERROR)
{
printf("Error Setting the IO configuration for XRA\n");
}
else
printf("Set IO config successfully\n");
#endif
return io;
}
The commented part #if 0 in GetGpiofactory() is same as the above
multiple if-else-if-else block, but if I uncomment the #if0 part to #if
1, for all the possible inputs only bool version of the overloaded
function i.e setCfgSetting(VirtualGpio::pinmap * const getpin, bool
const data) is invoked.
below is my main code.
main()
{
static struct VirtualGpio::pinconfig cfg = {
.pin = {
.location = VirtualGpio::GPIO_ON_GPIOEXP1_TCI,
.pinno = 0,
.portno = -1
},
.plrty = VirtualGpio::active_high,
.IsPullupCfgValid = true,
.IsTriStCfgValid = true,
.IsInputFilterValid = true,
.dic = VirtualGpio::OUTPUT,
.fptr = NULL
};
factory fac;
fac.GetGpiofactory(&cfg);
}
Surprised, the overloaded function works well if I don't use the ternary operator instead use multiple if-else if-else blocks. curious to understand the reason.
That is because the ternary operator always evaluates to a single type. You can't "return" different types with this operator.
When the compiler encounters such an expression he tries to figure out whether he can reduce the whole thing to one type. If that's not possible you get a compile error.
In your case there is a valid option using bool as a type. Because cfg->dic is an enum type which is implicitly convertible to bool. If you would use and enum class your code would not compile anymore showing you what your actual problem is (example).
Also I don't really see what the advantage of this kind of code is. In my opinion it makes the code much harder to read. You could reduce your ifs to just one, if you're concerned about too many of them:
if(acc == VirtualGpio::Pin)
setCfgSetting(&cfg->pin,cfg->dic);
else
setCfgSetting(&cfg->pin, cfg->dic == VirtualGpio::INPUT);
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
Iam new to using Core Foundations. I want to use dictionary to store some key value pair. The value must be a pointer to a struct. This pointer is pointing to dynamically allocated buffer.
CFMutableDictionaryRef init_hash_table() {
return CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
}
This is used to create the dictionary and the return value is stored as global variable.
CFNumberRef
create_hash_key(int sd) {
return CFNumberCreate(NULL, kCFNumberIntType, &sd);
}
int
add_hash_entry(CFMutableDictionaryRef dict, int sd, void *pkt) {
CFNumberRef key = create_hash_key(sd);
CFDictionarySetValue(dict, key, pkt);
return 0;
}
When I execute this code, I get segfault. I see that pkt has a valid address and key seems to be created. Does anyone know how to assign a pointer to value part?
Program received signal EXC_BAD_ACCESS, Could not access memory.
Reason: KERN_INVALID_ADDRESS at address: 0x0000000000000011
0x00007fff8c9f339f in objc_msgSend_fixup ()
Any ideas?
The problem is the kCFTypeDictionaryValueCallBacks argument. From the documentation:
kCFTypeDictionaryValueCallBacks
Predefined CFDictionaryValueCallBacks structure containing a set of
callbacks appropriate for use when the values in a CFDictionary are
all CFType-derived objects.
So in your case, CFRetain() is called on the pointer when the value is added to the
dictionary. This causes the crash because the pointer does not point to a CoreFoundation
object.
You can create the dictionary with
CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, NULL);
instead, so that no "reference counting" will be done on the value.
Alternatively, you can wrap the pointer into a CFDataRef and put that into the
dictionary.
In both cases it is your responsibility that the pointer is still valid
when the value is retrieved from the dictionary later.
Here is a simple example how you could implement refcounting for your custom objects:
typedef struct {
int refcount;
int val;
} mystruct;
const void *myretain(CFAllocatorRef allocator, const void *value)
{
mystruct *p = (mystruct *)value;
p->refcount++;
return p;
}
void myrelease(CFAllocatorRef allocator, const void *value)
{
mystruct *p = (mystruct *)value;
if (p->refcount == 1)
free(p);
else
p->refcount--;
}
int main(int argc, const char * argv[])
{
mystruct *p = malloc(sizeof(*p));
p->refcount = 1;
p->val = 13;
CFDictionaryValueCallBacks vcb = { 0 , myretain, myrelease, NULL, NULL };
CFMutableDictionaryRef dict = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &vcb);
int sd = 13;
CFNumberRef key = CFNumberCreate(NULL, kCFNumberIntType, &sd);
CFDictionarySetValue(dict, key, p);
// refcount == 2
myrelease(NULL, p);
// refcount == 1
mystruct *q = CFDictionaryGetValue(dict, key);
// refcount is still 1, "GetValue" does not increment the refcount
CFRelease(dict);
// object is deallocated
return 0;
}