Xailer Wiki

El entorno de desarrollo definitivo en Xbase

Herramientas de usuario

Herramientas del sitio


en:migrar.de.xharbour.a.harbour

¡Esta es una revisión vieja del documento!


MIGRATION GUIDE TO HARBOUR

As we know, Harbour and xHarbour have an origin and a common basis. In fact, xHarbour is a fork or derived from Harbour, and since that incident occurred the two have walked through different paths, but in parallel. During all this time there have been occasions when some parts of xHarbour were ported into Harbour to maintain compatibility, and the reverse has also happened.

However, and although both are compatible compilers by 99%, there are some differences to be taken into account if we want that our programs written on Xailer for xHarbour will also function on Xailer for Harbour.

This paper aims to show and how to overcome those differences. It is based on the experience of migration Xailer sources of xHarbour to Harbour, so it should cover almost all cases that can be given to any programmer.

The list of differences is divided into three blocks, as affecting the PRG code, C code, or Xailer itself. Some of the differences are easily detected by the compiler, either PRG level or C-level, but others are not detected at compile time, making it more difficult to fix. This is indicated in each difference.

At the PRG level

  • There are no global variables, so there are no GLOBAL nor GLOBAL EXTERNAL sentences. The closest thing to the global variables are the public variables, which are created with the sentence PUBLIC and are declared in the modules which use them with the sentence MEMVAR. It is detected by the compiler.
  • The IN operator does not exist, use the operator $ instead, which is the Clipper standard. It is detected by the compiler.
  • On SWITCH sentences, change DEFAULT with OTHERWISE. On xHarbour, DEFAULT indicates the part of code to execute when none of the previous CASE statements were true. In Harbour, this clause uses the command OTHERWISE, and with the same functionality. It is detected by the compiler.
  • The sentences TRY / CATCH / ALWAYS / END: in Harbour has been emulated. You should not have any problem, but maybe you can get some minor behavior difference on extreme cases.
  • You can not use any object member as indexes on loops FOR / NEXT. This means, that code like this:
       FOR ::nCounterr := 1 TO 100
          ...

    should be modified by something like this:

       FOR n := 1 TO 100
          ::nCounter := n
          ...

    Detected by the compiler.

  • You can not use array indexed on string types. You must use the Substr() function to extract individual characters. This extension was made on xHarbour long time ago, although it brought some problems (run-time errors difficult to detect), it had greatest advantages on some cases. However, this was never implemented in Harbour. For example:
       nTotal := 0
       FOR n := 1 TO Len( cString )
          nTotal += Asc( cString[ n ] )
       NEXT

    should be changed to:

       nTotal := 0
       FOR n := 1 TO Len( cString )
          nTotal += Asc( Substr( cString, n, 1 ) )
       NEXT

    Although this construction is also possible:

       nSuma := 0
       FOR EACH cChar IN @cString
          nTotal += Asc( cChar )
          cChar := Upper( cChar ) 
       NEXT

    Not detected at compile-time, but yes at run-time.

  • Change At() with 3 parameters to hb_At(). In xHarbour, the At() function admits a third parameter which indicates the first position to start the search. In Harbour, the At() function remains exactly the same as in Clipper, with two parameters and when you need the third parameter you must use the hb_At() function. Detected by the compiler.
  • The Trim() function does not admit the third parameter to indicate extra characters to delete like TAB, CR and LF from the end of the string. We have added the XA_Trim() function to overcome this lack. Detected by the compiler.
  • Change ADel() with 3 parameters for hb_ADel(). On xHarbour, the ADel() function admits a third parameter to reduce the array size. Harbour ignores this third parameter and you should change to hb_ADel() or resize the array manually with the ASize() function. Not detected at compile-time nor at run-time but may clearly brake your code.
  • Function ASizeAlloc() does not exist, there is nothing similar to this, you just need to eliminate it from your code. Detected at link time.
  • Change HB_QWith() with :__WithObject(). In xHarbour the HB_QWith() function returns the active object used on a WITH OBJECT / END WITH statement. On Harbour there is no such function, but you may use the method __WithObject() from any object to get the same result. Detected at link time.
  • Change HB_EnumIndex() with <obj>:__enumIndex(). Inn xHarbour the function HB_EnumIndex() returns the current active index on a FOR EACH / NEXT loop. In Harbour there is no such function, to retrieve the same index loop value you can call the method __enumIndex() over the variable that stores the current loop value. For example:
       FOR EACH oControl IN ::aControls
          LogDebug( oControl:__enumIndex() )
       NEXT

    Detected at link time.

  • Algunas funciones de fecha tienen el prefijo hb_ en Harbour. Estas funciones son las siguientes: DateTime(), Hour(), Minute(), TToS(), SToT(), TToC() y CToT(). Sólo hay que añadir el prefijo hb_ delante del nombre de la función. Se detecta al enlazar.
  • No se pueden utilizar índices negativos en los arrays. Hay que utilizar ATail( array ) o array[ Len( array ) ]. En xHarbour se podían utilizar índices negativos en los arrays, lo que permitía acceder a elementos de la cola de dicho array. P.ej, aData[ -1 ] devolvía el último elemento del array. En Harbour no existe esta posibilidad, y no hay más remedio que utilizar ATail(), que devuelve el último elemento del array, o calcular el índice basándonos en la longitud del array. No se detecta al compilar, pero sí al ejecutar el programa.
