Ansicht
Dokumentation

ABENKERNEL_METHODS - KERNEL METHODS

ABENKERNEL_METHODS - KERNEL METHODS

TXBHW - Original Tax Base Amount in Local Currency   PERFORM Short Reference  
Diese Dokumentation steht unter dem Copyright der SAP AG.
SAP E-Book

- Kernel-Methoden

Für die interne Verwendung können Methoden im ABAP-Kernel statt mit der Sprache ABAP implementiert werden.

Einleitung

Kernel-Methoden bieten die Möglichkeit, in C oder C++ implementierte Funktionalität des ABAP-Kernels direkt aufzurufen. Kernel-Methoden lösen die bisherigen Konzepte der C-Calls und System-Calls ab. Es sollten keine neuen C-Calls oder System-Calls mehr eingeführt werden.

Kernel-Methoden bieten die gleichen Prüfungen und Sicherheiten wie normale ABAP-Methoden. Außer den Konstruktoren und dem C-Destruktor können alle ABAP-Methoden als Kernel-Methoden implementiert werden. Weiterhin kann eine ABAP-Methode als Kernel-Methode und eine Kernel-Methode als ABAP-Methode innerhalb eines Pfads der Vererbungshierarchie redefiniert werden.

Für den C-Entwickler, welcher eine Kernel-Methode implementieren möchte, gibt es eine API, welches einen einfachen, performanten und sicheren Zugriff auf Argumente ermöglicht. Weiterhin werden auch klassenbasierte Ausnahmen unterstützt.

Definition von Kernel-Methoden

Deklaration in ABAP

Eine Kernel-Methode wird wie eine normale ABAP-Methode im Class Builder bzw. Deklarationsteil einer lokalen Klasse deklariert. Dass eine Methode als Kernel-Methode implementiert ist, ist für die Deklaration völlig irrelevant. Eine Kernel-Methode ist in ABAP daher wie eine normale ABAP-Methode verwendbar.

Implementierung in ABAP

Die Bestimmung einer Methode als Kernel-Methode wird im Implementierungsteil der Klasse durch den optionalen Zusatz BY KERNEL MODULE kmod1 kmod2 ... der Anweisung METHOD festgelegt. Dabei sind kmod1, kmod2, ... die Namen von Kernel-Modulen, welche die Methode implementieren. Die ABAP-Implementierung einer Kernel-Methode muss leer sein, d.h., zwischen METHOD und ENDMETHOD dürfen keine ABAP-Anweisungen stehen:

METHOD meth BY KERNEL MODULE kmod1 kmod2 ...
ENDMETHOD.

Konstruktoren und der C-Destruktor können nicht als Kernel-Methode implementiert werden. Für den C-Destruktor gibt es einen eigenen Mechanismus.

Hinter KERNEL MODULE kann eine Liste von Kernel-Modulen kmod1, kmod2, ... angegeben werden. Zurzeit können für kmod1, kmod2, ... ausschließlich C-Funktionen des Kernels angegeben werden. Die Liste hinter KERNEL MODULE wird vom Compiler von links nach rechts ausgewertet. Das erste Kernel-Modul in der Liste, für welche es eine Registrierung im Kernel gibt (siehe unten), wird bei der Generierung benutzt.

Wird in der Liste kein gültiges Kernel-Modul gefunden, so kommt es zu einem Syntaxfehler. Weiterhin gibt es zwei Standard-C-Funktionen, welche am Ende der Liste stehen können: FAIL und IGNORE. Wird eine dieser beiden Funktionen am Ende der Liste angegeben, so kommt es zu keinem Syntaxfehler, wenn in der vorhergehenden Liste kein gültiges Modul gefunden wird. Bei IGNORE wird der Aufruf einer solchen Kernel-Methode ignoriert (Verhalten wie leere ABAP-Implementierung), bei FAIL wird eine behandelbare Ausnahme der Klasse CX_SY_DYN_CALL_ILLEGAL_METHOD ausgelöst.

Beispiele

METHOD meth BY KERNEL MODULE xx_impl_630 xx_impl_620 xx_impl_610.

