Why does the second CREATE OBJECT generate a short dump? - abap

I have got a following case.
An include with a local interface definition
*&---------------------------------------------------------------------*
*& Include ZZZ_INCL_INTERFACE
*&---------------------------------------------------------------------*
INTERFACE lif_test.
METHODS:
test.
ENDINTERFACE. "lif_test
A report which uses this include and defines a class implementing this interface.
*&---------------------------------------------------------------------*
*& Report ZZZ_IMPL_A
*&
*&---------------------------------------------------------------------*
*&
*&
*&---------------------------------------------------------------------*
REPORT zzz_impl_a.
INCLUDE zzz_incl_interface.
*----------------------------------------------------------------------*
* CLASS lcl_test_a DEFINITION
*----------------------------------------------------------------------*
*
*----------------------------------------------------------------------*
CLASS lcl_test_a DEFINITION FINAL.
PUBLIC SECTION.
INTERFACES:
lif_test.
ENDCLASS. "lcl_test_a DEFINITION
*----------------------------------------------------------------------*
* CLASS lcl_test_a IMPLEMENTATION
*----------------------------------------------------------------------*
*
*----------------------------------------------------------------------*
CLASS lcl_test_a IMPLEMENTATION.
METHOD lif_test~test.
ENDMETHOD. "lif_test~test
ENDCLASS. "lcl_test_a IMPLEMENTATION
And a second report which also uses this include and defines a new class also implementing the same interface defined in the include.
*&---------------------------------------------------------------------*
*& Report ZZZ_IMPL_B
*&
*&---------------------------------------------------------------------*
*&
*&
*&---------------------------------------------------------------------*
REPORT zzz_impl_b.
INCLUDE zzz_incl_interface.
*----------------------------------------------------------------------*
* CLASS lcl_test_b DEFINITION
*----------------------------------------------------------------------*
*
*----------------------------------------------------------------------*
CLASS lcl_test_b DEFINITION FINAL.
PUBLIC SECTION.
INTERFACES:
lif_test.
ENDCLASS. "lcl_test_b DEFINITION
*----------------------------------------------------------------------*
* CLASS lcl_test_b IMPLEMENTATION
*----------------------------------------------------------------------*
*
*----------------------------------------------------------------------*
CLASS lcl_test_b IMPLEMENTATION.
METHOD lif_test~test.
ENDMETHOD. "lif_test~test
ENDCLASS. "lcl_test_b IMPLEMENTATION
CLASS lcl_main DEFINITION FINAL CREATE PRIVATE.
PUBLIC SECTION.
CLASS-METHODS:
main.
ENDCLASS.
CLASS lcl_main IMPLEMENTATION.
METHOD main.
DATA:
l_rif_test TYPE REF TO lif_test.
CREATE OBJECT l_rif_test TYPE ('\PROGRAM=ZZZ_IMPL_B\CLASS=LCL_TEST_B').
CREATE OBJECT l_rif_test TYPE ('\PROGRAM=ZZZ_IMPL_A\CLASS=LCL_TEST_A').
ENDMETHOD.
ENDCLASS.
END-OF-SELECTION.
lcl_main=>main( ).
The line CREATE OBJECT l_rif_test TYPE ('\PROGRAM=ZZZ_IMPL_A\CLASS=LCL_TEST_A') generates a short dump saying that the dynamically created object is not an implementation of lif_test. Could one explain why it is like that? It looks like a great hole in the language definition. To stop any discussion at the very beginning: No I cannot use dictionary definition of the interface.
EDIT: More descriptive short dump is obtained when trying to cast the instance of the object created by the second CREATE OBJECT.
DATA:
l_rcl_object TYPE REF TO object.
*...
CREATE OBJECT l_rcl_object TYPE ('\PROGRAM=ZZZ_IMPL_A\CLASS=LCL_TEST_A').
l_rif_test ?= l_rcl_object.
The short dump has it that
The content of the source variable does not fit in the target variable.
Source type: "\PROGRAM=ZZZ_IMPL_A\CLASS=LCL_TEST_A"
Target type: "\PROGRAM=ZZZ_IMPL_B\INTERFACE=LIF_TEST"
It looks like when I include a local definition of an interface in two places they become a two separate definitions, one of program ZZZ_IMPL_B and the second of ZZZ_IMPL_A.

