Extns & Diffs

Harbour Extensions & Differences

Harbour has too many extensions and difference over other compatible compilers ( xHarbour, xBase++, CLIP, FlagShip ) and especially Clipper.

As some example for language extensions:
* Class generation and management.

Clipper only allowed creation of objects from a few standard
classes.

In Harbour, you can create your own classes–complete with
Methods, Instance Variables, Class Variables and Inheritance.
Entire applications can be designed and coded in Object Oriented
style.

* `@<FunctionName>()`

Returns the pointer (address) to a function.

The returned value is not useful to application-level programming, but
is used at a low-level to implement object oriented coding.
(Internally, a class method is a static function and there is no
symbol for it, so it is accessed via its address).

* Class HBGetList()

Object oriented support for GetLists management.

* ProcName() support for class Method names.

Class Methods can be retrieved from the call stack.

* Optional Compile Time *strong type* declaration (and compile time
*type mismatch* warnings)

Example: LOCAL/STATIC Var `AS` …

* The Harbour debugger provides new interesting classes:

– Class HBDbWindow() could be the foundation for a generic multi-platform

– Class HBDbInput()

– Class HBDbMenu() implement both pull-down and popup menus.

Differences in Short:

Compile time support for merging multiple .prg modules
New language statements
FOR EACH
WITH OBJECT / END[WITH]
SWITCH / [ CASE / [EXIT] / … ] OTHERWISE / END[SWITCH]
BEGIN SEQUENCE [ WITH <ERRBLOCK> ] … END SEQUENCE
Extended codeblocks
Hash arrays
References to variables stored in arrays
Passing array and hash items by reference
Passing object variables by reference
Detached locals and references
Declaration and initialization of variables
Functions with variable number of parameters
Hb_ArrayToParams() function
Macros with declared symbols
Macro messages
Multivalue macros
Using [] operator for string items
Negative indexes in [] operator used to access items from tail
Using one character length string as numeric value
New bit operators
IN, HAS, LIKE operators
Pre/post incrementation/decrementation and <op>= operators
Global / global external (global_extern)
DATETIME/TIMESTAMP values
Literal date and TIMESTAMP values
Extended literal string in compiler and macrocompiler
Symbol items and function references
OOP scopes
OOP and multiinheritance
OOP and private/hidden datas
OOP and class object/class messages
Typed object variables
Object destructors
Scalar classes
Runtime class modification
Array and string preallocation
DIVERT statement
Namespaces
Multi window GTs and runtime GT switching
Multi thread support
Thread local work areas and concurrent work area access
Harbour tasks and MT support in DOS
Background task
Codeblock serialization / deserialization
Native RDDs
Regular expressions
INET sockets
I18N (internationalization) support
ZLIB (compression/decompression)
Serial port support
Macro compiler
Compiler library
PP library
Lexer
Contrib libraries
Portability
C level compatibility
HBRUN / XBSCRIPT
HBMK2
Performance and resource usage

github text:

This text describes most important differences between Harbour and xHarbour
with some references to Clipper and other compatible compilers like Xbase++,
CLIP, FlagShip.

Many thanks to Pritpal and Viktor for updating this text.

I hope that it will be updated in the future also by xHarbour developers,
It describes status of both compiler at the end of October 2009:
   Harbour 2.0.0 beta3 (revision 12788)
   xHarbour 1.2.1 (revision 6629)

Przemek,

(Przemyslaw Czerpak, druzus /at/ priv.onet.pl)

COMPILE TIME SUPPORT FOR MERGING MULTIPLE .PRG MODULES

Clipper allows to compile many .prg modules included by @<name>.clp
and/or SET PROCEDURE TO ... / DO ... [ WITH ... ] into single output
object. In such compilation it supports, separated for each .prg file,
file wide declarations when -n switch is used and allows to use more
then one static function with the same name if each of them is declared
in different .prg modules. This code illustrates such situation:

      /***** t1. prg *****/
      static s  := "t01:s"
      static s1 := "t01:s1"
      proc main()
         ? "===="
         ? s, s1
         p1();p2();p3()
         ? "===="
         do t2
         ? "===="
      return
      proc p1        ; ? "t01:p1"
      static proc p2 ; ? "t01:p2"
      static proc p3 ; ? "t01:p3"
      init proc pi ; ? "init t01:pi"
      exit proc pe ; ? "exit t01:pe"

      /***** t2. prg *****/
      static s  := "t02:s"
      static s2 := "t02:s2"
      proc t2()
         ? s, s2
         p1();p2();p3()
      return
      static proc p1 ; ? "t02:p1"
      proc p2        ; ? "t02:p2"
      static proc p3 ; ? "t02:p3"
      init proc pi ; ? "init t02:pi"
      exit proc pe ; ? "exit t02:pe"

It needs -n switch for file wide declarations and uses static/init/exit
functions with the same names but declared in different modules.
It can be compiled and linked by Clipper and Harbour, i.e.:
      cl t1.prg /n/w/es2
or:
      hbmk2 t1.prg -n -w -es2
and then executed.

xHarbour does not have such functionality and above code has to be adopted
to work with this compiler. Additionally it does not work well with case
sensitive file systems what can be seen in above example where it converts
"t1" to "T1" and then tries to include "T1.prg".

For users which have old Clipper code written for DOS file systems with
mixed upper and lower letters in file names used directly or indirectly
by procedure name, Harbour provides compile time switches which enable
automatic file name conversions for all files opened by compiler:
      -fn[:[l|u]|-]    set file name casing (l=lower u=upper)
      -fd[:[l|u]|-]    set directory casing (l=lower u=upper)
      -fp[:<char>]     set path separator
      -fs[-]           turn file name space trimming on or off (default)
This functionality is also local to Harbour so cannot be used with xHarbour
as workaround for above problem though it should be easy to add it to this
compiler in the future.

Both compilers support runtime switches for file name conversions.
      SET FILECASE LOWER | UPPER | MIXED
      SET DIRCASE LOWER | UPPER | MIXED
      SET DIRSEPARATOR <cDirSep>
      Set( _SET_TRIMFILENAME, <lOnOff> )
which can be used in programs not intended to work with different
file system(s) and with different OS(s).

NEW LANGUAGE STATEMENTS