Zuerst wird im Kernel nach xx_impl_630 gesucht. Danach wird im Kernel nach xx_impl_620 und danach nach xx_impl_610 gesucht. Wird keine der Funktionen gefunden, kommt es zum Syntaxfehler.

METHOD meth BY KERNEL MODULE xx_impl_630 xx_impl_620 FAIL.

Zuerst wird im Kernel nach xx_impl_630 gesucht. Danach wird im Kernel nach xx_impl_620 gesucht. Wird keine der Funktionen gefunden, kommt es nicht zu einem Syntaxfehler sondern beim Aufruf der Methode wird eine behandelbare Ausnahme der Klasse CX_SY_DYN_CALL_ILLEGAL_METHOD ausgelöst.

METHOD meth BY KERNEL MODULE xx_impl_620 xx_impl_610 IGNORE.

Zuerst wird im Kernel nach xx_impl_620 gesucht. Danach wird im Kernel nach xx_impl_620 gesucht. Wird keine der Funktionen gefunden, kommt es zu keinem Syntaxfehler sondern beim Aufruf der Methode wird die leere ABAP-Implementierung gerufen.

Implementierung im Kernel

Zurzeit können nur C-Funktionen als Kernel-Module von Kernel-Methoden verwendet werden. Der Ort der C-Funktionen ist frei innerhalb des Kernels wählbar. Es sind keine speziellen Includes des ABAP-Laufzeit-Frameworks nötig um die C-Funktion zu implementieren. Die C-Funktionen müssen eine bestimmte Schnittstelle besitzen. Die Schnittstelle selbst ist durch ein Makro namens ARGUMENTS verschalt. Alle benötigten Definitionen und Prototypen sind im Include //src/include/abkmeth.h enthalten. Dies ist das einzige Include, das zur Definition von C-Funktionen für Kernel-Methoden benötigt wird.

Da C-Funktionen in C und C++ definiert werden können, muss in C++ externC benutzt werden:

#include "abkmeth.h"
...
externC void name_of_cmodule( ARGUMENTS )
{
...
}

Eine C-Funktion, die eine Kernel-Methode implementiert, muss für die Kernel-Methode registriert werden. Wird der Name einer C-Funktion hinter METHOD meth BY KERNEL MODULE angegeben, die nicht für die Kernel-Methode registriert wurde, kommt es zu oben erwähntem Syntaxfehler. Es können mehrere C-Funktionen für eine Kernel-Methode registriert werden. Die Reihenfolge der in der Liste hinter METHOD meth BY KERNEL MODULE angegebenen Kernel-Module kmod1, kmod2, ... bestimmt, welche der registrierten C-Funktionen verwendet wird. Hierdurch ist es möglich eine Kernel-Methode abwärtskompatibel weiterzuentwickeln.

Damit Änderungen an der Registrierung aktiv werden, muss das Ziel lib des Projekts krn/runt neu kompiliert und der Kernel neu gelinkt werden.

Registrierung

C-Funktionen werden mit folgender Syntax für eine Kernel-Methode im Signaturfile //src/krn/runt/abkmeth.sig registriert, wobei alle ABAP-Bezeichner in Großbuchstaben angeben werden müssen:

KERNEL_METHOD("CLASS","METH", cfunc,argcnt)

Mit dieser Definition wird die C-Funktion cfunc für die Kernel-Methode meth einer globalen Klasse class registriert. Die C-Funktion erwartet eine Anzahl von argcnt Argumenten.

Kernel-Methoden lokaler Klassen in Class-Pools bzw. anderen ABAP-Programmen werden über folgende Makros registriert:

KERNEL_METHOD_CLASS_LOCAL("GCLASS","CLASS","METH",cmodule,argcnt)

KERNEL_METHOD_PROGRAM_LOCAL("PROG","CLASS","METH",cmodule,argcnt)

Die Wirkungsweise ist wie bei KERNEL_METHOD, nur dass bei lokalen Klassen in Class-Pools die globale Klasse gclass und bei programmlokalen Klassen das Programm prog anzugeben sind.

Argumente registrieren

Alle ABAP-Datenobjekte, wie Parameter, Attribute oder globale Daten, auf welche in C-Funktionen für Kernel-Methoden zugegriffen werden soll, werden als Argumente der C-Funktion behandelt.

