Determine if variable has been defined

-Zigo-

Member
Is there a way to find out, programatically by its name, whether a variable has been defined yet or not?
 

joey.jeremiah

ProgressTalk Moderator
Staff member
if the problem is duplicate var names in include files, it would need to be dealt with at compile time before the problem happens

Code:
&if defined( globVar ) = 0 &then

    define var str  as char no-undo.
    define var i    as char no-undo.

    &glob globVar defined

&endif /* defined */
 

-Zigo-

Member
joey.jeremiah said:
if the problem is duplicate var names in include files, it would need to be dealt with at compile time before the problem happens

Code:
&if defined( globVar ) = 0 &then

    define var str  as char no-undo.
    define var i    as char no-undo.

    &glob globVar defined

&endif /* defined */
I never thought of that. Cheers, that might work
 

joey.jeremiah

ProgressTalk Moderator
Staff member
hi Zigo ( cool name! )

sometimes ( actually lots of times ) i don't place var defs in the top of the
prog ( .p ). they all endup in the same place in the compiled r-code ( .r )

but near to the block, loop, in an include etc. where they're actually used,
well, just to make the prog clearer and easier to understand.

of course its not suitable for every and many cases like general widely used
vars e.g. "define var i as int no-undo." its more of an art then a science :)


you can see a more complete example in the timer library i recently posted

http://www.progresstalk.com/showthread.php?t=99500 hth
 

-Zigo-

Member
haha thanks...Zigo isn't my real name, it's a nickname...taken from dance music artists Spoiled & Zigo...Zigo's real name is Ziv Goland, who is also from Israel :)

Thanks for your comments, but I guess I should've pointed out that I'm not an absolute beginner, I've been using Progress for about 18 months.

I am trying to write a generic include file to be used in all our browsers. I want to keep the include file reference itself as simple as possible, and I'm just passing in the name of a variable. In some cases this variable may have been defined globally (for the whole procedure) or locally elsewhere (but which can be obtained by get-attribute). So at the point of referencing the include file, the variable in question may or may not exist. If it does, I want the value it has now. If I just define a new variable regardless, then I would lose the value of this larger-scoped variable.

Does that make any sense?! :confused:
 

joey.jeremiah

ProgressTalk Moderator
Staff member
joey jeremiah is a character from "degrassi" 90s canadian tv show :)


i'm not sure, i can think of a few diff cases that might fit that description.

could you post a simplified maybe pseudo code sample.
 

-Zigo-

Member
Well, the generic include file I'm trying to make is for the purpose of sorting a browser when you click a column. I've had it working perfectly fine for months (see my original thread), until a colleague pointed out a problem.

I'm using the AppBuilder in 9.1E. We have some dialog boxes in the form of a lookup, and the SmartBrowsers inside use filters in Xftr Advanced Query Options (XAQO) to pass the search criteria around. So an example of a query like this could be:

FOR EACH Genre WHERE Genre.Description BEGINS Input NO-LOCK

where 'Input' is listed in the XAQO.

I create a dynamic query in the include file to change the reference to 'Input' into its actual value (otherwise the QUERY-PREPARE statement fails...see other thread).

The act of setting 'Input' in XAQO stores it as an attribute of the procedure, using set-attribute-list. When the sort is performed on the browser, procedure adm-open-query-cases defines a local variable to obtain the value of 'Input' using get-attribute. As such, it is scoped to the local procedure, and the START-SEARCH trigger of the browser doesn't know anything about its value. No problem, you say, I can just use get-attribute again, and store the value in a local variable exactly the same as in adm-open-query-cases.

Where the problem lies is that, because I'm trying to encapsulate everything in a generic include file, and because I need to convert all variable references within the query into their actual current values, means that the query could just as likely refer to a variable defined in Definitions/Main Block and set anywhere within the entire procedure. Using get-attribute will fail because it won't have been set like that.

So I have to cater for both scenarios, where a variable reference in the query could have come from XAQO or been defined manually. I need to define a local variable in START-SEARCH if the variable came from XAQO, but I don't want to if it was defined globally for the procedure (because its value would be overwritten).

This all sounds very confusing, I'm sure :confused: Let me know if you understand what I'm on about, or if you would like proper code examples.
 

