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

Reply via email to