Die Argumentliste einer C-Funktion für eine Kernel-Methode ist nicht auf die Schnittstellenparameter der ABAP-Methode beschränkt und muss diese auch nicht vollständig enthalten. Bevor innerhalb von C-Funktionen für Kernel-Methoden auf Argumente zugegriffen werden kann, müssen diese ebenfalls registriert werden.

Die Registrierung der argcnt Argumente muss unmittelbar nach der Registrierung der C-Funktion mit KERNEL_METHOD folgen. Ein einzelnes Argument wird mit einem der folgenden Makros definiert (bzw. registriert):

ARGUMENT_basetype(index,"name",type_kind,"type",read_write)

ARGUMENT_$[C$|N$|X$](index,"name",type_kind,"type",read_write,length)

ARGUMENT_P(index,"name",type_kind,"type",read_write,length,decimals)

ARGUMENT_STRUCT(index,"name",type_kind,"type",read_write,ctype)

Mit diesen Makros wird ein Argument mit dem Namen name und einem Index index definiert.

Mit basetype muss der tatsächliche Typ des ABAP Datenobjekts nach folgender Tabelle bezeichnet werden. Wenn der basetype C, N, X, P oder STRUCT ist, müssen mehr Parameter angegeben werden, als bei den anderen Typen.

basetype ABAP-Datentyp Typ in C
C c mit Längenangabe SAP_CHAR (*) $[Length$]
C_GENERIC c ohne Längenangabe SAP_CHAR*
X x mit Längenangabe SAP_RAW (*) $[Length$]
X_GENERIC x ohne Längenangabe SAP_RAW*
N n mit Längenangabe SAP_CHAR (*) $[Length$]
N_GENERIC n ohne Längenangabe SAP_CHAR*
P p mit Längen- und Dezimalangabe SAP_BCD (*) $[Length$]
P_GENERIC p ohne Längen- und Dezimalangabe SAP_BCD*
D d SAP_DATE*
T t SAP_TIME*
UTCLONG utclong SAP_LLONG*
I i SAP_INT*
INT1 b SAP_INT1*
INT2 s SAP_SHORT*
INT8 int8 SAP_LLONG*
F f SAP_DOUBLE*
DECFLOAT16 decfloat16 DecFloat16
DECFLOAT34 decfloat34 DecFloat34
STRING string StrRef*
XSTRING xstring StrRef*
TABLE alle Tabellentypen TABH_REF*
OBJ_REF alle Objektreferenzen ObjRef*
DATA_REF alle Datenreferenzen FldRef*
STRUCT alle Strukturtypen registrierter Typ ctype*
ANY any void*
DATA data void*
SIMPLE simple void*
CSEQUENCE csequence void*
XSEQUENCE xsequence void*
NUMERIC numeric void*
CLIKE clike SAP_CHAR*
C_POINTER %_c_pointer void**

Die Parameter der Makros haben folgende Bedeutung:

  • name ist der Bezeichner eines beliebigen Datenobjekts in ABAP in Großbuchstaben, welches auch innerhalb einer ABAP-Implementierung der Kernel-Methode verwendet werden könnte. Insbesondere darf der Bezeichner Verknüpfungen über Komponentenselektoren enthalten, wie z.B. me->attr oder struc-comp.
  • index ist eine fortlaufende Nummer von 1 bis argcnt. Der Zugriff auf die Argumente erfolgt über diesen Index.
  • Für type_kind kann entweder TYPE oder TYPE_REF_TO angegeben werden.
  • type ist der Bezeichner eines beliebigen Datentyps in ABAP in Großbuchstaben, welcher auch innerhalb einer ABAP-Implementierung der Kernel-Methode verwendet werden könnte. Mit type_kind und type wird die Schnittstelle der Kernel-Methode in ABAP überprüft.
  • Für read_write kann entweder READ oder WRITE angegeben werden. Dies bestimmt, ob auf das Argument lesend oder schreibend zugegriffen werden kann und wird in den Zugriffsmakros ausgewertet.
  • Mit length muss bei ARGUMENT_$[C$|N$|X$|P$] die Länge aller ABAP-Datentypen mit generischer Länge angegeben werden. Bei c und n in Zeichen und bei x und p in Bytes.
  • Mit decimals muss bei ARGUMENT_P die Anzahl der Dezimalstellen angegeben werden.
  • Mit ctype muss bei ARGUMENT_STRUCT ein passender C-Typ angegeben werden. Dieser Typ sollte mit saphfile aus einer ABAP-Typdefinition generiert sein.

