Definition in der OVM-Datei (Beispiel):
myString : STRING; |
Lesender Zugriff im C-Quelltext:
OV_UINT i;
/* myString als
ganzes ausgeben */ printf("myString = %s\n",
pobj->v_myString);
/* myString in
einzelnen Zeichen
ausgeben */ if(pobj->v_myString)
{ /* Achtung, Zeiger könnte NULL sein (leerer String)! */
for(i=0; pc[i]; i++) {
printf("%ld.
Zeichen von myString = ‚%c'\n", pc[i]);
} |
Vergleich mit
einem anderen String:
#include "libov/ov_string.h"
... OV_INT
vgl; /*
vergleiche Strings */ vgl =
ov_string_compare(pobj->v_myString, "VW
Bulli");
if(vgl ==
0) {
/* identisch */
...
} else
if(vgl > 0) {
/* myString ist
"größer" als
"VW Bulli", z.B. "VW Kaefer"
... }
else {
/*
myString ist "kleiner" als "VW Bulli", z.B. "Opel
Astra"
... } |
Schreibender Zugriff:
#include
"libov/ov_string.h" #include
"libov/ov_macros.h" ... OV_RESULT
result; result
=
ov_string_setvalue(&pobj->v_myString, "Mitsubishi"); if(Ov_Fail(result))
{
/* konnte String
nicht
setzen */
... } |
Definition in der OVM-Datei (Beispiel: Integer und String):
dynIntVec[] : INT; dynStringVec[] : STRING; |
Lesender Zugriff im C-Quelltext:
OV_UINT i; /*
dynIntVec ausgeben */ for(i=0;
i<pobj->v_dynIntVec.veclen;
i++) {
printf("dynIntVec[%ld]
= %ld\n", i, pobj->v_dynIntVec.value[i]); /*
dynStringVec ausgeben */ for(i=0;
i<pobj->v_dynStringVec.veclen; i++) { printf("dynStringVec[%ld]
= %s\n", i, pobj->v_dynStringVec.value[i]); } |
Schreibender Zugriff im C-Quelltext (Zugriff auf einzelne Elemente):
#include
"libov/ov_macros.h" ... OV_UINT
len, i; OV_RESULT
result; /* dynIntVec einen
Vektor mit 13 (unterschiedlichen)
Elementen zuweisen */ len =
13; result
=
Ov_SetDynamicVectorLength(&pobj->v_dynIntVec, len, INT); if(Ov_OK(result))
{ for(i=0;
i<len; i++) {
pobj->v_dynIntVec.value
[i] = 3*i+15; } }
else { /* konnte
neuen Wert nicht setzen (Speichermangel) */ ... } /* dynStringVec
einen Vektor mit 5 (gleichen) Elementen
zuweisen */ len =
5; result
=
Ov_SetDynamicVectorLength(&pobj->v_dynStringVec, len, STRING); if(Ov_OK(result))
{ for(i=0;
i<len; i++) { Ov_SetStringValue(&pobj->v_dynIntVec.value[i],
"Ein String"); } } else { /*
konnte neuen
Wert nicht setzen (Speichermangel) */ ... } /* dynIntVec und
dynStringVec "löschen" (je 0
Elemente) */ Ov_SetDynamicVectorLength(&pobj->v_dynIntVec,
0,
INT); |
Schreibender
Zugriff im C-Quelltext (Vektor als ganzes setzen):
#include
"libov/ov_macros.h" ... OV_INT
myIntValue[] = { 0, 8, 15, 4711 }; OV_UINT
myIntLen =
sizeof(myIntValue)/sizeof(myIntValue[0]); /* = 4 */ OV_STRING
myStringValue[] = { "Ich",
"mag", "ACPLT/OV" }; OV_UINT
myStringLen =
sizeof(myStringValue)/sizeof(myStringValue[0]); /* = 3 */ OV_RESULT
result; /* dynIntVec
auf Wert in myIntValue setzen */ result
=
Ov_SetDynamicVectorValue(&pobj->v_dynIntVec, myIntLen, INT); if(Ov_Fail(result))
{
/* konnte Wert
nicht setzen (Speichermangel) */ ... } /*
dynStringVec auf Wert in myStringValue setzen */ result
= Ov_SetDynamicVectorValue(&pobj->v_dynStringVec, myStringLen, STRING); if(Ov_Fail(result))
{
/* konnte Wert
nicht setzen (Speichermangel) */ ... } |
Der
Speicher wird
auf einem sog. Memory Stack
reserviert. Der Aufrufende des Get-Accessors muß den Stack
vorher
„blockieren“ und später wieder freigeben. In der Get-Accessor-Routine
selbst
wird nur der Speicher vom Stack angefordert. Beispiel:
#include
"libov/ov_memstack.h" #include
"libov/ov_string.h" ... OV_STRING
mylib_myclass_myvariable_get( OV_INSTPTR_mylib_myclass pobj OV_STRING
pstr =
(OV_STRING)ov_memstack_alloc(18); ov_string_print(&pstr,
"x ist gleich %d"; 4711); return
pstr; |
Ja! Unbedingt vor dem Aufruf den Memory Stack „blockieren“ und hinterher wieder freigeben! Der Memory Stack dient der aufgerufenen Accessor-Routine als Quelle für dynamischen Speicher (vgl. vorherige Frage). Beispiel:
#include
"libov/ov_memstack.h" ... /*
Stapelspeicher blockieren */ ov_memstack_lock(); /* Wert via
get-Accessor holen und ausgeben */ printf("myvariable
= %s\n“,
mylib_myclass_myvariable_get(pobj)); /*
Stapelspeicher wieder freigeben */ |
So gut wie nicht ist zu beachten. Der evtl. notwendige dynamische Speicher gehört immer dem Aufrufer. Er darf in der Set-Accessor-Funktion nicht verändert, sondern nur gelesen werden. Dies kommt aber eigentlich schon im Funktionsprototyp zum Ausdruck (const):
OV_DLLFNCEXPORT OV_RESULT mylib_myclass_myvariable_set(
OV_INSTPTR_mylib_myclass ptest,
const
OV_XXX
value ) {
... } |
Wer
nicht auf Compiler-Warnungen achtet ist selber schuld!
Diese Informationen kann man über das entsprechende Klassenobjekt der Instanz erhalten.
Über die Beziehung „instantiation“ erhält man eine Objektreferenz auf das zugehörige Klassenobjekt. Dieses beinhaltet (Beziehung „containment“) wiederum die einzelnen Variablen-Beschreibungsobjekte deren Attribute gerade die Definitionen aus der ovm-Datei widerspiegeln (z.B. vartype oder comment):
ovm-Datei:
CLASS Test : CLASS .... VARIABLES
TestVar: BOOL
COMMENT =
".....";
END_VARIABLES;
...
|
C-Datei:
OV_DLLFNCEXPORT void myLib_Test_execute( OV_INSTPTR_ov_object
pobj ) { /* *
local variables */ OV_INSTPTR_ov_variable pvar; OV_INSTPTR_myLib_Test pgvc;
OV_INSTPTR_ov_class pclass;
OV_INSTPTR_ov_object pobj2; pgvc = Ov_StaticPtrCast(myLib_Test, pobj); pclass = Ov_GetParent(ov_instantiation, pobj); pobj2 = Ov_GetFirstChild(ov_containment,
pclass); pvar = NULL;
while (pobj2) {
if
(ov_string_compare(pobj2->v_identifier, "TestVar")==0) {
pvar = Ov_StaticPtrCast (ov_variable, pobj2);
break;
}
pobj2
= Ov_GetNextChild(ov_containment, pobj2); } if (pvar) {
....
pvar->v_vartype ...
.... pvar->v_comment ... } } |
Seit der libov - Version 1.6.2 beträgt die maximale Datenbasisgröße 2GB. Eine dynamische Vergrößerung soll prinzipiell bei einer OV-Compilierung mit dem Makro OV_DYNAMIC_DATABASE ermöglicht werden, ist aber zum gegenwärtigen Zeitpunkt nicht hinreichend ausgereift. Problem ist hier das Memory-Map-File für die Persistenz der Datenbasis. Eine dynamische Änderung dieses Files wird nicht von allen Betriebssystemen unterstützt.
In der ov-builder-Version 1.0.2 (OV-Produktversion 1.6.3) konnte dieser Fehler behoben werden.
Wird er, zumindest ab der ov-builder Version 1.0.1 (OV-Produktversion 1.6.3).
Bis einschließlich libov-version 1.6.3 ist dies nicht möglich.
Da die Definition einer Library in einer ovm-Datei dem Anlegen einer Instanz einer Library-Klasse entspricht, kann deren startup-Methode auch nicht überladen werden. Dennoch ist das Ausführen von Aktionen beim Laden einer Bibliothek über einen Umweg möglich. Dazu muss in der Makedatei generik.mk ein zusätzliches define eingeführt werden:
MYLIB_DEFINES = \
-DOV_DEBUG \
-DOV_COMPILE_LIBRARY_$(MYLIB_NAME) \ -Dov_library_open_$(MYLIB_NAME)= ov_library_open_$(MYLIB_NAME)_old
|
Dadurch wird beim Compilieren der vom Codegenerator erzeugten Datei <libname>.c die Funktion ov_library_open_<libane> in ov_library_open_<libname>_old umbenannt. Anstelle des Platzhalters <libname> muss der jeweilige Name der zu erzeugenden Bibliothek eingesetzt werden. In einer der C-Dateien der Bibliothek muss dann eine eigene ov_library_open_<libname> Funktion geschrieben werden, die das folgende Aussehen hat:
#undef ov_library_open_$(MYLIB_NAME)
OV_DLLFNCEXPORT OV_LIBRARY_DEF *ov_library_open_<libname>(void) {
static OV_LIBRARY_DEF OV_LIBRARY_DEF_<libname>_new;
OV_LIBRARY_DEF_<libname>_new = *ov_library_open_<libname>_old();
OV_LIBRARY_DEF_<libname>_new.setglobalvarsfnc =
ov_library_setglobalvars_<libname>_new;
return &OV_LIBRARY_DEF_<libname>_new;
}
|
Das undefine ist
notwendig, damit das global im makefile gesetzte define nicht auch auf
die eigene
ov_library_open Funktion angewendet wird. In dieser Funktion wird die
Initialisierungsfunktion der Bibliothek setglobalvarsfnc umgebogen auf
die
eigene Funktion ov_library_setglobalvars_<libname>_new. Diese
Funktion
muss in der gleichen C-Datei vor der
ov_library_open_<libname>
Funktion definiert sein:
OV_RESULT
ov_library_setglobalvars_<libname>_new(void) {
OV_RESULT result;
result =
ov_library_setglobalvars_<libname>(void);
if(Ov_OK(result)) { …
// eigene
Aktionen wie z.B. das Erzeugen von default-Objekten
...
}
return result;
|
Dieses Problem taucht dann auf, wenn lediglich die ov-Bibliothek mit dem Cygwin-Compiler erzeugt wurde. Für einen korrekten Ablauf ist es aber erforderlich das gesamte OV (und damit auch die Bibliotheken libplt.a, libks.a, libkscln.a, libkssvr.a und libmpm.a im plt-Verzeichnis) mit dem Cygwin-Compiler zu übersetzen (und linken).
Verwenden Sie das richtige make? Die Standard-Makefiles von OV sind GNU-
Makefiles und es muss das entsprechende GNU-Make-Programm verwendet werden.
Dieses finden Sie in den OV-Entwicklungstools, die standardmäßig unter c:\tools\bin zu finden sind. Verweist Ihre Path-Umgebungsvariable (Eingeben von „path“ in der Kommandozeile liefert die aktuelle Einstellung) nicht auf c:\tools\bin oder befindet sich diese Pfadangabe in der Reihenfolge hinter dem Borland-Compiler-Pfad (c:\bc5\bin), so wird das make-Programm nicht gefunden bzw. das Borland-make ausgeführt. In beiden Fällen kann dann das OV-Makefile nicht korrekt abgearbeitet werden. Abhilfe schafft entweder das explizite Ausführen des gnu-makes (c:\tools\bin\make) in dem build\nt-Verzeichnis ihrer Bibliothek oder das Eintragen der geänderten Path-Umgebungsvariable über die Systemsteuerung. Eine weitere Alternative stellt das Umbenennen des make-Programms z.B. in gmake (für GNU-make) dar. (Prüfen Sie evtl. auf Ihrem System, ob dies nicht bereits geschehen ist, und Sie deshalb mit make Ihre Bibliothek nicht übersetzen konnten).
Dieser Fehler ist in der codegen- Version 1.6.1 behoben (OV-Produktversion 1.6.3).
Dieses Problem kann dadurch verursacht werden, wenn Sie entweder in Ihrem Makefile
oder in den C-Dateien nicht mit der Compileroption OV_COMPILE_LIBRARY_libname den Quellcode übersetzt haben. Die vom ov-codegen erzeugten Funktionen werden ohne diese Option nicht mit dem „extern“ Attribut versehen, so dass der Verweis auf die entsprechenden Link-Funktionen nicht in der Externsektion der DLL eingetragen ist. Gleiches gilt übrigens auch für sämtliche C-Funktionen einer ov-Bibliothek.
Die Ursache hierfür kann unterschiedlicher Art sein. Entweder kann das Betriebssystem die zugehörige dll (Windows) bzw. das Shared-Object .so (Linux) nicht finden oder die Objektverwaltung kann die Bibliothek nicht als eine gültige OV-Bibliothek erkennen. Letzteres kann vorkommen, wenn die Bibliothek mit einer anderen OV-Version erzeugt wurde als der Server (Die Versionsnummern unterscheiden sich dann mindestens in der zweiten Stelle - z.B. wäre eine Bibliothek mit OV-1.3.0 übersetzt in einem OV-Server 1.5.1 nicht ausführbar, aber eine Bibliothek mit OV-1.5.0 durchaus - und kennzeichnet eine Änderung des OV-Datenmodells, was eine Ausführung prinzipiell verbietet). In solchen Inkompatibilitätsfällen muss entweder das OV selbst (also in ov/build/nt bzw. ov/build/linux „make install“ ausführen) oder die eigene Bibliothek neu übersetzt werden. Der Fall, dass das Betriebssystem die dynamische Bibliothek nicht finden kann liegt an der fehlenden oder fehlerhaften Umgebungsvariable OV_LIBRARY_PATH. Die hier eingetragenen Pfade teilt das OV dem Betriebssystem mit, wenn versucht wird, eine Bibliothek zu instantiieren. Ist diese Umgebungsvariable nicht gesetzt, so versucht das Betriebssystem die dynamischen Bibliotheken in dem Verzeichnis zu finden, in dem der OV-Server gestartet wurde oder in den Standard-Pfaden des jeweiligen Betriebssystems (bei WindowsNT z.B. winnt/system32). Unter Linux können die Standard-Pfade über die Umgebungsvariable LD_LIBRARY_PATH eingestellt werden. Eine Erweiterung der darin eingetragenen Pfade auf das Verzeichnis, das die Dateien libov.so und libovks.so enthält, ist für den Start des OV-Servers ohnehin notwendig.
Die
für
die Kompatibilität zu beachtende Version des OVs findet sich in einem
laufenden
OV-Server in /vendor/libov_version oder in der Headerdatei
ov/include/libov/
ov_version.h.
Seit
der libov-Version 1.6.3 gibt es ein zusätzliches Compiler-Makro
OV_EXPLAIN, das
dazu dient zur Veranschaulichung von OV auch die Assoziationen
„instantiation“
und „cotainment“ als Links über KS darzustellen. Ab der Version 1.6.4
besteht auch die Möglichkeit die KS Darstellung zur Laufzeit zu
verändern. Dazu muss in der Server Variable /vendor/serverconfiguration
der zweite Bool-Eintrag auf True bzw. False gesetzt werden.
Mit der
OV-Version 1.6.5 wurde dieses Problem behoben.
Normalerweise ist dies nicht der Fall. Beim Erzeugen eines Objektes werden die Methoden constructor, checkinit und startup genau einmal aufgerufen. Allerdings kann dies dann passieren, wenn die Objekterzeugung im constructor eines Objektes durchgeführt wird und das erzeugte Objekt gleichzeitig Kind (containment-Beziehung zwischen domain und object) des erzeugenden Objektes ist (oder in einer tieferen Hierarchieebene zum erzeugenden Objekt in Beziehung steht). Dann wird nämlich die startup Methode einmal direkt bei der Erzeugung ausgeführt und ein zweites Mal, wenn die auf den constructor folgende startup-Methode des erzeugenden Objektes ausgeführt wird, da in der Standardimplementierung ov_object_startup nicht nur die startup-Methoden aller parts (embedment), sonder auch aller childs (containment) ausgeführt werden. Abhilfe schafft hier die Abfrage des objectstate auf OV_OS_STARTED, der in der startup-Methode ov_object_startup gesetzt wird. Denkbar wäre auch, diese Abfrage in den OV-Kern des Objektes zu übernehmen. Dies ist zum Zeitpunkt der Version 1.6.2 nicht der Fall.