Skip to Content
2

Where am I being called from......

Dec 08, 2016 at 11:38 AM

452

avatar image

I have a global class definition that is sub-classed either directly to a class that implements the final requirement, or to a class that adds functionality that is not required by all sub-classes which is then sub-classed to a class that implements some other final requirement.


I would like a protected method that can be redefined (so that rules out a static method) that returns a different value based upon the 'level' from which that method was called.

Lets' say we have the following:

Dictionary Class - 'A' Value required 1
Sub Class of 'A' - 'B' Value required 2
Sub Class Of 'B' - 'C' Value required 3.

In general the method in class C would look like:

If I am not being called at this level
   Result = Super->method( ).
Else.
   Result = 3.
EndIf

In Class A, result would 2 and in the dictionary method it would just be a single statement returning the result of 1.

I have tried reading the call stack - this doesn't contain the class names to enable me to tell where it came from. I've also used the Get_Relative_Name method from the cl_Abap_TypeDescr class and for global methods (ie class 'A' above) it works - I can identify the actual class name but for local classes it always returns the 'Me' name even if the method is being called from a method in class B

So - to re-iterate my question - In Abap Objects how do I tell where I am being called from ?

Thanks and regards.

Rich

ps. Sorry if I don't have the right tags. - I haven't managed to figure this all out yet!

10 |10000 characters needed characters left characters exceeded

I don't exactly understand what you are looking for. Are you looking for the name of the class whose instance is based upon? (i.e. the "dynamic type", cl_abap_classdescr=>get_class_name)

0

oups, I think I understand your question now : you use the call stack to know the caller method, but it doesn't return the name of the class if the method belongs to a local class, it returns only the name of the include and the line. Is that your question?

0

Why am I doing this ?

I have to implement a series of custom interfaces. Each interface is encapsulated in a local class with it's own message class.

There are a set of operations that are common to each interface such as 'Is it active', or reading and parsing the data into an internal table, creating and updating an application log etc etc. This and the messages associated with these operations are contained in the interface base class and it's own message class. This is defined in a global class ("A" above)

Some of these interfaces update measurement documents, some don't so the functionality for this is in a class with it's own message class ("B" above).

Then come the classes that actually implement the interfaces one class (a wheel lathe) implements measurement points so is subclassed from "B". A Diesel pump interface doesn't so is sub-classed from "A". Both of these have their own message class.

To keep the programming structure the same I want a redefinable method (Message_Class( )) that when called returns the message class to use for the current message being output so the code is always something like:

ls_Msg-MsgId = Message_Class( ).

rather than

ls_Msg-MsgId = Message_Class_wl( ). "_wl _fl etc etc

It was a good thought......

Oh well!

thanks for your suggestions!

Regards

Rich

1

Hello,

  • I am not sure to understand the concrete meaning of "If I am not being called at this level". Is it possible to illustrate it with a little program ?
  • Trying to understand, I have created 3 "dictionary" classes (as you described) and a se38 program with 2 local subclasses

"3" dictionary classes

A se38 program

REPORT  ztest_class_level.
*----------------------------------------------------------------------*
*       CLASS lcl_b DEFINITION
*----------------------------------------------------------------------*
CLASS lcl_b DEFINITION INHERITING FROM zcl_a.
  PROTECTED SECTION.
    METHODS zzmethod_protected REDEFINITION.
ENDCLASS.                    "lcl_b DEFINITION
*----------------------------------------------------------------------*
*       CLASS lcl_b IMPLEMENTATION
*----------------------------------------------------------------------*
CLASS lcl_b IMPLEMENTATION.
  METHOD zzmethod_protected.
    rt_level = 'b'.
  ENDMETHOD.                    "zzmethod_protected
ENDCLASS.                    "lcl_b DEFINITION
*----------------------------------------------------------------------*
*       CLASS lcl_c DEFINITION
*----------------------------------------------------------------------*
CLASS lcl_c DEFINITION INHERITING FROM zcl_b.
  PROTECTED SECTION.
    METHODS zzmethod_protected REDEFINITION.
ENDCLASS.                    "lcl_b DEFINITION
*----------------------------------------------------------------------*
*       CLASS lcl_b IMPLEMENTATION
*----------------------------------------------------------------------*
CLASS lcl_c IMPLEMENTATION.
  METHOD zzmethod_protected.
    rt_level = 'c'.
  ENDMETHOD.                    "zzmethod_protected
ENDCLASS.                    "lcl_b DEFINITION

DATA g  TYPE            REF TO zcl_a.
DATA gt LIKE STANDARD TABLE OF g.

DATA level TYPE char1.

START-OF-SELECTION.

  CREATE OBJECT g TYPE zcl_a.  APPEND    g  TO gt. "level 1
  CREATE OBJECT g TYPE zcl_b.  APPEND    g  TO gt. "level 2
  CREATE OBJECT g TYPE zcl_c.  APPEND    g  TO gt. "level 3
  CREATE OBJECT g TYPE lcl_b.  APPEND    g  TO gt. "level b
  CREATE OBJECT g TYPE lcl_c.  APPEND    g  TO gt. "level c
  LOOP AT gt INTO g.

    level =   g->zzmethod_public( ).

    NEW-LINE.
    WRITE level.

  ENDLOOP.

