Include File - Bad Practice?

KMoody

Member
I need to initialize and use a set of variables in WHERE clauses across several programs. It has been suggested that I initialize my variables within an include and add that include file to my programs.

I am very reluctant to use includes for a couple reasons:
  • Within the programs, it is not obvious where the included variables come from.
  • Adding the include may add unnecessary overhead.

Are there any other reasons why using an include in this situation is a bad practice? Alternatively, is it actually all right to use an include file in this case?
 
Passing the values as input parameters would be a much better solution. If you need information back from the called procedures, add some output or input-output parameters.

If a larg number of values are required, consider passing a temp-table as a parameter.

An include with shared, YUCK!, variables is pretty much the last thing you want to do in this case.
 
I know the purists hate include files (for a number of valid reasons, usually related to bad code in general just crammed into includes)... but in the real world it can be simpler especially if you already have an include based infrastructure in place. For constants I prefer to use preprocessor defines myself instead of variables but its really a matter of style.

Depending on how the programs relate to each other you could use a function or internal procedure in the main/driver program that sets the local variables.

As far as overhead... once you compile the programs with the include files you end up with compiled binary code just like any other program without include files.
 
One way you can do this is to create a POJO - aaaaahrrg, I'm sorry, POAO ( plain old ABL object ) that contains theses as properties and pass it around in your application. You could even make this object a singleton to make sure it exists only once in your session. You just need to make sure that your singleton doesn't get garbage collected.

Personally I still do use include files in one single use case: Temp-Table and ProDataSet definitions included in the caller and the called object. This way it is ensured that the signature of the Temp-Table and/or ProDataSet matches. I don't see a point in abandoning the ABL as we used to know it just because we are going for OO. Temp-Tables and ProDataSets are a perfect example to me: Although they don't really map into the OO world I still use them because they just make life so much easier ...

Heavy Regards, RealHeavyDude.
 
Completely agree on the structure definition side of things.
Where icode causes a problem is where you have an include of Global Defines (2000 of them) that has to be included in every other piece of code in order for the system to work. Nightmare.
 
One way you can do this is to create a POJO - aaaaahrrg, I'm sorry, POAO ( plain old ABL object ) that contains theses as properties and pass it around in your application. You could even make this object a singleton to make sure it exists only once in your session. You just need to make sure that your singleton doesn't get garbage collected.

Actually, I am using a singleton class to define static variable values. Here's an example:

Code:
USING Progress.Lang.*.

CLASS customer_util:

    DEFINE STATIC TEMP-TABLE tt_FAKE_CUSTOMER_NUM no-undo
        FIELD cust-num LIKE CUSTOMER.CUST-NUM.

    DEFINE PUBLIC STATIC PROPERTY PROSPECT_CODE AS CHARACTER INITIAL 'HP' NO-UNDO
        GET.

     
    CONSTRUCTOR STATIC customer_util (  ):
     
        DEF VAR i AS INTEGER FORMAT "99" INIT 0.
     
        FIND FIRST tt_FAKE_CUSTOMER_NUM NO-LOCK NO-ERROR.
        IF NOT AVAILABLE tt_FAKE_CUSTOMER_NUM THEN
        DO:
            DO i = 1 TO 99:
                CREATE tt_FAKE_CUSTOMER_NUM.
                IF i < 10 THEN
                    tt_FAKE_CUSTOMER_NUM.cust-num = "    0" + STRING(i).
                ELSE
                    tt_FAKE_CUSTOMER_NUM.cust-num = "    " + STRING(i).
                /*            MESSAGE tt_FAKE_CUSTOMER_NUM.cust-num .*/
                RELEASE tt_FAKE_CUSTOMER_NUM.
            END.
        END.
     
    END CONSTRUCTOR.

END CLASS.

Unfortunately, I can't call the static class variable from within a WHERE clause.

This is invalid:
Code:
    FOR EACH customer WHERE CUSTOMER.ACCT-STATUS ne customer_util:PROSPECT_CODE
        NO-LOCK:
.........

So I'm either forced to hard-code the prospect code value into the WHERE clause (which I really don't want to do)...
Code:
    FOR EACH customer WHERE CUSTOMER.ACCT-STATUS ne 'HP'
        NO-LOCK:
