Beware of SalStrLop (in TD3.1)

If SalStrLop( sString ) is applied to an empty string then strange things can happen. This is because Gupta sets the internal buffer length to zero. When this string is concatenated to another string (say sString2) then the buffer length of sString2 will be shrinked by 1 each time. When you pass such a string to an external dll function crashes might occur because the string will be copied but only to the length of the buffer. Usually the terminating zero will then be stripped away. You get the picture.

Here is some code that shows the problem:

Set sTest = ''
Set sTest2 = 'A'
Set nBuffer = SalStrGetBufferLength( sTest ) ! nBuffer is 1 - just the trailing zero
Set nBuffer = SalStrGetBufferLength( sTest2 ) ! nBuffer is 2 - 'A' and trailing zero
Call SalStrLop( sTest )
Set nBuffer = SalStrGetBufferLength( sTest ) ! nBuffer is 0!
Set sTest2 = sTest2 || sTest
Set nBuffer = SalStrGetBufferLength( sTest2 ) ! nBuffer is 1!!

Workaround: Only apply SalStrLop when the string is not empty. Maybe write a function like this:

!!CB!! 134
Function: StrLopSafe
 Description:
 Returns
  Number:
 Parameters
  Receive String: sIn
 Static Variables
 Local variables
 Actions
  If ( sIn )
   Return SalStrLop( sIn )
  Else
   Return 0

In TD6.3 this problem does not exist.

Happy coding!

Advertisements

Calling General Window Class-functions via Window Handle

Gupta provides the ability to define general Window Classes which can be used as a base class for real window classes such as dialogs, FormWindows, controls, etc.
In addition there is the possibility to call a function of a window or control via its window handle. To do so you have to equip the handle with with a so-called window handle qualifier:
Call hWnd.Frm1.SomeFunction()
  • hWnd is the window handle of a form Windows.
  • Frm1 is the template name of a window or a window class.
  • The function SomeFunction() is a function that is defined in Frm1.
So far, so good. This is documented in the Gupta books or help.
But if the function SomeFunction() is defined in a General Window Class and Frm1 is a derivation you would have to write the call:
Call hWnd.GenWndCls.SomeFunction()

But unfortunately Gupta then unnecessarily comes up with a compile error „class GenWndCls, is a general window class. Hence it may serve only as a base class, and may not be qualified with a window handle“. This is very annoying because it is often useful to use a general Window Class as the basis for a form and a dialogue. Fortunately there is a simple workaround for this:

Call hWnd.GenWndCls..SomeFunction()

Yes – a simple additional dot and Gupta accepts the expression. Why is this so? The ..-Operator preceding a function call is a so-called late-bound reference that causes Gupta to look up the called function in all derived classes and executes the „deepest“ implementation. If SomeFunction() would have been defined in Frm1 then this implementation would be executed. If there are no additional implementations Gupta calls the first spurned function without complaint.

Happy coding.

General Window Class-Funktionen über ein Window Handle aufrufen

Gupta bietet die Möglichkeit, General Window Classes zu definieren, die als Basisklasse für echte Window Classes wie z.B. Dialoge, FormWindows, Steuerelemente, etc. dienen können.

Des weiteren gibt es die Möglichkeit, Funktionen eines Fensters oder Steuerelements über dessen Window Handle aufzurufen. Dazu muss man den Handle mit einem sogenannten Window Handle Qualifier ausstatten:

Call hWnd.Frm1.SomeFunction()
  • hWnd ist das Window Handle eines Form-Windows.
  • Frm1 ist entweder der Templatename eines Fensters oder eine Fensterklasse.
  • Die Funktion SomeFunction() ist eine Funktion, die in Frm1 definiert ist.

Soweit, so gut. Das ist alles in den Gupta Books bzw. in der Hilfe dokumentiert.

Hat man jedoch die Funktion SomeFunction() in einer General Window Class definiert, von der Frm1 ableitet, müsste man den Aufruf so schreiben:

Call hWnd.GenWndCls.SomeFunction()