Zugriff auf die Argumente

Nach der Registrierung der Argumente kann innerhalb der C-Funktion mit folgenden Makros auf sie zugegriffen werden. Mit Ausnahme des direkten Zugriffs auf den Datenkontrollblock benötigen die Zugriffsmakros keine Includes des ABAP-Laufzeit-Frameworks.

ARGUMENT_basetype_READ(index,"name");

Dieses Makro liefert die Leseadresse eines Arguments mit dem Typ const ctype, wobei ctype durch basetype nach obiger Tabelle festgelegt ist. Es müssen der Index und der Name des Arguments übergeben werden. Bei den generischen Typen müssen weitere Parameter angegeben werden (siehe unten). Für den Zugriff auf das Argument wird nur der Index benötigt. Damit die C-Funktion lesbarer wird und zusätzliche Konsistenzprüfungen ausgeführt werden können, muss aber auch der Name angegeben werden. Ist der Kernel im Debug-Modus kompiliert erfolgt eine Konsistenzprüfung zwischen Index und Name, weiterhin erfolgt eine Prüfung des angegebenen C-Typs und des ABAP-Typs des Arguments. Bei einem Fehler wird ein entsprechender ABAP-Laufzeitfehler ausgelöst (KMETH_INVALID_ARGUMENT_ID, KMETH_INVALID_ARGUMENT_NAME oder KMETH_INVALID_CTYPE_LENG). Im optimierten Kernel erfolgen keine Prüfungen.

ARGUMENT_basetype_WRITE(index,"name");

Dieses Makro hat die gleiche Semantik wie ARGUMENT_basetype_READ. Es wird aber die Schreibadresse zurückgegeben. Weiterhin erfolgt eine zusätzliche Prüfung, ob das Argument auch als Schreibargument definiert wurde. Erfolgt ein Schreibzugriff auf ein Argument, welches schreibgeschützt ist (z.B. Konstanten), wird der ABAP-Laufzeitfehler KMETH_ARGUMENT_READ_ONLY ausgelöst.

ARGUMENT_$[C$|N$]_READ(index,"name",lengthU);
ARGUMENT_$[C$|N$]_WRITE(index,"name",lengthU);
ARGUMENT_X_READ(index,"name",lengthR);
ARGUMENT_X_WRITE(index,"name",lengthR);

Mit diesen Makros muss bei den generischen Typen c, x und n die erwartete Länge in Bytes lengthR oder in Zeichen lengthU angeben werden.

ARGUMENT_P_READ(index,"name",lengthR,decimals);
ARGUMENT_P_WRITE(index,"name",lengthR,decimals);

Mit diesen Makros muss bei dem generischen Typ p die erwartete Länge in Bytes (lengthR) und die Anzahl der Dezimalstellen (decimals) angeben werden.

ARGUMENT_$[C_GENERIC$|N_GENERIC$|CLIKE$]_READ(index,"name",size_tU);
ARGUMENT_$[C_GENERIC$|N_GENERIC$|CLIKE$]_WRITE(index,"name",size_tU);
ARGUMENT_X_GENRIC_READ(index,"name",size_tR);
ARGUMENT_X_GENERIC_WRITE(index,"name",size_tR);

Mit diesen Makros muss bei den Typen C_GENERIC, X_GENERIC, N_GENERIC und CLIKE eine Variable vom Typ size_tU oder size_tR angegeben werden, in welche die Länge in Bytes oder Zeichen gestellt wird.

ARGUMENT_P_GENERIC_READ(index,"name",size_tR,decimals);
ARGUMENT_P_GENERIC_WRITE(index,"name",size_tR,decimals);

Mit diesen Makros muss beim Typ P_GENERIC neben der Länge size_tR auch eine Variable decimals für die Dezimalstellen angegeben werden.

ARGUMENT_STRUCT_READ(index,"name",ctype);
ARGUMENT_STRUCT_READ(index,"name",ctype);

