In Lisp sind - im Gegensatz zu vielen anderen Sprachen - Variablen völlig
untypisiert, d.h. an ein Symbol kann jeder beliebige Datentyp gebunden
werden. Wir können einer Variablen zunächst ein Ganzzahl, dann eine Liste
und später auch noch eine Zeichenkette oder einen Auswahlsatz zuweisen -
der Interpreter wird sich nicht darüber beschweren. In C/C++, Java,
Pascal, Delphi sieht das völlig anders aus. Jede Variable wird für einen
bestimmten Datentyp deklariert und darf dann auch nur diesen Datentyp
enthalten. Falls man in diesen Sprachen einer als Integer deklarierten
Variablen eine Zeichenkette zuweisen will, endet das nur mit einer
Fehlermeldung.
Auch Basic gehörte ursprünglich zu diesen typisierten Sprachen, allerdings
wurde in VisualBasic dieses Konzept durch die Einführung des Datentyps
Variant aufgeweicht. Einer Variablen vom Typ Variant kann man fast alle
Datentypen zuweisen, und jede Variable, die nicht für einen anderen
Datentyp deklariert wurde, hat automatisch den Typ Variant.
Wir wissen ja schon, dass wir es bei der VisualLisp- bzw.
ActiveX-Programmierung mit neuen Datentypen zu tun haben, und der variant
ist sicherlich neben VLA-OBJECT der am häufigsten vorkommende. Und was
kann in so einem Variant alles drin sein, womit müssen wir rechnen?
Ein Variant kann selbstverständlich eine Zahl (Ganz-, Real-) oder eine
Zeichenkette enthalten - da entsteht für Einsteiger selten ein Problem. Er
kann aber auch ein Objekt enthalten, dass wir dann mit den vl-Bordmitteln
weiterverarbeiten können. Schwieriger wird es, wenn ein Variant ein
Datenfeld (in VisualLisp heisst dieses dann safearray) enthält. Diese
Kombination bereitet den ActiveX-Einsteigern erfahrungsgemäss etwas
Kopfzerbrechen.
Werfen wir doch einmal einen Blick auf einen Variant - wir schauen uns nur
die Rückgabe einer Funktion an, die mit Sicherheit einen Variant
zurückgibt. Als Beispiel greifen wir auf die Eigenschaft 'Limits' des
aktiven Dokuments zu. Dazu können wir entweder den langen Weg gehen, d.h.
wir arbeiten uns bis zum 'ActiveDocument' vor, oder wir benutzen unseren
Funktionsfinder, was wahrscheinlich zu einem kürzeren Code führt:
(find-vla-function "*limits*")
= > ("VLA-GET-LIMITS" "VLA-PUT-LIMITS")
(vla-get-limits
(vla-get-activedocument
(vlax-get-acad-object)
)
)
#<variant 8197 ...>
Nun, was bedeutet die Zahl 8197? Es handelt sich hier offensichtlich nicht
um eine Speicherstelle in Hexadezimal-Schreibweise, wie wir sie von
anderen Datentypen her kennen. Ein Blick in die Online-Hilfe schafft
wieder einmal Aufklärung - nach einigem Suchen finden wir im VisualLisp
Developer's Guide unter der Überschrift Converting AutoLisp Datatypes to
ActiveX Data Types eine Tabelle, die uns weiterhilft. Dort wird eine Reihe
in Lisp vordefinierter Konstanten aufgelistet, und durch Ausprobieren
lässt sich herausfinden, dass die Konstante vlax-vbArray den Wert 8192
hat:
!vlax-vbarray => 8192
!vlax-vbdouble => 5
Eine Konstante mit dem Wert 8197 werden wir allerdings nicht finden. Das
seine Ursache darin, dass hier zwar keine binäre Codierung vorliegt, wie
wir sie z.B. von der Systemvariablen "Osmode" kennen, aber trotzdem Werte
zusammengezählt werden. Anhand der vlax-vb...-Konstanten können wir uns
nun zusammenreimen, dass es sich hier um ein Array handelt (8192)und dass
in diesem Array Doubles (5) enthalten sind.
Ein solcher Variant, der ein Array enthält, lässt sich leider nicht ohne
weiteres weiterbearbeiten - hier ist Konvertierungsarbeit angesagt. Werfen
doch einmal einen Blick auf die Funktionen, die zur Verarbeitung von
variants überhaupt zur Verfügung stehen:
-
(vlax-make-variant ...) werden wir bald benötigen, um den Datentyp
variant zu erzeugen
-
(vlax-variant-type ...) gibt uns eine Zahl zurück, die anzeigt,
welcher Datentyp enthalten ist - das sind natürlich genau diese
Zahlen, die auch in den vlax-Konstanten abgespeichert sind, z.B. 5 für
einen Double oder 8192 für ein Array
-
(vlax-variant-value ...) liest den Wert eines variant aus und gibt ihn
zurück. Damit werden wir uns noch näher befassen.
-
(vlax-variant-change-type ...) verursacht einen sog. type cast. Wird
z.B. von Double nach Integer konvertiert, gehen die Kommastellen
verloren. Nicht jede Kombination ist hier möglich und sinnvoll.
Hier soll es jetzt also um
variants gehen, die bei Aufruf von
(vlax-variant-type ...) eine Zahl zurückgeben, die größer als
8192 ist - die also ein Array enthalten. Ein Array, in der Online-Hilfe
auch Datenfeld genannt, hat wenig Ähnlichkeit mit den Listen, die wir
aus Lisp kennen. Ein Array hat zum Einen eine festgelegte Größe, d.h.
ein Zugriff mit einem ausserhalb des Bereichs liegenden Index führt
zu einem Fehler. Dies ist auch der Grund, weshalb man es in VisualLisp
Safe-Array genannt hat. Zum Anderen kann ein Array immer nur einen
einzigen Datentyp enthalten, während bei den Lisp-Listen natürlich
alle Kombinationen vorkommen können.
Mit der Funktion
(vlax-variant-value) bekommen wir also Zugriff
auf das im
variant abgelegte Array, der Datentyp ist nun
Safearray:
(vlax-variant-value
(vla-get-limits
(vla-get-activedocument
(vlax-get-acad-object)
)
)
)
=> #<safearray...>
Für das Auslesen eines Arrays wiederum stellt uns VisualLisp zwei
verschiedene Funktionen zur Verfügung. Die eine,
(vlax-safearray-get-element),
dient zum Auslesen einzelner Elemente - das einzige Argument ausser dem
Array selbst ist der Index (falls das Array mehrdimensional ist, erhöht
sich aber die Anzahl der Argumente, da die Anzahl der Indizes steigt.
Ein viel einfacherer Weg besteht allersings darin, das Array in eine
Lisp-Liste umzuwandeln. Dazu gibt es die praktische Funktion
(vlax-safearray->list) (Hier liegt kein Tippfehler vor, der
stilisierte Pfeil deutet die Richtung der Konvertierung an). Dass die
beiden 2D-Punkte, die die Zeichnungslimiten festlegen, als eine Liste
mit vier Koordinaten herauskommen, ist kein Problem der Konvertierung -
die Punkte liegen so bereits in dem linearen Array.
(vlax-safearray->list
(vlax-variant-value
(vla-get-limits
(vla-get-activedocument
(vlax-get-acad-object)
)
)
)
)
=> (0.0 0.0 420.0 297.0)
Das Zusammenspiel von Objekten, Collections, Arrays und Variants mag den
Einsteiger in die Materie vielleicht kurzfristig etwas irritieren. Diese
Dinge zu beherrschen ist allerdings keineswegs schwierig. Ärgerlich wird
es allerdings, wenn man plötzlich auf einen Datentyp in einem
variant
stösst, der offensichtlich in VisualLisp vergessen wurde. Wir verwenden
für ein Beispiel noch einmal die rekursive Form
(vlax*get-property),
die im letzten Kapitel vorgestellt wurde. Es soll die Hintergrundfarbe
der Layout-Fenster ausgelesen werden:
(vlax*get-property nil
'(GraphicsWinLayoutBackgrndColor Display Preferences)
)
=> #<variant 19 0>
Ein Variant des Typs 19 ist nirgendwo in der Online-Hilfe zu finden. Man
kann nur kombinieren, dass diese Farbe nicht als AutoCAD-Farbe angegeben
wird, sondern als RGB-Farbe - was eigentlich auch logisch erscheint. Es
liegt also mit dem Typ 19 offensichtlich ein eigener Datentyp für
RGB-Farbwerte vor. Diesen Farbwerten kann man beispielsweise in Excel
oder Access begegnen: es handelt sich um Integerwerte im Bereich zwischen
0 und 16777215, was dem Bereich einer vorzeichenlosen 24-Bit-Ganzzahl
entspricht. Auch das erscheint logisch, da ja RGB-Farben jeweils ein Byte
(8 Bit) für jeden der drei Farbkanäle benutzen.
Beim Variant-Typ 19 für RGB-Farben bleibt uns nichts anderes übrig, als
mit
(vlax-variant-change-type) einen 'type cast' nach
vlax-vbLong
durchzuführen. Dies hat keine gravierenden Konsequenzen, ausser dass jetzt
natürlich möglich wird, eine ungültige Farbnummer zuzuweisen, da der
Wertebereich des Long größer ist als der einer Farbe und auch negative
Zahlen vorkommen können.
Einen Variant vom Typ 19 mit VisualLisp zu erzeugen, ist auch kein Problem -
man muss nur auf eine
vlax-vb...-Konstante verzichten und den Typ
19 als Zahl angeben. Allerdings kann man sich die Mühe auch durchaus sparen,
da die ActiveX-Funktionen, die den Typ 19 als Argument erwarten auch einen
Long akzeptieren, solange er nicht aus dem Gültigkeitsbereich fällt.
Merkwürdig ist dabei jedoch, dass auch in VisualBasic für AutoCAD kein
Variant vom Typ 19 zu finden ist. Die VB-Funktion
RGB(red, green, blue)
gibt einen Long zurück!