.........

...or I have to create a new variable, set it to the static class variable value, and use the new variable in the WHERE clause.
Code:
DEFINE VARIABLE prospectAcctStatus LIKE CUSTOMER.ACCT-STATUS.
prospectAcctStatus = customer_util:PROSPECT_CODE.

FOR EACH customer WHERE CUSTOMER.ACCT-STATUS ne prospectAcctStatus
        NO-LOCK:
.........

I'd prefer to keep the actual prospect code value 'HP' within my class since I want my customer values and methods all in one place.
 
Last edited:
We use include files extensively, and while we definitely do some terrible things with them, for the most part, I'm OK with it and absent starting down the OO road, I'd probably do it again.

We have three rules that we follow:

1. Any .I must be independently compilable, that is, it must reference the includes it itself needs and use include guards.

2. .I's only ever contain internal procedures or functions (ie. never (OK OK almost never) 'free standing' code).

3. Said functions and IP's never refer to variables or buffers from the enclosing program.

Honestly, #1 and #2 we follow 99% of the time; #3 is a bit more nuanced in that it's true for IP's and functions defined inside .I's, but its not generally true for IP's and functions defined in a .P. The correctness of this practice is a topic of never-ending debate for us. But that's another story ;)
 
By the way, going back to the original post, 'it is not obvious where the included variables come from' is not an issue if you follow rules similar to ours. And 'may add unnecessary overhead' is, I think, totally false. Of course, IP's can add overhead, but (a) it's miniscule and (b) include file <> IP. Includes themselves I suppose add a billionth of a second of overhead while compiling only.
 
IMHO the one has nothing to do with the other.

Actually you are not using a static variable - you are using a static property. These are complete different things. Everytime you access a property you implicitely access the getter or setter ( even if it is the default one if not specified ). That means, you have a function or method call in a where clause which is not supported in the ABL.

You can also define a public static variable. The only thing with variables is, that you can't make the write accessor private and thus cannot prevent it being changed from the outside. Therefore properties are superior in many use cases.

You need to use an interim variable to hold the value of the static proprety the same way as if it would be returned by a method or internal function. That's the way it is in the ABL. One can always debate whether the implementation of properties in the ABL is good or bad. I think it is good - but it is the ABL's query capabilities that come short in your case. Whether that is good or bad might also be subject to discussion.

Include files are a complete different topic.

Heavy Regards, RealHeavyDude.
 
Greg, what do you mean by "include guard"?
I think that term originates in the C language.
Basically you have some preprocessor logic to prevent including the same include file more than once (eg. when it gets included from multiple library include files).
We use this, too. It goes like this:

xy.i has the following code:

&IF DEFINED(xy_i) = 0 &THEN
&GLOBAL-DEFINE xy_i YES

/* Code */

&ENDIF

Of course you need a naming convention so that xy_i is globally unique.
 
That's exactly what I mean, and Andre's example is almost exactly what we put in all of our includes. So in our world a typical largish program begins with 10-20 lines of .i declarations.

Another rule we have is that includes should be written and used in such a way that they never get used inline. That kind of include is truly stomach turning ;)
 
I better not show you some of my code ;)

In my defense -- I have been working on "cleaning up my act". I have a lot of cleaning up to do, it may be a while.
 
In your defense, I think you, like I, still have code from v6, where the editor was even more ghastly than today (it's where I learned the word 'ohnosecond', that's the moment between when your brain already sent your hand a signal to press f8 and the moment you realize that it's too late to stop your fingers from executing) and there were no internal procedures or functions. It's a miracle we ever got anything done ;)
 
Actually we do use quite a lot of inline includes, although that should get less because of the OOABL features.
The main reasons for these includes are implementing optional parameters (which can be implemented in OOABL by overloading methods, but that still doesn't give you named parameters which includes can do) and safeguarding code from API changes (For example a program/procedure introduces a new parameter, this parameter is not totally necessary/has a sensible default, and if you don't change all calling statements you will get runtime errors later. An include which wraps the run statement is useful to prevent the errors.)
 
Back
Top