Mit diesen Makros muss für alle strukturierten Typen STRUCT ein passender C-Typ ctype angegeben werden.

ARGUMENT_C_POINTER(index,"name");

Dieses Makro steht speziell für den Typ %_c_pointer zur Verfügung. Dieser Typ ist ein spezieller interner ABAP-Typ, welcher genau die Bytelänge eines C-Pointers hat (je nach Plattform 4, 8 oder 16 Byte). Der Typ wird immer auf den eingebauten ABAP-Typ x abgebildet. Wegen der variablen Länge und der Abhängigkeit von der Plattform werden nicht die Makros für den Typ X bzw. X_GENERIC verwendet.

ARGUMENT_IS_SUPPLIED(index,"name");

Dieses Makro hat die gleiche Semantik wie der Prädikatsausdruck IS SUPPLIED in ABAP. Es erfolgen die selben Konsistenzprüfungen wie bei ARGUMENT_READ.

ARGUMENT_DATA(index,"name",ctype);

Dieses Makro liefert den Datenkontrollblock mit dem C-Typ const DATA *. Es erfolgen die selben Konsistenzprüfungen wie bei ARGUMENT_READ. Das Makro ist nur aktiv, wenn das Include //src/include/abdata.h des ABAP-Laufzeit-Frameworks eingebunden wurde.

Ausnahmen auslösen

C-Funktionen, die Kernel-Methoden implementieren, können klassenbasierte Ausnahmen auslösen.

Ausnahmen registrieren

Voraussetzung ist, dass die entsprechenden globalen Ausnahmeklassen durch eine Erweiterung von //src/include/abexcpc.h registriert werden. Lokale Ausnahmeklassen können nicht registriert werden.

In //src/include/abexcpc.h wird die Ausnahmeklasse bekannt gemacht und es werden eventuelle Text-Ids definiert:

//src/include/abexcpc.h
...
CX_ABSTR (CX_..., "CX_...")
CX_TXTID (CX_..._bar, CX_..., "BAR")  /* special text for class */
...

Klassen können auch nur mit ihren Standardtext bekannt gemacht werden:

//src/include/abexcpc.h
...
CX_CLASS (CX_..., "CX_...")           /* class with standard text */
...

Die genaue Dokumentation ist in der Datei //src/include/abexcpc.h enthalten.

Damit eventuelle Attribute einer Ausnahmeklasse in einer C-Funktion versorgt werden können, muss die Datei //src/include/abexcpa.h erweitert werden, wobei der Name, der interne Typ gemäß //src/include/abtypes.h und die Bytelänge angegeben werden müssen:

//src/include/abexcpa.h
...
CX_ATTR (CX_..._attr1, CX_..., "ATTR1", TYPCSTRING, sizeofR(StrRef))
CX_ATTR (CX_..._attr2, CX_..., "ATTR2", TYPC, LEN_UC2RAW(30))
...

Schließlich sollten Ausnahmen ebenso wie Argumente in der Datei //src/krn/runt/abkmeth.sig registriert werden. Dies wird zwar nicht erzwungen, aber nur registrierte Ausnahmen werden bei der Syntaxprüfung auf ihre Existenz überprüft:

//src/krn/runt/abkmeth.sig
...
EXCEPTION(CX_...)
...

Ausnahmen auslösen

Eine C-Funktion kann durch den aufeinander folgenden Aufruf folgender Makros eine Ausnahme auslösen:

EXCEPTION_CREATE(CX_..._bar);
EXCEPTION_SET_CSTRING(CX_..._attr1, value, valueLength);
EXCEPTION_SET_C      (CX_..._attr2, value, valueLength);
EXCEPTION_RAISE();

Innerhalb der Makros EXCEPTION_CREATE oder EXCEPTION_RAISE erfolgt immer ein Long-Jump zum Extri, d.h. die C-Funktion zur Implementierung der Kernel-Methode wird per Long-Jump verlassen und das ABAP-Laufzeit-Framework erhält die Kontrolle. Die C-Funktion sollte deshalb vor dem Auslösen einer Ausnahme ihren temporären Speicher wieder freigeben! Wird die Ausnahme in ABAP mit CATCH ohne den Zusatz INTO abgefangen, erfolgt der Long-Jump in EXCEPTION_CREATE. Wird die Ausnahme mit dem Zusatz INTO (das Ausnahmeobjekt wird verwendet) oder gar nicht abgefangen, erfolgt der Long-Jump in EXCEPTION_RAISE.