Puede buscar dentro de un proyecto cualquier expresión que contenga un signo menos (-) dentro de unos corchetes con esta expresión regular: \[[^\]]*-[^\[]*\]

Con esto, obtendrá todos los posibles casos conflictivos. Lógicamente saldrán más casos, que no son incorrectos, pero ya deberá discriminarlos manualmente.

  • Al crear un objeto con la función de clase solamente, no se pasan los parámetros a su método New(). En xHarbour, si al construir un objeto se pasaban parámetros a su función de clase, estos valores se pasaban automáticamente al método New() del objeto. P.ej.,
       ::oBtn := TButton( Self )

    era equivalente a:

       ::oBtn := TButton():New( Self )

    Pero en Harbour no es así, y hay que llamar expresamente al método New() del objeto. En el caso de los objetos de Xailer es especialmente importante, puesto que el primer (y muchas veces el único) parámetro de New() es oParent, que podría quedar a Nil con el consiguiente cambio de comportamiento. No se detecta al compilar, y tampoco se produce ningún error al ejecutar el programa, pero produce cambios de comportamiento o errores que pueden ser más difíciles de detectar.

  • La validación del ámbito de los miembros de las clases es más estricta y correcta en Harbour. xHarbour daba por buenos algunos ámbitos de forma incorrecta, mientras que Harbour lo hace bien, y provoca que afloren errores que antes no se producían. P.ej., si desde TForm accedemos a una propiedad PROTECTED de un control, xHarbour lo permitía erroneamente, porque al derivar TForm también de TControl, creía que podía acceder a esa propiedad. Pero en realidad, esa propiedad pertenece a otro objeto (aunque sea de la misma jerarquía) que no es el formulario (pertenece al control) y por lo tanto debería producirse un error. Esta es la diferencia más difícil de solucionar. No se detecta al compilar, y aunque sí se generan errores en tiempo de ejecución, dependen de las condiciones. P.ej., un mismo miembro PROTECTED de una clase puede ser accedido desde un método de una clase hija, pero sólo si dicho miembro pertenece al objeto que hace la llamada. En la práctica, cuando se trate de miembros PROTECTED, podemos dar por bueno todo el código del tipo ::Propiedad o Self:Propiedad, mientras que las llamadas del tipo Objeto:Propiedad suelen ser incorrectas.
  • El ámbito PRIVATE en las clases funciona de forma completamente distinta en Harbour que en xHarbour. En ambos casos, este ámbito significa que ese miembro de la clase sólo puede ser accedido desde un método de la propia clase, y no desde fuera de la misma ni desde una clase hija. Pero además en Harbour, si se sobrecarga un miembro PRIVATE (es decir, se vuelve a declarar en una clase hija), se crea un nuevo miembro con el mismo nombre, pero en todo lo demás es completamente distinto al de su clase padre. Esto implica que cuando la clase padre cambia el valor de una propiedad PRIVATE, la clase hija no ve ese valor. Y lo mismo ocurre si la clase hija lo cambia. A todos los efectos son dos propiedades distintas, que no pueden ser accedidas desde fuera de la propia clase, aunque tengan el mismo nombre en la clase padre y en la clase hija. Esto no era así en xHarbour, que cuando se sobrecargaba una propiedad PRIVATE en una clase hija, se podía acceder a su valor tanto desde la clase padre como desde la hija.