Doch leider bringt Gupta dann unnötigerweise den Compilefehler „Class GenWndCls, is a general window class. Hence it may serve only as a base class, and may not be qualified with a window handle“. Das ist sehr ärgerlich, da es oft sinnvoll ist, eine General Window Class als Basis z.B. für eine Form und einen Dialog zu verwenden. Glücklicherweise gibt es dafür einen einfachen Workaround:

Call hWnd.GenWndCls..SomeFunction()

Ja – ein einfacher Punkt mehr und schon akzeptiert Gupta den Ausdruck. Warum ist das so? Der ..-Operator vor einem Funktionsaufruf ist eine sogenannte late-bound reference die bewirkt, dass Gupta die genannte Funktion in allen abgeleiteten Klassen sucht und die „tiefste“ Implementierung aufruft. Wäre SomeFunction() auch in Frm1 definiert, würde diese Implementierung aufgerufen. Gibt es keine weitere Implementierung ruft Gupta klaglos die zunächst verschmäte Funktion auf.

Happy coding.

Self Reference with Functional Classes in Gupta

With Gupta Team Developer you can create functional class datatypes (UDV) – comparable to classes from C# or Java. In contrast to the above frameworks with Gupta variables that are based on these data types are not only declared but also instantiated. This has a great positive effect: it avoids the runtimeerror „NullReferenceException“ which is quite common in C# and Java.

Sample C#

class SomeClass
{
        public Int32 someMember;
}
public void SomeMethod()
{
    SomeClass someObject;
        // NullReferenceException
    someObject.someMember = 7;

        // this works
    someObject = new SomeClass();
    someObject.someMember = 7;

}

Sample Gupta

Functional Class: SomeClass
    Instance Variables:
        Number: nSomeMember

Function: SomeFunction
    Local variables:
        SomeClass: someObject
    Actions:
        ! this works
        Set someObject.nSomeMember = 7
At the moment when the function SomeFunction() is called Gupta instantiates the class.

But this default behavior has also a disadvantage: the class (UDV) SomeClass cannot contain itself (easily) as an instance variable:

Functional Class: SomeClass
 Instance Variables:
     ! results in compile error: Circularly defined class.
     SomeClass: cSomeChild
Because as soon as SomeClass would be instantiated the instance variable cSomeChild would also be instantiated then their instance variable cSomeChild and so on.

The same problem occurs if Class1 is an instance variable of type Class2 and Class2 of Class1.

But there is an almost not documented way to create just such constructs: by setting the UDV to OBJ_Null when declaring it:

Functional Class: SomeClass
 Instance Variables:
     SomeClass: cSomeChild = OBJ_Null
A few comments:
  • I have discovered this option randomly in a single place in the help file of Gupta: as sample code in the description of the command SalObjIsNull.
  • Only UDVs can be set in the declaration section. For Number, String, DateTime, etc. TD unfortunately does not allow it. After all a constructor function for UDVs should come with TD 6.3 where you could set values for instance variables.
  • If you want to use the UDV cSomeChild you previously have to either assign an existing instance of the same or derived type or create an instance via the new command:
Set cSomeChild = new SomeClass

Examples of use

There are a lot of scenarios where it is useful or even necessary to use nested classes:

  • Mapping of hierarchical data structures such as XML documents or even all data structures that can be displayed in TreeViews that have more than one hierarchy level.
  • Double linked list structures
  • publish / subscriber classes which are mutually linked
  • tree structures such as red-black-index trees

Have fun experimenting with these structures.

Happy coding.

 

Selbstreferenz mit Functional Classes in Gupta

Mit Gupta Team Developer lassen sich Functional Class Datentypen (UDV) erstellen – vergleichbar mit Klassen in C# oder Java. Im Unterschied zu den genannten Frameworks sind Variablen, die auf diesen Datentypen basieren, bei Gupta im Normalfall nicht nur deklariert sondern auch instantiiert. Das hat einen großen positiven Effekt: Man vermeidet den in C# und Java recht verbreiteten Runtimefehler „NullReferenceException“.

Beispiel C#

class SomeClass
{
        public Int32 someMember;
}
public void SomeMethod()
{
    SomeClass someObject;
        // NullReferenceException
    someObject.someMember = 7;

        // this works
    someObject = new SomeClass();
    someObject.someMember = 7;

}