Die Ausnahmen werden im Laufzeit-Framework prozessiert, wie wenn sie in ABAP ausgelöst würden und es erfolgen die gleichen dynamischen Prüfungen.

Für das Setzen von Ausnahmeattributen stehen zurzeit folgende Makros zur Verfügung, die bei Bedarf auch erweitert werden können. Im Moment werden Strings, Integer und C-Felder unterstützt. Zur Verwendung siehe obige Sequenz.

EXCEPTION_SET_CSTRING_UC
EXCEPTION_SET_C

Wert mit Längenangabe.

EXCEPTION_SET_C_UC
EXCEPTION_SET_INT

Wert mit Null-Terminierung.

Hilfsprogramm für Kernel-Methoden

Das ABAP-Programm RSKMETH dient als Browser für die Registrierung von Kernel-Modulen. Mit seiner Hilfe kann man feststellen, welche C-Funktionen für welche Kernel-Methoden und welche Argumente/Ausnahmen für diese registriert sind. Dies ist hilfreich bei der Analyse von Syntaxfehlern, da bei Kernel-Methoden Informationen verarbeitet werden, welche nur aus den Kernel-Modulen ersichtlich sind.

Beispiel

Als Beispiel betrachten wir eine vereinfachte Rechenklasse für Gleitkommazahlen. Die Klasse hat ein Instanzattribut, in welchem immer das letzte Ergebnis gespeichert wird. Eine Methode führt eine Division aus und ist als Kernel-Methode implementiert. Ist der Divisor gleich Null, löst die Methode eine klassenbasierte Ausnahme aus.

Deklarationsteil der Klasse in ABAP

CLASS cl_my_calculation DEFINITION ...
...
  DATA last_result TYPE decfloat16.
...
  METHODS div
    IMPORTING p_dividend TYPE decfloat16 p_divisor TYPE decfloat16
    RETURNING VALUE(p_result) TYPE decfloat16.
...
ENDCLASS.

Signaturfile //src/krn/runt/abkmeth.sig im Kernel

...
KERNEL_METHOD(CL_MY_CALCULATION, DIV, xx_myDiv,4)
  ARGUMENT_F(1, "P_DIVIDEND",     TYPE, "F", READ)
  ARGUMENT_F(2, "P_DIVISOR",      TYPE, "F", READ)
  ARGUMENT_F(3, "P_RESULT",       TYPE, "F", WRITE)
  ARGUMENT_F(4, "ME->LAST_RESULT",TYPE, "F", WRITE)
  EXCEPTION("CX_MY_DIV_BY_ZERO")
...

C++-Quelltext //src/krn/.../mycalc.cpp im Kernel

#include "abkmeth.h"
...
externC void xx_myDiv( ARGUMENTS ){

  const SAP_DOUBLE *const dividend = ARGUMENT_F_READ(1,"P_DIVIDEND");
  const SAP_DOUBLE *const divisor  = ARGUMENT_F_READ(2,"P_DIVISOR");
  SAP_DOUBLE *result               = ARGUMENT_F_WRITE(3,"P_RESULT");
  SAP_DOUBLE *last_result          = ARGUMENT_F_WRITE(4,"ME->LAST_RESULT");

  if( 0 == *divisor )
  {
    EXCEPTION_CREATE(CX_MY_DIV_BY_ZERO);
    EXCEPTION_RAISE();
  }

  *result = *dividend / *divisor;
  *last_result = *result;

}

Implementierungsteil der Klasse in ABAP

CLASS cl_my_calculation IMPLEMENTATION.
...
METHOD div BY KERNEL MODULE xx_myDiv.
ENDMETHOD.
...
ENDCLASS.






Fill RESBD Structure from EBP Component Structure   CPI1466 during Backup  
Diese Dokumentation steht unter dem Copyright der SAP AG.

Length: 40722 Date: 20240523 Time: 151602     sap01-206 ( 462 ms )