1. FOR EACH
   Harbour supports all xHarbour functionality and it offers also additional
   features which are not available in xHarbour.
   a) it allows to iterate more then one variable
         FOR EACH a, b, c IN aVal, cVal, hVal
            ? a, b, c
         NEXT
   b) it allows to set descending order by DESCEND flag, f.e.:
         FOR EACH a, v IN aVal, cVal DESCEND
            ? a, b
         NEXT
   c) it has native support for hashes:
         FOR EACH x IN { "ABC" => 123, "ASD" => 456, "ZXC" => 789 }
            ? x, "@", x:__enumKey()
         NEXT
   d) it allows to assign string items, f.e.:
         s := "abcdefghijk"
         FOR EACH c IN @s
            IF c $ "aei"
               c := Upper( c )
            ENDIF
         NEXT
         ? s      // AbcdEfghIjk
   e) it gives OOP interface to control enumerator variables what
      is very important when more then one variable is iterated or
      when FOR EACH is called recursively, f.e.:
         hVal := { "ABC" => 123, "ASD" => 456, "ZXC" => 789 }
         FOR EACH x IN hVal
            ? x:__enumIndex(), ":", x:__enumKey(), "=>", x:__enumValue(), ;
              "=>", x:__enumBase()[ x:__enumKey() ]
         NEXT
   f) it gives very flexible OOP mechanism to overload FOR EACH behavior
      for user defined classes adding to above enumerator methods also
      __enumStart(), __enumStop(), __enumSkip() methods what allows to
      implement many different enumeration algorithms depending on used
      data
   g) it does not have any hardcoded limitations for recursive calls
      (it's limited only by available memory and HVM stack size), f.e.:
         proc main()
            p( 0 )
         return
         proc p( n )
            local s := "a", x
            ? n
            if n < 1000
               for each x in s
                  p( n + 1 )
               next
            endif
         return
   In xHarbour there is function hb_enumIndex() which is supported by
   Harbour in XHB library.

2. WITH OBJECT / END[WITH]
   In Harbour it does not have any hardcoded limitations for recursive
   calls (it's limited only by available memory and HVM stack size), f.e.:
      proc main()
         p( 0 )
      return
      proc p( n )
         ? n
         if n < 1000
            with object n
               p( n + 1 )
            end
         endif
      return
   It also uses OOP interface just like FOR EACH, so it's possible to
   use :__withObject() to access / assign current WITH OBJECT value.
   In xHarbour there are functions hb_QWith(), hb_WithObjectCounter()
   and hb_ResetWith() which are supported by Harbour in XHB library.

3. SWITCH / [ CASE / [EXIT] / ... ] OTHERWISE / END[SWITCH]
   In Harbour it uses jump table with predefined values what gives
   significant speed improvement in comparison to sequential PCODE
   evaluation just like in DO CASE statements.
   In xHarbour SWITCH does not use such jump table and generated
   PCODE is similar to the one used for DO CASE or IF / ELSEIF
   and only the main switch value calculation is optimized and
   reused for all statements so speed improvement is relatively
   small.
   In xHarbour instead of OTHERWISE the DEFAULT clause is used.
   As SWITCH values Harbour supports integer numbers and strings, f.e.:
      switch x
         case 1         ; [...]
         case 10002     ; [...]
         case "data"    ; [...]
         otherwise      ; [...]
      endswitch
   xHarbour supports only integer numbers and one character length strings
   like "A", "!", "x", " ", ...

4. BEGIN SEQUENCE [ WITH <errBlock> ]
   [ RECOVER [ USING <oErr> ] ]
   [ ALWAYS ]
   END SEQUENCE

   It's unique to Harbour. In xHarbour limited version of above statement
   exists as:
      TRY
      [ CATCH [<oErr>] ]
      [ FINALLY ]
      END
   TRY gives exactly the same functionality as:
      BEGIN SEQUENCE WITH {| e | Break( e ) }

With the exception to SWITCH implementation, in all other statements
described above, xHarbour causes performance reduction in PCODE evaluation
even if user does not use them at all. In Harbour they are implemented in
different way which does not cause any overhead and slowness for other code.

EXTENDED CODEBLOCKS

Both compilers support compile time extended codeblocks which allow
to use statements but with a little bit different syntax. Harbour uses
standard Clipper codeblock delimiters {}, f.e.:
      ? Eval( {| p1, p2, p3 |
                ? p1, p2, p3
                return p1 + p2 + p3
              }, 1, 2, 3 )
and xHarbour <>, f.e.:
      ? Eval( <| p1, p2, p3 |
                ? p1, p2, p3
                return p1 + p2 + p3
              >, 1, 2, 3 )

In Harbour extended codeblocks works like nested functions and supports
all function attributes, f.e. they can have own static variables or
other declarations which are local to extended codeblocks only and
do not effect upper function body.
In xHarbour the compiler was not fully updated for such functionality
and extended codeblocks were added to existing compiler structures what
causes that not all language constructs work in extended codeblocks
and creates a set of very serious compiler bugs, f.e., like in this code
with syntax errors but which is compiled by xHarbour without even single
warning giving unexpected results at runtime:
      #ifndef __XHARBOUR__
         #xtranslate \<|[<x,...>]| => {|<x>|
         #xcommand > [<*x*>]       => } <x>
      #endif
      proc main()
         local cb, i
         for i:=1 to 5
            cb := <| p |
                     ? p
                     exit
                     return p * 10
                  >
            ?? Eval( cb, i )
         next
      return
It's possible to create many other similar examples which are mostly
caused by missing in the compiler infrastructure for nested functions
support.
This can be fixed if someone invest some time to clean xHarbour compiler.

HASH ARRAYS

Both compilers have support for hash arrays. They are similar to
normal arrays but also allow to use non integer values as indexes
like string, date, non integer number or pointer (in Harbour) items.
They can be created using => for list of keys and values enclosed
inside {}, f.e.:
      hVal := { "ABC"    => 123.45, ;
                100.1    => Date(), ;
                100.2    => 10,     ;
                100      => 5,      ;
                Date()-1 => .t. }
and then items can be accessed using [] operator, f.e.:
      ? hVal[ "ABC" ]      // 123.45
      ? hVal[ 100 ]        // 5
      ? hVal[ Date()-1 ]   // .t.
      ? hVal[ 100.2 ]      // 10
      ? hVal[ 100.1 ]      // Date()

By default hash items in both compiler support automatic adding new elements
on assign operation. It can be disabled using one of hash item functions.
Harbour has additional extension which allows to enable auto-add with default
values also for access operation and reference operator. It also supports
well passing hash array items by reference and has some other minor
extensions.

xHarbour does not support auto-add on access or reference operations and
passing hash array items by reference does not work (see passing array and
hash item by reference).

Both compilers have set of functions to make different operations on hash
arrays which give similar functionality. In Harbour they use HB_H prefix
(f.e. hb_HScan()) in xHarbour H prefix (f.e. HScan())

xHarbour has additional functionality which can be enabled for each hash
array using HSetAACompatibility() function. It's an index where is stored
information about the order in which items were added to hash array and
set of functions with HAA prefix to operate on hash array items using this
index instead of real position in hash array, i.e. haAGetValueAt() which
works like HGetValueAt().
In Harbour such functionality also exists and is enabled by default in all
hash array but the internal implementation is completely different. Harbour
does not emulate associative arrays by special index which keeps assign order
but it uses real natural order in hash array. It means that associative array
indexes are equal to regular hash indexes so it does not need any translation
between them and Harbour users can use regular hash array functions for
associative array indexes. So separated functions for accessing by other
index are not necessary. The Harbour implementation is also more efficient
because it does not introduce linear index updating each time new key is
added to hash array.
Harbour emulates HAA*() xHarbour functions in XHB library but only for
compatibility with existing xHarbour code.
This functionality can be disabled for each hash array in Harbour by
hb_HKeepOrder() function which sorts key-value pairs in hash array and
removes the access index.

REFERENCES TO VARIABLES STORED IN ARRAYS

In xHarbour the behavior of references stored in array is reverted in
comparison to Clipper or Harbour.
In Clipper and Harbour VM executing code like:
         aVal[ 1 ] := 100
clears unconditionally 1st item in aVal array and assigns to it value 100.
xHarbour checks if aVal[ 1 ] is an reference and in such case it resolves
the reference and then assign 100 to the destination item.
On access Clipper and Harbour VM executing code like:
         x := aVal[ 1 ]
copy to x the exact value stored in aVal[ 1 ]. xHarbour checks is aVal[ 1 ]
is an reference and in such case it resolves the reference and then copy to x
the value of reference destination item.
It can be seen in code like:
      proc main
         local p1 := "A", p2 := "B", p3 := "C"
         ? p1, p2, p3
         p( { @p1, p2, @p3 } )
         ? p1, p2, p3

      proc p( aParams )
         local x1, x2, x3

         x1 := aParams[ 1 ]
         x2 := aParams[ 2 ]
         x3 := aParams[ 3 ]

         x1 := Lower( x1 ) + "1"
         x2 := Lower( x2 ) + "2"
         x3 := Lower( x3 ) + "3"

Harbour and Clipper shows:
      A B C
      a1 B c3
but xHarbour:
      A B C
      A B C

It's not Clipper compatible so in some cases it may cause portability
problems. F.e. code like above was used in Clipper as workaround for
limited number of parameters (32 in Clipper). But it allows to directly
assign items of arrays returned by hb_AParams() and updating corresponding
variables passed by references (see functions with variable number of
parameters below).
Anyhow the fact that xHarbour does not have '...' operator which can
respect existing references in passed parameters and does not support
named parameters in functions with variable number of parameters causes
that reverted references introduce limitation, f.e. it's not possible
to make code like:
   func f( ... )
      local aParams := hb_AParams()
      if Len( aParams ) == 1
         return f1( aParams[ 1 ] )
      elseif Len( aParams ) == 2
         return f2( aParams[ 1 ], aParams[ 2 ] )
      elseif Len( aParams ) >= 3
         return f3( aParams[ 1 ], aParams[ 2 ], aParams[ 3 ] )
      endif
   return 0

which will respect references in parameters passed to f() function.

PASSING ARRAY AND HASH ITEMS BY REFERENCE

Harbour supports passing array and hash items by reference, f.e.:
      proc main()
         local aVal := { "abc", "klm", "xyz" }, ;
               hVal := { "qwe"=>"123", "asd"=>"456", "zxc"=>"789" }
         ? aVal[1], aVal[2], aVal[3], hVal["qwe"], hVal["asd"], hVal["zxc"]
         p( @aVal[2], @hVal["asd"] )
         ? aVal[1], aVal[2], aVal[3], hVal["qwe"], hVal["asd"], hVal["zxc"]

      proc p( p1, p2 )
         p1 := '[1]'
         p2 := '[2]'

Compiled by Harbour above code shows:
      abc klm xyz 123 456 789
      abc [1] xyz 123 [2] 789

In xHarbour only passing array items by reference works but does not work
passing hash items by reference though it does not generate either
compile time or run time errors so the above code can be compiled and
executed but it shows:
      abc klm xyz 123 456 789
      abc [1] xyz 123 456 789

PASSING OBJECT VARIABLES BY REFERENCE

Both compilers support passing object variables by reference though this
functionality in xHarbour is limited to pure instance or class variables
only and does not work for SETGET methods. In Harbour it works correctly.
This code illustrates the problem:

      proc main()
         local oBrw := TBrowseNew()
         ? oBrw:autoLite
         oBrw:autoLite := !oBrw:autoLite
         ?? "=>", oBrw:autoLite
         p( @oBrw:autoLite )
         ?? "=>", oBrw:autoLite

      proc p( x )
         x := !x

Harbour prints:
      .T.=> .F.=> .T.
but xHarbour prints:
      .T.=> .F.=> .F.
without generating any compile or run time errors.

DETACHED LOCALS AND REFERENCES

When local variables are used in codeblocks then it's possible that
the codeblocks will exist after leaving the function when they were
created. It's potentially very serious problem which have to be resolved
to avoid internal VM structure corruption. In Clipper, Harbour and
xHarbour special mechanism is used in such situation. Local variables
are "detached" from VM stack so they are still accessible after leaving
the function, f.e.:
      proc make_cb()
         local n := 123
         return {|| ++n  }
We call such variables "detached locals".
Here there are two important differences between Clipper and [x]Harbour.
In Clipper variables are detached when function exits (returns) and it
does not know which variables were used but simply detach whole local
variable frame from VM stack. It's very important to know that because
it can be source of serious memory problems in OS like DOS.
This simple code illustrates it:

      // link using RTLINK and run with //e:0 //swapk:0
      // repeat test second time with additional non empty parameter <x>
      #define N_LOOPS 15
      #xcommand FREE MEMORY => ? 'free memory: ' + ;
                                 AllTrim( Str( Memory( 104 ) ) )
      proc main( x )
         local n, a
         a := Array( N_LOOPS )
         FREE MEMORY
         for n := 1 to N_LOOPS
            a[n] := f( x )
            FREE MEMORY
         next
      return
      func f(x)
         local cb, tmp, ref
         tmp := Space( 60000 )
         if Empty( x )
            cb := {|| .t. }
         else
            cb := {|| ref }
         endif
      return cb

If you execute above program with non empty parameter then 'tmp' variable
is detached with codeblock which uses 'ref' variable and not released as
long as codeblock is still accessible. It means that in few iterations
all memory are allocated and program crashes. Clipper's programmers should
know that and be careful when use detached local and if necessary clear
explicitly other local variables before returning from the function by
setting NIL to them.
In Harbour and xHarbour only variables explicitly used in codeblocks
are detached and detaching is done when codeblock is created and original
local variables are replaced by references. It is possible because Harbour
and xHarbour support multilevel references chains so it works correctly
also for local parameters passed be reference from parent functions.
In Clipper only one level references are supported what creates second
important differences. When Clipper detaches frame with local parameters
then it has to unreference all existing references breaking them. This code
illustrates it:

      proc main()
         local cb, n := 100
         mk_block( @cb, @n )
         ? "after detaching:"
         ? Eval( cb ), n
      return
      proc mk_block( cb, n )
         n := 100
         cb := {|| ++n }
         ? "before detaching:"
         ? Eval( cb ), n
      return

Above code compiled by Clipper shows:

      before detaching:
             101        101
      after detaching:
             102        101

so after detaching the references to 'n' variable is broken and codeblocks
access his own copy of this variables.
In Harbour it works correctly so the results are correct and it shows:

      before detaching:
             101        101
      after detaching:
             102        102

In xHarbour ( for unknown to me reasons ) Clipper bug is explicitly emulated
though it was possible to fix it because xHarbour inherited from Harbour
the same early detaching mechanism with multilevel references so just like
in Clipper xHarbour programmers have to carefully watch for possibly broken
references by detached locals and add workarounds for it if necessary.

DECLARATION AND INITIALIZATION OF VARIABLES

Clipper parses variable declaration in a little bit different way then
Harbour and xHarbour. It makes it in two passes. In first pass it collects
names and scope of all declared variables and then in second pass this
information is available during variable initialization. This can be
illustrated by the following example:

      /*** tst.prg ***/
      proc main()
         local cb := {|| QOut( n + 5 ), QOut( f ) }  // (*)
         field f in table
         local n := 10
         Eval( cb )
      return

In the line which initializes cb code (*) we are using local variable n
and field f. Both are declared below the line in which codeblock is
initialized anyhow Clipper does not recognize it as undeclared variables
and uses their later declarations. If you compile above code using Clipper
with -n -w -es2 switches, i.e.
   cl tst -n -w -es2
then it's compiled without any compile time warnings or errors. Then
during execution it shows 15 for the first QOut() function call and
generates runtime error
   Error BASE/1002  Alias does not exist: TABLE
for the second QOut() call. It means that it correctly recognized scope
of both variables and also bound alias TABLE with field F though it was
declared one line below codeblock initialization.

In fact Clipper probably does not make two passes but parsing declarations
which have to be at the beginning of function or module it stores names of
variables which should be initialized with the initialization expressions.
Then when all declarations are processed for each line with declared and
initialized variables it generates code which pushes on VM stack results
of initialization expressions and then code which pops them initializing
variables. As result in Clipper this code cannot work:
      local x := 10, y := x + 2
because Clipper generate PCODE like:
      push 10
      push x
      push 2
      add
      pop y
      pop x
but this code:
      local x := 10
      local y := x + 2
works correctly because declarations were in separated lines and in such
case Clipper generates PCODE like:
      push 10
      pop x
      push x
      push 2
      add
      pop y

In Harbour and xHarbour all variables are declared in the moment when they
are processed. It means that during compilation of above example using
      harbour tst -n -w -es2
both compilers generate compile time warnings:
      tst.prg(2) Warning W0001  Ambiguous reference 'N'
      tst.prg(2) Warning W0001  Ambiguous reference 'F'
but it also means that in Harbour and xHarbour it's possible to write code
like:
      proc main()
         local x := 10, y := x + 2
         ? x, y
      return
and unlike Clipper both compilers generates correct PCODE which shows
         10       12
Maybe in the future we add support for Clipper compatible local variable
initialization covered by -kc Harbour compiler switch.

Xbase++ uses mixed behavior. Just like Clipper it stores variables with
initialization expressions but then it generates slightly different code
initializing variables one by one without line groping like in Clipper.

Please also note that in Clipper PRIVATE and PUBLIC declarations are
executable statements so they are not used as declarations by
Clipper compiler even if -a compiler switch is used. So when we talk
about initialization then it means that we are talking about LOCAL
variables. STATIC variables are initialized in different way at
application startup so cannot use local variables as initializers though
due to bug in Clipper in some cases compiler can accept local variables
in such context and then it may cause VM crash or error at runtime,
i.e. this code:

      proc main()
         local n
         static s := {|| n }
         Eval( s )
      return

is cleanly compiled by Clipper and Xbase++ but it causes RTE in
Clipper and FATAL ERROR LOG in Xbase++.
Harbour and xHarbour correctly report compile time error for it.

FUNCTIONS WITH VARIABLE NUMBER OF PARAMETERS

Both compilers support them though xHarbour is limited to all parameters
and does not support unnamed parameters. In Harbour you can declare some
named parameters and then unnamed just like in many other languages, f.e.:
      func f( p1, p2, p3, ... )
The unnamed parameters can be used in different statements passing them
by '...' operator, f.e. as array items:
      proc main()
         AEval( F( "1", "2", "A", "B", "C" ), {|x, i| QOut( i, x ) } )
      func f( p1, p2, ... )
         ? "P1:", p1
         ? "P2:", p2
         ? "other parameters:", ...
         return { "X", ... , "Y", ... "Z" }
or as array indexes:
      proc main()
         local a := { { 1, 2 }, { 3, 4 }, 5 }
         ? aget( a, 1, 2 ), aget( a, 2, 1 ), aget( a, 3 )
      func aget( aVal, ... )
      return aVal[ ... ]

or as function parameters:
      proc main()
         info( "test1" )
         info( "test2", 10, Date(), .t. )
      proc info( msg, ... )
         QOut( "[" + msg +"]: ", ... )

The '...' operator saves references when push parameters and it can be
used also in codeblocks, f.e.:
      bCode := {| a, b, c, ... | QOut( a, b, c ), QOut( "[", ..., "]" ) }

All parameters can be accessed also using hb_AParams() function but
in xHarbour it works correctly only for functions which does not use
any local parameters or declared with variable number of parameters
or when number of declared parameters is not smaller then number of
passed parameters. This code illustrates it:
      proc main()
         p1("A","B","C")
         p2("A","B","C")
         p3("A","B","C")
         p4("A","B","C")
         p5("A","B","C")
      proc p1
         ? ProcName()+"(), parameters:", PCount()
         AEval( hb_AParams(), {|x,i| QOut(i,"=>",x) } )
      proc p2
         local l
         ? ProcName()+"(), parameters:", PCount()
         AEval( hb_AParams(), {|x,i| QOut(i,"=>",x) } )
      proc p3(x)
         ? ProcName()+"(), parameters:", PCount()
         AEval( hb_AParams(), {|x,i| QOut(i,"=>",x) } )
      proc p4(...)
         ? ProcName()+"(), parameters:", PCount()
         AEval( hb_AParams(), {|x,i| QOut(i,"=>",x) } )
      proc p5(a,b,c,d,e)
         ? ProcName()+"(), parameters:", PCount()
         AEval( hb_AParams(), {|x,i| QOut(i,"=>",x) } )

In xHarbour it's only possible to declare all parameters as unnamed, f.e.:
      func f( ... )
and then access them using hb_AParams() or PValue() (in Harbour it's called
hb_PValue()) function. There is no support for named parameters and ...
operator.

In xHarbour due to reverted behavior of references stored in array items
assign operation to items in array returned by hb_AParams() changes
corresponding parameters passed by reference. It does not happen in
Harbour where item references stored in arrays work like in Clipper.

hb_ArrayToParams() FUNCTION

Harbour has special function which allows to convert array into
list of items which can be used as function parameters, array
values or array indexes in the same way as '...' operator:
   hb_ArrayToParams( <aValue> ) -> <aValue>[ 1 ] [, <aValue>[ N ] ]
i.e.:
      proc main( ... )
         local aParams := hb_AParams(), n
         /* remove parameters starting with "--" */
         n := 1
         while n < Len( aParams )
            if Left( aParams[ n ], 2 ) == "--"
               hb_ADel( aParams, n, .t. )
            else
               ++n
            endif
         enddo
         ? "Public parameters:", hb_ArrayToParams( aParams )
      return

Note for Clipper users: 'hb_ADel( aParams, n, .t. )' in above example
works like 'ADel( aParams, n ); ASize( aParams, Len( aParams ) - 1 )'.

This functionality is unique to Harbour and xHarbour does not support
hb_ArrayToParams() or similar function.

MACROS WITH DECLARED SYMBOLS

In Clipper all constant strings in PRG code with "&" character inside
are preprocessed at runtime by macro compiler. It checks if characters
after "&" are valid variable name (starts with "_" or letter and then
mix of "_", letters and digits until first different character) and if
yes and such private or public variable exists and it contains string
then the original string is modified and &<varname>[.] is substituted
by variable contents, i.e.
   private var := "ABC"
   ? "[&var-rest][&var.rest]" // prints: [ABC-rest][ABCrest]
Please note that "." at the end of variable name has special meaning.
It can be used to mark end of macro variable name and is eaten during
substitution. This feature works only for memvars (private and public)
variables. If Clipper detects that variable declared as local, static
or field is used in such context then it generates compile time error,
i.e.:
   local var := "text"
   ? "&var"

   clipper tst.prg
   [...]
   Compiling T48.PRG

   TST.PRG(2) Error C2081  Macro of declared symbol: '&OK'

By default Harbour is Cl*pper compatible and also generates such
compile time error (E0042).
Many people do not know about this functionality in Clipper and Harbour
and are quite often surprised when hearing about it.
Harbour offers two compiler switches to control this feature. It can be
completely disabled by -km compiler switch.
   -km => turn off macro-text substitution
When this switch is used for constant strings with "&" character inside
faster code is generated which does not activate macro-compiler at runtime
so they are taken as is. This switch does not affect real macros.
It only changes the runtime behavior of strings constants with "&"
character inside (macro-text).
The second switch -kd extends above functionality and allows to use
macro-texts and macros with declared symbols:
   -kd => accept macros with declared symbols
It means that also fields, local and static variables can be used in
macro-texts and macros.
The above example compiled with -kd switch does not generate compile
time error and final application shows "text" on the screen.
When -km switch is used then "&var" is shown by final application.

Harbour supports macro expansion for expressions with declared symbols
also for codeblocks when -kd is used. It allows to write code like:
   cbVar := {|| &cLocal + cPrivate }
or:
   cbVar := {|| &cLocalPref.func&cPriv1( cPriv2 ) }
or:
   ? &cLocalPref.func&cPriv1( cPriv2, &cStatic )
etc.
If it's possible then for macro-codeblocks Harbour compiler tries to
generate early eval code in which macros are expanded when codeblock
is created. Otherwise macros are expanded each time codeblock is
evaluated.
In xHarbour macro-text substitution for pure strings is always enabled
like in Clipper but it does not detect situation when fields, local
or static variables are used in macro-texts so it does not generate
compile time errors in such case.
xHarbour has similar to -kd extension always enabled but limited to
macro variables used inside code blocks and it works only if other
macros are not used in the same expression. When more complicated
examples are used xHarbour compiler generates broken code which
causes RTE or GPF during execution. It also does not support
codeblocks which contain mixed macros and refuses to compile such
code.

This example illustrates macros with declared symbols.

      #ifndef __XHARBOUR__
         #pragma -kd+
      #endif
      proc main()
         memvar nPrivate
         memvar cPriv1, cPriv2
         local cbVar1, cbVar2
         local cLocal := "10", cLocalPref := "hb_"
         static cStatic := "'123xyz'"
         private nPrivate := 10000
         private cPriv1 := "test1", cPriv2 := "abc"
      #ifndef __XHARBOUR__
         cbVar1 := {|| &cLocal + nPrivate + Val( &cStatic ) }
         cbVar2 := {|| &cLocalPref.func&cPriv1( cPriv2, &cStatic ) }
         ? Eval( cbVar1 )
         ? Eval( cbVar2 )
      #endif
         ? &cLocal + nPrivate + Val( &cStatic )
         cLocal := "upp"
         ? &cLocal.er( &cStatic )
         ? hb_funcTest1( cPriv2, &cStatic )
         ? &cLocalPref.func&cPriv1( cPriv2, &cStatic )
         ? &cLocalPref.func&cPriv1( cPriv2, &cStatic )
      return

      func hb_funcTest1( s1, s2 )
      return s1 + ":" + s2

Above code can be compiled by xHarbour but it will not work exploiting
some problems in this compiler and generated code.

This feature can be useful also in porting some other xBase compatible
code to Harbour because some compilers just like xHarbour accepted
in some limited way officially unsupported syntax with macros using
declared symbols.

MACRO MESSAGES

Both compilers Harbour and xHarbour support macros as messages.
Clipper does not. This example shows such macro messages usage:

      proc main()
         memvar var
         local o := ErrorNew(), msg := "cargo"
         private var := "CAR"

         o:&msg := "<cargo>"
         o:&var.go += "<value1>"
         o:&msg += "<value2>"
         o:&( msg ) += "<value3>"
         o:&( Upper( msg ) ) += "<value4>"

xHarbour does not support macro messages in assignment context.
Older xHarbour versions for macro messages with <op>= operators
or pre/post incrementation/decrementation GPF at runtime and
current ones compile above PRG code without any errors but
generate broken PCODE which may cause different side effects
(RTE, silent wrong processing or even memory corruption) - it
depends on the used context.

MULTI-VALUE MACROS

In the early Harbour days was added basic support for multi-value macros
which can be evaluated to list of values, f.e.:
      ? &("1,2,3")
should show:
      1,  2,  3
The implementation of this extension was not accepted by many of Harbour
developers and it was one of the main reasons of the xHarbour fork.
In Harbour it was later fully removed and implemented from scratch using
different internal algorithms and structures. Now Harbour supports multi-value
macros in code like:
      proc main()
         local s1 := "'a', 'b', 'c'", s2 := "1,3", a
         ? &s1
         a := { { "|", &s1, 'x', &s2, 'y' }, 'x', &s2 }
         ? "a[1] items:"
         AEval( a[ 1 ], {| x, i | QOut( i, x ) } )
         ? "a["+s2+"] =>", a[ &s2 ]
      return

xHarbour which inherited the original implementation after over 6 years
still cannot execute correctly above code.

USING [] OPERATOR FOR STRING ITEMS

xHarbour supports using [] operator to access single characters in
string items. Harbour doesn't by default but it has strong enough
OOP API to allow adding such extension without touching core code
even by user at .prg level. It was implemented in Harbour in XHB.LIB.
This code can be compiled and executed by both compilers:
      #ifndef __XHARBOUR__
         #include "xhb.ch" // add xHarbour emulation to Harbour
      #endif
      proc main()
         local s := "ABCDEFG"
         ? s, "=>", s[2], s[4], s[6]
         s[2] := Lower( s[2] )
         s[4] := Lower( s[4] )
         s[6] := Lower( s[6] )
         ?? " =>", s
      return

Warning!. There is one difference in above implementation introduced
intentionally to Harbour. xHarbour never generates errors for wrong
indexes in [] operator used for string items but simply returns "",
f.e. add to above code:
      ? ">" + s[0] + "<", ">" + s[1000] + "<"
If [] operator is used for other type of items RTE is generated.
Harbour will generate RTE in all cases. If someone needs strict XHB
compatibility here then he should adopt code overloading [] operator
for strings in XHB.LIB for his own preferences removing the RTE.

NEGATIVE INDEXES IN [] OPERATOR USED TO ACCESS ITEMS FROM TAIL

xHarbour supports negative indexes in [] operator. They are used
to access items from tail, f.e. aVal[ -1 ] is the same as
aVal[ Len( aVal ) ] or ATail( aVal ).
By default Harbour core code does not give such functionality but
it has strong enough OOP API to allow adding such extension without
touching core code even by user at .prg level. It was implemented
in Harbour in XHB.LIB.
This code can be compiled and executed by both compilers:

      #ifndef __XHARBOUR__
         #include "xhb.ch" // add support for negative indexes in Harbour
      #endif
      proc main()
         local s := "ABCDEF", a := {"1", "2", "3", "4", "5", "6"}
         ? s, "=>", s[1], s[2], s[3], s[4], s[5], s[6], "=>", ;
           s[-1], s[-2], s[-3], s[-4], s[-5], s[-6]
         ? a[1], a[2], a[3], a[4], a[5], a[6], "=>", ;
           a[-1], a[-2], a[-3], a[-4], a[-5], a[-6]
      return

Warning! see above note about indexes out of bound used with [] operator
and string items.

USING ONE CHARACTER LENGTH STRING AS NUMERIC VALUE

xHarbour uses one byte strings as numeric values corresponding to ASCII
value of the byte in a string, f.e.:
      ? "A" * 10 // 650
By default Harbour core code does not give such functionality but
it has strong enough OOP API to allow adding such extension without
touching core code even by user at .prg level. It was implemented
in Harbour in XHB.LIB.
This code can be compiled and executed by both compilers:

      #ifndef __XHARBOUR__
         #include "xhb.ch"
      #endif
      proc main()
         local c := "A"
         ? c * 10, c - 10, c + 10, c * " ", Chr( 2 ) ^ "!"
      return

and gives the same results.
Anyhow the emulation is not full here. It works only for .prg code.
In xHarbour standard C API functions/macros were modified to use
one byte string items as numbers. It's potential source of very serious
problems, f.e. ordSetFocus("1") should chose index called "1" or maybe
index 49 or what should return AScan( {49,"1"}, "1" ) (1 or 2)? so
Harbour developers decided to not add anything like that to core code
so in Harbour functions written in C refuse to accept one byte string
as number and code like
      ? Str( "0" )
generates runtime error instead of printing '        48'.

OOP INTERFACE TO HASH ARRAYS

xHarbour allows to access items in hash array using OOP interface.
hVal[ "ABC" ] := 100 can be alternatively written as hVal:ABC := 100.
Using OOP interface is slower then [] operator but it works for all
indexes which are valid upper case [x]Harbour identifiers.
By default Harbour core code does not give such functionality but
it has strong enough OOP API to allow adding such extension without
touching core code even by user at .prg level. It was implemented
in Harbour in XHB.LIB.
This code can be compiled and executed by both compilers:

      #ifndef __XHARBOUR__
         #include "xhb.ch"
      #endif
      proc main()
         local hVal := {=>}
         hVal["ABC"] := 100
         hVal:QWE := 200
         hVal:ZXC := 300
         hVal:QWE += 50
         ? hVal:ABC, hVal:QWE, hVal:ZXC
         ? hVal["ABC"], hVal["QWE"], hVal["ZXC"]
      return

Some Harbour users used to compile Harbour core code with HB_HASH_MSG_ITEMS
macro which enables such functionality directly in core code but it's not
necessary with current code and it exists for historical reasons.
It's possible that in the future support for above macro will be removed
or it will be replaced by runtime switch which can be enabled/disabled for
each hash array separately.

$ OPERATOR EXTENSIONS (arrays and hashes)

In Harbour and xHarbour $ operator can be used to check if some key
or hash pair belongs to hash, f.e.:
      ? "abc" $ { "qwe"=>100, "abc"=>200, "zxc"=>300 }
In xHarbour $ operator can be also used to check if value belongs
to array. It works like AScan() but with exact comparison for strings,
f.e.:
      ? "abc" $ { "qwe", "abc", "zxc" } // result .T.
      ? "a" $ { "qwe", "abc", "zxc", { "a" } } // result .F.
By default Harbour core code does not allow to use $ operator for arrays
and generate the same as Clipper RT error but it has strong enough OOP API
to allow adding such extension without touching core code even by user at
.prg level. It was implemented in Harbour in XHB.LIB so both compilers
can compile and execute this code:
      #ifndef __XHARBOUR__
         #include "xhb.ch"
      #endif
      proc main()
         ? "abc" $ { "qwe", "abc", "zxc" } // result .T.
         ? "a" $ { "qwe", "abc", "zxc", { "a" } } // result .F.
      return

Warning! Xbase++ also supports $ operator for arrays but it makes non
         exact comparison so ` "a" $ { "abc" } ' gives .T. in Xbase++
         and .F. in xHarbour or in Harbour when xHarbour compatibility
         library is used. Harbour users who need strict Xbase++ compatibility
         should create own code to overload $ operators used for arrays
         which will follow exact Xbase++ rules.
         CLIP also has some code for such extension but it has two bugs.
         1st is a semi bug: it uses non exact comparison but reverts
         arguments so ` "abc" $ { "a" } ' gives .T.
         2nd which is critical: it has wrong stop condition so does
         not stop scanning when locates 1st matching item. It should
         be fixed and I do not know what will be the final CLIP behavior.

NEW BIT OPERATORS

xHarbour supports five new bit operators: &, |, ^^, <<, >>
      <x> & <y>      - bit and
      <x> | <y>      - bit or
      <x> ^^ <y>     - bit xor
      <x> << <y>     - bit shift left
      <x> >> <y>     - bit shift right
Harbour does not have such operators but it has set of bit functions
(HB_BIT*()) which are fully optimized at compile time giving such
functionality without extending language syntax and introducing new
operators:
      <x> & <y>      => hb_bitAnd( <x>, <y> )
      <x> | <y>      => hb_bitOr( <x>, <y> )
      <x> ^^ <y>     => hb_bitXor( <x>, <y> )
      <x> << <y>     => hb_bitShift( <x>, <y> )
      <x> >> <y>     => hb_bitShift( <x>, -<y> )

IN, HAS, LIKE OPERATORS

xHarbour has three operators defined as identifier: IN, HAS, LIKE
IN is synonym of $ operator and it's translated by lexer to $.
It does not give any new functionality. For portable code use $.
HAS and LIKE are regular expressions operators. In Harbour they have
the same functionality as hb_regexHas() and hb_regexLike() functions:
      <x> HAS <y>    => hb_regexHas( <y>, <x> )
      <x> LIKE <y>   => hb_regexLike( <y>, <x> )

Using identifiers as operators is not compatible with Clipper preprocessor
precedence rules and introduces bugs to language syntax so Harbour will
never support it directly as operators. Using operators for regular
expression introduce yet another unpleasant thing. It forces linking
regular expression library with final application even if it does not
use RegEx at all. In Harbour RegEx library is optional and linked only
when user use one of HB_REGX*() functions or explicitly use REQUEST
command.

PRE/POST INCREMENTATION / DECREMENTATION AND <op>= OPERATORS

Clipper compiling PRG code with expressions containing operators like
pre and post ++ and -- and also <op>= internally translates them to
normal operators and then generated PCODE for such translated depressions,
i.e.:
   a += 10
   b /= c
   ++d
   e--
   ? --f
   ? g++
is translated to:
   a := a + 10
   b := b / c
   d := d + 1
   e := e - 1
   ? ( f := f - 1 )
   ? g ; g := g + 1

As you can see it causes that modified expression is used more then once.
Usually twice but in some cases (i.e. '? g++' in example above even 3 times.
For simple expression like variables such internal compile time modification
is not visible for users and does not create any difference.
Anyhow if modified expression is complex one which is evaluated and changes
some states (expression with side effects) then such compiler behavior creates
very serious problems. In practice in Clipper it's not possible to use code
like:
   aValue[ ++i ] += field->QUANTITY       // (*)
because this line is translated to:
   aValue[ ++i ] := aValue[ ++i ] + field->QUANTITY
then to:
   aValue[ i := i + 1 ] := aValue[ i := i + 1 ] + field->QUANTITY
for := operator the left side is calculated first so effectively Clipper
generates code which works like:
   aValue[ i + 2 ] := aValue[ i + 1 ] + field->QUANTITY
   i := i + 2
what is very far from the initial (*) expression.
In Harbour we decided to fix it and all expressions modified by pre and
post ++/-- and <op>= operators are evaluated exactly once. It means that
now Harbour users can safely write code with (*) like expressions.
Anyhow if someone needs strict Clipper behavior then -kc Harbour compiler
switch restore it.
Here is simple code which illustrates it:

   proc main()
      local a, i, n
      a := AFill( Array( 10 ), 100 )
      i := n := 0
      while i < Len( a )
         a[ ++i ] -= n++
      enddo
      AEval( a, {| x | QOut( x ) } )
   return

This problem is also fixed in Xbase++ so it's possible to exchange code
using such expressions between Harbour and Xbase++.
In xHarbour the behavior of such expressions in in practice undefined.
In most of cases it behaves like Clipper (i.e. it gives the same wrong
results in above example) but not always because some not cleanly
implemented extensions changed above behavior for chosen operators
executed in some context (i.e. modifying -= to += in above example
causes that xHarbour generates correct code which give the same results
as Harbour and Xbase++). Additionally so far no one has tried to control
such things in xHarbour compiler so the behavior of different operators
and/or context where they behaves differently has been changing few times
as side effect of some other modifications.

The correct behavior of pre and post ++/-- and <op>= operators is also
very important for OOP programmers because it guaranties that messages
are send to exactly the same object. This example illustrates it:

   proc main()
      local o
      o := ErrorNew()
      ? "assign..."
      ?? f( o ):cargo := 0
      ? "predec..."
      ?? --f( o ):cargo
      ? "postinc..."
      ?? f( o ):cargo++
   return
   func f( o )
      ?? " F()"
   return o

In Harbour and Xbase++ function F() is executed only once for each
expression but in Clipper and xHarbour once for 'assign', then twice for
'predec' and finally three times for 'postinc'. In this code function f()
returns the same object 'o' on each call so it's not a problem but code
where the expression can return different objects will not work correctly
so OOP programmers working with Clipper or xHarbour should remember about
it and not create such code.

GLOBAL / GLOBAL EXTERNAL (GLOBAL_EXTERN)

xHarbour supports application wide static variables called GLOBALs.
To declare GLOBAL variable you have to put
      GLOBAL <varname> [ := <initValue> ]
before the 1st function in compiled .prg module and use -n compiler
switch. In xHarbour GLOBAL variables cannot be declared inside function
body. If user wants to use GLOBAL variable in different .prg module
then he has to add declaration
      GLOBAL EXTERNAL <varname>
to his code before the 1st function in compiled .prg module and use
-n compiler switch. Also, GLOBAL EXTERNAL declaration cannot be used
in function body. Unlike other variables GLOBALs declared in xHarbour
reserve used names so they cannot be used in the same module in local
function declarations, f.e. xHarbour cannot compile code like:
      GLOBAL var
      GLOBAL EXTERNAL var2
      func F1( var )
         return var * 2
      func F2()
         FIELD var2
         return var2
due to name conflicts. In general users should be careful using the same
names for different type of variables in xHarbour.
GLOBAL variables in xHarbour need static link time bindings so they
do not work with dynamically loaded PCODE functions from .hrb files
or shared libraries (.dll, .so, .sl, .dyn, ...). Just like STATICs
they cannot be accessed by macro compiler.
Harbour does not support GLOBALs.

DATETIME / TIMESTAMP VALUES

Both compilers support DATETIME/TIMESTAMP values though they use different
implementation.

In Harbour it's new type TIMESTAMP for which ValType() function returns
"T". It has its own HVM arithmetic similar to the one used by DATE type
but not exactly the same. The difference (-) between two TIMESTAMP values
is represented as number where integer part is number of days and fractional
part is time in given day. Non-exact comparison (=, >, <, >=, <=) for
TIMESTAMP and DATA value assumes that both values are equal if the date
part is the same. Such semantic is also respected by native RDDs when
mixed DATE and TIMESTAMP values are used in indexes, seeks, scopes, etc.
When number is added to DATE type then like in Clipper only integer part
increase (decrease) DATE value but when it's added to TIMESTAMP value then
fractional part is also significant. When TIMESTAMP value is added to DATE
value then as result new TIMESTAMP value is calculated. Here is detail
information about relational and arithmetic operators in Harbour.
Timestamp values in relational operators <, <=, >, >=, =, ==
      - When two timestamp values are compared then VM compares date and
        time parts in both values.
      - When date and timestamp values are used in <, <=, >, >=, =
        operations then VM compares only date part in both values.
      - When date and timestamp values are used in == operation then VM
        compares date part in both values and then check if time part
        of timestamp value is 0.
Timestamp values in + and - math operations:
      <t> + <t> => <t>
      <t> - <t> => <n>
      <t> + <n> => <t>
      <n> + <t> => <t>
      <t> - <n> => <t>
      <d> + <t> => <t>
      <t> + <d> => <t>
      <d> - <t> => <n>
      <t> - <d> => <n>
   when number is result or argument of timestamp operation then its integer
   part is a number of days and fractional part represents time in day.

In xHarbour DATE type was extended to hold information about time. Clipper
compatible DATE arithmetic in HVM was modified to respect fractional part
in numbers which was used for time part.
The xHarbour DATETIME implementation introduces incompatibilities to Clipper
(f.e. compare Clipper and xHarbour results in code like: '? Date() + 1.125'
so in some cases existing Clipper code has to be carefully adopted to work
correctly with xHarbour but it's fully functional solution though it needs
some minor fixes in conversions between datetime values and numbers.

The difference between Harbour and xHarbour can be seen in this code:
      proc main()
         local dVal, tVal
         dVal := Date()
         tVal := dVal + {^ 02:00 }  // {^ 02:00 } timestamp
                                    // constant, see below
         ? ValType(dVal), ValType(tVal)
         ? dVal; ? tVal
         dVal += 1.125  // In Clipper and Harbour it increases
                        // date value by 1
         tVal += 1.25   // it it increases timestamp value by 1 day
                        // and 6 hours
         ? dVal; ? tVal
         ? dVal = tVal  // In Harbour .T. because date part is the same
         ? Date() + 0.25, Date() + 0.001 == Date()
      return
Harbour shows:
      D T
      05/20/09
      05/20/09 02:00:00.000
      05/21/09
      05/21/09 08:00:00.000
      .T.
      05/20/09 .T.

and xHarbour shows:
      D D
      05/20/09
      06/25/21 02:00:00.00
      05/21/09 03:00:00.00
      06/26/21 08:00:00.00
      .F.
      05/20/09 06:00:00.00 .F.

Recently to xHarbour "T" type was introduced with some of Harbour TIMESTAMP
code in RDD but VM was not modified to follow Harbour/RDD modifications.
So now some parts of xHarbour are not synced and I cannot say what is the
goal of DATETIME values and their arithmetic in xHarbour VM for the future.

LITERAL DATE AND TIMESTAMP VALUES

Both compilers tries to support VFP like datetime constant values
in the following format:
      { ^ [ YYYY-MM-DD [,] ] [ HH[:MM[:SS][.FFF]] [AM|PM] ] }
In Harbour and VFP the following characters can be used as date delimiters:
"-", "/" and "."
xHarbour supports only "/" as date delimiter.
There is no limit on number of digits in YYYY, MM, DD, HH, MM, SS, FFF
parts. Important is only their value. This is the format in semi PP notation:
      { ^ <YEAR> <sep:/-> <MONTH> <sep:/-> <DAY> [[<sep2:,>]
        [ <HOUR> [ : <MIN> [ : <SEC> [ . <FRAQ> ] ] ] [AM|PP] ] }
NOTE: there is one important difference between Harbour and VFP/xHarbour in
decoding above format. In VFP and xHarbour when date part is missed then
it's set by default to: 1899-12-30 so this code:
      { ^ 12:00 }
gives the same results as:
      { ^ 1899-12-30 12:00 }
Harbour does not set any default date value when timestamp constant contains
only time part.
Harbour supports VFP syntax only in Compiler. It's not supported in
macro-compiler and it can be disabled in the future so it's not suggested
to use with Harbour programs.

Only Harbour supports date constant (in compiler and macro-compiler) in the
form d"YYYY-MM-DD" f.e.:
      d"2009-05-20"
Also delimiter "/" or "." can be used instead of "-".

Only Harbour supports timestamp constant (in compiler and macro-compiler)
in the form t"YYYY-MM-DD HH:MM:SS.fff"
The exact accepted timestamp pattern is is:
      YYYY-MM-DD [H[H][:M[M][:S[S][.f[f[f[f]]]]]]] [PM|AM]
f.e.:
      tValue := t"2009-03-21 5:31:45.437 PM"
or:
      YYYY-MM-DDT[H[H][:M[M][:S[S][.f[f[f[f]]]]]]] [PM|AM]
with literal "T" as date and time part delimiters (XML timestamp format),
f.e.:
      tValue := t"2009-03-21T17:31:45.437"
The following characters can be used as date delimiters: "-", "/", "."
if PM or AM is used HH is in range < 1 : 12 > otherwise in range < 0 : 23 >

Harbour compiler and macro-compiler support also date constants in the
form 0dYYYYMMDD f.e.:
      0d20090520.

EXTENDED LITERAL STRING IN COMPILER AND MACRO-COMPILER

Harbour and xHarbour support extended strings with C like escaped values
as e"...", f.e.:
      ? e"Helow\r\nWorld \x21\041\x21\000abcdefgh"
They works in compiler and macro-compiler but xHarbour macro-compiler does
not support strings with embedded 0 so they are not fully functional here.

SYMBOL ITEMS AND FUNCTION REFERENCES

Harbour supports SYMBOL items ( ValType(funcSym) == "S" ) which can be used
as function or message references. They have similar functionality to
SYMBOL objects in Class(y) and understands NAME, EXEC and EVAL messages.
They can be created literally by compiler using @<funcName>(), f.e:
      funcSym := @Str()
and in such case they also create explicit reference (link time binding)
to given functions or by macro compiler, f.e.:
      funcSym := &("@Upper()")
what does not create any explicit bindings. This is simple code which
uses symbol items:
      proc main()
         local funcSym
         funcSym := @Str()
         ? funcSym:name, "=>", funcSym:exec( 123.456, 10, 5 )
         funcSym := &("@Upper()")
         ? funcSym:name, "=>", funcSym:exec( "Harbour" )
      return

xHarbour does not have such functionality though it can create at
compile time function references using a little bit different syntax:
      ( @<funcName>([<anyExpList,...>]) )
the different syntax is side effect of bugs in grammar rules only and
probably will be fixed somewhere (macro-compiler does not support such
syntax at all).
In xHarbour above code creates pointer item ( ValType(funcSym) == "P" )
which can be used in some cases like in Harbour but because xHarbour VM
does not know if given pointer item is function reference or not then in
such context xHarbour has to accept any pointer items as function
references so any user mistake can cause GPF or some HVM structure
corruptions.

STRONG TYPED VARIABLES

Harbour and xHarbour allow to declare type of variables using syntax similar
to Visual Object which was adopted also by FlagShip and some other xBase
compatible languages (i.e. FlagShip):
   LOCAL var AS STRING
Anyhow so far in both compilers it is only source code decoration and it's
simply ignored during compilation. The syntax is similar but not the same.
In VO:
   LOCAL var1, var2 AS LOGICAL
means that var1 and var2 are logical variables and are initialized to .F.
at runtime. In Harbour and xHarbour 'AS <type>' has to be repeated after
each variable so in above code only var2 is strongly typed but not var1.
To declare both variables as logical ones it should be changed to:
   LOCAL var1 AS STRING, var2 AS STRING
and to make implicit initialization user has to write code like:
   LOCAL var1 AS STRING := "foo", var2 AS STRING := "bar"
instead of:
   LOCAL var := "foo", var2 := "bar" AS STRING
Such syntax is also not compatible with syntax of typed object variables
(see TYPED OBJECT VARIABLES below) where
   VAR v1, v2 AS LOGICAL
declares both variables as logical ones.
This can strongly confuse users so in the future adding fully functional
support for strong typed variables probably it will be changed to syntax
compatible with other xBase-like languages.
Now please remember that neither Harbour nor xHarbour make type validation
during compilation and at runtime and typed variables are not implicitly
initialized to empty value of given type.

OOP SCOPES

Harbour and xHarbour support scopes in class declaration.
It's possible to declare exported, protected and hidden messages and then
at runtime scopes are checked. Anyhow there are important differences in
both implementation. In xHarbour for internal implementation .prg module
symbol table was used so all classes declared in the same .prg file (or
#included from the same .prg file) and also all other functions in this
file have exactly the same scope and works like internal methods which
can access hidden data. Harbour is Class(y) compatible so each class
and external functions are separated. If programmer needs to give some
rights to other classes or external functions then he can explicitly
declare friend classes and functions by simple:
      FRIEND CLASS <ClassName,...>
and:
      FRIEND FUNCTION <FuncName,...>
It's also possible to give unprotected access to all other functions
declared in the same .prg module just like in xHarbour by using
'MODULE FRIENDLY' clause in class declaration, i.e.:
      CREATE CLASS myCls INHERIT parent MODULE FRIENDLY
         [...]
      ENDCLASS
So in Harbour all scopes aspects are explicitly controlled by programmer
and AFAIK there are no side effects forced by internal implementation.
Important is also the fact in Harbour each codeblock block created explicitly
or by macro-compiler inherits rights from functions when it was created
and then has the same scope. It means that it's not possible to break scope
protection using codeblocks and it's very important functionality for real
private (hidden) data because it makes it accessible for codeblocks created
by object methods.

AFAIK xHarbour does not have such functionality and codeblock scopes depends
on module symbol table.

OOP AND MULTI-INHERITANCE

Harbour and xHarbour support multiple inheritance just like Class(y).
Anyhow only Harbour correctly resolves possible name conflicts
by casting. xHarbour and Class(y) in Clipper does not work correctly
if few ancestors have instance variables with the same name even if
explicit casting is used. This code illustrate the problem:

      #ifdef __HARBOUR__
         #include "hbclass.ch"
      #else
         #include "class(y).ch"
      #endif
      proc main()
         local o
         o := mycls():new()
         o:var := "VAR"
         o:cls1:var := "VAR1"
         o:cls2:var := "VAR2"
         o:cls3:var := "VAR3"
         ? o:var, o:cls1:var, o:cls2:var, o:cls3:var
         o:var := "[var]"
         o:cls1:var := "[var1]"
         o:cls2:var := "[var2]"
         o:cls3:var := "[var3]"
         ? o:var, o:cls1:var, o:cls2:var, o:cls3:var
      return

      CREATE CLASS MYCLS INHERIT CLS1, CLS2, CLS3
      EXPORTED:
         VAR      VAR
      ENDCLASS

      CREATE CLASS CLS1
      EXPORTED:
         VAR      VAR
      ENDCLASS

      CREATE CLASS CLS2
      EXPORTED:
         VAR      VAR
      ENDCLASS

      CREATE CLASS CLS3
      EXPORTED:
         VAR      VAR
      ENDCLASS

Expected results are:
      VAR VAR1 VAR2 VAR3
      [var] [var1] [var2] [var3]
and Harbour shows such results.
xHarbour and Class(y) gives wrong results:
      VAR VAR3 VAR3 VAR3
      [var] [var3] [var3] [var3]

Please note that Harbour does not make any tricks with compile time static
bindings. It uses only dynamic bindings and resolves all problems with
instance area super casting.

Correct instance area super casting is very important functionality when
program is created by few programmers and it's necessary to create classes
which inherits from many other ones written be different programmers to
resolve possible name conflicts. So for bigger projects is one of the most
important thing.

OOP AND PRIVATE/HIDDEN DATA

In bigger projects where different programmers work on different classes
which are later used as ancestors of some new classes it's extremely important
to give support for real private (hidden) data for each class programmer
which will not cause problems if name conflicts appears. Otherwise
each method and instance variable name used by each programmer has to be
agreed with project manager to eliminate possible name conflicts. In really
big projects such name conflicts can be nightmare a which eliminates some
languages.

Harbour resolves this problem automatically. All hidden variables and
messages are automatically cast to the class which is the owner of
calling code. To make it working Harbour needs fully functional scope
protection and multi-inheritance (see OOP SCOPES and OOP AND MULTI-INHERITANCE).
Everything is done automatically without any explicit casting or other
then declaring variables or methods as HIDDEN source code modification.
This code illustrates it. We have two classes and each of them has its own
_private_ 'temp' instance variable and 'set' method. 3rd class inherits
from both of them and executes it's public methods which internally
access hidden ones but without any explicit casting. In Harbour all is done
automatically by HVM:

      #include "hbclass.ch"
      PROC MAIN()
         LOCAL o
         o := mycls():new()
         o:action( "X" )
         o:action( "Y" )
         WAIT
      RETURN

      CREATE CLASS MYCLS INHERIT CLS1, CLS2
      EXPORTED:
         METHOD   ACTION
      ENDCLASS

      METHOD ACTION( x )
         ? ::plus( x )
         ? ::minus( x )
      RETURN Self


      CREATE CLASS CLS1
      HIDDEN:
         VAR      TEMP
         METHOD   SET
      EXPORTED:
         METHOD   PLUS
      ENDCLASS

      METHOD SET( x )
         IF ::TEMP == NIL
            ::TEMP := "C1"
         ENDIF
         ::TEMP += ":" + Upper( x )
      RETURN Self

      METHOD PLUS( x )
      RETURN ::SET( x ):TEMP


      CREATE CLASS CLS2
      HIDDEN:
         VAR      TEMP
         METHOD   SET
      EXPORTED:
         METHOD   MINUS
      ENDCLASS

      METHOD SET( x )
         IF ::TEMP == NIL
            ::TEMP := "c2"
         ENDIF
         ::TEMP -= ">" + Lower( x )
      RETURN Self

      METHOD MINUS( x )
      RETURN ::SET( x ):TEMP

Expected results from this code are:
         C1:X
         c2>x
         C1:X:Y
         c2>x>y

and Harbour shows exactly such results.
Neither xHarbour nor Class(y) has such functionality what seems to be very
serious limitation for big projects.
In xHarbour above code shows:
      c2>x
      c2>x>x
      c2>x>x>y
      c2>x>x>y>y

To compile it with classy it's necessary to make some minor modification
and change in CLS1:
      METHOD SET
to:
      MESSAGE SET METHOD SET1
and in CLS2
      METHOD SET
to:
      MESSAGE SET METHOD SET2
updating method names in the code.
Then it can be compiled but like in xHarbour only one SET method is visible
and only one TEMP instance variable so it shows only:
      C1:X
and then generates scope violation error because unlike xHarbour Class(y)
does not define all classes as "MODULE FRIENDLY".

OOP AND CLASS OBJECT/CLASS MESSAGES

Neither Harbour nor xHarbour support class objects, class messages
and class instance variables like Class(y) does.

But both compilers support shared variables which can be shared with
all object of the same class or shared variables which can be
shared with all object of given class and its descendants.
Class methods are not supported at all by both compilers (they cannot be
without class object) though xHarbour wrongly use class messages as normal
messages.

In the future we plan to introduce real class objects like in Class(y) and
some other languages (i.e. Xbase++) so it's important for portability to
write code which is Class(y) friendly and never use class function directly
as constructor, i.e. instead of writing code like:
      o := mycls( p1, p2, p3 )
programmer should use:
      o := mycls():new( p1, p2, p3 )
otherwise the code will not work with future Harbour versions supporting
real class objects. Even if programmer do not plan to pass any parameters
to constructor then he should call :NEW() method:
      o := mycls():new()
and please remember that :NEW() will be class method so it should not
be redefined as constructor in user class. Instead :INIT() method should
be used as constructor. It's executed automatically when object is
created from the :NEW() method.

In Class(y) and Xbase++ class function never returns final object.
It returns class object which is completely different and this object
understands few messages which allow to manage class, check inheritance
scheme, check object definition and access many different information
about the class and object. It also understands message :NEW() which
creates final object so programmer can make something like:

    oClass := myclass()
    oObj1 := oClass:new()
    oObj2 := oClass:new()

It's very powerful system which allows to easy create constructions
which are still not available for Harbour and xHarbour users, i.e.
it's possible to write fully functional object inspector in just
few lines.
It also resolves many internal problems, i.e. now in many places we
have to create dummy objects just to extract some information about
the class. Each call to class function creates new object in current
implementation. In Class(y) class function returns exactly the same
class object on each call so it's very fast and programmer can call
this function as many times as he need without creating new objects.
Current Harbour and xHarbour implementation blocks writing some more
complicated code which needs to precisely follow each object instance
overloading constructors and destructors or even implement dynamic
initialization code because it will be activated in hidden way when
dummy objects are created.

In xHarbour when parameters are passed to class function then :NEW()
method is executed inside class function.
I plan to eliminate current limitations and open new possibilities
adding class object functionality and it's the reason why I haven't
tried to replicate xHarbour behavior in Harbour. It simply won't work
in the future.

Anyhow if someone wants to simulate xHarbour like behavior in Harbour
then he can modify hbclass.ch from Harbour. It's enough to change
ENDCLASS definition to:

#xcommand ENDCLASS [<lck: LOCK, LOCKED>] => ;
            oClass:Create() ; [<-lck-> __clsLock( oClass:hClass ) ] ;;
         always ;;
            __clsUnlockDef( @s_oClass, oClass ) ;;
         end sequence ;;
         oInstance := oClass:Instance() ;;
         if __objHasMsg( oInstance, "InitClass" ) ;;
            oInstance:InitClass( HB_CLS_PARAM_LIST ) ;;
         end ;;
      else ;;
         oInstance := s_oClass:Instance() ;;
      end ;;
      if PCount() > 0 ;;
         return oInstance:new( HB_CLS_PARAM_LIST ) ;;
      endif ;;
      return oInstance AS CLASS _CLASS_NAME_ ;;
   #undef  _CLASS_MODE_ ; #define _CLASS_MODE_ _CLASS_IMPLEMENTATION_

But please remember it's not supported by Harbour and it may stop to
work in the future.

TYPED OBJECT VARIABLES

Harbour supports strong typed object variables, f.e.:
      CREATE CLASS MyClass
         VAR   var1 AS INTVAR
         VAR   var2 AS NUMERIC
         VAR   var3 AS DATE
         VAR   var3 AS CHARACTER
      ENDCLASS
And validates assigned values at runtime just like in Class(y).
Variables declared as numeric, logical, date and timestamp without
explicit initialization value (INIT clause) are initialized to empty
value of given type. This functionality can be disabled defining
HB_CLS_NOAUTOINIT macro before including hbclass.ch.
xHarbour can compile above code but AS <type> is only used to
initialize numeric and logical values to 0 and .f.

OBJECT DESTRUCTORS

Both Harbour and xHarbour support object destructors and both
compilers uses reference counters and garbage collector to
execute destructors. Anyhow the low-level implementation is
different in the two compilers. In Harbour HVM is reentrant safe
so the implementation of destructors is much simpler. It also
keeps full control about reference counters and detects user
code errors bound with complex items in .prg and C code what
greatly helps to detect problems in user code or 3rd party
libraries and gives protection against internal HVM memory
corruption. In the last years it also helped to located few
very hard to exploit bugs in core code.

In xHarbour there is some basic protection but it was never
fully functional. Sometimes it may cause that internal error
message is generated but in most of cases internal HVM memory
is silently corrupted. A good example which illustrates what
may happen is in Harbour source repository in harbour/tests/destruct.prg
This code can be compiled and executed by Harbour and xHarbour.
But only Harbour version detects user errors generating RTE
and is necessary in all cases to keep internal memory cleaned
protecting against corruption. The xHarbour version behavior
is random because this .prg code corrupts internal HVM memory.
It's possible that it will be executed without any visible
errors or it will generate GPF or internal error. But in all
cases it can be well seen using tools like Valgrind or CodeGuard
that it corrupts internal memory so the results are unpredictable.
I.e. this is part of Valgrind log generated during execution
of above destruct.prg compiled by xHarbour:

   ==22709== Invalid read of size 2
   ==22709==    at 0x426E235: hb_gcItemRef (garbage.c:646)
   ==22709==    by 0x425EDE5: hb_memvarsIsMemvarRef (memvars.c:2396)
   ==22709==    by 0x426E674: hb_gcCollectAll (garbage.c:847)
   ==22709==    by 0x426E899: HB_FUN_HB_GCALL (garbage.c:1179)
   ==22709==  Address 0x485b096 is 22 bytes inside a block of size 56 free'd
   ==22709==    at 0x4023B7A: free ()
   ==22709==    by 0x42837A9: hb_arrayReleaseBase (arrays.c:1588)
   ==22709==    by 0x428381A: hb_arrayRelease (arrays.c:1602)
   ==22709==    by 0x4256B94: hb_itemClear (fastitem.c:248)
   [...]
   ==22709== Invalid write of size 2
   ==22709==    at 0x426E4C6: hb_gcItemRef (garbage.c:652)
   ==22709==    by 0x425EDE5: hb_memvarsIsMemvarRef (memvars.c:2396)
   ==22709==    by 0x426E674: hb_gcCollectAll (garbage.c:847)
   ==22709==    by 0x426E899: HB_FUN_HB_GCALL (garbage.c:1179)
   ==22709==  Address 0x485b096 is 22 bytes inside a block of size 56 free'd
   ==22709==    at 0x4023B7A: free ()
   ==22709==    by 0x42837A9: hb_arrayReleaseBase (arrays.c:1588)
   ==22709==    by 0x428381A: hb_arrayRelease (arrays.c:1602)
   ==22709==    by 0x4256B94: hb_itemClear (fastitem.c:248)
   [...]
   ==22709== Invalid read of size 4
   ==22709==    at 0x426E223: hb_gcItemRef (garbage.c:603)
   ==22709==    by 0x425EDE5: hb_memvarsIsMemvarRef (memvars.c:2396)
   ==22709==    by 0x426E674: hb_gcCollectAll (garbage.c:847)
   ==22709==    by 0x426E899: HB_FUN_HB_GCALL (garbage.c:1179)
   ==22709==  Address 0x0 is not stack'd, malloc'd or (recently) free'd
   ==22709==  Process terminating with default action of signal 11 (SIGSEGV)
   ==22709==  Access not within mapped region at address 0x0

Harbour executes above code cleanly without any errors:

   ==28376== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 3 from 1)
   ==28376== malloc/free: in use at exit: 0 bytes in 0 blocks.
   ==28376== malloc/free: 6,164 allocs, 6,164 frees, 10,323,064 bytes allocated.
   ==28376== All heap blocks were freed -- no leaks are possible.

Missing protection and error detection is not only problem for programmers.
It also causes that in xHarbour core code few bad bugs have not been fixed
so far though they exist for years. It also extremely time consuming problem
for core developers when users or 3rd party developers reports problems with
some memory corruption and it's necessary to locate the reason.
I remember how much time I lost for such things working on xHarbour
in the past so later when I moved to Harbour it was one of the main goal
for me to resolve the problem.

In xHarbour there is disabled code which was designed to detect such
problems and it can be enabled by setting HB_ARRAY_USE_COUNTER_OFF macro
but it causes horrible runtime overhead and was never finished to be ready
as production extension. Such solution seems to be dummy way so I do not
believe that it will be ever finished.

SCALAR CLASSES

Both compilers support scalar classes and allows to add OOP functionality
to native types like numeric, character, array, hash, codeblock, date, ...
It's possible to overload default scalar classes provided by Harbour and
xHarbour or use ASSOCIATE CLASS command to bound any class with some native
type. It's also possible to overload the behavior of some operators if it's
not already defined for given types. Anyhow it's not possible to change
operator precedence which is the same for all types and defined at compile
time.

RUNTIME CLASS MODIFICATION

Harbour and xHarbour use dynamic bindings so it's possible to modify class
definitions at runtime. To avoid possible very serious problems which can
be caused by such class modification Harbour provides LOCKED clause in class
definition. xHarbour does not have such functionality and even documented
command like OVERRIDE CLASS or EXTEND CLASS as official features though
they break inheritance scheme and never worked correctly in the whole
xHarbour history. They are available in Harbour only in xHarbour compatible
library after including xhbcls.ch for porting existing xHarbour code to
Harbour and work exactly like in xHarbour having the same problems so they
are not planned to be part of Harbour core code.

ARRAY AND STRING PREALLOCATION

Both compilers uses array and string preallocation to optimize resize
operation, f.e. in code like:
      s := ""
      for i := 0 to 255
         s += Chr( i )
      next
or:
      a := {}
      for i := 0 to 255
         AAdd( a, Replicate( Chr( i ), 5 ) )
      next
the string inside 's' variable and array inside 'a' variable are not
resized 256 times but much seldom. It gives noticeable speed improvement
with the cost of additional memory used by application. Anyhow in Harbour
internal HVM and item structures are smaller then in xHarbour so usually
the total memory usage in Harbour, even with extensive preallocation, is
smaller then in the same application compiled by xHarbour with disabled
string and array preallocation.

The speed difference can be really huge in some application. It's possible
to create test code which will be 100 times faster due to this feature
so it's really important functionality.

The internal implementation of these optimization in both compilers is
different. In xHarbour only chosen left side expression are optimized
(in practice only 'var += ...' and 'arr[ <n> ] += ...') but in Harbour
all with the exception to 'o:<msg> += ...' optimization which can be
enabled only optionally at Harbour compile time using HB_USE_OBJMSG_REF
macro (there is no -k? option to control it by user yet), it can be well
seen in code like:
      proc main()
         memvar v
         local t, i, s
         private v := ""
         t := SecondsCPU()
         s := "v"
         for i := 1 to 100000
            &s += L2Bin( i )
         next
         t := SecondsCPU() - t
         ? "time:", t, "sec."
      return
it's enough to compare time difference:
      Harbour  0.13 sec.
      xHarbour 15.91 sec.

DIVERT STATEMENT

xHarbour supports

      DIVERT TO <pFuncPointer> | <pMessagePointer> ;
             [ OF <oNewSelfOrNil> ] [ FLAGS <nDivertFlags> ]

statement which allow to switch current function/method execution context
to the new one. The detail description of the above extension can be found
in xHarbour ChangeLog.027 at:
      2007-12-07 22:30 UTC-0500 Ron Pinkas <ron/at/xharbour.com>

It's very dangerous extension which needs really deep knowledge about
compiler and HVM internals to use it safely, because, for called function
HVM does not create new stack frame. Instead it simply adopts current
one. It means that programmer using above statement have to exactly know
HVM registers used by different language constructions and which ones are
correctly saved and then restored or cleaned by DIVERT statement.
Any mistake can cause unpredictable behavior like GPF or internal HVM
structure corruption.

The worst thing in above extension is the fact that it effectively blocks
using HVM stack by compiler for optimization (i.e. to store some values in
temporary stack variables) and also adding some new language constructions
which may try to use HVM stack as temporary holder for some values so in
the future such new language constructions will have to use explicitly
defined new HVM registers just like the ones defined in current xHarbour
VM for WITH OBJECT or FOR EACH statements increasing unnecessary overhead
also in code which will not use either DIVERT TO or new language
constructions, so it introduces very serious problem for further compiler
improvement and syntax extensions.

In theory it should be faster method to switch the execution context
than normal call but in practice it introduces new overheads directly
or indirectly (by blocking some possible improvements) so in summary
it causes slowness.

Probably only some examples which uses DIVERT TO with bigger number
of parameters in a loop can show some speed improvement but in real
life application in most of cases it's necessary to call functions/
procedures/methods in standard way which will be slower so in fact
it reduces overall performance.

I tried to make some tests with above statement but quite fast I exploited
few bugs in the pure DIVERT TO implementation in HVM so I was not able
to make serious tests for interactions with different language constructions.
xHarbour simply GPFs or makes some other strange things before and I do
not have time to check what exactly is broken in this case or analyze
possible bad side effects looking at xHarbour core code.
I only scanned xHarbour core code for real life DIVERT TO usage and
the only one practical usage of above extension in xHarbour core code
is workaround for missing '...' operator working like in Harbour
(see: FUNCTIONS WITH VARIABLE NUMBER OF PARAMETERS) so I do not think
that Harbour users will ever ask about it.

For all of the above reasons I think that Harbour will never have
anything like that at least as documented language extension.

NAMESPACEs

xHarbour has compile time support for namespaces which uses by default
static bindings with optional support for dynamic bindings in HVM which
is necessary for .hrb and macro-compiler. To exchange information about
defined by user namespaces xHarbour generates <namespace>.xns files
during compilation of .prg ones which define public functions in given
namespace. These files are automatically included by xHarbour compiler
if namespace is requested in PRG code. They are also necessary for C
compiler to resolve static bindings so they are explicitly included in
.c files generated by xHarbour.

xHarbour compiler seems to always overload existing .xns files and
there is no description for format of .xns files. It means that without
some manual modification namespace can be defined only in single .prg
file.

In xharbour CVS in doc/namespace.txt is basic description for used syntax
but I cannot find any deeper information about expected functionality so
it's hard for me to comment on it. I simply do not know the final goals
for this feature and why it was introduced. I do not even know if it has
been finished or not and some modifications/extensions are planned.
After few tests I can only say that it does not work correctly on case
sensitive file systems so probably it's not widely used functionality
if no one reported the problem so it has not been fixed.
If xHarbour developers can make some short description for this feature
then I'll add it to this text.

Harbour does not have such functionality and AFAIK none of developers
has planed to add similar to xHarbour namespace functionality.
Also none of Harbour users requested about it so seems that it will
not be added to Harbour.

Anyhow Harbour users and developers requested about functionality
which allow to control public symbol scopes at runtime.
It's necessary to create limited in functionality (reduced list of
public functions which can be accessed) environment to execute untrusted
code, i.e. loaded from .hrb files or dynamically compiled and registered
.prg code.

In the future I plan to work on such functionality. If possible I'll
try to resolve also some other problems which can appear in programs
loading and executing external code like support for thread local public
symbols. It should greatly help to improve some programs like HTTP
servers. Probably this code should also help in implementing dynamic
namespace support so maybe we introduce it but seems that it will be
completely different thing then namespaces in current xHarbour.

MULTI WINDOW GTs AND RUNTIME GT SWITCHING

Harbour allows to initialize more then one GT drivers at runtime
and switch between active GTs in single application. It can be
done using the following functions:
      hb_gtCreate( <cGtName>, [<nStdIn>], [<nStdOut>], [<nStdErr>] ) -> <pGT>
      hb_gtSelect( <pGT> ) -> <pPrevGT>
if all references to allocated GT drivers are cleaned in visible items
then GT windows is automatically closed.

Number of allocated GTs depends on existing resources. I.e. some GTs like
GTWVT or GTXWC for each hb_gtCreate() call creates new console window so
application can create many different windows and switch between them.
It's very nice feature for MT mode because current GT driver in Harbour
is thread attribute so each thread in Harbour can allocate it's own
independent console window if platform supports GT driver with such
possibility (graphical GTs like GTWVT, GTXWC, GTWVG).
Some GTs like GTWIN, GTOS2 or GTDOS have to share the same resources
(OS console windows, video screen buffer) so their parallel usage is
limited but allocating them does not block allocating any other GTs,
i.e. application can use one GTWIN console (MS-Windows console window)
and many GTWVT console windows.

This feature can be used also with non graphical GTs which uses stream
input/output like GTTRM, GTCGI or GTSTD, i.e. user can create terminal
server and for each internet connection allocate new GTTRM or GTSTD
driver or can execute CGI procedures in newly allocated GTCGI drivers
with direct output redirection to given handle passed to hb_gtCreate().
In some other cases it can be usable to execute some part of code using
GTNUL driver to pacify all output and disable input.

This functionality is also supported by virtual GT drivers GTCTW used
to emulate Clipper Tools window system. GTCTW has also separate support
for MT mode and keep current window as thread attribute so each thread
can use its own CT window independently.

In Harbour contrib there are some GTs and mixed GT/GUI libraries which
extensively use multi window functionality like GTWVG, GTQTC.

Not all GTs in Harbour has been adopted to work simultaneously in multi
thread mode with more then one instance so user should not allocate more
then one instance of the following GTs GTDOS, GTWIN, GTOS2, GTCRS, GTSLN,
GTPCA.

These GTs are limited by resources or have alternative GT drivers with
similar functionality so it's hard to say if Harbour developers invest
time in updating them.

The above functionality is local to Harbour and does not exists in
xHarbour. Anyhow xHarbour borrowed from Harbour most of new GT code so
it should be possible to implement (at least partially due to limited
in xHarbour MT mode) some of the above features.

MULTI THREAD SUPPORT

Both compilers support multi thread programming and in both
compilers to use threads it's necessary to use MT version of VM.

In Harbour it's only single HVM library because in both versions
(ST and MT) Harbour VM gives exactly the same public API so it's
not necessary to create separate versions of any other libraries
or create different .prg code for MT and ST modes.
The separate ST version of VM can be linked with applications
which do not create any threads to improve the performance because
it's a little bit faster then MT VM. The speed difference can be
compared using speedtst.prg (results below). For other platforms/C
compilers it may be a little bit bigger or smaller anyhow it's
not very huge and Harbour in MT mode is still much faster then
xHarbour in ST mode.

For .prg programmer the only difference between applications linked
with single thread VM and multi thread VM is in hb_threadStart()
results. This function linked with ST VM always fails and new thread
is never created.

Harbour gives set of functions with hb_thread*() and hb_mutex*()
prefix to manage threads and synchronize them. It also supports
THREAD STATIC variables what allows to easy adopt any existing code
to MT mode. Whole Clipper compatible .prg API using internally static
variables like GET system was adopted for MT mode and work with thread
local statics.

Most of core code had been rewritten to be reentrant safe so it does
not have to be protected by any mutexes what greatly helps in
scalability. Below is a short description which shows which resources
are thread local which ones are shared and how they are initialized:
      Thread local attributes inherited from parent thread:
         - code page
         - language
         - SETs
         - default RDD
         - I18N translation set
      Thread local attributes initialized to default value:
         - public variable GetList := {}
           (of course if public variables are not shared with parent
           thread).
         - error block (initialized by calling ErrorSys())
         - math error handler and math error block
         - default macro compiler features setting (controlled by
           hb_SetMacro())
         - RDDI_* settings in core RDDs (some 3rd party RDDs may use
           global settings)
         - thread static variables
         - SetKey() are reset to empty array (Clipper compatible automatic
           binding between F1 key and HELP() if such function/procedure was
           registered in HVM is done only for main thread).
      Other thread local resources:
         - memvar (public and private) variables (except the ones which
           are shared with parent threads)
         - work areas
      Resources which can be shared or copied form parent to child thread
      on user request when thread is created:
         - public variables
         - private variables
      Global resources synced automatically:
         - functions and procedures
         - class definitions
         - RDDs
         - GT drivers
         - lang modules
         - codepage modules
         - active GT driver/console window (by default GT is inherited
           from parent thread so also all GT internal settings are shared
           between threads using the same GT anyhow each thread can create
           new GT and set it as its own GT driver - such functionality is
           available only in this GTs which can use independent system
           resources for input and output, i.e. separate windows in GUI
           GTs like GTWVT, GTXWC, GTQTC or input/output handles in terminal
           stream GTs like GTCGI, GTSTD, GTTRM).
      Other global resources:
         - .prg static variables (Harbour core code does not use them)
In practice the only one not synced automatically element in Harbour
is write access to the same item with complex variable. This part
have to be synced by user and automatic synchronization here will
strongly reduce scalability.

Harbour also supports Xbase++ compatible MT API with SYNC and CLASS SYNC
methods, SIGNAL and THREAD classes, work area zones and thread functions.
It allows to easy port Xbase++ code to Harbour. The main difference
between Harbour and Xbase++ is write protection to complex items.
Harbour gives only read protection so threads can access the same
complex variable simultaneously without any restrictions as long
as it's not modified (assigned) by some other thread. Assign operations
to variable which can be accessed simultaneously by other threads
need explicit synchronization by user code. In Xbase++ it should
be synced automatically so users have to use own synchronization
only to keep synced his application logic.

For sure Xbase++ is more user friendly here. Anyhow such solution
strongly reduce scalability so the cost of such automatic protection
is very high. Additionally in most of cases simultaneous access and
assign operations on the same complex variable need additional
user synchronization to eliminate race condition so it does not
help much in real programming. Anyhow it's interesting in some
cases so it's possible that we add such functionality to Harbour
in alternative HVM library to not reduce current performance.

In xHarbour for MT mode it's necessary to use different set of RTL libraries
not only MT version of VM lib. MT mode change the behavior of core features
and many parts of xHarbour core code have been never adopted to MT mode so
it's hard to clearly define which resources are thread local and which ones
are global and what should be synchronized by user. Many things are not
synced at all and race conditions can cause internal HVM structure
corruptions, i.e. class creation code where many threads can try to
register the same class simultaneously so it needs yet a lot of work to
reach real production functionality. It also needs serious cleanup because
there are race conditions even in basic MT API. Missing from the beginning
clear definition of MT mode caused that in last years not all xHarbour
modifications were MT safe and some of them even broke existing functionality.
xHarbour MT API is present only in MT version of HVM and supports only some
subset of Harbour functionality.
The only one xHarbour MT feature not existing in current Harbour code is
support for:
      CRITICAL [STATIC] FUNCTION | PROCEDURE( [<xParams,...> )
but in current xHarbour CVS such functionality is broken. The problem can
be seen at compile time so looks that none of core developers were using
it in last years. This feature in xHarbour was implemented only in .c
code generated by xHarbour compiler (-gc[0-3] output) so it was never
working with .hrb (-gh) files or some interpreters like xBaseScript.
xHarbour has support for SYNC methods which were designed to replicate
Xbase++ functionality but their real behavior is neither Xbase++ nor
Harbour compatible. It seems to be close to SYNC CLASS methods in Xbase++
and Harbour though it's not exactly the same. There is no support for
real Xbase++ SYNC methods and Xbase++ MT programming functions and classes.

In summary MT mode in xHarbour looks like a work in progress, started few
years ago and never finished. Instead some other core code modifications
in last years systematically introduced new non MT safe code and in few
cases even broke existing MT extensions.

I cannot say if xHarbour developers plan to make something with it.
Now I cannot even describe it well because looking at the xHarbour source
I do not know what is intentional decision, what is a bug in implementation
and what is unfinished code, i.e. such code:
      JoinThread( StartThread( @func() ) )
in xHarbour has race condition and can generate RT error if new thread
finished before join operation begin. For me it's bad design decision
or fatal side effect of wrong implementation but I do not plan to guess.
This is very trivial example and I see in core code much more serious
and more complicated problems I had to resolve in Harbour which are not
touched at all in xHarbour.

I know that some people created MT programs using xHarbour but for me
current xHarbour MT mode is not production ready. At least it is very
far from current Harbour functionality and quality.

THREAD LOCAL WORK AREAS AND CONCURRENT WORK AREA ACCESS

In Harbour and Xbase++ work areas are local to the thread. It means
that each thread has its own independent work areas and aliases.
Anyhow it's possible to move work area from one thread to other one
using known from Xbase++ zero space. In Xbase++ each thread can
move his work area to zero space using:
   dbRelease( [<nWorkArea>|<cAlias>], [<bAreaBlock>] ) --> lSuccess
Then this work area can be attached by other thread using:
   dbRequest( [<cAlias>], [<lFreeArea>], ;
              [<@bAreaBlock>], [<lWait>] ) --> lSuccess
In Harbour above functions are available in XPP emulation library.
There are also core Harbour functions giving this functionality:
   hb_dbDetach( [<nWorkArea>|<cAlias>], [<xCargo>] ) -> <lSuccess>
   hb_dbRequest( [<cAlias>], [<lFreeArea>], ;
                 [<@xCargo>], [<lWait>] ) -> <lSuccess>
This is very powerful mechanism which allows to concurrently access
the same tables, i.e. this PP rules illustrates it:
   #xcommand UNLOCK WORKAREA [<cAlias>] => hb_dbDetach( <cAlias> )
   #xcommand LOCK WORKAREA <cAlias>   => hb_dbRequest( <cAlias>, .T.,, .T. )
after opening the table (USE) thread executes
   UNLOCK WORKAREA
and move work area to zero space. Then each thread which wants to make
some operations on work area has to encapsulate it in lock/unlock code,
i.e.:
   LOCK WORKAREA "DOCUMENTS"
      COUNT TO nInvoices FOR Year( DOCUMENTS->DATE ) == Year( Date() )
   UNLOCK WORKAREA

In xHarbour by default work areas are global to the application and there
is no protection mechanism against concurrent access so if two threads try
to access the same work area in the same time they can corrupt internal
RDD data. It means that for safe concurrent WA access in xHarbour user have
to create his own protection mechanism (different version of LOCK WORKAREA/
UNLOCK WORKAREA commands using some other synchronization methods available
in xHarbour, i.e. mutexes).
Later support for thread local work areas was added to xHarbour but
without any mechanism which allows to move work area from one thread
to another. This can be controlled by global SET:
   Set( _SET_WORKAREAS_SHARED, <lOnOff> )
or commands:
   SET WORKAREAS SHARED       => Set( _SET_WORKAREAS_SHARED, .T. )
   SET WORKAREAS PRIVATE      => Set( _SET_WORKAREAS_SHARED, .F. )
(SETs are global not thread local in xHarbour MT model)
Setting _SET_WORKAREAS_SHARED to .F. disables global work areas and
switches to thread local ones.
There is no technical or mathematical reason to keep such strange
implementation because it does not give any new functionality to
programmer in comparison to Xbase++ and Harbour but allows to easy
corrupt RDD internals.

HARBOUR TASKS AND MT SUPPORT IN DOS

Harbour supports threads also in systems without native thread support.
It has special code which emulates task switching so Harbour MT programs
can be compiled and executed in systems like DOS.
Harbour tasks are used by default in DOS builds in MT HVM.
They can be enabled in all other builds instead of native thread support
though on some platforms not tested so far which does not support POSIX
<ucontext.h> task code may need some modifications to safe/restore execution
context.

For .prg code Harbour tasks behave like native threads but it's important
to know that Harbour does not add in some magic way MT support to OS-es
like DOS or to CRTL. If one task executes OS or CRTL code then the context
is not switched. It will be switched when it will return from such code.

BACKGROUND TASK

In xHarbour after setting _SET_BACKGROUNDTASKS to .T. main HVM loop
periodically (once per some number of iterations defined by user in
_SET_BACKGROUNDTICK) executes user defined code interrupting current
context. This functionality is called Background Tasks. Because it's
defined in main HVM loop then it does not work for code generated with
-gc3 compiler switch. This functionality is also enabled for MT HVM
version though with different internal semantic. When _SET_BACKGROUNDTASKS
is .T. all threads try to execute user defined code using thread local
HVM loop counter initialized to some fixed value (HB_VM_UNLOCK_PERIOD=5000)
instead of_SET_BACKGROUNDTICK.

Harbour does not have such functionality and probably will not have in
such form for the following reasons:
      1. it works only from HVM loop so it will not work for code which
         does not use it, f.e. generated with -gc3 compiler switch
      2. such implementation causes same speed overhead even if programmer
         does not use background tasks
      3. the frequency of executing background task is very irregular
      4. in MT programs background tasks are executed in different way
         then in ST mode and the frequency depends on number of threads
         and their activity.
      5. in Harbour MT programs such functionality can be very easy
         implemented by user without touching core code.
      6. it's possible that in the future HVM will support asynchronous
         events what is more general mechanism and also allows to easy
         implement background tasks without touching core code.

CODEBLOCK SERIALIZATION / DESERIALIZATION

xHarbour has support for codeblock serialization. It's done by simple
storing compiled codeblock body (PCODE) in string variable and then
restoring it from such variable. Harbour does not support it intentionally
and in such form it will never be supported by HVM because it can work
only in some very limited situations and then can be source of very bad
bugs like internal HVM memory corruption or absolutely unexpected runtime
behaviors. Compiled PCODE contains references to symbol tables or even
memory addresses (i.e. macro-compiled codeblocks). If application stores
such codeblock in some external holder, i.e. memo file then after restoring
by different instance of the same application memory addresses can be
different so for macro-compiled codeblocks it's not safe at all. For
codeblocks compiled by compiler it can work until exactly the same
application is used. But any modifications in the code can change
symbol table so after restoring the result can be unpredictable.
Codeblock like:
      {|x| QOut( x ) }
can be defined in code where just after QOut() in symbol table is FErase().
Small modification in application code can cause that symbols will be
moved and above codeblock after restoring will effectively execute FErase()
instead of QOut(). What does it mean for programs which makes something like:
      Eval( cb, "The most import file is:" )
      Eval( cb, cDataFile )
      Eval( cb, "Please make backup." )
I do not have to explain.
With full respect to xHarbour users IMO such "extension" is giving knife
into monkey hands.
Codeblock serialization can be implemented in safe way but it needs at
least some partial PCODE (de|re)compiler. If Harbour users find codeblock
serialization as important extension then maybe we create such PCODE
recompiler and such functionality will be added. Anyhow it will be necessary
to keep recompiler code synced with compiler and it will be additional job
in any future compiler modifications which can change generated PCODE.

NATIVE RDDs

Both compilers shares most of RDD code. The differences are only in
some recently added features to both compilers like timestamp values
or MT mode support which are in xHarbour not fully implemented yet.
Some of the most recent modifications in xHarbour core code are local
to xHarbour only and I hope will never be part of Harbour core code.
In xHarbour RDDs starting with "RE" prefix, i.e. REDBFCDX are simple
copy of original RDDs where hb_file*() functions are replaced with
hb_fileNet*() so these are the same RDDs but with different file
transfer layer. Instead of standard OS file IO they use dedicate
file server on TCP IP socket.

In Harbour it's possible to dynamically register many alternative
RDD IO APIs and use them simultaneously in one application.
HBNETIO library in Harbour implements this so without touching even
single line in RDD code it works for all core RDDs and also 3rd
party ones if they use Harbour RDD IO API (hb_file*() functions).
Additionally client and server code in HBNETIO are fully MT safe
so can be used in MT programs without any problems.
Harbour has also similar library called HBMEMIO which allows to
use native RDDs with pseudo files stored in memory instead of real
files.
This feature was recently copied from Harbour to xHarbour but I
haven't tested if it works or not. For sure RE*DBF RDDs still exists.

In both compilers maximal file size for tables, memos and indexes is
limited only by OS and file format structures. Neither Harbour nor
xHarbour introduce own limits here.
The maximal file size for DBFs is limited by number of records
2^32-1 = 4294967295 and maximal record size: 2^16-1 = 65535 what
gives nearly 2^48 = 256 TiB as maximal .dbf file size.
The maximal memo format size depends on used memo type: DBT, FPT
or SMT and size of memo block. It's limited by maximal number of memo
blocks = 2^32 and size of memo block so it's 2^32*<size_of_memo_block>.
The default memo block size for DBT is 512 bytes, FPT - 64 bytes and
for SMT 32 bytes. So for standard memo block sizes the maximum are:
DBT-> 2 TiB, FPT-> 256 GiB, SMT-> 128 GiB. The maximal memo block size in
Harbour is 2^32 and minimal is 1 byte and it can be any value between
1 and 65536 and then any number of 64 KiB blocks. The last limitation
is introduced as workaround for some wrongly implemented in other
languages memo drivers which were setting only 16 bits in 32-bit field
in memo header. Most of other languages has limit for memo block
size at 2^15 and the block size has to be power of 2. Some of them
also introduce minimal block size limits. If programmers plans to share
data with programs compiled by such languages then he should check
their documentation to not create memo files which cannot be accessed
by them.

Maximal NTX file size for standard NTX files is 4 GiB and it's limited
by internal NTX structures. Enabling 64-bit locking in [x]Harbour change
slightly used NTX format and increase maximum NTX file size to 4 TiB.
The NTX format in [x]Harbour has also many other extensions like support
for multi-tag indexes or using record number as hidden part of index key
and many others which are unique to [x]Harbour. In practice all of CDX
extensions are supported by NTX in [x]Harbour.
The NSX format in [x]Harbour is also limited by default to 4 GiB but like
in NTX enabling 64-bit locking extend this limit to 4 TiB. It also supports
common to NTX and CDX set of features.
The CDX format is limited to 4 GiB by it's internal structure. In Harbour
just like in NTX and NSX enabling 64-bit locking slightly change the
format increasing maximum index size to 2 TiB for standard CDX page length
(512 bytes). DBFCDX and SIXCDX RDDs in Harbour support also user defined
index page size. It can be set as power of 2 value from 512 up to 8192.
Longer index pages also increase maximum file size. For 8192 bytes pages
maximum CDX file size in Harbour is 32 TiB. Enabling longer index pages
automatically change internal index format to the same as used with
64-bit locking. Longer index pages allow to use longer index key and can
interact with total index size and performance. User can make test with
different values. Harbour RDDs automatically recognize index format so
can open to work with all versions without any user settings.
I plan to add support for defined by user index page size to NTX and
NSX RDDs in the future.
So far xHarbour does not support this extended format in DBFCDX so it
cannot read such indexes created by Harbour.

Of course all such extended formats are not binary compatible with
original ones and so far can be used only by Harbour and xHarbour
(except DBFCDX) RDDs. In ADS the .adi format is modified CDX format
so Harbour can recognize them as modified CDX indexes anyhow in ADI
indexes the key values are converted to follow collation rules when
binary comparison is used and in Harbour doesn't.

All of the above sizes can be reduced by operating system (OS) or
file system (FS) limitations so it's necessary to check what is
supported by environment where [x]Harbour applications are executed.

REGULAR EXPRESSIONS

Harbour and xHarbour support regular expressions. In xHarbour they are bound
with HVM and always present. In Harbour they are fully optional. Harbour
supports few different regular expression libraries and it's possible to
use platform or CRTL native libs or PCRE which is included in Harbour.
Harbour always uses native for given library API. xHarbour supports only
PCRE modified to use POSIX regex interface which does not support strings
with embedded Chr( 0 ) characters.

Both compilers can store and reuse compiled regular expressions.
xHarbour stores them in string items and Harbour in GC pointer items.
Portable code should respect these differences.

INET SOCKETS

Both compilers support IP4 sockets with similar .prg API created by
Giancarlo Niccolai though both use different low-level implementations.
In xHarbour IP4 functions have Inet*() prefix.
In Harbour they have hb_inet*() prefix in core code and xHarbour Inet*()
functions are available in XHB library.

Harbour has also public C socket API (hbsocket.h) which is not reduced
to IP4 protocols only and is the base low-level code used for .prg level
[hb_]inet*() function implementation. This socket implementation was
rewritten from scratch, it's MT safe and was designed to hide platform
differences in BSD socket implementation. It also works in DOS builds
using WATT-32 library.
This API is available for users also at PRG level by hb_socket*() functions.

I18N SUPPORT

Harbour and xHarbour compilers have build-in support for
internationalization (I18N) and it's enabled during compilation using
the same compiler time switch -j[<file>] but the low-level implementation
in compilers and at runtime is completely different.

In xHarbour during compilation with -j switch compiler looks for all
call like i18n( <cConstValue> ) collects them and then stores them using
internal xHarbour binary format in file with .hil. The base name of such
file is taken from compiler .prg module or set by user by optional <file>
argument of -j switch. If file already exists then strings extracted from
compiled code are always appended to existing file without merging or
checking for duplicated strings. Duplicated strings are eliminated by
'hbdict' program which should be used to create translation table.
This programs reads .hil file show all existing strings and allow to
type translation for each existing string and then saves the translation
table in files with .hit extension using another internal xHarbour binary
format. At runtime application may load .hit file and then i18n() function
will look if string passed as 1st parameter exists in translation table
and if yes then it returns translated string and if not it returns original
string. Only pure 1 to 1 translation exists without any extensions like
context domains, support for plural forms or automatic CP translations.
Detail information about I18N in xHarbour can be found in xHarbour CVS
in doc/hbi18n.txt file.

Harbour compiler is very close to 'gettext' functionality with some
additional extensions. It recognize the following functions at compile
time:
      hb_i18n_gettext( <cText> [, <cDomain> ] )
      hb_i18n_gettext_strict( <cText> [, <cDomain> ] )
      hb_i18n_gettext_noop( <cText> [, <cDomain> ] )
      hb_i18n_ngettext( <nCount>, <cText> [, <cDomain> ] )
      hb_i18n_ngettext_strict( <nCount>, <cText> [, <cDomain> ] )
      hb_i18n_ngettext_noop( <nCount>, <cText> [, <cDomain> ] )
and then generates .pot text files which are gettext compatible so it's
possible to use gettext tools to process them (merge, translate, update,
etc.). Additionally Harbour has own tool called hbi18n which can merge
.pot files, add automatic translations from other .po[t] or .hbl files
and generate compiled Harbour I18N binary modules as .hbl files.
Harbour supports plural forms translations, context domains, automatic
CP translations, etc. The plural form support in Harbour is extended
in comparison to gettext and allows to use non US languages as base.
In MT mode each thread inherits I18N translation module from parent
thread but then can set and use its own one.
There is a set of HB_I18N_*() functions available for programmers at
runtime which allows to make different operations on compiled I18N
modules and also .po[t] files.
Using -w3 switch during compilation enable additional validation of
hb_i18n_[n]gettext*() parameters so compiler generates warnings.
Just like in original gettext it's suggested to use #define or #xtranslate
macros instead of direct calls to hb_i18n_[n]gettext*() functions, i.e.:
      #xtranslate _I( <x,...> ) => hb_i18n_gettext( <x> )
      #xtranslate _IN( <x,...> ) => hb_i18n_ngettext( <x> )
or:
      #xtranslate i18n( <x,...> ) => hb_i18n_gettext( <x> )
It allows to keep source code shorter and if necessary easy switch to
STRICT (for strict parameter validation) or NOOP (disabled at compile
time I18N support) versions.
Additionally Harbour compiler can recognize user I18N functions. They
have the same name as above hb_i18n_*() functions but with additional
user '_*' suffix so they are in the form like:
      hb_i18n_[n]gettext{_strict,_noop,}_*([<params,...>])
Using them users can easy introduce their own I18N runtime modules.
To reduce dependencies on external tools by default Harbour uses own
format for compiled .po[t] files but it's planned to add also native
runtime gettext support as optional user I18N interface.

ZLIB

Harbour RTL gives support at .prg level to ZLIB (HB_Z*()) and GZIP
(HB_GZ*()) functions. In xHarbour RTL only ZLIB compression/decompression
is available by: hb_Compress(), hb_Uncompress(), hb_CompressBufLen(),
hb_CompressError() and hb_CompressErrorDesc() functions.
In Harbour these functions are available in XHB library.
Original Harbour ZLIB API is different. It does not have any non MT safe
extensions, it's protected against possible overflows and it's more closer
to original ZLIB one.

SERIAL PORT SUPPORT

Harbour gives common multi-platform PRG and C level API for serial port
communication by hb_com*() functions in core libraries. It also implements
few other known in Clipper serial port interfaces in contrib libraries
like CT3 COM_*() functions in hbct library or basic support for Telepath(y)
interface in hbtpathy library. All such implementations use at low-level
code the core hb_com*() interface so they can work on all platforms.
xHarbour CVS contains only telepath library with some very basic Telepath(y)
interface which is not available for all platforms.
xHarbour.com distribute also library compatible with CT3 COM*() interface
but only for MS-Windows builds.
It's also possible to ask on xHarbour.news group for HBCOM library.
AFAIK binaries are available for MS-Windows and Linux.
In Harbour the interface given by HBCOM library is also available
inside HBCOMM contrib library which internally uses hb_com*() core
API so it's portable between different OS-es.

MACRO COMPILER

In Harbour macro compiler supports the same expressions as compiler.
The only one and documented difference is support for VFP like datetime
constant values {^...} which are supported only by compiler.
It's guaranteed that any valid expressions not longer then 8 MiB will be
cleanly compiled and executed by Harbour. It's possible to compile and
execute longer expressions (in practice there is no maximal size limit)
but in such case it's not guaranteed that all expressions can be compiled,
f.e. macro expressions containing string constant values longer then 16 MiB
cannot be compiled. It's only guaranteed that if Harbour cannot compile
macro expression then it generates RT error and never generates broken
code.

In xHarbour there are differences between macro compiler and compiler
and not all expressions supported at compile time can be used in macro
compiler. The maximum size for valid expressions which are always correctly
compiled by xHarbour is 32 KiB. Expressions longer then 32 KiB can be compiled
without any RT error by xHarbour but it's possible that wrong code will be
generated for them which may cause any unpredictable behavior so user should
not use such expressions. There is no clean way in xHarbour to check if macro
expression longer then 32 KiB will be correctly compiled.

Just like Clipper neither Harbour nor xHarbour support statements in macro
compiler so extended codeblocks also cannot be compiled.
Now such functionality is supported only by compiler library in Harbour.

In Clipper the documented maximum size of expression which can be compiled
by macro compiler is 256. Sometime Clipper's macro compiler can compile
a little bit longer expressions generating correct PCODE anyhow just like
in xHarbour it may also cause some unpredictable results (i.e. application
crash) though in most of cases it's RT error during macro compilation.

COMPILER LIBRARY

Harbour has compiler library which allows to integrate Harbour compiler
with user applications and use it at runtime to compile new code which
can be also immediately executed or dynamically registered as part of
code. In Harbour compiler code is fully MT safe so the compiler library
can be safely used in MT programs, f.e. for parallel compilation.
Now compiler library is on pure GPL license so any programs which wants
to use it have to respect the GPL license too.

PP LIBRARY

Harbour and xHarbour has PP library which allows to use preprocessor
in user .prg code. Current PP in both compilers are fully MT safe without
any internal locks so it can be used concurrently in MT programs.

LEXER

xHarbour uses SIMPLEX as lexer for compiler and macro compiler.
Introducing this lexer was next reason of the xHarbour fork.
Internally SIMPLEX uses a lot of static variables and C compile time
macros to speed-up some operations. It's faster than lexer used before
based on code generated by FLEX but it's not MT safe and very hard
to debug due to code hidden by nested C macros. The reduced version of
SIMPLEX is used by xHarbour in macro compiler.

The preprocessor and lexer are not integrated in xHarbour. The preprocessed
code is converted from tokens used by PP to a string and then this string
is divided to tokens again by SIMPLEX.

Harbour compiler accepts tokens used by preprocessor so in fact it does
not have separate compiler lexer at all. The compiler lexer code (complex.c)
is only simple translator of token values between PP and bison terminal
symbols. For macro compiler Harbour by default uses small lexer (macrolex.c)
which is optimized for speed but it can also use PP lexer when Harbour is
compiled with HB_MACRO_PPLEX. All Harbour lexers are fully MT safe without
any internal locks. They are also much faster then xHarbour ones.

CONTRIB LIBRARIES

Some of xHarbour core libraries like CT, TIP or ODBC are supported by
Harbour as contrib library. In practice Harbour covers whole xHarbour
functionality with some fixes and extensions. In contrib tree Harbour
has also many other libraries which are unique to Harbour, like HBCURL,
RDDSQL, HBSSL, GTWVG, HBQT, HBXBP, ... which give many important
extensions. Some of them can be easily ported to xHarbour but some others
not due to not fully functional MT model in xHarbour or missing some
important functionality like thread safe support for many GTs in single
application and multi window GTs with runtime window switching. It's
possible that in the future some important fixes and extensions will
be done in xHarbour core code what allow to port some of Harbour contrib
libraries to xHarbour.

Detail description of contrib libraries needs separate text and will be
much longer then this whole document and maybe will be created in the
future.

PORTABILITY

Both compilers were ported to many different platforms on different hardware.
The list of supported platforms is really long: different *nixes (Linux,
macOS, SunOS, HP-UX, *BSD,...) on little and big-endian 32 and 64-bit
machines, DOS, OS2, Windows (32/64-bit) and WinCE (Harbour only).
In practice they can be ported quite easy to nearly each OS and hardware
if it passes two important conditions: it's ASCII based machine and has
support for 8bit bytes. Due to cleaner code it's easier to port Harbour
to new platform then xHarbour but the difference seems to not be huge
for someone who has enough knowledge about destination platform.
Now all popular operating systems are supported by both compilers
with the exception to WinCE which is not supported by xHarbour yet.

C LEVEL COMPATIBILITY

The main difference between Harbour and xHarbour in public C API
is in value quantifiers. Harbour fully respect 'const' variable
attribute and does not remove it from returned values. It helps to
keep code clean and locate places where string constant values
are overwritten what is illegal. It also helps C compiler to better
optimize the code because it has additional information that some
variables/memory regions are read-only and cannot be modified during
code execution what can give some additional speed improvement.
If some C code does not respect it then during compilation with
Harbour header files C compilers usually generate warning and
C++ ones errors, i.e. code like:
      char * pszValue = hb_parc( 1 );
is wrong because hb_parc() returns pointer to read-only strings
which cannot be changed by user code. So this code should be
changed to:
      const char * pszValue = hb_parc( 1 );
Now if compiled code tries to make something like:
      pszValue[ index ] = 'X';
what is illegal in Harbour, xHarbour and also in Clipper for pointers
returned by _parc() function then compiler generate warning or error
about writing to read-only location.
In summary it means that Harbour forces to keep code clean and use
more strict declarations but xHarbour and Clipper don't and wrong
code can be silently compiled without any warnings or errors.
The second important difference is in hb_par*() and hb_stor*() functions.
In Harbour there are hb_parv*() and hb_storv*() functions which accepts
parameters like in Clipper _par*() and _stor*() functions with optional
array indexes, i.e.:
      const char * pszValue = hb_parvc( 1, 1 );
      int          iValue   = hb_parvni( 1, 2 );
and functions without 'v' which do not allow to access array items, i.e.:
      const char * pszValue = hb_parc( 1 );
      int          iValue   = hb_parni( 2 );
These functions (without 'v') are also much more restrictive on accepted
parameters and do not make hidden parameter translations, i.e. hb_parl()
returns HB_TRUE only for logical parameters and does not accept numeric
values like hb_parvl() or _parl().
New functions without array index support were introduced to safely access
parameters without strict type checking. Such code:
      HB_FUNC( NOT )
      {
         int iValue = _parni( 1 );
         hb_retni( ~iValue );
      }
is not fully safe when array parameter is passed from .prg level, i.e.
      ? NOT( { 5, 4, 7 } )
In such case the behavior is unpredictable because _parni() function
tries to access from C function frame unexciting parameter with array
index. Depending on used hardware (CPU) different things may happen.
On some platforms _parni( 1 ) always return 0, on some other random
value from the array and on some others with hardware function frame
protection it generates exception and application crash. To make the
above code safe programmer should change it to:
      HB_FUNC( NOT )
      {
         int iValue = HB_ISNUM( 1 ) ? _parni( 1 ) : 0;
         hb_retni( ~iValue );
      }
or:
      HB_FUNC( NOT )
      {
         int iValue = _parni( 1, 0 );
         hb_retni( ~iValue );
      }
xHarbour does not have hb_par*() and hb_stor*() functions without
optional array index parameters so they cannot be safely used to
access parameters without type checking. It may also create problems
when xHarbour code is moved to Harbour because hb_par*() and
hb_stor*() functions in xHarbour work like hb_parv*() and hb_storv*()
in Harbour. It can be seen in code like:
      int iValue = hb_parni( 1, 1 );
C compiler refuses to compile such code with Harbour header files
and it has to be modified to:
      int iValue = hb_parvni( 1, 1 );
For portable code which access array items using hb_par/hb_stor API
I suggest to use hb_parv*() and hb_storv*() functions and add in
some header file:
      #ifdef __XHARBOUR__
         #define hb_parvc        hb_parc
         #define hb_parvni       hb_parni
         [...]
         #define hb_storvc       hb_storc
         #define hb_storvni      hb_storni
         [...]
      #endif
or use Clipper compatible functions defined in extend.api: _par*()
and _stor*().
I hope that in the future xHarbour will have above #define translation
in core code or maybe even support for hb_parv*() and hb_storv*() functions.

Next important difference between Harbour and xHarbour is access to internal
HVM structures. In Harbour internal HVM structures are hidden by default so
programmers cannot access them, i.e. PHB_ITEM is mapped as 'void *' pointer
so code like:
      iValue = pItem->item.asInteger.value;
cannot be compiled and programmers should change it to use official API:
      iValue = hb_itemGetNI( pItem );
It's very important for core developers because public and internal C API
is separated so we can easy introduce modifications in HVM internals without
worrying about backward compatibility and it allows to keep the same code
for different HVM versions (i.e. for MT and ST modes) so in Harbour only
HBVM library is different for MT mode and all others are the same.
Such separation also greatly increase C code quality eliminating possible
GPF traps, wrong behavior in code created without enough knowledge about
HVM internals or code which blocks adding new extensions.
It's something what should be done in xHarbour core code where lot of code
is simply wrong due to direct access to HVM internals, i.e.
source/rtl/txtline.c[299]:
      Term[i] = (char *) (&Opt)->item.asString.value;
and we have GPF trap if given array item is not a string.
source/rtl/version[108]:
      PHB_ITEM pQuery = hb_param( 1, HB_IT_INTEGER );
      [...]
      if( pQuery )
      {
         int iQuery = pQuery->item.asInteger.value;
         [...]
so it does not work if passed numeric parameter is not internally stored
as HB_IT_INTEGER value and programmer cannot control it yourself, i.e.
parameters can be read from table or passed from remote client and then
after deserialization stored as HB_IT_LONG or HB_IT_DOUBLE.
source/rtl/direct.c[124]:
      HB_ITEM_NEW( SubDir );
      hb_fsDirectory( &SubDir, szSubdir, szAttributes, FALSE, TRUE );
      hb_fsDirectoryCrawler( &SubDir, pResult, szFName, szAttributes, sRegEx );
This code creates complex item (array) on C stack so garbage collector does
not know anything about it so it cannot be activated otherwise it will cause
GPF. It means that this code blocks adding automatic garbage collector
activation in memory manager.
These are only examples and anyone can find many other similar problems
in xHarbour core code so in my opinion API separation with core code
cleanup is something what has to be done in xHarbour in the future.
Of course such separation does not mean that we can forbid 3rd party
programmers to access HVM internals. Programmers creating code for Harbour
can include before any other Harbour header files "hbvmint.h", i.e.:
      #include "hbvmint.h"
      #include "hbapi.h"
and it enable access to HVM internals anyhow in such case we do not
guaranty that such code will work in the future Harbour versions so
we strongly recommend to not use it.
In Harbour core code only HBVM library is compiled with access to
HVM internals. All other code uses official API. In contrib code
"hbvmint.h" is used in few places in XHB library to emulate some core
HVM xHarbour extensions.

In xHarbour there are few functions which allows to create string items
without terminating '\0' characters like: hb_itemPutCRaw(),
hb_itemPutCRawStatic(), hb_retclenAdoptRaw(). Because string items
without terminating '\0' character exploit problems in any C code which
needs ASCIIZ string creating possible GPF traps in all str*() and similar
functions then in Harbour such functionality is illegal and will never
be added to core code. Additionally in Harbour functions corresponding to
xHarbour functions: hb_retclenStatic(). hb_itemPutCLStatic() have additional
protection against creating such strings and generate internal error when
programmer tries to pass wrong strings without trailing '\0' character.
It may exploit problems in wrong code ported to Harbour.

Most of other public C functions in Harbour and xHarbour are compatible
anyhow there are some differences i.e. in hash array or timestamp API
which have to be covered by #ifdef __XHARBOUR__ conditional compilation.
Programmers creating portable code have to now about them.
For people who wants to use only extended API for code used for Clipper
and [x]Harbour I suggest to use small definition:
      #ifndef __HARBOUR__
         #define HB_FUNC( fun )  CLIPPER fun ( void )
      #endif
This definition allows to easy create code which can be compiled with
Clipper and Harbour or xHarbour header files (extend.api).
It can be even added to Clipper's extend.api and then we can compile
this code:
      #include "extend.api"
      HB_FUNC( NOT )
      {
         int iValue = _parni( 1, 0 );
         hb_retni( ~iValue );
      }
for Clipper, Harbour and xHarbour. Please only remember that linkers
used with [x]Harbour are case sensitive and always use upper letters
for function names. In Clipper it was not necessary.

HBRUN / XBSCRIPT

xHarbour has .prg code interpreter mostly written also as .prg code.
It's called xbscript. AFAIK It's also based for commercial product
xBaseScript distributed by xHarbour.com.
In Harbour the same functionality was reached in just few lines in
a program called hbrun because it's possible to use compiler library.
HBRUN can compile any given code just like Harbour Compiler (in fact
it uses the same compiler code from compiler library) and then dynamically
execute it.

Unlike XBSCRIPT the HBRUN does not cause any performance reduction in
comparison to standalone compilation with Harbour compiler to PCODE
(-gc[0-2], -gh) because exactly the same PCODE is generated and later
executed in both cases. Also the compilation phase is many times faster
then in XBSCRIPT.
The commercial version of xBaseScript has some extensions which allows
to integrate it with IE and/or IIS. HBRUN from Harbour does not have such
functionality yet.

HBMK2

It's very nice new tool in Harbour which hides differences between
platforms and C compiler used with Harbour. It should help new
users to start working with Harbour (i.e. we can create copies of
hbmk2 executable file called 'clipper' and 'rtlink' or 'blinker'
and then it's possible to use them with original Clipper make file
projects (i.e. in .rmk files created for Clipper and RMAKE).
hbmk2 also nicely helps in creating binaries for different platforms
(cross compilation), i.e. in creating WinCE programs in Linux or
desktop Windows.

There are many features supported by this tool which can greatly
help new and advanced Harbour users. Their detail description is
not this text goal. If someone is interested in list of supported
options then he can use hbmk2 with '--help' parameter. In the nearest
future hbmk2 should replace hb* scripts.

This tool is unique to Harbour and does not exist in xHarbour.

PERFORMANCE AND RESOURCE USAGE

Harbour internal structures are optimized for memory usage and are smaller
than xHarbour ones so total memory usage by Harbour is also smaller.
The difference depends on type of code but usually Harbour needs about
20%-25% less memory then xHarbour. The size of final executable is also
usually smaller due to reduced dependencies which are necessary for pure
HVM code though in current days it's less important issue.
Much bigger difference is in performance.

Both compiler shares a lot of common C code in some subsystems like RDDs
so in many operations which needs a lot of CPU to execute the same C code
the performance is nearly the same. The difference begins to be noticeable
when we compare HVM performance and time necessary to execute .prg code.
Harbour is ~75% faster in ST mode and over 100% faster in MT mode.
In harbour/tests/speedtst.prg we have simple test which can be used to
compare performance of different compilers.
The tests below were done using AMD Phenom(tm) 8450 Triple-Core Processor
2100 MHZ with 32-bit Linux kernels.

Here are results for ST mode:

   2009-07-28 20:48:12 Linux 2.6.25.20-0.4-pae i686
   Harbour 2.0.0beta2 (Rev. 11910) GNU C 4.4 (32-bit) x86
   THREADS: 0
   N_LOOPS: 1000000
   [ T000: empty loop overhead ]...................................0.03
   ====================================================================
   [ T001: x := L_C ]..............................................0.02
   [ T002: x := L_N ]..............................................0.02
   [ T003: x := L_D ]..............................................0.02
   [ T004: x := S_C ]..............................................0.05
   [ T005: x := S_N ]..............................................0.03
   [ T006: x := S_D ]..............................................0.03
   [ T007: x := M->M_C ]...........................................0.06
   [ T008: x := M->M_N ]...........................................0.04
   [ T009: x := M->M_D ]...........................................0.04
   [ T010: x := M->P_C ]...........................................0.06
   [ T011: x := M->P_N ]...........................................0.04
   [ T012: x := M->P_D ]...........................................0.04
   [ T013: x := F_C ]..............................................0.15
   [ T014: x := F_N ]..............................................0.25
   [ T015: x := F_D ]..............................................0.10
   [ T016: x := o:Args ]...........................................0.14
   [ T017: x := o[2] ].............................................0.10
   [ T018: Round( i / 1000, 2 ) ]..................................0.21
   [ T019: Str( i / 1000 ) ].......................................0.55
   [ T020: Val( s ) ]..............................................0.28
   [ T021: Val( a [ i % 16 + 1 ] ) ]...............................0.39
   [ T022: DToS( d - i % 10000 ) ].................................0.33
   [ T023: Eval( { || i % 16 } ) ].................................0.36
   [ T024: Eval( bc := { || i % 16 } ) ]...........................0.20
   [ T025: Eval( { |x| x % 16 }, i ) ].............................0.29
   [ T026: Eval( bc := { |x| x % 16 }, i ) ].......................0.20
   [ T027: Eval( { |x| f1( x ) }, i ) ]............................0.31
   [ T028: Eval( bc := { |x| f1( x ) }, i ) ]......................0.24
   [ T029: Eval( bc := &("{ |x| f1( x ) }"), i ) ].................0.23
   [ T030: x := &( "f1(" + Str(i) + ")" ) ]........................2.95
   [ T031: bc := &( "{|x|f1(x)}" ), Eval( bc, i ) ]................2.97
   [ T032: x := ValType( x ) +  ValType( i ) ].....................0.34
   [ T033: x := StrZero( i % 100, 2 ) $ a[ i % 16 + 1 ] ]..........0.62
   [ T034: x := a[ i % 16 + 1 ] == s ].............................0.21
   [ T035: x := a[ i % 16 + 1 ] = s ]..............................0.23
   [ T036: x := a[ i % 16 + 1 ] >= s ].............................0.23
   [ T037: x := a[ i % 16 + 1 ] <= s ].............................0.23
   [ T038: x := a[ i % 16 + 1 ] < s ]..............................0.23
   [ T039: x := a[ i % 16 + 1 ] > s ]..............................0.23
   [ T040: AScan( a, i % 16 ) ]....................................0.24
   [ T041: AScan( a, { |x| x == i % 16 } ) ].......................2.34
   [ T042: iif( i%1000==0, a:={}, ) , AAdd(a,{i,1,.T.,s,s2,a2 ]....0.70
   [ T043: x := a ]................................................0.04
   [ T044: x := {} ]...............................................0.12
   [ T045: f0() ]..................................................0.06
   [ T046: f1( i ) ]...............................................0.10
   [ T047: f2( c[1...8] ) ]........................................0.10
   [ T048: f2( c[1...40000] ) ]....................................0.09
   [ T049: f2( @c[1...40000] ) ]...................................0.09
   [ T050: f2( @c[1...40000] ), c2 := c ]..........................0.11
   [ T051: f3( a, a2, s, i, s2, bc, i, n, x ) ]....................0.31
   [ T052: f2( a ) ]...............................................0.11
   [ T053: x := f4() ].............................................0.45
   [ T054: x := f5() ].............................................0.22
   [ T055: x := Space(16) ]........................................0.17
   [ T056: f_prv( c ) ]............................................0.31
   ====================================================================
   [ total application time: ]....................................20.27
   [ total real time: ]...........................................20.51

   2009-07-28 20:48:46 Linux 2.6.25.20-0.4-pae i686
   xHarbour build 1.2.1 Intl. (SimpLex) (Rev. 6517) GNU C 4.4 (32 bit) ?
   THREADS: 0
   N_LOOPS: 1000000
   [ T000: empty loop overhead ]...................................0.09
   ====================================================================
   [ T001: x := L_C ]..............................................0.02
   [ T002: x := L_N ]..............................................0.00
   [ T003: x := L_D ]..............................................0.02
   [ T004: x := S_C ]..............................................0.00
   [ T005: x := S_N ]..............................................0.01
   [ T006: x := S_D ]..............................................0.01
   [ T007: x := M->M_C ]...........................................0.02
   [ T008: x := M->M_N ]...........................................0.02
   [ T009: x := M->M_D ]...........................................0.01
   [ T010: x := M->P_C ]...........................................0.02
   [ T011: x := M->P_N ]...........................................0.06
   [ T012: x := M->P_D ]...........................................0.01
   [ T013: x := F_C ]..............................................0.18
   [ T014: x := F_N ]..............................................0.20
   [ T015: x := F_D ]..............................................0.10
   [ T016: x := o:Args ]...........................................0.27
   [ T017: x := o[2] ].............................................0.05
   [ T018: Round( i / 1000, 2 ) ]..................................0.30
   [ T019: Str( i / 1000 ) ].......................................0.69
   [ T020: Val( s ) ]..............................................0.34
   [ T021: Val( a [ i % 16 + 1 ] ) ]...............................0.51
   [ T022: DToS( d - i % 10000 ) ].................................0.44
   [ T023: Eval( { || i % 16 } ) ].................................0.54
   [ T024: Eval( bc := { || i % 16 } ) ]...........................0.36
   [ T025: Eval( { |x| x % 16 }, i ) ].............................0.43
   [ T026: Eval( bc := { |x| x % 16 }, i ) ].......................0.33
   [ T027: Eval( { |x| f1( x ) }, i ) ]............................0.58
   [ T028: Eval( bc := { |x| f1( x ) }, i ) ]......................0.48
   [ T029: Eval( bc := &("{ |x| f1( x ) }"), i ) ].................0.51
   [ T030: x := &( "f1(" + Str(i) + ")" ) ]........................4.70
   [ T031: bc := &( "{|x|f1(x)}" ), Eval( bc, i ) ]................5.62
   [ T032: x := ValType( x ) +  ValType( i ) ].....................0.64
   [ T033: x := StrZero( i % 100, 2 ) $ a[ i % 16 + 1 ] ]..........0.87
   [ T034: x := a[ i % 16 + 1 ] == s ].............................0.28
   [ T035: x := a[ i % 16 + 1 ] = s ]..............................0.28
   [ T036: x := a[ i % 16 + 1 ] >= s ].............................0.27
   [ T037: x := a[ i % 16 + 1 ] <= s ].............................0.28
   [ T038: x := a[ i % 16 + 1 ] < s ]..............................0.27
   [ T039: x := a[ i % 16 + 1 ] > s ]..............................0.28
   [ T040: AScan( a, i % 16 ) ]....................................0.47
   [ T041: AScan( a, { |x| x == i % 16 } ) ].......................3.95
   [ T042: iif( i%1000==0, a:={}, ) , AAdd(a,{i,1,.T.,s,s2,a2 ]....0.89
   [ T043: x := a ]................................................0.01
   [ T044: x := {} ]...............................................0.11
   [ T045: f0() ]..................................................0.14
   [ T046: f1( i ) ]...............................................0.19
   [ T047: f2( c[1...8] ) ]........................................0.19
   [ T048: f2( c[1...40000] ) ]....................................0.18
   [ T049: f2( @c[1...40000] ) ]...................................0.18
   [ T050: f2( @c[1...40000] ), c2 := c ]..........................0.22
   [ T051: f3( a, a2, s, i, s2, bc, i, n, x ) ]....................0.39
   [ T052: f2( a ) ]...............................................0.18
   [ T053: x := f4() ].............................................0.81
   [ T054: x := f5() ].............................................0.49
   [ T055: x := Space(16) ]........................................0.33
   [ T056: f_prv( c ) ]............................................0.75
   ====================================================================
   [ total application time: ]....................................34.52
   [ total real time: ]...........................................34.96

And here are results for MT mode:

   2009-07-28 20:52:43 Linux 2.6.25.20-0.4-pae i686
   Harbour 2.0.0beta2 (Rev. 11910) (MT) GNU C 4.4 (32-bit) x86
   THREADS: 0
   N_LOOPS: 1000000
   [ T000: empty loop overhead ]...................................0.04
   ====================================================================
   [ T001: x := L_C ]..............................................0.07
   [ T002: x := L_N ]..............................................0.02
   [ T003: x := L_D ]..............................................0.02
   [ T004: x := S_C ]..............................................0.05
   [ T005: x := S_N ]..............................................0.03
   [ T006: x := S_D ]..............................................0.04
   [ T007: x := M->M_C ]...........................................0.05
   [ T008: x := M->M_N ]...........................................0.05
   [ T009: x := M->M_D ]...........................................0.05
   [ T010: x := M->P_C ]...........................................0.05
   [ T011: x := M->P_N ]...........................................0.05
   [ T012: x := M->P_D ]...........................................0.04
   [ T013: x := F_C ]..............................................0.16
   [ T014: x := F_N ]..............................................0.24
   [ T015: x := F_D ]..............................................0.10
   [ T016: x := o:Args ]...........................................0.15
   [ T017: x := o[2] ].............................................0.09
   [ T018: Round( i / 1000, 2 ) ]..................................0.22
   [ T019: Str( i / 1000 ) ].......................................0.62
   [ T020: Val( s ) ]..............................................0.28
   [ T021: Val( a [ i % 16 + 1 ] ) ]...............................0.41
   [ T022: DToS( d - i % 10000 ) ].................................0.39
   [ T023: Eval( { || i % 16 } ) ].................................0.44
   [ T024: Eval( bc := { || i % 16 } ) ]...........................0.27
   [ T025: Eval( { |x| x % 16 }, i ) ].............................0.38
   [ T026: Eval( bc := { |x| x % 16 }, i ) ].......................0.24
   [ T027: Eval( { |x| f1( x ) }, i ) ]............................0.40
   [ T028: Eval( bc := { |x| f1( x ) }, i ) ]......................0.29
   [ T029: Eval( bc := &("{ |x| f1( x ) }"), i ) ].................0.31
   [ T030: x := &( "f1(" + Str(i) + ")" ) ]........................2.83
   [ T031: bc := &( "{|x|f1(x)}" ), Eval( bc, i ) ]................3.16
   [ T032: x := ValType( x ) +  ValType( i ) ].....................0.38
   [ T033: x := StrZero( i % 100, 2 ) $ a[ i % 16 + 1 ] ]..........0.75
   [ T034: x := a[ i % 16 + 1 ] == s ].............................0.28
   [ T035: x := a[ i % 16 + 1 ] = s ]..............................0.29
   [ T036: x := a[ i % 16 + 1 ] >= s ].............................0.29
   [ T037: x := a[ i % 16 + 1 ] <= s ].............................0.29
   [ T038: x := a[ i % 16 + 1 ] < s ]..............................0.29
   [ T039: x := a[ i % 16 + 1 ] > s ]..............................0.29
   [ T040: AScan( a, i % 16 ) ]....................................0.30
   [ T041: AScan( a, { |x| x == i % 16 } ) ].......................2.87
   [ T042: iif( i%1000==0, a:={}, ) , AAdd(a,{i,1,.T.,s,s2,a2 ]....0.81
   [ T043: x := a ]................................................0.03
   [ T044: x := {} ]...............................................0.13
   [ T045: f0() ]..................................................0.07
   [ T046: f1( i ) ]...............................................0.11
   [ T047: f2( c[1...8] ) ]........................................0.11
   [ T048: f2( c[1...40000] ) ]....................................0.11
   [ T049: f2( @c[1...40000] ) ]...................................0.11
   [ T050: f2( @c[1...40000] ), c2 := c ]..........................0.14
   [ T051: f3( a, a2, s, i, s2, bc, i, n, x ) ]....................0.33
   [ T052: f2( a ) ]...............................................0.11
   [ T053: x := f4() ].............................................0.45
   [ T054: x := f5() ].............................................0.24
   [ T055: x := Space(16) ]........................................0.19
   [ T056: f_prv( c ) ]............................................0.38
   ====================================================================
   [ total application time: ]....................................23.10
   [ total real time: ]...........................................23.78

   2009-07-28 20:53:24 Linux 2.6.25.20-0.4-pae i686
   xHarbour build 1.2.1 Intl. (SimpLex) (Rev. 6517) (MT) GNU C 4.4 (32 bit) ?
   THREADS: 0
   N_LOOPS: 1000000
   [ T000: empty loop overhead ]...................................0.20
   ====================================================================
   [ T001: x := L_C ]..............................................0.00
   [ T002: x := L_N ]..............................................0.00
   [ T003: x := L_D ]..............................................0.00
   [ T004: x := S_C ]..............................................0.00
   [ T005: x := S_N ]..............................................0.00
   [ T006: x := S_D ]..............................................0.00
   [ T007: x := M->M_C ]...........................................0.19
   [ T008: x := M->M_N ]...........................................0.23
   [ T009: x := M->M_D ]...........................................0.18
   [ T010: x := M->P_C ]...........................................0.24
   [ T011: x := M->P_N ]...........................................0.24
   [ T012: x := M->P_D ]...........................................0.21
   [ T013: x := F_C ]..............................................0.17
   [ T014: x := F_N ]..............................................0.21
   [ T015: x := F_D ]..............................................0.07
   [ T016: x := o:Args ]...........................................0.36
   [ T017: x := o[2] ].............................................0.05
   [ T018: Round( i / 1000, 2 ) ]..................................0.43
   [ T019: Str( i / 1000 ) ].......................................0.83
   [ T020: Val( s ) ]..............................................0.41
   [ T021: Val( a [ i % 16 + 1 ] ) ]...............................0.65
   [ T022: DToS( d - i % 10000 ) ].................................0.56
   [ T023: Eval( { || i % 16 } ) ].................................0.78
   [ T024: Eval( bc := { || i % 16 } ) ]...........................0.47
   [ T025: Eval( { |x| x % 16 }, i ) ].............................0.66
   [ T026: Eval( bc := { |x| x % 16 }, i ) ].......................0.49
   [ T027: Eval( { |x| f1( x ) }, i ) ]............................0.88
   [ T028: Eval( bc := { |x| f1( x ) }, i ) ]......................0.73
   [ T029: Eval( bc := &("{ |x| f1( x ) }"), i ) ].................0.72
   [ T030: x := &( "f1(" + Str(i) + ")" ) ]........................5.69
   [ T031: bc := &( "{|x|f1(x)}" ), Eval( bc, i ) ]................6.18
   [ T032: x := ValType( x ) +  ValType( i ) ].....................0.79
   [ T033: x := StrZero( i % 100, 2 ) $ a[ i % 16 + 1 ] ]..........1.12
   [ T034: x := a[ i % 16 + 1 ] == s ].............................0.37
   [ T035: x := a[ i % 16 + 1 ] = s ]..............................0.36
   [ T036: x := a[ i % 16 + 1 ] >= s ].............................0.42
   [ T037: x := a[ i % 16 + 1 ] <= s ].............................0.36
   [ T038: x := a[ i % 16 + 1 ] < s ]..............................0.37
   [ T039: x := a[ i % 16 + 1 ] > s ]..............................0.38
   [ T040: AScan( a, i % 16 ) ]....................................0.64
   [ T041: AScan( a, { |x| x == i % 16 } ) ].......................6.21
   [ T042: iif( i%1000==0, a:={}, ) , AAdd(a,{i,1,.T.,s,s2,a2 ]....1.29
   [ T043: x := a ]................................................0.00
   [ T044: x := {} ]...............................................0.15
   [ T045: f0() ]..................................................0.21
   [ T046: f1( i ) ]...............................................0.26
   [ T047: f2( c[1...8] ) ]........................................0.27
   [ T048: f2( c[1...40000] ) ]....................................0.27
   [ T049: f2( @c[1...40000] ) ]...................................0.25
   [ T050: f2( @c[1...40000] ), c2 := c ]..........................0.33
   [ T051: f3( a, a2, s, i, s2, bc, i, n, x ) ]....................0.57
   [ T052: f2( a ) ]...............................................0.27
   [ T053: x := f4() ].............................................0.99
   [ T054: x := f5() ].............................................0.65
   [ T055: x := Space(16) ]........................................0.38
   [ T056: f_prv( c ) ]............................................1.24
   ====================================================================
   [ total application time: ]....................................50.90
   [ total real time: ]...........................................51.49

Please note that in xHarbour MT mode many important things resolved in
Harbour have not been implemented so far so it's hard to say how may
look final performance.

Recently I've repeated above tests on the same Hardware but using 64-bit
Linux kernels and 64-bit Harbour builds and here the difference is even
bigger. Here are results for ST mode:

   2009-10-27 13:24:41 Linux 2.6.31.3-1-desktop x86_64
   Harbour 2.0.0beta3 (Rev. 12776) GNU C 4.4 (64-bit) x86-64
   THREADS: 0
   N_LOOPS: 1000000
   [ T000: empty loop overhead ]...................................0.02
   ====================================================================
   [ T001: x := L_C ]..............................................0.03
   [ T002: x := L_N ]..............................................0.02
   [ T003: x := L_D ]..............................................0.02
   [ T004: x := S_C ]..............................................0.03
   [ T005: x := S_N ]..............................................0.03
   [ T006: x := S_D ]..............................................0.03
   [ T007: x := M->M_C ]...........................................0.03
   [ T008: x := M->M_N ]...........................................0.04
   [ T009: x := M->M_D ]...........................................0.03
   [ T010: x := M->P_C ]...........................................0.04
   [ T011: x := M->P_N ]...........................................0.03
   [ T012: x := M->P_D ]...........................................0.03
   [ T013: x := F_C ]..............................................0.10
   [ T014: x := F_N ]..............................................0.17
   [ T015: x := F_D ]..............................................0.08
   [ T016: x := o:Args ]...........................................0.11
   [ T017: x := o[2] ].............................................0.06
   [ T018: Round( i / 1000, 2 ) ]..................................0.15
   [ T019: Str( i / 1000 ) ].......................................0.32
   [ T020: Val( s ) ]..............................................0.18
   [ T021: Val( a [ i % 16 + 1 ] ) ]...............................0.28
   [ T022: DToS( d - i % 10000 ) ].................................0.28
   [ T023: Eval( { || i % 16 } ) ].................................0.29
   [ T024: Eval( bc := { || i % 16 } ) ]...........................0.18
   [ T025: Eval( { |x| x % 16 }, i ) ].............................0.24
   [ T026: Eval( bc := { |x| x % 16 }, i ) ].......................0.18
   [ T027: Eval( { |x| f1( x ) }, i ) ]............................0.27
   [ T028: Eval( bc := { |x| f1( x ) }, i ) ]......................0.22
   [ T029: Eval( bc := &("{ |x| f1( x ) }"), i ) ].................0.22
   [ T030: x := &( "f1(" + Str(i) + ")" ) ]........................2.05
   [ T031: bc := &( "{|x|f1(x)}" ), Eval( bc, i ) ]................2.41
   [ T032: x := ValType( x ) +  ValType( i ) ].....................0.27
   [ T033: x := StrZero( i % 100, 2 ) $ a[ i % 16 + 1 ] ]..........0.54
   [ T034: x := a[ i % 16 + 1 ] == s ].............................0.17
   [ T035: x := a[ i % 16 + 1 ] = s ]..............................0.19
   [ T036: x := a[ i % 16 + 1 ] >= s ].............................0.18
   [ T037: x := a[ i % 16 + 1 ] <= s ].............................0.19
   [ T038: x := a[ i % 16 + 1 ] < s ]..............................0.19
   [ T039: x := a[ i % 16 + 1 ] > s ]..............................0.18
   [ T040: AScan( a, i % 16 ) ]....................................0.22
   [ T041: AScan( a, { |x| x == i % 16 } ) ].......................2.01
   [ T042: iif( i%1000==0, a:={}, ) , AAdd(a,{i,1,.T.,s,s2,a2 ]....0.53
   [ T043: x := a ]................................................0.02
   [ T044: x := {} ]...............................................0.08
   [ T045: f0() ]..................................................0.05
   [ T046: f1( i ) ]...............................................0.08
   [ T047: f2( c[1...8] ) ]........................................0.07
   [ T048: f2( c[1...40000] ) ]....................................0.07
   [ T049: f2( @c[1...40000] ) ]...................................0.08
   [ T050: f2( @c[1...40000] ), c2 := c ]..........................0.09
   [ T051: f3( a, a2, s, i, s2, bc, i, n, x ) ]....................0.21
   [ T052: f2( a ) ]...............................................0.08
   [ T053: x := f4() ].............................................0.34
   [ T054: x := f5() ].............................................0.16
   [ T055: x := Space(16) ]........................................0.12
   [ T056: f_prv( c ) ]............................................0.21
   ====================================================================
   [ total application time: ]....................................15.60
   [ total real time: ]...........................................15.61

   2009-10-27 13:25:10 Linux 2.6.31.3-1-desktop x86_64
   xHarbour build 1.2.1 Intl. (SimpLex) (Rev. 6629) GNU C 4.4 (64 bit) ?
   THREADS: 0
   N_LOOPS: 1000000
   [ T000: empty loop overhead ]...................................0.06
   ====================================================================
   [ T001: x := L_C ]..............................................0.05
   [ T002: x := L_N ]..............................................0.03
   [ T003: x := L_D ]..............................................0.04
   [ T004: x := S_C ]..............................................0.05
   [ T005: x := S_N ]..............................................0.03
   [ T006: x := S_D ]..............................................0.05
   [ T007: x := M->M_C ]...........................................0.05
   [ T008: x := M->M_N ]...........................................0.04
   [ T009: x := M->M_D ]...........................................0.04
   [ T010: x := M->P_C ]...........................................0.06
   [ T011: x := M->P_N ]...........................................0.04
   [ T012: x := M->P_D ]...........................................0.04
   [ T013: x := F_C ]..............................................0.25
   [ T014: x := F_N ]..............................................0.17
   [ T015: x := F_D ]..............................................0.11
   [ T016: x := o:Args ]...........................................0.30
   [ T017: x := o[2] ].............................................0.07
   [ T018: Round( i / 1000, 2 ) ]..................................0.32
   [ T019: Str( i / 1000 ) ].......................................0.87
   [ T020: Val( s ) ]..............................................0.32
   [ T021: Val( a [ i % 16 + 1 ] ) ]...............................0.48
   [ T022: DToS( d - i % 10000 ) ].................................0.43
   [ T023: Eval( { || i % 16 } ) ].................................0.62
   [ T024: Eval( bc := { || i % 16 } ) ]...........................0.40
   [ T025: Eval( { |x| x % 16 }, i ) ].............................0.47
   [ T026: Eval( bc := { |x| x % 16 }, i ) ].......................0.37
   [ T027: Eval( { |x| f1( x ) }, i ) ]............................0.66
   [ T028: Eval( bc := { |x| f1( x ) }, i ) ]......................0.56
   [ T029: Eval( bc := &("{ |x| f1( x ) }"), i ) ].................0.58
   [ T030: x := &( "f1(" + Str(i) + ")" ) ]........................5.95
   [ T031: bc := &( "{|x|f1(x)}" ), Eval( bc, i ) ]................6.57
   [ T032: x := ValType( x ) +  ValType( i ) ].....................0.76
   [ T033: x := StrZero( i % 100, 2 ) $ a[ i % 16 + 1 ] ]..........0.90
   [ T034: x := a[ i % 16 + 1 ] == s ].............................0.25
   [ T035: x := a[ i % 16 + 1 ] = s ]..............................0.25
   [ T036: x := a[ i % 16 + 1 ] >= s ].............................0.24
   [ T037: x := a[ i % 16 + 1 ] <= s ].............................0.25
   [ T038: x := a[ i % 16 + 1 ] < s ]..............................0.25
   [ T039: x := a[ i % 16 + 1 ] > s ]..............................0.24
   [ T040: AScan( a, i % 16 ) ]....................................0.46
   [ T041: AScan( a, { |x| x == i % 16 } ) ].......................4.10
   [ T042: iif( i%1000==0, a:={}, ) , AAdd(a,{i,1,.T.,s,s2,a2 ]....0.92
   [ T043: x := a ]................................................0.04
   [ T044: x := {} ]...............................................0.16
   [ T045: f0() ]..................................................0.21
   [ T046: f1( i ) ]...............................................0.23
   [ T047: f2( c[1...8] ) ]........................................0.25
   [ T048: f2( c[1...40000] ) ]....................................0.24
   [ T049: f2( @c[1...40000] ) ]...................................0.24
   [ T050: f2( @c[1...40000] ), c2 := c ]..........................0.28
   [ T051: f3( a, a2, s, i, s2, bc, i, n, x ) ]....................0.41
   [ T052: f2( a ) ]...............................................0.24
   [ T053: x := f4() ].............................................0.94
   [ T054: x := f5() ].............................................0.63
   [ T055: x := Space(16) ]........................................0.41
   [ T056: f_prv( c ) ]............................................0.86
   ====================================================================
   [ total application time: ]....................................37.14
   [ total real time: ]...........................................37.16

Harbour is noticeable faster in 64-bit mode and xHarbour slower but
I haven't analyzed the reasons. Few years ago xHarbour was also faster
in 64-bit modes so it's probably problem with some compile time settings.
Harbour build system allows to use different compile time switches
for static and shared libraries and the bigger difference is probably
result of better tuned switches for static libraries which cannot be
used for shared ones so xHarbour cannot use them by default.

The last test we can make is scalability in real multi CPU environment.

   2009-07-28 21:07:56 Linux 2.6.25.20-0.4-pae i686
   Harbour 2.0.0beta2 (Rev. 11910) (MT)+ GNU C 4.4 (32-bit) x86
   THREADS: 2
   N_LOOPS: 1000000
                                                           1 th.  2 th.  factor
   ============================================================================
   [ T001: x := L_C ]____________________________________  0.19   0.07 ->  2.65
   [ T002: x := L_N ]____________________________________  0.12   0.06 ->  1.97
   [ T003: x := L_D ]____________________________________  0.12   0.06 ->  1.97
   [ T004: x := S_C ]____________________________________  0.21   0.26 ->  0.81
   [ T005: x := S_N ]____________________________________  0.15   0.08 ->  1.99
   [ T006: x := S_D ]____________________________________  0.16   0.08 ->  2.00
   [ T007: x := M->M_C ]_________________________________  0.19   0.13 ->  1.42
   [ T008: x := M->M_N ]_________________________________  0.18   0.09 ->  1.98
   [ T009: x := M->M_D ]_________________________________  0.21   0.14 ->  1.51
   [ T010: x := M->P_C ]_________________________________  0.18   0.13 ->  1.45
   [ T011: x := M->P_N ]_________________________________  0.17   0.09 ->  1.89
   [ T012: x := M->P_D ]_________________________________  0.17   0.09 ->  2.00
   [ T013: x := F_C ]____________________________________  0.44   0.30 ->  1.48
   [ T014: x := F_N ]____________________________________  0.57   0.32 ->  1.81
   [ T015: x := F_D ]____________________________________  0.32   0.17 ->  1.94
   [ T016: x := o:Args ]_________________________________  0.44   0.24 ->  1.81
   [ T017: x := o[2] ]___________________________________  0.29   0.16 ->  1.83
   [ T018: Round( i / 1000, 2 ) ]________________________  0.59   0.28 ->  2.12
   [ T019: Str( i / 1000 ) ]_____________________________  1.21   0.69 ->  1.74
   [ T020: Val( s ) ]____________________________________  0.61   0.33 ->  1.86
   [ T021: Val( a [ i % 16 + 1 ] ) ]_____________________  0.93   0.50 ->  1.85
   [ T022: DToS( d - i % 10000 ) ]_______________________  0.90   0.56 ->  1.60
   [ T023: Eval( { || i % 16 } ) ]_______________________  1.00   1.24 ->  0.81
   [ T024: Eval( bc := { || i % 16 } ) ]_________________  0.59   0.32 ->  1.83
   [ T025: Eval( { |x| x % 16 }, i ) ]___________________  0.81   1.03 ->  0.79
   [ T026: Eval( bc := { |x| x % 16 }, i ) ]_____________  0.59   0.30 ->  1.96
   [ T027: Eval( { |x| f1( x ) }, i ) ]__________________  0.93   1.10 ->  0.85
   [ T028: Eval( bc := { |x| f1( x ) }, i ) ]____________  0.71   0.38 ->  1.89
   [ T029: Eval( bc := &("{ |x| f1( x ) }"), i ) ]_______  0.70   0.40 ->  1.74
   [ T030: x := &( "f1(" + Str(i) + ")" ) ]______________  5.71   3.36 ->  1.70
   [ T031: bc := &( "{|x|f1(x)}" ), Eval( bc, i ) ]______  6.36   4.21 ->  1.51
   [ T032: x := ValType( x ) +  ValType( i ) ]___________  0.86   0.51 ->  1.68
   [ T033: x := StrZero( i % 100, 2 ) $ a[ i % 16 + 1 ] ]  1.52   0.85 ->  1.80
   [ T034: x := a[ i % 16 + 1 ] == s ]___________________  0.62   0.35 ->  1.80
   [ T035: x := a[ i % 16 + 1 ] = s ]____________________  0.67   0.38 ->  1.75
   [ T036: x := a[ i % 16 + 1 ] >= s ]___________________  0.66   0.37 ->  1.76
   [ T037: x := a[ i % 16 + 1 ] <= s ]___________________  0.66   0.38 ->  1.73
   [ T038: x := a[ i % 16 + 1 ] < s ]____________________  0.67   0.38 ->  1.75
   [ T039: x := a[ i % 16 + 1 ] > s ]____________________  0.65   0.33 ->  1.98
   [ T040: AScan( a, i % 16 ) ]__________________________  0.67   0.33 ->  2.01
   [ T041: AScan( a, { |x| x == i % 16 } ) ]_____________  5.87   3.63 ->  1.62
   [ T042: iif( i%1000==0, a:={}, ) , AAdd(a,{i,1,.T.,s ]  1.88   1.45 ->  1.29
   [ T043: x := a ]______________________________________  0.15   0.07 ->  1.96
   [ T044: x := {} ]_____________________________________  0.37   1.00 ->  0.37
   [ T045: f0() ]________________________________________  0.23   0.14 ->  1.58
   [ T046: f1( i ) ]_____________________________________  0.31   0.20 ->  1.57
   [ T047: f2( c[1...8] ) ]______________________________  0.32   0.18 ->  1.80
   [ T048: f2( c[1...40000] ) ]__________________________  0.31   0.17 ->  1.78
   [ T049: f2( @c[1...40000] ) ]_________________________  0.30   0.21 ->  1.40
   [ T050: f2( @c[1...40000] ), c2 := c ]________________  0.36   0.24 ->  1.51
   [ T051: f3( a, a2, s, i, s2, bc, i, n, x ) ]__________  0.74   0.42 ->  1.78
   [ T052: f2( a ) ]_____________________________________  0.31   0.16 ->  1.95
   [ T053: x := f4() ]___________________________________  0.96   0.57 ->  1.66
   [ T054: x := f5() ]___________________________________  0.59   0.39 ->  1.53
   [ T055: x := Space(16) ]______________________________  0.48   0.32 ->  1.50
   [ T056: f_prv( c ) ]__________________________________  0.86   0.56 ->  1.53
   ============================================================================
   [   TOTAL   ]_________________________________________ 46.76  30.75 ->  1.52
   ============================================================================
   [ total application time: ]...................................106.45
   [ total real time: ]...........................................77.52


   2009-07-28 21:11:44 Linux 2.6.25.20-0.4-pae i686
   xHarbour build 1.2.1 Intl. (SimpLex) (Rev. 6517) (MT)+ GNU C 4.4 (32 bit) ?
   THREADS: 2
   N_LOOPS: 1000000
                                                           1 th.  2 th.  factor
   ============================================================================
   [ T001: x := L_C ]____________________________________  0.46   0.20 ->  2.33
   [ T002: x := L_N ]____________________________________  0.37   0.18 ->  2.04
   [ T003: x := L_D ]____________________________________  0.38   0.22 ->  1.68
   [ T004: x := S_C ]____________________________________  0.42   0.38 ->  1.10
   [ T005: x := S_N ]____________________________________  0.36   0.21 ->  1.69
   [ T006: x := S_D ]____________________________________  0.39   0.19 ->  2.07
   [ T007: x := M->M_C ]_________________________________  1.39   1.22 ->  1.14
   [ T008: x := M->M_N ]_________________________________  1.42   1.12 ->  1.27
   [ T009: x := M->M_D ]_________________________________  1.44   1.16 ->  1.24
   [ T010: x := M->P_C ]_________________________________  1.46   1.27 ->  1.15
   [ T011: x := M->P_N ]_________________________________  1.43   1.10 ->  1.30
   [ T012: x := M->P_D ]_________________________________  1.40   1.10 ->  1.27
   [ T013: x := F_C ]____________________________________  0.76   0.41 ->  1.84
   [ T014: x := F_N ]____________________________________  0.76   0.43 ->  1.76
   [ T015: x := F_D ]____________________________________  0.53   0.28 ->  1.87
   [ T016: x := o:Args ]_________________________________  1.04   0.96 ->  1.09
   [ T017: x := o[2] ]___________________________________  0.51   0.25 ->  2.04
   [ T018: Round( i / 1000, 2 ) ]________________________  1.15   0.98 ->  1.17
   [ T019: Str( i / 1000 ) ]_____________________________  1.97   1.51 ->  1.30
   [ T020: Val( s ) ]____________________________________  1.19   0.86 ->  1.38
   [ T021: Val( a [ i % 16 + 1 ] ) ]_____________________  1.65   1.23 ->  1.35
   [ T022: DToS( d - i % 10000 ) ]_______________________  1.54   1.14 ->  1.35
   [ T023: Eval( { || i % 16 } ) ]_______________________  1.91   2.78 ->  0.68
   [ T024: Eval( bc := { || i % 16 } ) ]_________________  1.46   1.34 ->  1.09
   [ T025: Eval( { |x| x % 16 }, i ) ]___________________  1.60   2.77 ->  0.58
   [ T026: Eval( bc := { |x| x % 16 }, i ) ]_____________  1.32   1.30 ->  1.02
   [ T027: Eval( { |x| f1( x ) }, i ) ]__________________  2.13   3.10 ->  0.69
   [ T028: Eval( bc := { |x| f1( x ) }, i ) ]____________  1.75   1.78 ->  0.98
   [ T029: Eval( bc := &("{ |x| f1( x ) }"), i ) ]_______  1.77   1.79 ->  0.99
   [ T030: x := &( "f1(" + Str(i) + ")" ) ]______________ 11.34  14.11 ->  0.80
   [ T031: bc := &( "{|x|f1(x)}" ), Eval( bc, i ) ]______ 13.19  17.26 ->  0.76
   [ T032: x := ValType( x ) +  ValType( i ) ]___________  2.11   1.65 ->  1.28
   [ T033: x := StrZero( i % 100, 2 ) $ a[ i % 16 + 1 ] ]  2.80   1.78 ->  1.57
   [ T034: x := a[ i % 16 + 1 ] == s ]___________________  1.17   0.69 ->  1.71
   [ T035: x := a[ i % 16 + 1 ] = s ]____________________  1.21   0.57 ->  2.10
   [ T036: x := a[ i % 16 + 1 ] >= s ]___________________  1.15   0.62 ->  1.87
   [ T037: x := a[ i % 16 + 1 ] <= s ]___________________  1.10   0.60 ->  1.86
   [ T038: x := a[ i % 16 + 1 ] < s ]____________________  1.11   0.63 ->  1.75
   [ T039: x := a[ i % 16 + 1 ] > s ]____________________  1.14   0.61 ->  1.88
   [ T040: AScan( a, i % 16 ) ]__________________________  1.66   1.17 ->  1.41
   [ T041: AScan( a, { |x| x == i % 16 } ) ]_____________ 12.39  13.63 ->  0.91
   [ T042: iif( i%1000==0, a:={}, ) , AAdd(a,{i,1,.T.,s ]  2.95   2.79 ->  1.06
   [ T043: x := a ]______________________________________  0.37   0.19 ->  1.94
   [ T044: x := {} ]_____________________________________  0.70   2.29 ->  0.31
   [ T045: f0() ]________________________________________  0.78   0.73 ->  1.07
   [ T046: f1( i ) ]_____________________________________  0.91   0.87 ->  1.05
   [ T047: f2( c[1...8] ) ]______________________________  0.92   0.85 ->  1.08
   [ T048: f2( c[1...40000] ) ]__________________________  0.91   0.84 ->  1.08
   [ T049: f2( @c[1...40000] ) ]_________________________  0.89   0.83 ->  1.07
   [ T050: f2( @c[1...40000] ), c2 := c ]________________  1.02   0.90 ->  1.13
   [ T051: f3( a, a2, s, i, s2, bc, i, n, x ) ]__________  1.50   1.14 ->  1.32
   [ T052: f2( a ) ]_____________________________________  0.92   0.85 ->  1.08
   [ T053: x := f4() ]___________________________________  2.45   1.95 ->  1.26
   [ T054: x := f5() ]___________________________________  1.75   1.75 ->  1.00
   [ T055: x := Space(16) ]______________________________  1.19   1.02 ->  1.16
   [ T056: f_prv( c ) ]__________________________________  4.51   4.36 ->  1.03
   ============================================================================
   [   TOTAL   ]_________________________________________106.08 105.94 ->  1.00
   ============================================================================
   [ total application time: ]...................................287.58
   [ total real time: ]..........................................212.02

As we can see in xHarbour we do not have any improvement executing MT
programs on multi-CPU machines while in Harbour the speed is noticeably
better. It means that Harbour is quite well scalable and users should
expect speed improvement executing MT parallel programs on multi CPU
machines. For some programs like MT servers it may be critical - programs
compiled by Harbour can be quite well improved by simple hardware
upgrade to 4, 8, 16 or more CPU machines.

Source: https://github.com/harbour/core/blob/master/doc/xhb-diff.txt
Advertisements

Coding Guidelines

Coding Guidelines

( by Greg Holmes )

Language Syntax 
The general rule of thumb is: built-in features in lowercase, and custom-written functions in mixed case. 
When specifying the complete syntax of a language element in documentation, the input items, parameters, and so on are referred to using the following symbols:

 Symbol  Description
< >  Indicates user input item
( )  Indicates function argument list
[ ]  Indicates optional item or list
{ }  Indicates code block or literal array
| |  Indicates code block argument list
–>  Indicates function return value
 Repeated elements if followed by a symbol
Intervening code if followed by a keyword
,  Item list separator
|  Indicates two or more mutually exclusive options
@  Indicates that an item must be passed by reference
*  Indicates a compatibility command or function

For example:

    len(<cString>|<aArray>) --> nLength

Metasymbols provide a place holder for syntax elements, and they describe the expected data types. A metasymbol consists of one or more lowercase data type designators followed by a mixed case description. This is known as Hungarian Notation.

 Designator  Description
a  Array
b  Code block
c  Character expression
d  Date expression
exp  Expression of any type
id  Literal identifier
l  Logical expression
m  Memo field
n  Numeric expression
o  Object
x  Extended expression

In this example, dnLower and dnUpper can be either date or numeric:

    @...get...range <dnLower>, <dnUpper>
Filenames and Aliases 
All filenames, in any context, are in upper case. Filenames follow DOS naming conventions (preferably limited to letters, numbers, and the underscore).

    use CUSTOMER
    nHandle := fopen('DATAFILE.DAT')

When referring to specific file types in documentation, include the period.
e.g. “A program is stored in a text file with a .PRG extension.” 
Alias names follow the same conventions as filenames, but are limited to A-Z, 0-9, and the underscore. If a filename begins with a number or contains unusual characters, an alias must be specified when the file is opened or an error will result. 
Note that CA-Clipper does not natively support Windows 95 long filenames, although third-party libraries are available to add the capability.

Fieldnames 
Fieldnames are all uppercase, and always include the alias of the table. Fieldnames may contain underscores, but should not begin with one (because the underscore is generally used to indicate an internal symbol).

    @ 10, 10 say BANKS->BRANCH
    nAge := CUSTOMER->CUST_AGE
Memory Variables 
Memory variables consist of a lowercase type designator followed by a mixed case description (see Hungarian Notation). Although CA-Clipper only recognizes the first 10 characters as unique, variable names may be longer.

    cString := "Hello World"
    nYearlyAverage := CalcYearAvg()

If you use Hungarian Notation for your memory variable names and include the table alias with fieldnames, there will be no conflict between the two.

Commands, Functions, and Keywords 
All built-in commands, functions, and keywords are lowercase. In documentation, the font should be Courier or a similar font. If fonts are not available, then bold or CAPITALIZE the word for emphasis. 
Never use abbreviations — this practice is not necessary with a compiler, although it was common in the early days of dBase (which was an interpreter). 
There should never be a space between the function name and the opening parenthesis. Also, note that the iif() function should never be spelled if().

    replace CUSTOMER->CUSTNAME with cCustName
    nKey := inkey(0)

When specifying commands that have clauses in documentation, separate the keywords with an ellipsis (...) and do not include the to clause, unless it is followed by the file,print, or screen keywords.

    copy...sdf
    set message...center
    @...say...get
Programmer-Defined Functions & Procedures 
These begin with an uppercase letter, followed by mixed case letters as appropriate.

    ? StripBlanks("Hello there, this will have no spaces.")

Function and procedure names may contain underscores, but should not begin with one (they may conflict with internal functions which often start with an underscore). There should be only one return statement per function or procedure, and it should not be indented.

    function SomeFunc (...)
      .
      . <statements>
      .
    return cResult

The return value of a function is not enclosed in parentheses, although parentheses may be used to clarify a complex expression.

    return nValue
    return (nCode * 47) + nAnswer
Preprocessor Directives 
Preprocessor directives are lowercase and are preceded by the # sign.

    #include 'INKEY.CH'

Optionally, you may use single quotes around header files that come with CA-Clipper and double quotes around your own. This convention is purely voluntary, but it helps to distinguish between the two. For example:

    #include 'INKEY.CH'
    #include "MY_APP.CH"

Manifest constants are uppercase.

    #define ESCAPE   27
    if lastkey() == ESCAPE

Pseudo-function names should also be uppercase.

    #define AREA(length, width)   ((length)*(width))
Declarations 
Local variables are grouped according to functionality, and may be declared on one or more lines. The declarations appear as the first code at the beginning of a function or procedure.

    procedure Main ( )
    local nTop, nLeft, nBottom, nRight
    local cOldScreen, cOldColor, nOldCursor

Variables may be declared one per line and accompanied by a description.

    local nCount        // Number of records found.
    local nTotal        // Sum of dollars.

The description can be omitted if better variable names are chosen.

    local nRecordCount
    local nDollarTotal

Variables can be initialized when they are declared, although it is often clearer (and safer) to initialize them immediately before they are used.

    local nRecordCount:=0
    local nDollarTotal:=0
Logicals 
The .T. and .F. are typed in uppercase.
Operators 
The in-line assignment operator (:=) is used for all assignments, and the exact comparison operator (==) is used for all comparisons.

    lContinue := .T.
    nOfficeTotal := nRegionTotal := 0
    lDuplicate := (CUSTFILE->CUSTNAME == cCustName)
    if nLineCount == 4  ...
    if left(PRODUCT->CODE, 3) == left(cProdCode, 3)  ...

Although the compound assignment operators (+=-=*=, etc.) are convenient, they should not be used if readability suffers.

    // The traditional way to accumulate:
    nTotal := nTotal + INVDETAIL->PRICE
    // A good use of a compound assignment operator:
    nTotal += INVDETAIL->PRICE
    // But what does this do?
    nVal **= 2

The increment (++) and decrement (--) operators are convenient, but can lead to obscure code because of the difference between prefix and postfix usage.

    nRecCount++
    nY := nX-- - --nX        // Huh?
Spacing 
Whenever a list of two or more items is separated by commas, the commas are followed by a space.

    MyFunc(nChoice, 10, 20, .T.)

Spaces may be used between successive parentheses.

    DoCalc( (nItem > nTotal), .F. )
    cNewStr := iif( empty(cStr), cNewStr, cStr + chr(13) )

Spaces should surround all operators for readability.

    nValue := 14 + 5 - (6 / 4)

In declarations, often spaces are not used around the assignment operator. This tends to make searching for the declaration of a variable easier.

    local lResult:=.F., nX:=0

Thus, searching for “nX :=” would find the lines where an assignment is made, while searching for “nX:=” would find the declaration line (such as the local above).

Indentation 
Indenting control structures is one of the easiest techniques, yet it improves the readability the most. 
Indent control structures and the code within functions and procedures 3 spaces.

    procedure SaySomething
       do while .T.
          if nTotal < 50
             ? "Less than 50."
          elseif nTotal > 50
             ? "Greater than 50."
          else
             ? "Equal to 50."
          endif
          ...
       enddo
    return

Case statements in a do…case structure are also indented 3 spaces.

    do case
       case nChoice == 1
          ? "Choice is 1"
       case ...
          ...
       otherwise
          ...
    endcase
Tabs 
Do not use tabs in source code — insert spaces instead. Tabs cause problems when printing or when moving from one editor to another, because of the lack of a standard tab width between editors and printers. Typically, printers expand tabs to 8 spaces which easily causes nested control structures to fall off the right-hand side of the page. Commonly, a source code editing program will insert the appropriate number of spaces when the <TAB> key is hit.
Line Continuation 
When a line of code approaches the 80th column, interrupt the code at an appropriate spot with a semicolon and continue on the next line. Indent the line so that it lines up in a readable manner.

    set filter to CUSTFILE->NAME  == 'John Smith  ';
            .and. CUSTFILE->STATE == 'OR'

To continue a character string, end the first line with a quote and a plus sign and place the remainder on the next line. Try to choose a logical place in the string to break it, either at a punctuation mark or after a space.

    @ 10, 10 say "The lazy brown fox tripped over " + ;
                 "the broken branch."
Quotes 
Use double quotes for text that needs to be translated (will appear on the screen), and single quotes for other strings.

    ? "Hello World!"
    cColor := 'W+/B'
    SelectArea('PROP')

This is a simple but extremely effective technique because translation departments often want to see the messages in context (in the source code), so the different quote types indicate which messages are to be translated and which should be left alone.

Comments 
Comments are structured just like English sentences, with a capital letter at the beginning and a period at the end.

    // Just like a sentence.
    /* This comment is longer. As you
       can see, it takes up two lines */

You may encounter old-style comment indicators if you maintain older (Summer’87 and earlier) code.

    && This is an older-style of comment indicator.
    *  The asterisk is also old.

For in-line comments, use the double slashes.

    use CUSTOMER            // Open the data file.
    goto bottom             // The last record.

Note that the ‘//‘ of in-line comments begins at column 40, if possible. This leaves enough room for a useful comment.

Source :  http://www.ghservices.com/gregh/clipper/guide.htm

FT Toolkit Overview

NANFOR.LIB Working Group G. Scott [71620,1521]
Overview UCLA
Version 2.1 October, 1992

THE NANFORUM TOOLKIT (NANFOR.LIB)
PUBLIC DOMAIN USER SUPPORTED CLIPPER FUNCTION LIBRARY

.

1 INTRODUCTION

This is a standard for establishing and maintaining NANFOR.LIB, a public-domain, user-supported library of functions designed to interface with Computer Associates CA-Clipper, version 5.01a, and later. You are encouraged to read it over and forward comments to Glenn Scott, CIS ID [71620,1521].

1.1 History

In October and November of 1990, a discussion on the evolution of third-party products, vendors, and marketing took place on the CompuServe Information Service’s Nantucket Forum (NANFORUM). During this discussion, a forum subscriber named Alexander Santic suggested the idea of a user-supported Clipper function library, available to all on the CompuServe Information Service (CIS). A number of subscribers, including several Clipper third party developers, and some Nantucket employees, expressed their support. This standard was a first step toward organizing such an endeavor.

Release 1.0 of the toolkit was made available in April, 1991 and had nearly 150 functions. By the time version 2.0 was released in August, 1991, the 1.0 library had been downloaded nearly 700 times by CompuServe users. By October of 1992, release 2.0 had been downloaded over 2100 times. The source code had been downloaded nearly 1500 times. In addition, release 2.0 was placed on the massive Internet archive site called SIMTEL20 where it was downloaded by CA- Clipper users worldwide. Over the course of the year that release 2.0 was available, seven patches were issued, each one gathering nearly 1000 downloads.

Computer Associates International, Inc. acquired Nantucket in the summer of 1992 and subsequently renamed NANFORUM to simply CLIPPER. In addition, the Clipper product itself was renamed to CA-CLIPPER. Despite the name changes, forum members decided to keep the toolkit’s name as “The Nanforum Toolkit,” partly for nostalgia. References to NANFORUM in this RFC have been replaced with CLIPPER.

1.2 Trademarks

CA-Clipper is a registered trademark of Computer Associates International, Inc. Computer Associates will be referred to as CA throughout this document.

1.3 Relationship to CA and third party

NANFOR.LIB is a project independent of any third party developer or CA. There is no official “sanction” or “seal of approval” from CA of any kind. In addition, NANFOR.LIB routines will be accepted and included without regard for whether or not routines performing a similar function are included in a commercial third party or CA product.

It is desired that NANFOR.LIB not compete with third party products but rather fill in the holes in CA-Clipper’s standard library. However, there will be some overlap into commercial third-party library functions, so it would be best if this is never taken into consideration when deciding on including a particular function.

Developers submitting NANFOR.LIB routines can and will be corporate developers, third party developers, independent consultant / programmers, hobbyists, and other CA-Clipper people. Perhaps even CA employees will contribute. No one is excluded or included due to any particular affiliation.

CA employees submitting functions are doing so as individuals, and are not making a policy of involving CA in the project, nor are they committing CA to supporting the public domain library.

1.4 CA-Clipper version supported

NANFOR.LIB functions, no matter what language they are written in, will be designed to work with CA-Clipper version 5.01a and later. Many of the functions, particularly those that use the EXTEND system, will be compatible with the Summer 1987 version of CA-Clipper. However, ensuring Summer 87 compatibility will be the responsibility of the user. If a user wants a function to work with Summer 87, she will have to modify the code herself if necessary. In many cases, this is a trivial task.

1.5 Queries from new users

Queries from new users interested in finding NANFOR.LIB should be handled in a uniform and courteous way. A short text file will be created that will briefly explain NANFOR.LIB, who the current people maintaining it are, and how to get a hold of it. This text message can be sent in response to any query. TAPCIS users will find this method very easy to implement.

2 DISTRIBUTION

2.1 Public Domain

NANFOR.LIB, its source code, and documentation will be public-domain software. It is not for “sale”, and shall not be sold. No fee or contribution of any kind will be required for anyone wanting a copy, other than what they would normally pay to download it from CompuServe. Users will be encouraged to submit functions via CompuServe.

2.2 Official repository

It is possible that copies of NANFOR.LIB will be downloaded and distributed elsewhere. This is encouraged, but the only copy of NANFOR.LIB and all associated documentation that will be maintained by volunteers is in an appropriate library on the CIS CLIPPER Forum.

2.2.1 Contents

The deliverables that make up the official posting on CompuServe shall be:

2.2.1.1 NFLIB.ZIP

This will contain the files NANFOR.LIB (library), and NANFOR.NG (Norton Guide).

2.2.1.2 NFSRC.ZIP

This will contain all the library source code, makefile, and other source-code related materials.

2.2.1.3 NFINQ.TXT

This is a short text file used as a response to new user queries (see paragraph 1.5)

2.2.1.4 NFRFC.ZIP

This contains an ASCII format, as well as a WordPerfect 5.1 format copy of NANFOR.RFC named NFRFC.TXT (ASCII) and NFRFC.WP5 (WordPerfect 5.1).

2.2.1.5 NFHDRS.ZIP

This contains templates of the file and documentation header blocks, including a sample, for prospective authors (FTHDR.PRG, FTHDR.ASM, FTHDR.SAM)

2.2.1.6 PATx.ZIP

These are patch files (see paragraph 4.5.1).

3 POLICY ON INCLUDING FUNCTIONS

3.1 “Best Function”

It is possible that more than one developer will submit a function or package of functions that perform substantially the same services. In that event, the referees will choose one to be included based on power, functionality, flexibility, and ease of use. Due to the cooperative, non-commercial nature of the library, no one’s feelings should be hurt by excluding duplicate functions.

In addition, it is possible that two substantially similar functions or packages will benefit from merging them together to provide new functionality. This will be the prerogative of the referees (see paragraph 6.3), in close consultation with the authors.

3.2 Public Domain

Each author submitting source code must include as part of that code a statement that this is an original work and that he or she is placing the code into the public domain. The librarian (see paragraph 6.1) and referees should make a reasonable effort to be sure no copyrighted source code, such as that supplied with some third party libraries, makes it into NANFOR.LIB. However, under no circumstances will the librarian, referees, or any other party other than the submitter be responsible for copyrighted code making it into the library accidentally.

3.3 Source code

Full source code must be provided by the author for every routine to be included in NANFOR.LIB. No routine, no matter what language, will be put into the library on the basis of submitted object code.

3.4 Proper submission

Due to the volume of submissions expected, librarians and referees may not have the time to fix inconsistencies in documentation format, function naming, and other requirements. Therefore, the librarian shall expect source code to arrive in proper format before proceeding further with it.

3.5 Quality and perceived usefulness

In a cooperative effort like this, it is very difficult to enforce some standard of quality and/or usefulness. For example, a package of functions to handle the military’s “Zulu time” may be very useful to some, and unnecessary to others.

The Nanforum Toolkit will by its very nature be a hodgepodge of routines, some of very high quality, some not so high. It is up to the users to improve it. It will be complete in some areas and vastly inadequate in others. It is up to the users to fill in the holes.

We shall err on the side of including “questionable” functions, provided they seem to work. Debates on the quality of the library’s source code shall be encouraged and will take place in the proper message section of the CompuServe CLIPPER forum.

4 LIBRARY MAINTENANCE PROCEDURE

4.1 Selection procedure

Source code will be submitted to the librarian, the documenter (see paragraph 6.2), or one of the referees. Code will be added if it has been reviewed, and approved by at least one, but preferably two, referees.

Code not meeting the documentation or source code formatting standards will generally be returned to the author with instructions.

Referees will test the submitted code. When the referees have finished evaluating a submission, they will report their approval or disapproval to the librarian, with comments.

Every effort should be made to make sure that the C and ASM functions are reviewed by referees with suitable C and ASM experience.

4.2 Update interval

As new functions are submitted, they will added to the library, and the documentation updated. Because this is a volunteer project, and because of the complexity involved in coordinating testing, documentation, and delivery, there will be no fixed interval for updates.

4.3 Version control

NANFOR.LIB will use a numeric version number as follows:

The major version will be numeric, starting from 1. This will change with each quarterly update. The minor version will change with each bug fix. This will start with zero and continue until the next major update, at which point it will revert to zero again.

Typical version numbers might be 1.1, 2.12, 15.2, etc.

The .LIB file, and all associated files, will carry a date stamp corresponding to the day it is released on the CLIPPER forum. The file time stamps shall correspond to the version number (i.e., 1:03am is version 1.3).

4.4 Announcing updates

As the library and its associated documentation are updated, simple announcements will be posted on the CLIPPER forum. This is the only place where an update shall be announced. An update will be announced after it has been successfully uploaded to the appropriate library on CompuServe.

4.5 Bug reports and fixes

The librarian will correlate and verify all bug reports, with the help of the referees. If the referees believe a bug to be serious, they will fix it and the librarian will release a maintenance upgrade immediately. If they consider it a minor bug, they will fix it but wait for the next scheduled upgrade to release it. In this case, a bug fix may be released as a “Patch.”

4.5.1 Patches

A “patch” is simply an ASCII text file containing instructions for editing the source code to a misbehaving function or group of functions. Patches may appear in the CIS library before a maintenance release or quarterly upgrade. A patch file will have a name of the form

PATn.ZIP

where <n> is a number starting from 1. Patches will be numbered sequentially. Patches will be deleted every time a new version of NANFOR.LIB goes on-line.

A patch zipfile may optionally contain .OBJ files to be replaced in user libraries via a LIB utility.

4.6 Technical Support

Technical support will work just as any technical subject on the CompuServe CLIPPER forum works. Users will post questions and suggestions to a particular message area or thread, and anyone who knows the answer should respond. No one is obliged to answer, but it is considered good form to respond with something, even if one doesn’t know the answer.

Support will include help on recompiling the routines or modifying the source.

4.7 Linker Compatibility

In order to assist users of CA-Clipper third party linkers (such as WarpLink or Blinker), NANFOR.LIB may need to broken up into root and overlay sections. How this will be done will be determined when splitting becomes necessary.

The librarian is not responsible for testing every possible linker for NANFOR.LIB compatibility. It is hoped that linker users will submit appropriate link scripts or other documentation for posting in the appropriate section on the CLIPPER forum.

4.8 Splitting NANFOR.LIB by functional category

It is possible that at some future date, it will make sense to split NANFOR.LIB into separate functional areas (e.g., video routines vs. date routines, etc). This RFC will be modified accordingly should that need arise.

5 FUNCTION CODING STANDARDS

The goal of this standard is not to force anyone to rewrite his code for this library, but to create some consistency among the functions so that they may more easily maintained and understood by all CA-Clipper developers, both novice and advanced.

However, it is extremely important that anyone submitting code attach the proper headers and documentation and fill them out correctly. This will make it much easier for code to be added to the library.

5.1 Required sections for each function
5.1.1 Header (author name/etc, version ctrl info)

Figure 1 shows a header that must be included at the top of every piece of source code submitted to the library. This header will work with both CA-Clipper and C code. For ASM code, substitute each asterisk (“*”) with a semicolon (“;”) and delete the slashes (“/”).

/*
 * File......:
 * Author....:
 * CIS ID....: x, x
 * Date......: $Date$
 * Revision..: $Revision$
 * Log file..: $Logfile$
 *
 *
 * Modification history:
 * ---------------------
 *
 * $Log$
 *
 */
Figure 1 - Standard function header.

Note that the date, revision, logfile, and modification history fields will be maintained by the librarian and should not be edited or adjusted by code authors.

The “File” field shall contain the source file name. This is often independent of the individual function name. For example, a function named ft_screen() would be included in SCREEN.PRG. As a rule, source files (.PRG, .C, .ASM) should not have the “FT” prefix.

The “Author” field should have the author’s full name, and CIS number. A CIS number is important, as this will make bug fixing and other correspondence easier.

5.1.2 Public domain disclaimer

Authors shall simply state “This is an original work by [Author’s name] and is hereby placed in the public domain.”

5.1.3 Documentation block
/* $DOC$
 * $FUNCNAME$
 *
 * $ONELINER$
 *
 * $SYNTAX$
 *
 * $ARGUMENTS$
 *
 * $RETURNS$
 *
 * $DESCRIPTION$
 *
 * $EXAMPLES$
 *
 * $SEEALSO$
 *
 * $INCLUDE$
 *
 * $END$
 */

Figure 2 – Standard Documentation Header

The documentation block must be carefully formatted as it is used by the documenter to produce the Norton Guide documentation for the library.

The keywords enclosed in dollar-signs delimit sections of the documentation header analogous to those in the CA-Clipper 5.0 documentation. Documentation should be written in the same style and flavor as the CA material, if possible. Refer to the CA-Clipper documentation for more detail and numerous examples.

The documentation will appear on comment lines between the keywords. Examples are optional. Do not put documentation on the same line as the comment keyword.

Note that the $DOC$ and $END$ keywords serve as delimiters. Do not place any text between $DOC$ and $FUNCNAME$, or any documentation after the $END$ keyword, unless that documentation belongs in the source code file and not in the resultant Norton Guide file.

The $FUNCNAME$ keyword should be followed by the function name, with parentheses, and no arguments or syntax, such as:

$FUNCNAME$            
   ft_screen()

Note the indent for readability. Parentheses shall be added after the function name as shown above.

The $ONELINER$ keyword should be followed by a simple statement expressing what the function does, phrased in the form of a command, e.g.:

$ONELINER$
          Sum the values in an array

The length of the entire $ONELINER$ shall not exceed 60 characters (this is a Norton Guide limitation).

The $SYNTAX$ keyword should be followed by a CA- standard syntax specifier, such as:

$SYNTAX$
         ft_screen( <nTop> [,<nBottom>] ) -> NIL

All parameters have proper prefixes (see paragraph 5.4), and are enclosed in <angle brackets>. Optional parameters are enclosed in [square brackets] as well. An arrow should follow, pointing to the return value. If there is no return value, it should be NIL. Any others should be preceded with the proper prefix (see the CA- Clipper documentation).

The $SEEALSO$ field provides a way to generate cross-references in the Norton Guide help documentation. Use it to point the user to other related functions in the forum toolkit. For example, if ft_func1() is also related to ft_func2() and ft_func3(), the field would look like this:

$SEEALSO$
ft_func2() ft_func3()

Note that fields are separated by spaces and the parentheses are included.

The $INCLUDE$ area allows you to specify what files are included by this function (this will be used to organize the on-line help file, and possibly the master makefile). An example would be

$INCLUDE$
int86.ch int86.inc

Other documentation fields should be self- explanatory. Review the appendix for a sample. All fields are required and must be filled in. Examples should not be considered optional.

5.1.4 Sample header and documentation block

Refer to the Appendix for a sample header and documentation block.

5.1.5 Test driver

A test driver is an optional section of C or CA- Clipper code that will only be compiled under certain circumstances. Developers are encouraged to include a short “test section” in front of their code.

The test driver shall be surrounded by the following pre-processor directives, and placed at the top of the source file:

#ifdef FT_TEST
     [test code]
 #endif

The test driver is currently optional, but authors submitting Clipper code should seriously consider adding it. It is a good way to include short demos within a piece of source code, yet pay no penalty because it is only compiled if needed. It will be invoked when a #define is created that says “#define FT_TEST.” This is a way for submitters to include short test routines with their functions and yet keep it all in one source file. This will be useful to end users.

This test driver may become required in a future version of the RFC.

5.1.6 Code

The source code shall be formatted as described in paragraph 5.4.

5.2 Function names

All NANFOR.LIB functions start with one of two prefixes. If the function is to be called by user programs, then it will begin with the prefix

FT_       ("F", "T", underscore)

Note that “FT” is a mnemonic for “Forum Toolkit.” If the function is “internal” to a module, then it will be prefixed by an underscore:

_FT ( Underscore, "F", "T" )

with no trailing underscore. Examples:

FT_CURDIR() "external"
_ftAlloc() "internal"
5.3 Librarian’s authority to change function names

Some functions will be submitted that either (1) bear a similar name to another function in the library, or (2) bear an inappropriate name. For example, a function called FT_PRINT that writes a character to the screen could be said to be named inappropriately, as a name like FT_PRINT implies some relationship to a printer. The librarian shall have the responsibility to rename submitted functions for clarity and uniqueness.

5.3.1 Changing a function name after it has been released

Once the library is released with a particular function included, then a function name should generally be frozen and not renamed. To do so would probably cause difficulties with users who had used the previous name and are not tracking the changes to the library.

5.4 Source code formatting
5.4.1 Clipper

Clipper code shall be formatted in accordance with CA’s currently defined publishing standard. Although there will surely be some debate over whether this is a good idea, in general, the goal is to provide something consistent that all CA- Clipper developers will recognize.

Minor deviations will be permitted.

The CA standard usually means uppercase keywords, and manifest constants, and lower case everything else.

In addition, identifiers shall be preceded with the proper metasymbol:

 n Numeric
 c Character or string
 a Array
 l Logical, or boolean
 d Date
 m Memo
 o Object
 b Code block
 h Handle
 x Ambiguous type

Refer to the CA-Clipper documentation for samples of CA’s code publishing format.

5.4.2 C

C source code shall be formatted in a generally accepted way, such as Kernighan and Ritchie’s style used in the book _The C Programming Language_.” The use of CA’s EXTEND.H is encouraged.

5.4.3 ASM

No particular formatting conventions are required for assembly language source code, since assembly code formatting is fairly standard. Lowercase code is preferred. Be sure to include the proper documentation header information, as described above.

Do not place ASM code in DGROUP. See paragraph 5.11.

5.5 Organization into .PRGs

Since many different people will be submitting routines, it is probably best if all routines that belong together are housed in the same .PRG. If there is some reason to split the .PRG, the referees and the librarian will handle that as part of library organization.

5.6 Header files

Including a “.ch” or “.h” or “.inc” file with each function would get unwieldy. For the purpose of NANFOR.LIB, all #defines, #ifdefs, #commands, #translates, etc that belong to a particular source file shall be included at the top of that source file. Since few submissions will split over multiple source files, there will usually be no need to #include a header in more than one place.

If a “ch” file will make the end user’s job of supplying parameters and other information to NANFOR.LIB functions easier, then it shall be submitted as a separate entity. The referees will decide on whether to include these directives in a master NANFOR.CH file.

5.7 Clipper 5.0 Lexical Scoping

NANFOR.LIB routines that are written in CA-Clipper will make use of CA-Clipper 5.0’s lexical scoping features to insulate themselves from the rest of the user’s application.

For example, all “privates” shall generally be declared “local.”

If a package of Clipper functions is added to the library, then the lower-level, support functions will be declared STATIC as necessary.

5.8 Use of Publics

Authors shall not use PUBLIC variables in NANFOR.LIB functions, due to the potential interference with an end-user’s application or vice versa.

If a global is required for a particular function or package of functions, that global shall be accessed through a function call interface defined by the author (.e.g, “ft_setglobal()”, “ft_getglobal()”, and so on). Globals such as these shall be declared static in the .PRG that needs them.

5.9 Use of Macros (“&” operator)

The use of macros in NANFOR.LIB functions will be, for the most part, unnecessary. Since this is a CA-Clipper 5.0 library, the new 5.0 codeblock construct should be used instead. Anyone having trouble figuring out how to convert a macro to a codeblock should post suitable questions on the CLIPPER forum on CompuServe.

5.10 Use of Static Functions

Any CA-Clipper 5.0 function that is only needed within the scope of one source file shall be declared STATIC. This applies mostly to NANFOR.LIB “internals” (names with an “_ft” prefix) that user programs need not access.

5.11 Use of DGROUP in ASM Functions

Use of DGROUP in assembly language functions shall be avoided, in accordance with CA’s recommendations. Assembly functions written for NANFOR.LIB shall use a segment named _NanFor, as in the following example:

Public FT_ChDir
Extrn _ftDir:Far
Segment _NanFor Word Public "CODE"
 Assume CS:_NanFor
Proc FT_ChDir Far
 .
 .
 .
 Ret
 Endp FT_ChDir
 Ends _NanFor
End
5.12 Use of "Internals"

Use of CA-Clipper “internals” by code authors is allowed. However, should any code make use of an internal, i.e., a function or variable that is not part of the published CA-Clipper API, then that internal shall be clearly marked in the documentation (under “DESCRIPTION”) and in the actual code, everywhere the internal is used.

5.13 Procedures for compiling functions
5.13.1 Clipper

Clipper functions will be compiled under the current release of CA-Clipper 5.0, with the following compiler options:

/n /w /l /r

Note that neither line numbers nor debugging information will find its way into NANFOR.LIB, to keep the code size down. End users may recompile NANFOR.LIB with these options enabled if they want to view NANFOR.LIB source in the debugger.

5.13.2 ASM

Assembly functions must compile successfully under any MSDOS assembler capable of producing the proper .OBJ file. However, care should be taken not to use any macros or special syntax particular to one vendor’s assembler, because that would make it difficult for end users to recompile the source. The preferred assembler is MASM, followed by TASM.

5.13.3 C

C functions must compile successfully under any C compiler capable of interfacing to CA-Clipper. Obviously, Microsoft C, version 5.1, is the preferred development environment. Care should be taken, when writing C code, not to use any special compiler features particular to one vendor’s C compiler, because that would make it difficult for end users to recompile the source.

5.14 Functions requiring other libraries

It is very easy to write functions in C that call the compiler’s standard C library functions. However, NANFOR.LIB can make no assumptions about the end user’s ability to link in the standard library or any other library. Therefore, no function will be added to NANFOR.LIB that requires any other third party or compiler manufacturer’s library.

6 ADMINISTRATIVE DETAILS

6.1 Librarian

The librarian will be the person who rebuilds the library from the sources and uploads the resulting deliverables to the proper CLIPPER forum library on CompuServe. The librarian generally does *not* test code or edit source code to repair formatting errors.

6.2 Documenter

The documenter is responsible for maintaining the Norton and guides and keeping it in sync with each new release.

6.3 Referees

Referees are volunteers who read source code, clean it up, compile it, look for problems like potentially problematic C code, decide on which function is best, consolidate common functions, etc. They make sure the header and documentation blocks are present. There is no election or term for refereedom. One simply performs the task as long as one can and bows out when necessary.

6.4 Transitions

Not everyone will be able to stay around forever to keep working on this project. Therefore, it is the responsibility of each referee, documenter, or librarian to announce as far in advance as possible his or her intention to leave, in order to give everyone a chance to come up with a suitable replacement. Don’t let it die!

7 CONTRIBUTORS

Current contributors, directly and indirectly, to this document include:

Don Caton [71067,1350]
Bill Christison [72247,3642]
Robert DiFalco [71610,1705]
Paul Ferrara [76702,556]
David Husnian [76064,1535]
Ted Means [73067,3332]
Alexander Santic [71327,2436]
Glenn Scott [71620,1521]
Keith Wire [73760,2427]
Craig Yellick [76247,541]
James Zack [75410,1567]

NOTES :

  • In Harbour library file name of NanForum Toolkit is hbnf.a
  • Maybe some functions :
    • obsoleted,
    • used some low-level hardware access or some OS specific features,
    • so not included in hbnf library.

== Exactly equal

 


 ==
 Exactly equal--binary                           (Relational)
------------------------------------------------------------------------------
 Syntax

     <exp1> == <exp2>

 Type

     All

 Operands

     <exp1> and <exp2> are expressions of the same data type to be
     compared.

 Description

     The exactly equal operator (==) is a binary operator that compares two
     values of the same data type for exact equality depending on the data
     type.  It returns true (.T.) if <exp1> is equal to <exp2> according to
     the following rules:

     .  Array:  Compares for identity.  If <exp1> and <exp2> are
        variable references to the same array, returns true (.T.); otherwise,
        returns
        false (.F.).

     .  Character:  Comparison is based on the underlying ASCII code.
        ASCII codes for alphabetic characters are ascending (e.g., the code
        for "A" is 65 and the code for "Z" is 90).  Unlike the relational
        equality operator (=) , true (.T.) is returned if <exp1> and <exp2>
        are exactly equal including trailing spaces; otherwise, the
        comparison returns false (.F.).  SET EXACT has no effect.

     .  Date:  Dates are compared according to the underlying date
        value; behaves the same as the relational equality operator (=).

     .  Logical:  True (.T.) is exactly equal to true (.T.), and false
        (.F.) is exactly equal to false (.F.).

     .  Memo:  Treated the same as character.

     .  NIL:  True (.T.) if compared to a NIL value; false (.F.) if
        compared to a value of any other data type.

     .  Numeric:  Compared based on magnitude; behaves the same as the
        relational equality operator (=).

     .  Object: Treated the same as array.

 Examples

     .  These examples illustrate how the == operator behaves with
        different data types:

        // Character
        ? "A" == "A"             // Result: .T.
        ? "Z" == "A"             // Result: .F.
        ? "A" == "A "            // Result: .F.
        ? "AA" == "A"            // Result: .F.

        // Array or object

        aOne := { 1, 2, 3 }
        aTwo := { 1, 2, 3 }
        aThree := aOne
        ? aOne == aTwo           // Result: .F.
        // values within the arrays are equal, but the arrays,
        // themselves, are separate and distinct
        ? aOne == aThree         // Result: .T.

See Also: $ < <= <> = (equality) > >=

: Send

 


 :
 Send--binary                                    (Object)
------------------------------------------------------------------------------
 Syntax

     <object>:<message>[(<argument list>)]

 Type

     Object

 Operands

     <object> is the name of the object that is to receive the message.

     <message> is the name of a method or instance variable.

     <argument list> is a list of parameters that are passed to the
     specified message.  The parentheses surrounding the argument list are
     optional if no parameters are supplied in the message send.  By
     convention, however, parentheses distinguish a message send with no
     parameters from an access to an exported instance variable.

 Description

     Each class defines a set of operations that can be performed on objects
     of that class.  Perform these operations by sending a message to the
     object using the send operator (:).

     When a message is sent to an object, the system examines the message.
     If the object is of a class that defines an operation for that message,
     the system automatically invokes a method to perform the operation on
     the specified object.  If the class does not define a method for the
     specified message, a runtime error occurs.

     Executing a message send produces a return value, much like a function
     call.  The return value varies depending on the operation performed.

 Examples

     .  In this example, myBrowse is the name of a variable that
        contains a TBrowse object reference.  pageUp() is a method that
        specifies the operation to be performed.  The available operations
        and corresponding methods vary depending on the class of the object.
        The Error, Get, TBColumn, and TBrowse classes are documented in this
        chapter.

        myBrowse:pageUp()

     .  This example forces the checkbox state to true (.T.):

        myCheck : Select (.T.)

Overview of OOP

Overview of Object-Orientation

“After years of relative obscurity, object orientation appears to be entering the mainstream of commercial computing for both software developers and end users. A shift to object orientation is occurring simultaneously across a wide range of software components, including languages, user interfaces, databases, and operating systems. While object-oriented programming is no panacea, it has already demonstrated that it can help manage the growing complexity and increasing costs of software development”.

 

                – from Object-Oriented Software by

                               Winblad, Edwards & King

This quotation is an excellent summary of what is happening in the world of computing today. Although exciting research and development is taking place on many fronts, no single software topic currently enjoys as wide a scope or impact as object orientation. Some of the most advanced and powerful software products available today incorporate object orientation as a central concept: languages such as Smalltalk, C++, and Actor; leading edge minicomputer databases such as Ontos and Servio Logic’s Gemstone; expert system development tools such as Neuron Data’s Nexpert Object and Level 5 Object from Information Builders; and graphical user interfaces (GUIs) such as Microsoft Windows, as well as UNIX GUIs such as Open Software Foundation’s Motif, and Sun Microsystems’ Open Look.

Although object orientation applies in slightly different ways in each of the areas mentioned above, the same basic concepts are being applied in each case. Because of its broad scope, the term is often misused, especially in marketing claims; indeed, articles have been written on this subject, such as “My Cat is Object-Oriented”, by Roger King of the University of Colorado.

This abuse often arises from the fact that object orientation in user interfaces is not easy to define clearly, and it is through user interfaces that end users encounter object orientation, usually without realizing it. Some vendors assert that their products are object-oriented merely because they use screen windows – the windows are objects, the argument goes, and therefore the program is object-oriented.

This is perhaps an extreme of misrepresentation, but the situation is complicated by in-between products such as Microsoft Windows. At both the user interface and programming level, Windows is object-oriented in many ways, but in other ways falls far short of being “fully” object-oriented.

The aspect of object orientation which Class(y) addresses is that of object-oriented languages. The features required of an object-oriented language are well defined, and existing language products set something of a standard in this area. Once familiar with the principles of object-oriented languages, it becomes much easier to differentiate between true and false claims about object orientation in other areas.

One of the main driving forces for the adoption of OOP is likely to be the need to produce programs that run under graphical user interfaces such as Microsoft Windows. This means that changing from procedural to object-oriented programming may involve changing not just the language being used, but the operating environment, resulting in an extremely steep learning curve.

While GUIs promise to make life easier for the end user, they will only make it harder for the programmer, unless we are prepared to change our programming style. Writing programs with a completely object-oriented architecture simplifies development for GUIs, since the program architecture reflects the architecture of the underlying environment.

Although we cannot write Microsoft Windows applications using standard Clipper just yet, we can prepare ourselves by starting to develop object-oriented programs. This will allow us to climb the learning curve gradually, rather than suddenly being forced to learn a new programming style as well as the complexities of event driven programming in a GUI.

We’ll start our climb of the learning curve with a brief look at the history of object-oriented languages, followed by an introduction to object-oriented concepts.

Brief History of Object-Oriented Languages

The concept of an object class and inheritance, central to object-oriented languages, was first implemented in the language Simula 67, an extension of Algol 60 designed in 1967 by Ole-Johan Dahl and Kristen Nygaard from the University of Oslo and the Norwegian Computing Center (Norsk Regnesentral). Although Simula, as it is now called, is a general purpose programming language, it is not in wide usage.

A major milestone in the development of object-oriented languages was the Smalltalk research project at the Xerox Corporation’s Palo Alto Research Centre (PARC). Starting in the early 1970s, the Smalltalk project, initiated by Alan Kay, had as its goals more than just the development of a programming language; rather, a complete integrated environment was the goal, including an object-oriented language, development tools, and a graphical interface. The standard components of modern graphical user interfaces, such as windows, icons, and the mouse, were pioneered at Xerox PARC.

The Smalltalk language itself was the first ‘true’ object-oriented language in that it dealt exclusively with objects. All subsequent object-oriented languages have been based on the concepts used in Smalltalk. Smalltalk was important, not just for its language, but for the development tools available in the Smalltalk environment. These include class browsers and object inspectors. A class browser is a very powerful tool which allows program code to be edited in a much more convenient and structured way than with conventional editors. Because of the inherently well-defined structure of object-oriented programs, the class browser is capable of displaying a given program’s class hierarchy in graphical form, allowing the user to ‘point and shoot’ to select a particular method (procedure) to be edited. Many programming tasks become menu driven, such as the creation of new classes, modifying the structure of the inheritance tree, and modifying the structure of a class. These operations are more complex and tedious when performed in a traditional editing environment.

Tools such as these are an integral part of the promise of object-oriented technology. They can simplify a programmer’s life, reducing development time and costs. Although they are a rarity in the DOS world at present, as the move toward object-oriented technology grows, and as we move towards GUIs like Microsoft Windows, these tools will become more commonplace.

What is an Object?

One of the fundamental reasons that object orientation is enjoying such success as a programming paradigm is very simple: the real world is made up of objects. An invoice is an object. A stock item is an object. A balance sheet is an object. An entire company is an object. Objects can contain other objects (this is called composition); and in this way complete systems can be constructed using objects.

But what is an object from a programming point of view? Simply put, it is a collection of related data, which is associated with the procedures which can operate on that data.

By this definition, most well-structured programs could be said to contain objects. This is another contributing factor to the confusion surrounding the definition of object orientation.

It is in fact possible to write programs in an object-oriented way in many traditional procedure-oriented languages. However, without the support provided by an object orientated languages, many compromises have to be made.

An object-oriented language formalizes the relationship of the data within an object to the program code which can operate on that data, by requiring that the compiler or interpreter be informed which procedures are allowed to operate on an object’s data.

Before we can clarify our definition of an object further, we need to explore a few other concepts.

Classes, Instances and Instance Variables

In any given system, many objects will exist. Many of these objects will be very similar to each other: for example, you might have thousands of invoice objects stored on disk. Although each one is different, they all share a common set of attributes. The same operations are valid on all of them. There is a term to describe such a collection of similar objects: it is called a class.

A class can be thought of as a template, or specification, for creating an object. The class itself consists of details specifying what the structure of its objects should be. The class can also be said to ‘contain’ the program procedures which are permitted to operate on objects of that class.

For example, an Invoice class would contain procedures for printing and updating invoices. It would also contain details of the structure of an invoice object, for example that each invoice object must contain variables named date, customer, amount, etc.

To look at it another way, we can define an object as an instance of a class. A given class may have many instances of itself (objects) in existence at any one time. Each of these instances has a structure determined by the class to which it belongs, but they are distinguished from each other by the data within that structure, which is stored in instance variables. The term instance variable distinguishes a variable that belongs to an object class from the ordinary stand-alone variables that we are used to. Instance variables are contained within objects, and are not directly accessible outside of those objects, although they can be accessed by sending messages to their objects.

Messages and Methods

Earlier, an object was defined as a module containing both procedures and data. An object’s procedures are known as methods. This terminology helps to distinguish them from procedures which are not associated with an object, since there are fundamental differences. In a fully object-oriented system such as Smalltalk, there are no procedures, only methods. In a hybrid system such as C++, or Clipper with Class(y), both methods and procedures can coexist.

Methods are not called in the same way that procedures are. Rather, messages are sent to objects, which respond by executing the appropriate method. All valid operations on or using the data in an object are defined by its methods, so all operations on an object are accomplished by sending messages to the object. Because of this, it is not necessary for other objects to access, or even know about, the internals of foreign objects. Objects behave like black boxes: send them a message, and they respond by executing the appropriate method. Send the print message to an invoice object, and it will respond by printing itself. This black box approach is known generally as encapsulation, and while it is possible to achieve in procedural systems, object-oriented systems actively encourage, support and enforce it.

Inheritance – Superclasses and Subclasses

Common properties among groups of classes can often be combined to form a parent class, or superclass. For example, it might make sense for a Quotation class, an Order class, and an Invoice class to all share the same superclass, a Sales Document class. The Quotation, Order, and Invoice classes are thus subclasses of the Sales Document class. This is known as inheritance. The subclasses inherit all the properties of their superclass, and may add unique, individual properties of their own. This concept can be extended further, with subclasses of subclasses. Such class hierarchies are a common feature of object-oriented systems.

Inheritance is one of the most powerful features of object-oriented programming, since it allows reuse of existing code in new situations without modification. When a subclass is derived from a superclass, only the differences in behavior need to be programmed into the subclass. The superclass remains intact and will usually continue to be used as is in other parts of the system, while other subclasses are using it in different ways.

Polymorphism

The term polymorphism in this context refers to the fact that the same message, such as print, can result in different behaviors when sent to different objects. Sending the print message to a graph object has a different effect than it would on a balance sheet object. With a traditional procedural approach, the programmer is forced to differentiate procedure names, using names like PrnBalSheet and PrintGraph. In an object-oriented language, this differentiation is unnecessary, and in fact unwise.

Polymorphism has benefits for the programmer in that the same name can be used for conceptually similar operations in different classes, but its implications go deeper than that. It means that a message can be sent to an object whose class is not known. In a procedural system, given a variable of unknown type, a CASE statement would typically be required to test the type of the variable and pass it to the correct procedure for the desired operation. In an object-oriented system, a message is sent to the object with no testing, and the object responds accordingly.

This has important implications for inheritance, since it means that methods belonging to classes near the root of the inheritance tree do not need to know details of the subclasses which may be inherited from them. By sending messages with standardized names to the objects with which it deals, generic methods can be written which can later be used with any class which supports the required messages.

Anton van Straaten

 

 This article borrowed from manual of famous Clipper 3tdh party Library : CLASS(Y)

 http://www.the-oasis.net/ftpmaster.php3?content=ftplib.htm

Harbour New Data types

Data type & Syntax extensions in Harbour

In addition to Clipper’s scalar ( Character, Number, Date, Logical, MEMO, Nil ) and complex ( array, CodeBlock )  data types; Harbour has extended data types: pointer as scalar and object and hach as complex type.

For standard data types please refer here and/or here.

In database files (tables) data types of fields are predefined in structure of table.

For extended field types please refer here.

For data items other than fields (such as variables and manifest constants); in general, type of data  determined automatically by system, when assigning a value. The first basic way of this, is assigning a “literal” value.

For a working sample about constants please refer here.

cString := "This is a string" // A character string enclosed by a string delimiter
nNumber := 123.45 // A numeric value combined digits, decimal point and a sign ( + / - )
lTrue   := .T. // A T (tYy) or F (fNn) letter enclosed by two periods (.)
aArray  := {} // Arrays can be assigned literally by enclosed with curly brace

In addition to this basic literal value notations, Harbour has also extended notations:

– Data Types determined by special prefixs

— 0x… : Hexadecimal constant

  nNumber := 0x0A  // 0x prefix implies the string as Hexadecimal String  
                   // and type of resulting value become as Numeric (N) 
  ? nNumber, VALTYPE( nNumber ) // 10 N

— 0d… date constant

    dDate_1 := 0d20121225  // 0d prefix implies the string a date string 
                           // ( instead of using CTOD() )
                           // and type of resulting value become as Date (D) 
    ? dDate_1, VALTYPE( dDate_1 ) // 25.12.2012 D

– Special literal string formats

— d”…” : Date constant

dDate_2 := d"2012-12-26" ? dDate_2, VALTYPE( dDate_2 ) // 26.12.2012 D

— t”…” : Time constant

tTime_1 := dDate_2 + t”01:31:06″

? tTime_1, VALTYPE( tTime_1 ) // 26.12.2012 01:31:06.000 T

— e”…” : Escape sequences

Escape sequences are used to define certain special characters within string literals.

( Prefix by “\” escape sequence codes within that string )

The following escape sequences are available in C and C++ language :

Escape
sequence Description            Representation

   '     single quote          byte 0x27
   "     double quote          byte 0x22
   ?     question mark         byte 0x3f
         backslash             byte 0x5c

         null character        byte 0x00
   a     audible bell          byte 0x07
   b     backspace             byte 0x08
   f     form feed - new page  byte 0x0c
   n     line feed - new line  byte 0x0a
   r     carriage return       byte 0x0d
   t     horizontal tab        byte 0x09
   v     vertical tab          byte 0x0b

   nnn   arbitrary octal value byte nnn
   xnn   arbitrary hexadecimal value byte nn

   unnnn arbitrary Unicode value.
          May result in several characters. code point U+nnnn
   Unnnnnnnn arbitrary Unicode value.
           May result in several characters. code point U+nnnnnnnn

Note that all sequences not available in Harbour.

For the new complex data type Hash, there is a literally assigning way :

hHash := { => }    // => sign indicates the hash

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

PROCEDURE Main()
SET CENT ON
SET DATE GERM
CLS

* Data Types determined by special prefixs

** 0x... : Hexadecimal constant

nNumber := 0x0A // 0x prefix implies the string as Hexadecimal String 
// and type of resulting value become as Numeric(D)
? nNumber, VALTYPE( nNumber ) // 10 N
** 0d... date constant 

 dDate_1 := 0d20121225 // 0d prefix implies the string a date string 
                       // ( instead of using CTOD() )
                       // and type of resulting value become as Date (D) 

? dDate_1, VALTYPE( dDate_1 ) // 25.12.2012 D
* Special literal string formats
** d"..." : Date constant
dDate_2 := d"2012-12-26"
? dDate_2, VALTYPE( dDate_2 ) // 26.12.2012 D 

** t"..." : Time constant
tTime_1 := dDate_2 + t"01:31:06"
? tTime_1, VALTYPE( tTime_1 ) // 26.12.2012 01:31:06.000 T

** e"..." : Escape sequences 

? e"This is\na string\nformatted by\nEscape sequences \x21

/* The result : 
This is
a string
formatted by
Escape sequences !
*/
@ MAXROW(), 0 WAIT "EOF DTS_Exts.prg" 
RETURN // DTS_Exts.Main() 

*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ DTS_Exts