In my code I want to call the CLASS_CONSTRUCTOR method from the static method ADD_BOOK.
However I receive this syntax error:
The direct call of the special method "CLASS_CONSTRUCTOR" is not
possible.
Here is the code:
CLASS lcl_books DEFINITION.
PUBLIC SECTION.
CLASS-METHODS class_constructor.
CLASS-METHODS add_book.
...
ENDCLASS.
CLASS lcl_books IMPLEMENTATION.
METHOD class_constructor.
SELECT * FROM zgib_bmabuecher INTO TABLE gt_return.
ENDMETHOD.
METHOD add_book.
DATA lf_check TYPE n VALUE 0.
LOOP AT gt_return INTO mf_books.
IF if_book-isbn = mf_books-isbn.
lf_check = 1.
ENDIF.
ENDLOOP.
IF lf_check = 0.
INSERT zgib_bmabuecher FROM if_book.
ENDIF.
lcl_books=>class_constructor( ).
ENDMETHOD.
ENDCLASS.
You have all the information in the error message. You cannot (german: darfst nicht) call the class constructor explicitly. It is always called automatically and just once whenever the class is used for the first time.
If you want to reuse the coding of the class constructor then put it in another class method, for example like this.
CLASS lcl_books DEFINITION.
"...
PRIVATE SECTION.
CLASS-METHODS:
select_books.
"...
ENDCLASS.
CLASS lcl_books IMPLEMENTATION.
METHOD class_constructor.
select_books( ).
ENDMETHOD.
METHOD select_books.
SELECT * FROM zgib_bmabuecher INTO TABLE gt_return.
ENDMETHOD.
Then change your add_book method to
METHOD add_book.
DATA lf_check TYPE n VALUE 0.
LOOP AT gt_return INTO mf_books.
IF if_book-isbn = mf_books-isbn.
lf_check = 1.
ENDIF.
ENDLOOP.
IF lf_check = 0.
INSERT zgib_bmabuecher FROM if_book.
ENDIF.
select_books( ).
ENDMETHOD.
ENDCLASS.
Related
I want to pass an error local object in a class method which will display a detail error to the user.
This is the current code:
CATCH cx_root INTO lcx_general_error.
DATA(lv_longtext) = lcx_general_error->get_longtext( ).
lcx_general_error->get_source_position(
IMPORTING
program_name = lv_program_name
include_name = lv_include_name
source_line = lv_program_line
).
DATA(lv_program_include) = |{ lv_program_name }/ { lv_include_name }|.
DATA(lv_length_message) = strlen( lv_longtext ).
DATA(lv_error_message1) = lv_longtext(50).
IF lv_length_message > 50.
DATA(lv_remaining) = lv_length_message - 50.
DATA(lv_error_message2) = lv_longtext+50(lv_remaining).
ENDIF.
MESSAGE e001 WITH lv_error_message1 lv_error_message2
lv_program_include
lv_program_line.
Instead, I want to create a class method and pass any local object that refers to any error and display the error detail message:
CATCH cx_root INTO lcx_general_error.
lo_fi_uploads->display_error( lcx_general_error ).
How to create and use this parameter in the local class?
Exceptions are regular classes with regular object instances, so declare them like any other object parameter:
METHODS display_error
IMPORTING
exception TYPE REF TO cx_root.
In the method’s implementation you can then paste the code you already have:
METHOD display_error.
DATA(lv_longtext) = exception->get_longtext( ).
exception->get_source_position(
IMPORTING
program_name = DATA(lv_program_name)
include_name = DATA(lv_include_name)
source_line = DATA(lv_program_line)
).
DATA(lv_program_include) = |{ lv_program_name }/ { lv_include_name }|.
DATA(lv_length_message) = strlen( lv_longtext ).
DATA(lv_error_message1) = lv_longtext(50).
IF lv_length_message > 50.
DATA(lv_remaining) = lv_length_message - 50.
DATA(lv_error_message2) = lv_longtext+50(lv_remaining).
ENDIF.
MESSAGE e001 WITH lv_error_message1 lv_error_message2
lv_program_include
lv_program_line.
ENDMETHOD.
People often fear that working with exceptions might accidentally trigger them. That won’t happen. As long as you do not invoke the RAISE statement, exceptions are really quite ordinary objects. You can even instantiate them with NEW without triggering them.
With SQLAlchemy, I'm finding that sometimes I mis-type a name of an attribute which is mapped to a column, which results in rather difficult to catch errors:
class Thing(Base):
foo = Column(String)
thing = Thing()
thing.bar = "Hello" # a typo, I actually meant thing.foo
assert thing.bar == "Hello" # works here, as thing.bar is a transient attribute created by the assignment above
session.add(thing)
session.commit() # thing.bar is not saved in the database, obviously
...
# much later
thing = session.query(Thing)...one()
assert thing.foo == "Hello" # fails
assert thing.bar == "Hello" # fails, there's no even such attribute
Is there a way to configure the mapped class so assigning to anything which is not mapped to an SQLAlchemy column would raise an exception?
Ok, the solution seems to be to override __setattr__ method of the base class, which allows us to check if the atribute already exists before setting it.
class BaseBase(object):
"""
This class is a superclass of SA-generated Base class,
which in turn is the superclass of all db-aware classes
so we can define common functions here
"""
def __setattr__(self, name, value):
"""
Raise an exception if attempting to assign to an atribute which does not exist in the model.
We're not checking if the attribute is an SQLAlchemy-mapped column because we also want it to work with properties etc.
See http://stackoverflow.com/questions/12032260/ for more details.
"""
if name != "_sa_instance_state" and not hasattr(self, name):
raise AttributeError("Attribute %s is not a mapped column of object %s" % (name, self))
super(BaseBase, self).__setattr__(name, value)
Base = declarative_base(cls=BaseBase)
Sort of "strict mode" for SQLAlchemy...
Override the __get__ method of objects, and check to see if it is in the column (by storing it with the class definition or runtime search)
More information here from SO.
I would like to create a generic function. I'm novice in generic.
I've 3 private lists of different type. I want a public generic method for return 1 item of the list.
I've the code below. (I have it simplifie)
TFilter = class
private
FListFilter : TObjectList<TFilterEntity>;
FListFilterDate : TObjectList<TFilterDate>;
FListFilterRensParam : TObjectList<TFilterRensParam>;
public
function yGetFilter<T>(iIndice : integer) : T;
....
function TFilter .yGetFilter<T>(iIndice : integer) : T;
begin
if T = TFilterEntity then
result := T(FListFilter.Items[iIndice])
else
....
end;
I know that code doesn't run, but can you tell me if it's possible to do a thing that it ?
Just introduce a constraint of the generic parameter T. It has to be a class.
From the documentation:
A type parameter may be constrained by zero or one class type. As with interface type constraints, this declaration means that the compiler will require any concrete type passed as an argument to the constrained type param to be assignment compatible with the constraint class.
Compatibility of class types follows the normal rules of OOP type compatibilty - descendent types can be passed where their ancestor types are required.
Change declaration to:
function yGetFilter<T:class>(iIndice : integer) : T;
Update
It appears that in XE5 and earlier you get a compiler error:
E2015 Operator not applicable to this operand type
at this line:
if T = TFilterEntity then
In XE6 and above this bug is fixed.
To circumvent, do as David says in a comment:
if TClass(T) = TFilterEntity then
I have several custom exception classes that were created "With Message Class". Since I can't directly get a message from them, I want to create a utility method that returns a BAPIRET2 from a given exception based on the values in IF_T100_MESSAGE~T100KEY. However, I can't provide that method with a generic CX_ROOT importing parameter as this class is not message-enabled. I also can't create a generic message-enabled exception class as new classes have to inherit from one of CX_STATIC_CHECK, CX_DYNAMIC_CHECK, or CX_NOCHECK.
How can I then retrieve the message details from an unspecified exception? Should I create a method that receives a CX_ROOT and then does up to three calls to methods with an import typed to each of the three possible subclasses? Or are there better alternatives?
You could prepare a type descriptor of the interface (once):
DATA: lr_t100_descr TYPE REF TO cl_abap_intfdescr.
lr_t100_descr ?= cl_abap_typedescr=>describe_by_name( 'IF_T100_MESSAGE' ).
and then examine each exception as it comes your way:
DATA: lr_t100_exception TYPE REF TO if_t100_message.
IF lr_t100_descr->applies_to( ir_any_exception ) = abap_true.
lr_t100_exception ?= ir_any_exception.
" ...
ENDIF.
You could use the message collector object, so for example
DATA:
excp type ref to CX_ROOT,
bapi_messages type BAPIRETTAB,
message_collector type ref to IF_RECA_MESSAGE_LIST.
FIELD_SYMBOLS:
<bapi_message> TYPE BAPIRET2.
message_collector = cf_reca_message_list=>create( ).
TRY.
" some code which may cause and exception
CATCH cx_root into excp.
message_collector->add_from_exxeption( io_exception = excp).
ENDTRY.
bapi_messages = message_collector->get_list_as_bapiret( ).
LOOP AT bapi_messages ASSIGNING <bapi_message>.
" write out message
ENDLOOP.
It is well worth checking out the message collector object.
For example
http://wiki.scn.sap.com/wiki/display/profile/2007/07/09/Message+Handling+-+Finding+the+Needle+in+the+Haystack
For a logging class I use something like this:
METHOD add_message_exception.
DATA:
lr_type TYPE REF TO cl_abap_typedescr,
lr_class TYPE REF TO cl_abap_classdescr,
lr_intf TYPE REF TO cl_abap_intfdescr,
l_bapiret2 TYPE bapiret2,
lr_msg TYPE REF TO if_t100_message.
CHECK ir_exception IS NOT INITIAL.
l_bapiret2-type = i_type.
"Test for T100KEY interface
cl_abap_classdescr=>describe_by_object_ref(
EXPORTING
p_object_ref = ir_exception
RECEIVING
p_descr_ref = lr_type
EXCEPTIONS
reference_is_initial = 1
OTHERS = 2 ).
TRY.
lr_class ?= lr_type.
IF sy-subrc = 0.
lr_class->get_interface_type(
EXPORTING
p_name = 'IF_T100_MESSAGE'
RECEIVING
p_descr_ref = lr_intf
EXCEPTIONS
interface_not_found = 1
OTHERS = 2 ).
IF sy-subrc = 0.
lr_msg ?= ir_exception. "Cast to interface
l_bapiret2-id = lr_msg->t100key-msgid.
l_bapiret2-number = lr_msg->t100key-msgno.
cl_message_helper=>set_msg_vars_for_if_t100_msg( text = lr_msg ).
l_bapiret2-message_v1 = sy-msgv1.
l_bapiret2-message_v2 = sy-msgv2.
l_bapiret2-message_v3 = sy-msgv3.
l_bapiret2-message_v4 = sy-msgv4.
l_bapiret2-message = me->get_msg(
i_msgid = l_bapiret2-id
i_msgno = l_bapiret2-number ).
ENDIF.
ENDIF.
CATCH cx_root.
"Pokémon exception handling
ENDTRY.
"No T100KEY Interface available
IF lr_msg IS INITIAL.
l_bapiret2-message = ir_exception->if_message~get_text( ).
l_bapiret2-message_v1 = sy-msgv1.
l_bapiret2-message_v2 = sy-msgv2.
l_bapiret2-message_v3 = sy-msgv3.
l_bapiret2-message_v4 = sy-msgv4.
ENDIF.
ENDMETHOD.
Hope this helps as I struggled with the same problem. Maybe there is some adjustment needed, but I think you get the basic idea. This method can handle
I might be missing something, but can't you just use IF_MESSAGE~GET_TEXT which is present on CX_ROOT ?
Otherwise, I would make it the responsibility of the custom exception class to have a method that can return a proper message ( it might rely on the utility method you are planning on ).
I need something like this:
function fn_get_all_propperties (obj : TObject) : TObjectList<TTypeKind>;
But:
[DCC Error] uFuncMain.pas(20): E2511 Type parameter 'T' must be a class type
What type should be the result of a function?
The problem is that TObjectList is defined as follows:
TObjectList<T: class> = class(TList)
....
end;
The T: class in the definition means that the generic parameter T is constrained to be a class. But TTypeKind is not a class. It is a value type.
So the compiler rejects your attempted generic instantiation as being invalid because it does not satisfy the constraint.
So you cannot use TObjectList<T> here and instead should use TList<T>. Your function should be defined like this:
function fn_get_all_properties(obj: TObject): TList<TTypeKind>;