ABAP: Program terminates without providing any error on Exception Handling - abap

The following program compiles fine, but terminates - without any error - on execution. The program terminates when an exception is raised and class is called, Why does the program terminate? Looks like the program terminates when r_exception is exported, but not imported
*&---------------------------------------------------------------------*
*& Report Z_TEST102
*&
*&---------------------------------------------------------------------*
*&
*&
*&---------------------------------------------------------------------*
REPORT z_test102.
TYPES: ty_fuel TYPE p DECIMALS 2,
ty_cargo TYPE p DECIMALS 2.
TYPE-POOLS icon.
*----------------------------------------------------------------------*
* CLASS lcx_list_error DEFINITION
*----------------------------------------------------------------------*
*
*----------------------------------------------------------------------*
CLASS lcx_list_error DEFINITION INHERITING FROM cx_static_check.
PUBLIC SECTION.
METHODS get_exc_text
RETURNING value(result) TYPE string.
ENDCLASS. "lcx_list_error DEFINITION
*---------------------------------------------------------------------*
* CLASS lcx_list_error IMPLEMENTATION
*---------------------------------------------------------------------*
*
*---------------------------------------------------------------------*
CLASS lcx_list_error IMPLEMENTATION.
METHOD get_exc_text.
result = 'The list contains an error.'(err).
ENDMETHOD. "get_exc_text
ENDCLASS. "lcx_list_error IMPLEMENTATION
*----------------------------------------------------------------------*
* CLASS lcl_airplane DEFINITION
*----------------------------------------------------------------------*
*
*----------------------------------------------------------------------*
CLASS lcl_airplane DEFINITION.
PUBLIC SECTION.
"---------------------------------------------
CONSTANTS: pos_1 TYPE i VALUE 30.
METHODS: constructor IMPORTING
im_name TYPE string
im_planetype TYPE saplane-planetype.
METHODS: display_attributes
RAISING lcx_list_error.
CLASS-METHODS: display_n_o_airplanes.
EVENTS: airplane_created.
PRIVATE SECTION.
"----------------------------------------------
METHODS: get_technical_attributes
IMPORTING im_type TYPE saplane-planetype
EXPORTING ex_weight TYPE s_plan_wei
ex_tankcap TYPE s_capacity
RAISING cx_bc401_invalid_planetype.
DATA: name TYPE string,
planetype TYPE saplane-planetype.
CLASS-DATA: n_o_airplanes TYPE i.
ENDCLASS. "lcl_airplane DEFINITION
*------------------------------------------------------------------*
* CLASS lcl_airplane IMPLEMENTATION *
*------------------------------------------------------------------*
CLASS lcl_airplane IMPLEMENTATION.
METHOD constructor.
name = im_name.
planetype = im_planetype.
n_o_airplanes = n_o_airplanes + 1.
RAISE EVENT airplane_created.
ENDMETHOD. "constructor
METHOD display_attributes.
DATA: weight TYPE saplane-weight,
cap TYPE saplane-tankcap,
r_exception TYPE REF TO cx_root,
exc_text TYPE string.
WRITE: / icon_ws_plane AS ICON,
/ 'Name of Airplane'(001), AT pos_1 name,
/ 'Type of airplane: '(002), AT pos_1 planetype.
* handle exception in case of invalid planetype:
TRY.
get_technical_attributes( EXPORTING im_type = planetype
IMPORTING ex_weight = weight
ex_tankcap = cap ).
WRITE: / 'weight:'(003), weight,
'tankkap:'(004), cap.
CATCH cx_bc401_invalid_planetype INTO r_exception.
exc_text = r_exception->get_text( ).
WRITE: / exc_text COLOR COL_NEGATIVE.
"Program terminates here!
RAISE EXCEPTION TYPE lcx_list_error
EXPORTING previous = r_exception.
ENDTRY.
ENDMETHOD. "display_attributes
METHOD display_n_o_airplanes.
WRITE: /, / 'number of airplanes: '(ca1),
AT pos_1 n_o_airplanes LEFT-JUSTIFIED, /.
ENDMETHOD. "display_n_o_airplanes
METHOD get_technical_attributes.
SELECT SINGLE weight tankcap FROM saplane
INTO (ex_weight, ex_tankcap)
WHERE planetype = im_type.
IF sy-subrc <> 0.
RAISE EXCEPTION TYPE cx_bc401_invalid_planetype
EXPORTING planetype = im_type.
ENDIF.
ENDMETHOD. "get_technical_attributes
ENDCLASS. "lcl_airplane IMPLEMENTATION
START-OF-SELECTION.
DATA: r_airplane TYPE REF TO lcl_airplane.
***** Passenger Plane ********************************************
CREATE OBJECT r_airplane EXPORTING
im_name = 'LH BERLIN'
im_planetype = '400-400'. "'747-400'.
lcl_airplane=>display_n_o_airplanes( ).
CALL METHOD r_airplane->display_attributes.
Exception Class Definition:

Related

Using a default value for a function parameter which depends of other parameter

I'd like to create an script which takes an input file and optionally an output file. When you don't pass an output file, the script uses the same filename as the input but with the extension changed. I don't know how to write a default parameter which changes the extension.
#!/usr/bin/env raku
unit sub MAIN(
Str $input where *.IO.f, #= input file
Str $output = $input.IO.extension("txt"), #= output file
Bool :$copy, #= copy file
Bool :$move, #= move file
);
Unfortunately that doesn't work:
No such method 'IO' for invocant of type 'VMNull'
in block <unit> at ./copy.raku line 5
How can I do something like that?
error message is less than awesome but program not working is expected because you have in the signature
Str $output = $input.IO.extension("txt")
but the right hand side returns an IO::Path object with that extension but $output is typed to be a String. That is an error:
>>> my Str $s := "file.csv".IO.extension("txt")
Type check failed in binding; expected Str but got IO::Path (IO::Path.new("file.t...)
in block <unit> at <unknown file> line 1
>>> sub fun(Str $inp, Str $out = $inp.IO.extension("txt")) { }
&fun
>>> fun "file.csv"
Type check failed in binding to parameter '$out'; expected Str but got IO::Path (IO::Path.new("file.t...)
in sub fun at <unknown file> line 1
in block <unit> at <unknown file> line 1
Sometimes compiler detects incompatible default values:
>>> sub yes(Str $s = 3) { }
===SORRY!=== Error while compiling:
Default value '3' will never bind to a parameter of type Str
------> sub yes(Str $s = 3⏏) { }
expecting any of:
constraint
but what you have is far from a literal, so runtime detection.
To fix it, you can either
change to Str() $output = $inp.IO.extension("txt") where Str() means "accept Any object and then cast it to Str". So $output will end up being a string like "file.txt" available in MAIN.
similar alternative: Str $output = $inp.IO.extension("txt").Str but it's repetitive in Str.
change to IO::Path() $output = $inp.IO.extension("txt"). Similarly, this casts to whatever recieved to an IO::Path object, so, e.g., you'll have "file.txt".IO available in $output. If you do this, you might want to do the same for $input as well for consistency. Since IO::Path objects are idempotent to .IO (in eqv sense), no other part of the code needs changing.

Initialize a parameter in a derived type

I am still wrapping my head around Fortran with an "object oriented" flavor.
Is it possible to initialize a variable in a derived type, and what if I'd like that variable to be a parameter?
For example, a classic animal, cat, bee set of types that should set the number of legs of each animal:
animal module
module animal_module
implicit none
type, abstract :: animal
private
integer, public :: nlegs = -1
contains
...
cat module
module cat_module
use animal_module, only : animal
implicit none
type, extends(animal) :: cat
private
! error here about redefining nlegs...
integer, public :: nlegs = 4
...
I've found online the keyword initial, but my compiler (Intel) complains about the keyword with a syntax error.
Addendum
I've tried a custom constructor but apparently I am unable to write one for derived types. This is my attempt, only on the cat type:
module cat_module
use animal_module, only : animal
implicit none
type, extends(animal) :: cat
private
real :: hidden = 23.
contains
procedure :: setlegs => setlegs
procedure :: speak
end type cat
interface cat
module procedure init_cat
end interface cat
contains
type(cat) function init_cat(this)
class(cat), intent(inout) :: this
this%nlegs = -4
end function init_cat
...
program oo
use animal_module
use cat_module
use bee_module
implicit none
character(len = 3) :: what = "cat"
class(animal), allocatable :: q
select case(what)
case("cat")
print *, "you will see a cat"
allocate(cat :: q)
...
print *, "this animal has ", q%legs(), " legs."
As the animal type has integer, public :: nlegs = -1, I expected the cat to have -4 legs, but alas, it's still -1.
It is not possible for a component to be a named constant. Also, it isn't possible to declare a component of an abstract type and then define default initialization for its value in an extending type. Attempting to redeclare the component with default initialization leads to the error of the question.
High Performance Mark's comment offers one route: provide a custom constructor for each extending type setting the value appropriately. You can further set the component to private for encapsulation or protection.
Alternatively, you can provide a "getter" type-bound procedure which references a named constant:
module animal_module
implicit none
type, abstract :: animal
contains
procedure(getter), deferred :: nlegs
end type animal
abstract interface
integer function getter(creature)
import animal
class(animal) creature
end function getter
end interface
end module animal_module
module cat_module
use animal_module, only : animal
implicit none
type, extends(animal) :: cat
contains
procedure :: nlegs => nlegs_cat
end type cat
contains
integer function nlegs_cat(creature)
class(cat) creature
integer, parameter :: my_cat_has_legs=3
nlegs_cat = my_cat_has_legs
end function nlegs_cat
end module cat_module
use cat_module
implicit none
class(animal), allocatable :: fido
fido = cat()
print*, "Fido has", fido%nlegs(), "legs"
end
Finally, initial is not standard Fortran (and would have similar problems).

How to use in a submodule a exception created in module (in ocaml)

I have a module type A with an exception. A will be implemented by B and C
module type A = sig
type t
val f: t->t->t
exception DivisionParZero
end
module B : A = struct
type t = int
let f a b=
if (a==0) then raise DivisionParZero else b/a
end
end
Ocamlc says when it compiles B:
Error: This variant expression is expected to have type exn
The constructor DivisionParZero does not belong to type exn
I don't understand why it doesn't work.
A is a signature that B needs to fulfill. In your context it means that you have to write the declaration line again:
module B : A = struct
type t = int
exception DivisionParZero
let f a b=
if (a==0) then raise DivisionParZero else b/a
end
You can experiment a little by returning a random value instead of raising the exception and you will see that the compiler tells you that your implementation does not fit the signature:
Error: Signature mismatch:
Modules do not match:
sig type t = int val f : int -> int -> int end
is not included in
A
The extension constructor `DivisionParZero' is required but not provided
File "test.ml", line 4, characters 2-27: Expected declaration

What does the second colon in "List:D:" mean in Perl 6?

In the doc.perl6.org, i've seen many methods like this:
method sum(List:D: --> Numeric:D)
I konw List:D is a type of List that is defined, but what does the colon after the D mean (i.e. the second one in List:D:)?
I found some explain in S12-objects:
=head2 Invocants
Declaration of the invocant is optional. You may always access the
current invocant using the keyword self.
...
To mark an explicit invocant, just put a colon after it:
method doit ($x: $a, $b, $c) { ... }
but I don't understand, it's somewhat strange at first glance.
By default methods have an invocant of self
So both of these would be equivalent:
method foo ( $a ){…}
method foo ( \self: $a ){…} # generates warning
So expanding the first example out to what it is sort-of short for
method sum( List:D \self: --> Numeric:D ){…} # generates warning
Basically you write it that way if you want to specify the type of the invocant (first argument) to a method, but just want to use self rather than specify a new variable.
The reason it uses the : to split up the invocant from the rest of the parameters is to simplify the common case where you don't specify the invocant, or type of the invocant.
When you define a sub with a basic type constraint like this:
sub testB (Str $b) { say $b; }
then you can call it with an actual instance of the type in question as well as with the type object itself:
> testB("woo")
woo
> testB(Str)
(Str)
The :D is an additional type constraint, so you can only pass a "defined" instance:
sub testC (Str:D $c) { say $c; }
> testB(Str)
(Str)
> testC("hoo")
hoo
> testC(Str)
Parameter '$c' of routine 'testC' must be an object instance of type 'Str', not a type object of type 'Str'. Did you forget a '.new'?
in sub testC at <unknown file> line 1
in block <unit> at <unknown file> line 1
More details can be found here

OCAML first class modules signature inference

1) Suppose there is a module
module Int_Sig_1 =
struct
let x = 1
end
;;
2) and an explicit signature of this module
module type INT_SIG =
sig
val x:int
end
;;
3) and i create a first-class module based on the above module and module type
let int_sig_1 = (module Int_Sig_1:INT_SIG)
4) Now i create another module with no explicit signature but same inferred signature as above
module Int_Sig_2 =
struct
let x =2
end
;;
5) As written in Chapter 10 of the book Real World OCAML "The module type doesn't need to be part of the construction of a first-class module if it can be inferred", i try to create a second first class module using the above module but with no explicit module type
let a2 = (module Int_Sig_2);;
I get the following error
Error: The signature for this packaged module couldn't be inferred.
6) then i try to do the same as above in 5, but this time I put the first class module with no module type being create as an element of a list, where the head of the list is a first class module that was created out of an explicit signature in 3 above.
let int_sig= [int_sig_1;(module Int_Sig_2)];;
val int_sig : (module INT_SIG) list = [<module>; <module>] ;;
My question is why 5 above gives me an error and 6 does not fail ?
The problem with (5) is that in general, there are multiple module types that could be infered. In your example, there is at least two valid module types which could be used to pack Int_Sig_2:
module type empty = sig end
module type with_x = sig val x:int end
In other words, both
let a2 = (module Int_Sig_2: empty)
let a2_bis = (module Int_Sig_2:with_x)
are valid. Consequently, the type checker will not try to infer a module type in this situation.
Contrarily, in your example (6), the type of the list is determined by its first element, whose type is (module INT_SIG_2), therefore the type-checker can use this information to infer that the expected type for the second element of the list is (module INT_SIG_2). Nevertheless, reversing the two elements yields a type error. In other words, this is fine:
[(module struct let x = 2 end: with_x); (module struct let x = 1 end)]
however, the reverse yields
[(module struct let x=2 end); (module struct let x = 3 end:with_x)];;
Error: The signature for this packaged module couldn't be inferred.
This is due to the fact that the type-checker has a left-to-right bias and types first the first element of the list first.