A nivel C

  • hb_par???() y hb_stor???() no admiten el parámetro extra que permiten usar estas funciones en arrays. Cuando se usan con arrays hay que sustituirlas por hb_parv???() y hb_storv???(). Se detecta al compilar.
  • hb_parc() es de tipo const char *, en vez de char *, por lo que hay que hacer casting a (LPSTR) o declarar las variables como const char *. Se detecta al compilar.
  • hb_arrayGetCPtr() también es de tipo const char * por lo que se aplica exactamente lo mismo que a hb_parc(). Se detecta al compilar.
  • hb_parl() devuelve FALSE si el parámetro no es de tipo lógico (p.ej. numérico). En xHarbour devolvía TRUE si el parámetro era numérico y su valor era distinto de 0. Esto puede provocar errores lógicos o de comportamiento. No se detecta al compilar, ya que internamente en C, el tipo BOOL es un INT, ni genera errores en tiempo de ejecución, por lo tanto es difícil de detectar y corregir.
  • Hay que sustituir hb_itemPutCPtr() por hb_itemPutCLPtr(). Se detecta al compilar.
  • No existe hb_rectAdopt(), hay que cambiarla por hb_retc_buffer(). Se detecta al compilar.
  • No se puede utilizar una estructura HB_ITEM directamente. Siempre hay que usar un puntero de tipo PHB_ITEM y crear el item usando hb_itemNew( NULL ). Se detecta al compilar.
  • PHB_ITEM esta declarado como void *, por lo que no se pueden utilizar los miembros de la estructura HB_ITEM directamente. En su lugar, hay que utilizar las funciones del API de Harbour. P.ej., en vez de usar pItem–>type, hay que usar hb_itemType( pItem ). Se detecta al compilar.
  • Tampoco se pueden utilizar los miembros de otras estructuras de la VM de Harbour, como HB_DYNS, HB_SYMBOL, etc.. Esto implica que no se pueden utilizar llamadas del tipo hb_vmPushSymbol( hb_dynsymFindName( “METODO” )–>pSymbol ). En su lugar, hay que utilizar hb_vmPushDynSym( hb_dynsymFindName( “METODO” ) ). Es decir, en la práctica, hay que sustituir todas las llamadas a hb_vmPushSymbol() por hb_vmPushDynSym(). Se detecta al compilar.
  • No se puede utilizar XA_ObjSend() para asignar una propiedad con el valor Nil. En su lugar hay que utilizar la nueva función XA_ObjSendNil().
  • Las macros ISNIL(), ISNUM(), ISCHARACTER(), etc., han sido renombradas. En su lugar hay que utilizar los mismos nombres con el prefijo HB_ (p.ej. HB_ISNIL(), HB_ISNUM(), etc.). Se detecta al compilar.

Dentro de Xailer

  • Es necesario utilizar la distro de Harbour proporcionada por Xailer, debido a una modificación necesaria en el módulo classes.c para poder aplicar la ampliación del motor de objetos de Xailer. La modificación consiste solamente en la adición de dos pequeñas funciones al final de dicho módulo.
  • Ahora, los eventos se guardan en una DATA interna con ámbito PROTECTED y con el mismo nombre del evento con una F delante, al igual que siempre se han guardado los valores reales de las propiedades. El contenido real de dicha DATA no se puede utilizar ni asignar de ninguna forma, puesto que representa punteros de memoria, y su manipulación sólo puede provocar GPFs. P.ej., el evento OnClick, se guarda en la DATA interna FOnClick de ámbito PROTECTED. A efectos prácticos, en lo único que afecta es en que hay que evitar nombrar a algún miembro de una clase con el mismo nombre de un evento con una F delante.
  • Cuando se utilice la cláusula AS en una propiedad, y ésta sea de tipo array de objetos, pondremos los caracteres [] detrás del nombre de la clase. P.ej.:
       PROPERTY aForms AS TForm[]

    Esto es así por las limitaciones de la cláusula AS, que sólo admite los tipos básicos, y no clases concretas. P.ej., se puede utilizar AS NUMERIC, pero no AS TForm. Se ha modificado el motor de objetos para que se elimine completamente la cláusula AS con aquellos tipos que no sean básicos, y evitar errores posteriores. No obstante, si se incluyen los caracteres [] detrás del tipo, la cláusula AS se convierte a AS ARRAY.

  • Para evitar problemas con el funcionamiento del ámbito PRIVATE, se cambian todos los PRIVATE por PROTECTED.
  • SetKey es una palabra reservada. Ha habido que cambiar el nombre del método SetKey de THotkey por SetHotKey. No se detecta al compilar, pero provoca cambios de comportamiento.
en/migrar.de.xharbour.a.harbour.1349950711.txt.gz · Última modificación: 2022/02/07 17:03 (editor externo)

Herramientas de la página