The reason for this behavior is that INCLUDE does just that - it includes the contents of the include at the point of the INCLUDE statement, which is slightly better than copy&paste from a developer's point of view, but the same thing as far as the system is concerned. You get two interface definitions in distinct programs that are equal, but not identical. One solution might be to generate a report, a subroutine pool, a function pool or a module pool that contains the interface. These program types generate their own loads, so you'll get an interface \PROGRAM=ZZZ_MY_SUBROUTINE_POOL\INTERFACE=LIF_TEST that you can then use from your other programs. Note that you'll still have to write this subroutine pool to the repository, just keeping it locally won't help.
(Other than that, why exactly isn't generating global interface and classes an option?)

You should put your class A and B definitions and implementations inside separate includes, and include those along with the interface inside the report containing your main class. Like this:
report zzz_impl.
include zzz_incl_interface.
include zzz_incl_a.
include zzz_incl_b.
class lcl_main definition final create private.
public section.
class-methods:
main.
endclass.
class lcl_main implementation.
method main.
data l_rif_test type ref to lif_test.
create object l_rif_test type lcl_test_b.
create object l_rif_test type lcl_test_a.
endmethod.
endclass.
end-of-selection.
lcl_main=>main( ).
The reason for the shortdump, is because you're trying to use a variable of the type \PROGRAM=ZZZ_IMPL_B\INTERFACE=LIF_TEST to refer to an object which implements \PROGRAM=ZZZ_IMPL_A\INTERFACE=LIF_TEST.
Edit: I'm not entirely sure what you mean with your comment, but maybe generating the entire report is possible for your use case? For example:
report zzz_impl_gen.
data source_interface type standard table of char255.
append `interface lif_test.` to source_interface.
append ` methods:` to source_interface.
append ` test.` to source_interface.
append `endinterface.` to source_interface.
data source_class_a type standard table of char255.
append `class lcl_test_a definition final.` to source_class_a.
append ` public section.` to source_class_a.
append ` interfaces:` to source_class_a.
append ` lif_test.` to source_class_a.
append `endclass.` to source_class_a.
append `class lcl_test_a implementation.` to source_class_a.
append ` method lif_test~test.` to source_class_a.
append ` endmethod.` to source_class_a.
append `endclass.` to source_class_a.
data source_class_b type standard table of char255.
append `class lcl_test_b definition final.` to source_class_b.
append ` public section.` to source_class_b.
append ` interfaces:` to source_class_b.
append ` lif_test.` to source_class_b.
append `endclass.` to source_class_b.
append `class lcl_test_b implementation.` to source_class_b.
append ` method lif_test~test.` to source_class_b.
append ` endmethod.` to source_class_b.
append `endclass.` to source_class_b.
data source_main type standard table of char255.
append `class lcl_main definition final create private.` to source_main.
append ` public section.` to source_main.
append ` class-methods:` to source_main.
append ` main.` to source_main.
append `endclass.` to source_main.
append `class lcl_main implementation.` to source_main.
append ` method main.` to source_main.
append ` data l_rif_test type ref to lif_test.` to source_main.
append ` create object l_rif_test type lcl_test_b.` to source_main.
append ` create object l_rif_test type lcl_test_a.` to source_main.
append ` write: / 'Hello, World!'.` to source_main. " Just to test if it works
append ` endmethod.` to source_main.
append `endclass.` to source_main.
data source_form type standard table of char255.
append `form main.` to source_form.
append ` lcl_main=>main( ).` to source_form.
append `endform.` to source_form.
data source_all type standard table of char255.
append `program subpool.` to source_all.
append lines of source_interface to source_all.
append lines of source_class_a to source_all.
append lines of source_class_b to source_all.
append lines of source_main to source_all.
append lines of source_form to source_all.
data generated_program type string.
data message type string.
data sid type string.
generate subroutine pool source_all name generated_program message message shortdump-id sid.
perform ('MAIN') in program (generated_program) if found. " Important, subroutine name must be in upper case!

Related

Using interface name as Variable type

In solidity, see below code ..... How is that the interface name is being used as variable type ?
See the comments in below code
pragma solidity 0.8.11;
import "../interfaces/ISpool.sol";
// ... other imports
abstract contract VaultBase is IVaultBase, VaultImmutable, SpoolOwnable, SpoolPausable, BaseConstants {
using Bitwise for uint256;
using SafeERC20 for IERC20;
/* ========== STATE VARIABLES ========== */
/// #notice The central Spool contract
ISpool internal immutable spool; // ISpool is interface name and it is
// being used as variable type. What this means ?
}
ISpool interface:
interface ISpool is ISpoolExternal, ISpoolReallocation, ISpoolDoHardWork, ISpoolStrategy, ISpoolBase {}
How is that the interface name is being used as variable type ?
Interface type variables work the same way as Contract type variables.
They serve as as pointer to the specified address, assuming that there is a contract deployed on this address that implements this interface.
Example: If the ISpool interface declares a function foo(uint256) external returns (bool), your contract can call this function and assign the return value directly:
spool = ISpool(address(0x123));
bool result = spool.foo(1);
If the specified address doesn't implement this specific function that you're trying to call, the call fails with an exception.

Statement not accessible

there is error in line 49 where I wrote CALL METHOD lclref->lcm . how can i solve it?
REPORT ZPRACTICE_TILL_NOW.
include zlc.
include zinc.
START-OF-SELECTION.
create OBJECT lclref.
CALL METHOD lclref->lcm .
first include:
class zlcl DEFINITION.
PUBLIC SECTION.
METHODS: lcm.
ENDCLASS.
CLASS zlcl IMPLEMENTATION.
method lcm.
new-LINE.
WRITE: 'method called '.
ENDMETHOD.
ENDCLASS.
second include:
*&---------------------------------------------------------------------*
*& Include ZINC
*&---------------------------------------------------------------------*
data lclref TYPE REF TO zlcl.
The error 'Statement not accessible' is because lclref is an object .
To call a method of an object which is referenced to a class ( in this case object -> lclref and class ->zlcl ).
you can directly specify the object and the method .
Hence the corrected code would be : lclref->lcm.
Have fun.
*&---------------------------------------------------------------------*
*& Include ZINC
*&---------------------------------------------------------------------*
data lclref TYPE REF TO zlcl.
Are you sure first INCLUDE is zinc
class zlcl DEFINITION.
PUBLIC SECTION.
METHODS: lcm.
ENDCLASS.
CLASS zlcl IMPLEMENTATION.
method lcm.
new-LINE.
WRITE: 'method called '.
ENDMETHOD.
ENDCLASS.
When I tried with your code I did not getting any error. Please check your INCLUDE which one is the first?
I do not know your SAP version If supports you can use inline declaration. You can see the below example. No need to an extra data declarations
DATA(lo_lcl) = new zlcl( ).
lo_lcl->lcm( ).

Use apply on a data class in Kotlin

I know how to use the apply function on a normal Kotlin class but have not been able to use it with a data class:
data class Person(name: String)
val person = Person().apply {
name = "Tony Stark"
}
I get a compile message of:
No value passed for parameter 'name'
The issue is that name is a constructor parameter only and not made a property, which is invalid for the data class concept anyway. Fix like this:
data class Person(val name: String)
The apply function works similar with any class. But there are some errors in your code snippet:
Parameter in Person constructor didn't mentioned as var or val, so there is no fields name in that class. It would be better to make it var to be able to change value.
You made class's constructor with 1 parameter, but trying to use empty constructor - it is error.

Cannot access expected class constructor parameters in kotlin multi-platform

I'm currently working on a multi-platform module using kotlin. To do so, I rely on the expect/actual mechanism.
I declare a simple class in Common.kt:
expect class Bar constructor(
name: String
)
I'd like to use the defined class in a common method (also present in Common.kt):
fun hello(bar: Bar) {
print("Hello, my name is ${bar.name}")
}
The actual implementation is defined in Jvm.kt:
actual data class Bar actual constructor(
val name: String
)
The problem is I got the following error inside my hello function
Unresolved reference: name
What am I doing wrong?
Expected classes constructor cannot have a property parameter
Therefore it is necessary to describe the property as a class member with val name: String
Actual constructor of 'Bar' has no corresponding expected declaration
However, for the actual constructor to match the expected declaration the number of parameters has to be the same. That is why the parameter is also added name: String in the constructor in addition to the existence of the property.
expect class Bar(name: String) {
val name: String
}
actual class Bar actual constructor(actual val name: String)
Note: If we leave the constructor empty of the expected class we see how the IDE complains when adding a constructor in the current class for the incompatibility.
GL
It should be val name in the expect part as well, either in the constructor parameter list or as a member property.

SystemVerilog interface - Passing parameters after module declaration

Given the following module declaration:
module ( myinterface.mymodport mybus, ... );
And assuming that myinterface has parameters, how do I specify them?
The interface instantiation happens only in the testbench, but now I want to synthesize the DUT, so the TB disappears.
This is an oversight in the SystemVerilog LRM. There's no syntax to specify a required set of parameters for an interface in a module header.
You might check your synthesis tool to see if they provide any way of specifying parameter overrides for the top-level synthesis instance.
You specify the parameter when you instantiate the interface; you do not specify it in the port list of the module. Given
interface myinterface #(parameter DATA_SIZE = 0);
...
All you need is
module mymodule (myinterface.mymodport mybus);
...
because somewhere else you have
myinterface #(.DATA_SIZE(64)) i();
interface myinterface #(parameter DATA_SIZE = 0);
logic [DATA_SIZE-1:0] AWID;
logic [31:0] AWADDR;
modport mymodport (input AWID, AWADDR);
endinterface
module mymodule (myinterface.mymodport mybus);
initial
$display("mymodule");
endmodule
module top;
myinterface #(.DATA_SIZE(64)) i();
mymodule m (.mybus(i));
endmodule
https://www.edaplayground.com/x/528x