Executing this program, the result is

global-classes.jpg (302.1 kB)
se38-list.jpg (32.0 kB)
1

I believe you want to determine the inheritance level, not call stack level.

Above screenshot has lcl_two inheriting from lcl_one, and lcl_three inheriting from lcl_two.

This information is visible in debugger but it appears debugger classes can't be used in custom programs.

docpv.png (7.8 kB)
1
* Please Login or Register to Answer, Follow or Comment.

3 Answers

Horst Keller
Dec 08, 2016 at 06:05 PM
1

I'm afraid there's no simple solution.

Show 2 Share
10 |10000 characters needed characters left characters exceeded

Hi Horst!

I'd gathered that!

0

Don't ask me why, but ABAP doesn't offer a direct way for doing that.

After SYSTEM _CALLSTACK you might play around with strictly internal

LOAD REPORT

parts CONT and TRIG for static code analysis, but maybe it is better to pass the identity of the caller explicitly to the caller by offering an appropriate parameter.

2
Raghu Govindarajan Dec 08, 2016 at 06:03 PM
0

Use function module SYSTEM_CALLSTACK, you should be able to see the full hierarchy of where the call came from.

Show 6 Share
10 |10000 characters needed characters left characters exceeded

Hi Raghu,

I mentioned above that the call stack does not help because it does not contain the class name.

Regards,

Rich

0

(comment about "the call stack does not contain the class name" -> to be more precise, a call stack may contain the name of a class pool being its framework program (classpool====CP), but if it's a local class there's only the include name and line number)

0

I understand that this question is for local classes, but just to complete this for anyone else looking for a solution with standalone global classes, yes the SYSTEM_CALLSTACK does contain the class name and method name. The MAINPROGRAM will have the class name, as Sandra mentioned in its full name. If you want the "common" name, pass that through CL_OO_CLASSNAME_SERVICE=>GET_CLSNAME_BY_INCLUDE. If you want the method, take the INCLUDE from the SYSTEM_CALLSTACK and pass it through CL_OO_CLASSNAME_SERVICE=>GET_METHOD_BY_INCLUDE - that will actually give you both the class and the method.

EDIT: Clarified that this solution posted is not for this question, but just a completion of a thought for anyone looking for a solution for global classes

0

cl_oo_classname_service methods work only with methods from global classes (i.e. source code units "classpool===CMxxx") ; methods (implementations) from local classes are often within the same include program (for instance "classpool===CCIMP").

0

Sandra, I made a couple of edits to my comment - hope it clarifies that I was saying the same thing as you are.

1

I have a thought about a solution based on your particular requirement, especially since you said that if it is not called at that level, you are calling the SUPER->METHOD(). Since you are just calling SUPER, you don't need to know the name of the local class, what you need to know is the number of levels deep you need to go. You can get that information from the Call Stack. Based on that you can call the Super with your depth counter and keep doing that until the counter is 1 and then that is the level you don't call the SUPER.

.... 
LOOP AT callstack WHERE eventname = "method_name".
COUNTER = COUNTER + 1.
ENDLOOP.
...
IF counter > 1.
counter = counter - 1
result = SUPER->method( counter ).
ELSE.
result = 3
ENDIF.
0
Raghu Govindarajan Dec 09, 2016 at 08:31 PM
0

BTW, just a general question - why is this all implemented as local classes and not standalone classes? You should be able to extract them to standalone classes pretty easily and the one thing ABAP gives you over other languages is to declare the classes as "Friends" so they can access each other's private and protected methods and attributes.

Messages "classes" being linked to exception class will also give you another layer of flexibility. For example, (I don't know if this will work in your case), you could always just raise your exception from where ever and CATCH it in the calling program.

Now, I just keep getting ideas! Here is another one, in your CONSTRUCTOR for each local class, you can increment an optional counter if it is called from another of your classes. If it is not from another class and a direct call from the program, then don't increment the counter.

This is definitely solvable with a little creativity - I don't think you need to make major programming changes, just a few strategic ones. Hope one of these sets of an idea at least!

Show 1 Share
10 |10000 characters needed characters left characters exceeded

Hi Raghu,

A good question. They are being implemented as local classes because they only have a single use and no static methods, whereas the base class also provides constant values to a controlling program via static methods. I was trying to implement something that does not rely on shall we say "custom programming" even as simple as it would be (ie using provided classes). The idea of a counter looks good to start with, but there is the situation (outlined above) where the "implementing" class is subclassed from 2 super classes (ie A->B->C or A->C) dependant on the functionality required so the count would be different for the same required message class. And as Horst say's above, to use the call stack you end up effectively parsing the code.

Thanks for all your ideas though and thanks to every one who has answered.

Regards

Rich

0