Jeff,
This is off the top of my head, but I might accidentally type something useful
anyway, so I thought I'd try to respond.
First, I want to make sure I understand the problem. I'm assuming that your
application has lots of globals, such that you want to reduce the impact on the
dynamic heap by storing them in the storage heap. Or perhaps you want to segment
your global variable usage such that the ones you want to be persistant are
always stored in some record in the storage heap. Regardless, you want to store
your data there, and you want to make access to that data as transparent and
inexpensive as possible.
The first problem you're having is with writing two functions for each variable
you want in the storage heap: a getter and a setter. Faced with a problem
similar to this in the Palm OS Emulator (I frequently have to transfer data back
and forth between "emulated" memory and Poser's data space), I used the
pre-processor. I don't care what other's feel about using the pre-processor --
this was the only tool that worked for me.
Instead of:
SGlobals.Set( offsetof(SGlobalsStruct, bGoingBack), (Boolean)true);
Boolean bTmp = SGlobals.Get( offsetof( SGlobalsStruct, bGoingBack));
I see your writing a lot of code that looks like the following:
SET_GLOBAL_BOOL(bGoingBack, true);
Boolean bTmp = GET_GLOBAL_BOOL(bGoingBack);
First of all, this is a lot simpler to type; it's very minimal. Second, the
macros can expand to just about anything you want, including the instructions
you currently have.
I actually see those macros expanding as follows:
#define SET_GLOBAL_BOOL(field, value) \
SetGlobalBool(offsetof(SGlobalsStruct, field), value)
#define GET_GLOBAL_BOOL(field) \
GetGlobalBool(offsetof(SGlobalsStruct, field))
The two functions might then be implemented as:
void SetGlobalBool(size_t offset, Boolean value)
{
// Lock down the record handle:
char* pGlob = (char*) MemHandleLock(m_recH);
ErrFatalDisplayIf (!pGlob, "Unable to lock down globals record.");
Err err = DmWrite(pGlob, offset, &value, sizeof(Boolean));
ErrFatalDisplayIf(err, "Unable to mutate global.");
// Unlock the record handle, return the value
MemHandleUnlock(m_recH);
}
Boolean GetGlobalBool(size_t offset)
{
// Lock down the record handle:
char* pGlob = (char*) MemHandleLock(m_recH);
ErrFatalDisplayIf (!pGlob, "Unable to lock down globals record.");
Boolean bRet = *(Boolean*) &pGlob[offset];
// Unlock the record handle, return the value
MemHandleUnlock(m_recH);
return bRet;
}
You would then have a small set of functions and macros, two for each *type*,
not two for each *variable*. Actually, for scalar types, you could reduce that a
little bit more. Just define SetGlobalInt and SetGlobalInt to take the *size* of
the integer, and have the macros provide that information (and have the getter
macro cast the result to the appropriate type). That is:
#define SET_GLOBAL_BOOL(field, value) \
SetGlobalInt(offsetof(SGlobalsStruct, field), sizeof(Boolean), value)
#define GET_GLOBAL_BOOL(field) \
(Boolean) GetGlobalInt(offsetof(SGlobalsStruct, field), sizeof(Boolean))
void SetGlobalInt(size_t offset, size_t size, unsigned long value)
{
// Lock down the record handle:
char* pGlob = (char*) MemHandleLock(m_recH);
ErrFatalDisplayIf (!pGlob, "Unable to lock down globals record.");
char* valueP = &value;
if (size == 1)
valueP += 3;
else if (size == 2)
valueP += 2;
Err err = DmWrite(pGlob, offset, valueP, size);
ErrFatalDisplayIf(err, "Unable to mutate global.");
// Unlock the record handle, return the value
MemHandleUnlock(m_recH);
}
UInt32 GetGlobalInt(size_t offset, size_t size)
{
// Lock down the record handle:
char* pGlob = (char*) MemHandleLock(m_recH);
ErrFatalDisplayIf (!pGlob, "Unable to lock down globals record.");
UInt32 bRet = size == 1 ? *(unsigned char*) &pGlob[offset] :
size == 2 ? *(unsigned short*) &pGlob[offset] :
size == 4 ? *(unsigned long*) &pGlob[offset] :
0; // Error case
// Unlock the record handle, return the value
MemHandleUnlock(m_recH);
return bRet;
}
This takes care of the transparency and inexpesiveness -- but only in terms of
code size. It's still very expensive to in terms of execution speed. So the next
refinement might be to fetch data 1K at a time from the storage heap and cache
that locally. Then, if the next data you want is also in the 1K page, you can
retrieve if much faster. The PalmDebugger uses this technique when accessing the
remote device's memory over the serial cable.
Anyway, I hope I've understood your problem correctly and that this helps.
-- Keith