-Zigo-

Member
To put it another way, try this:

Code:
DEFINE VARIABLE c AS CHARACTER   NO-UNDO.

c = "abc".

RUN a.

PROCEDURE a:
    MESSAGE c VIEW-AS ALERT-BOX INFO BUTTONS OK.

    IF NO THEN
        DEFINE VARIABLE c AS CHARACTER   NO-UNDO.

    MESSAGE c VIEW-AS ALERT-BOX INFO BUTTONS OK.
END PROCEDURE.

The first message displays "abc" since it was defined as scoped globally to the entire procedure.

The IF condition obviously tests false, so in theory it shouldn't define a new locally-scoped variable. However it does, and so the final message shows the value of this new variable, which is blank.

I need to know whether the larger-scoped variable already exists, and only define a local one if it doesn't.
 

jdgibson

New Member
-Zigo- said:
To put it another way, try this:

Code:
DEFINE VARIABLE c AS CHARACTER   NO-UNDO.
 
c = "abc".
 
RUN a.
 
PROCEDURE a:
    MESSAGE c VIEW-AS ALERT-BOX INFO BUTTONS OK.
 
    IF NO THEN
        DEFINE VARIABLE c AS CHARACTER   NO-UNDO.
 
    MESSAGE c VIEW-AS ALERT-BOX INFO BUTTONS OK.
END PROCEDURE.

The first message displays "abc" since it was defined as scoped globally to the entire procedure.

The IF condition obviously tests false, so in theory it shouldn't define a new locally-scoped variable. However it does, and so the final message shows the value of this new variable, which is blank.

I need to know whether the larger-scoped variable already exists, and only define a local one if it doesn't.


Variables defininitions are processed at compile time with an entry being created in the compilers symbol table for the the variable when it encounters it in the code and memory being reserved for it in the compiled code. So in theory and practice your if statement makes no differance to the declaration of the variable c in the procedure a.

What you are trying to do is impossible except through the use of compile time directives but if the developer that defines variable c at the top level doesn't know about the variable c in procedure a they probably wouldn't know to set the compile time global-define directive either.
 
-Zigo- said:
I am trying to write a generic include file to be used in all our browsers. I want to keep the include file reference itself as simple as possible, and I'm just passing in the name of a variable. In some cases this variable may have been defined globally (for the whole procedure) or locally elsewhere (but which can be obtained by get-attribute). So at the point of referencing the include file, the variable in question may or may not exist. If it does, I want the value it has now. If I just define a new variable regardless, then I would lose the value of this larger-scoped variable.

Does that make any sense?! :confused:

As bulklodd said, you can't do it at run time. As Joey said, you can do it at compile time.

I must admit, I'm not entirely sure what you are trying to do, but assuming you're on at least V9, you may be able to do what you are attempting with a session super procedure.

Example:

Code:
a = GetValue('MyVariable').

IF a = ? THEN
DO:
   a = 'Whatever'.
   SetValue('MyVariable', a).
END.

sort of thing.
 

-Zigo-

Member
Lee Curzon said:
I must admit, I'm not entirely sure what you are trying to do, but assuming you're on at least V9, you may be able to do what you are attempting with a session super procedure.

Example:

Code:
a = GetValue('MyVariable').

IF a = ? THEN
DO:
   a = 'Whatever'.
   SetValue('MyVariable', a).
END.
sort of thing.

I tried to explain what I'm trying to do in my post starting "Well, the generic include file..." but it's not easy to explain in words. I'm using 9.1E btw.

I understand your semi-pseudo-code and that is pretty much what I'm trying to do, but I can't see any possible way in which to make it work in practice. Unless I'm missing something obvious, in which case please elaborate on your suggestion.

The problem comes from when I attempt to find the value of a variable that doesn't exist. Even using NO-ERROR still results in a compile-time error. If I could code this dynamically such that it compiles ok, and gives a return value of ? at run-time, this would be ideal.
 
Sorry,

I was suggesting using pseudo-variables.

eg.

Rather than using

DEFINE MyVariable .... in your code.

You would create a data structure in your controlling super, using eg. a temp-table, or a db table if you wanted to persist your variables across sessions.

Access to this table would be controlled through get/set functions.

The variable would be defined along the following lines:

Code:
Procedure/Function SetValue [defined in your session super]

Input Parameter icVariableName
Input Parameter icValue

:

CREATE ttVariable.
ASSIGN
   ttVariable.Name = icVariableName
   ttVariable.Value = icValue
.

End Procedure/Function.


Then...

SetValue('MyVariable', 'Whatever').
 
Sorry, I completely missed your post 8 above, where you described the problem and your version.

If the definitions of the variables you want to determine the existence of at runtime are outside your control (eg. in ADM), then you can't do much.

On the other hand, if you get to define the variables in the first place, I think the following util does something similar to what I am suggesting, though I haven't used it before, and it looks a bit too comprehensive for what you are trying to do:

http://www.peg.com/utilities/svm/session-variable-manager.zip
 
-Zigo- said:
To put it another way, try this:

Code:
DEFINE VARIABLE c AS CHARACTER   NO-UNDO.

c = "abc".

RUN a.

PROCEDURE a:
    MESSAGE c VIEW-AS ALERT-BOX INFO BUTTONS OK.

    IF NO THEN
        DEFINE VARIABLE c AS CHARACTER   NO-UNDO.

    MESSAGE c VIEW-AS ALERT-BOX INFO BUTTONS OK.
END PROCEDURE.

The equivalent code using pseudo-variables managed by a session super would be:

Code:
SetValue("c", "abc").

RUN a.

PROCEDURE a:

    DEF VAR Test AS CHARACTER NO-UNDO.

    Test = GetValue('c'). /* Set above */

    IF Test = ? THEN
    DO:
        Test = 'abc'.
        SetValue('c'. Test). /* Set c to whatever you want here */
    END.

    MESSAGE GetValue('c') VIEW-AS ALERT-BOX INFO BUTTONS OK.

END PROCEDURE.
 
btw. It sounds like you are using ADM1.

If at all possible, use ADM2 instead, as ADM1 is generally considered a 'learning experience' for Progress Corp.

You may find you can do what you are trying to do with that. Certainly you can sort on columns in a (static) SmartBrowse.
 

-Zigo-

Member
Thanks for your suggestions Lee.

Unfortunately as much as I'd like to move to ADM2, it's not really in the interest of our company to do so right now, given that everything generally works fine as it is in ADM1 (and has been doing so for the last 15 years).

I looked at that Session Variable Manager you linked to, and yes that is overly complicated for my task at hand.

I understand your pseudo-variable suggestion and this could be quite useful in some situations, but unfortunately it won't help me here. I'm trying to drop a generic include file in the START-SEARCH trigger of all our browsers where sorting on columns hasn't been set up (and there's quite a lot of them). As I said, some of our software is pretty old and I'm not going to start changing them all to use pseudo-variables. As such, the variables referenced by the query in the browsers already exist...it's just that they're not always defined in the same place per program.

I think I'll have to admit defeat on this one, sadly :(
 

joey.jeremiah

ProgressTalk Moderator
Staff member
like dynamic languages

you can write a proc library with basic functionality that will let you manage
vars dynamically

create vars if they do not already exist once they're referenced

change var type at run-time (dynamic typing)

handle scoping including internal proc and functions, and garabage
collection


maybe you can go with set/get and types function names e.g. setDate,
getChar, getInt etc.

dynamic temp-tables to hold the data

scoping, especially for internal proc and functions, needs more thought to
go into it. but between us we can come up with something practical

i dont have any plans to write one. but if its really important to you maybe
we can talk about a small contracting job
 

-Zigo-

Member
Variables defininitions are processed at compile time with an entry being created in the compilers symbol table for the the variable when it encounters it in the code and memory being reserved for it in the compiled code. So in theory and practice your if statement makes no differance to the declaration of the variable c in the procedure a.

In that case, why doesn't this work:

Code:
c = "hello".
DEFINE VARIABLE c AS CHARACTER   NO-UNDO.

At the point I'm trying to reference c, it hasn't been declared/defined. It obviously matters to the compiler where these definitions take place in the same block of code.

So if the compiler takes this into account, my earlier example (post #9) should work the way I want it to. Or at least, I should be able to find a way of knowing whether the variable has been defined at any given point.

Seems to me like yet another Progress inconsistency...
 
Top