Beispiel Gupta

Functional Class: SomeClass
    Instance Variables:
        Number: nSomeMember

Function: SomeFunction
    Local variables:
        SomeClass: someObject
    Actions:
        ! this works
        Set someObject.nSomeMember = 7

Gupta instantiiert die Klasse in dem Moment, in dem die Funktion SomeFunction() aufgerufen wird.

Dieses Standardverhalten hat aber auch einen Nachteil: Die Klasse (UDV) SomeClass kann sich selbst nicht (ohne weiteres) als Instanzvariable enthalten:

Functional Class: SomeClass
 Instance Variables:
     ! results in compile error: Circularly defined class.
     SomeClass: cSomeChild

Denn sobald SomeClass instantiiert würde, würde auch dessen Instance Variable cSomeChild instantiiert, anschließend dessen Instance Variable cSomeChild usw.

Dasselbe Problem tritt auf, wenn Class1 eine Instanzvariable vom Typ Class2 besitzt und Class2 eine von Class1.

Aber es gibt eine fast nicht dokumentierte Möglichkeit, eben doch solche Konstrukte zu erstellen: Indem man die UDV in der Deklaration auf OBJ_Null setzt:

Functional Class: SomeClass
 Instance Variables:
     SomeClass: cSomeChild = OBJ_Null

Ein paar Anmerkungen dazu:

  • Ich habe diese Möglichkeit zufällig an einer einzigen Stelle in der Hilfedatei von Gupta entdeckt: Als Beispielcode bei der Beschreibung des Befehls SalObjIsNull.
  • Nur UDVs können bei der Deklaration festgelegt werden. Bei Number, String, DateTime etc. lässt der TD das leider nicht zu. Immerhin soll mit TD 6.3 eine Konstruktor-Funktion für UDVs kommen, in denen man u.a. Werte für Instanzvariablen festlegen kann.
  • Wenn man die UDV cSomeChild verwenden möchte, muss man vorher entweder eine andere, bereits bestehende Instanz desselben oder abgeleiteten Typs zuweisen oder mit per new-Befehl eine Instanz erzeugen:
Set cSomeChild = new SomeClass

Anwendungsmöglichkeiten

Es gibt eine Menge Szenarien, in denen es äußerst nützlich oder sogar notwendig ist, verschachtelte Klassen zu verwenden:

  • Abbildung von hierarchischen Datenstrukturen, wie z.B. XML-Dokumente oder überhaupt alle Datenstrukturen, die man in TreeViews mit mehr als einer Ebene darstellen möchte oder kann.
  • Doppelt verknüpfte Listenstrukturen
  • Publish- Subscriber-Klassen, die wechselseitig verknüpft werden
  • Baumstrukturen – z.B. Red-Black-Index-Bäume

Viel Spaß beim Experimentieren.

Happy coding.

Date Literal in Gupta

The other day I stumbled over a small but fine undocumented feature in the Gupta TeamDeveloper: the date literal.
It does not happen very often but sometimes you need to define a fixed date in the source code. So far we have done like that

dtValue = SalDateConstruct (2015, 11, 21, 22, 35, 12)

But this is easier and more elegant,

dtValue = 2015-11-21-22.35.12

more flexible

dtValue = 2015-11-21

and precise

dtValue = 2015-11-21-22.35.12.123456

down to the microsecond.

Happy coding.

Datum-Literal in Gupta

Neulich bin ich wieder über kleines, aber feines, undokumentiertes Feature im Gupta TeamDeveloper gestolpert: Das Datum-Literal.

Es kommt zwar nicht häufig vor, aber manchmal muss man ein festes Datum im Quelltext definieren. Bislang haben wir das mit

dtValue = SalDateConstruct( 2015, 11, 21 , 22, 35, 12)

erledigt.

Es geht aber auch einfacher und eleganter:

dtValue = 2015-11-21-22.35.12

und flexibler

dtValue = 2015-11-21

und exakter

dtValue = 2015-11-21-22.35.12.123456

bis hinunter zur Mikrosekunde.

Happy Coding.