Przemyslaw Czerpak wrote:
+ added new functions for string manipulation:
char * hb_itemLockReadCPtr( PHB_ITEM pItem, ULONG * pulLen );
char * hb_itemLockWriteCPtr( PHB_ITEM pItem, ULONG * pulLen );
void hb_itemUnLockCPtr( char * pszString );
It's recommended to use them instead of hb_itemGetCPtr().
Pointer to string buffer returned by hb_itemLockReadCPtr() will
be always valid even if source item will be cleared, destroyed or
overwritten until hb_itemUnLockCPtr() is called. Each locked string
has to be unlocked to avoid memory leaks. After unlocking the string
pointer cannot be longer used.
hb_itemLockWriteCPtr() works like hb_itemLockReadCPtr() but string
pointer returned by this function is writable so user can change
the body of string item. It's the _ONLY_ one way when it's possible.
Modifying string items using pointers returned by hb_parc() or
hb_itemGetCPtr() or extracted directly from HB_ITEM body is _FORBIDDEN_
and can cause unpredictable results (GPF when constant/readonly memory
pages are changed, changing many different items which share the same
memory buffer, etc.).
This is code illustrates how to use hb_itemLockReadCPtr()/
hb_itemUnLockCPtr() and it's also good example why hb_itemGetCPtr()
is very danger function and cannot be used in such case - if you
replace hb_itemLockReadCPtr() with hb_itemGetCPtr() and remove
hb_itemUnLockCPtr() then you will have buggy code.
HB_FUNC( MYFUNC )
{
PHB_ITEM pObject = hb_param( 1, HB_IT_OBJECT )
if( pObject )
{
char * pszName1, * pszName2;
PHB_ITEM pResult;
pResult = hb_objSendMsg( pObject, "POP", 0 );
pszName1 = hb_itemLockReadCPtr( pResult, NULL );
pResult = hb_objSendMsg( pObject, "POP", 0 );
pszName2 = hb_itemLockReadCPtr( pResult, NULL );
if( pszName1 && pszName2 )
hb_retc_buffer( hb_xstrcpy( NULL,
"[", pszName1, "]-[", pszName2, "]", NULL ) );
hb_itemUnLockCPtr( pszName1 );
hb_itemUnLockCPtr( pszName2 );
}
}
Hi,
what are situations and rules to use hb_itemLockReadCPtr()? Pointer to C
string could be obtained not only by using hb_itemGetCPtr(). There is a
number of other methods, hb_parc() is an example.
HB_FUNC( MYFUNC2 )
{
char* szParam = hb_parc( 1 );
hb_vmDynSym( hb_dynsymFind( "SOMEOTHERFUNC" ) );
hb_vmPushNil();
hb_vmPushLocalByRef( 1 );
hb_vmDo( 1 );
... any operation on szParam is invalid ...
}
Well, a little bit artificial example, but it could be realistic if
hb_vm*() is hidden under call of some other function.
The problem of your example is that pResult is return item of hb_stack,
and it could be reused to store other items if we call some function
that operates on hb_stack, hb_objSendMsg() in this case.
If pResult would be owned not by hb_stack, but owned by MYFUNC, we'll
have no problem. Let's last line of hb_objSendMsg() be
return hb_itemNew( hb_stackReturnItem() );
instead of
return hb_stackReturnItem();
In this case MYFUNC owns pResult and is responsible to free it. MYFUNC
code would be:
pResult1 = hb_objSendMsg( pObject, "POP", 0 );
pszName1 = hb_itemGetCPtr( pResult1 );
pResult2 = hb_objSendMsg( pObject, "POP", 0 );
pszName2 = hb_itemGetCPtr( pResult2 );
if( pszName1 && pszName2 )
hb_retc_buffer( ... );
hb_itemRelease( pResult1 );
hb_itemRelease( pResult2 );
Locking idea is replaced by idea of owning, unlocking item is equivalent
to freeing it. This code looks a little more clear for me. BTW, this
approach to solve problem of reusing hb_stack's return item is used in
hb_itemDo().
The problem of MYFUNC2 is a little more complicated, because here we use
ExtendAPI and we change value of parameter, not a return value.
ExtendAPI does not operate on items and item could not be owned by
MYFUNC2, but a rule of thumb could be made: if function allows to change
it's parameters on hb_stack (ex., by passing it by reference), it should
make item's copy for itself:
PHB_ITEM pItem = hb_itemNew( hb_param( 1, HB_IT_STRING ) );
char* szParam = hb_itemGetCPtr( pItem );
hb_vmDynSym( hb_dynsymFind( "SOMEOTHERFUNC" ) );
hb_vmPushNil();
hb_vmPushLocalByRef( 1 );
hb_vmDo( 1 );
... some operation on szParam ...
hb_itemRelease( pItem );
Of cause arrays could be a source of nasty bugs:
char* szParam = hb_parc( 1, 1 );
since arrays are always a references.
I feel my letter is more like brainstorming before a sleep than a letter
of the ideas I really want to see implemented. Usage of
hb_stackReturnItem() is more fast than making a copy of item. In many
cases hb_stackReturnItem() is OK. Maybe we can use hb_stackReturnItem()
for internal code and hb_itemNew( hb_stackReturnItem() ) for public API.
I could be hb_itemLock*()/hb_itemUnLockCPtr() is the best (and optimal)
solution, but hb_parc( 1, 1 ) example shows, that 3rd party programmer
should have an idea about hb_stack, items, etc, and switch to ItemAPI if
any of called subfunctions can operate on hb_stack. I do not find an
easy way to force writing a safe code or find hidden bugs.
Best regards,
Mindaugas
_______________________________________________
Harbour mailing list
Harbour@harbour-project.org
http://lists.harbour-project.org/mailman/listinfo/harbour