Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations strongm on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

ATODBL Returns 0

Status
Not open for further replies.

MichaelHuber

Programmer
May 22, 2003
27
US
Can anyone explain these results?
When I pass a short string to a function expecting a long string, then try ATODBL on the string, it returns 0!

Here's some sample code:

DEFINE FUNCTION FN1(MYINPUT/A3)
MYLEN/I3 = ARGLEN(3, MYINPUT, 'I3');
MYVAL/D3 = ATODBL(MYINPUT, '03', 'D3');
FN1/A40 = 'MYINPUT:{' | MYINPUT | '} MYLEN:' | EDIT(MYLEN) | ' MYVAL:' | EDIT(MYVAL);
END
-RUN
-SET &INVAL1 = '1 ';
-SET &OUTVAL1 = FN1(&INVAL1);
-TYPE &OUTVAL1
-SET &INVAL2 = '12 ';
-SET &OUTVAL2 = FN1(&INVAL2);
-TYPE &OUTVAL2
-SET &INVAL3 = '123';
-SET &OUTVAL3 = FN1(&INVAL3);
-TYPE &OUTVAL3
-SET &INVAL4 = '1234';
-SET &OUTVAL4 = FN1(&INVAL4);
-TYPE &OUTVAL4
-TYPE Above results are expected.
-TYPE Below results are FUBAR.
-SET &INVAL5 = '1';
-SET &OUTVAL5 = FN1(&INVAL5);
-TYPE &OUTVAL5
-SET &INVAL6 = '12';
-SET &OUTVAL6 = FN1(&INVAL6);
-TYPE &OUTVAL6


Thanks for any ideas you might have!
By the way, I'm using WebFOCUS 5.2
Regards,
Michael
 
When calling subroutines, everything is passed 'by reference'. That's why you always need to indicate the length of alpha strings, so the subroutine knows where they end.

In your code:

MYLEN/I3 = ARGLEN(3, MYINPUT, 'I3');
MYVAL/D3 = ATODBL(MYINPUT, '03', 'D3');

The second arg of ATODBL is the length of the input, in alpha format (for historic reasons). If you give a length LONGER than the actual length, the subroutine keeps interpreting the bytes BEYOND the end of the real string, and, since what's in memory is PROBABLY not a numeric in character form, it returns 0, as the input isn't numeric. Imagine I pass '1', with a length of '03', and the next area in memory has the string 'FOCUS'. The subroutine sees the string as '1FO' (the 'real' string, plus the first 2 bytes of the next area in memory), which it can'rt convert to numeric, so it gives a zero.

Why not change the call to ATODBL to reflect the 'true' length of the string, which you got in MYLEN. So, it might look like this:

MYLEN/I3 = ARGLEN(3, MYINPUT, 'I3');
MYVAL/D3 = ATODBL(MYINPUT, MYLEN , 'D3');
 
If you'll notice, the function claims that MYLEN for '1 ' is 1, and MYLEN for '1' is 3!!

Somehow, the parameter-passing is messing things up. The documentation claims that if the argument to the DEFINE FUNCTION is too short, it pads the string with spaces. We did a UFMT to figure out what was actually in the string. It was a NULL rather than a space. ATODBL & ARGLEN deal with spaces perfectly well, as you can see by '1 ' and '12 '. Unfortunately, it can't deal with NULL characters at the end of a string.

As a workaround, we converted the NULLs to spaces by putting the following line at the top of the function:
MYINPUT/A3 = CTRAN(3, MYINPUT, 0, 32, 'A3');

It converts all the nulls (ASCII=0) to spaces (ASCII=32dec or 20hex). It's certainly not a true fix, but it works until IBI realizes there's a problem (either in the parameter-passing OR in the documentation OR in the way ARGLEN & ATODBL treat null characters).
 
For what it's worth, I did some testing in MVS/TSO although I recognize this is Web Focus. In my environment, when I run this code, none of it works! Why? The function is looking for alpha input (A3), but dialogue manager is passing the variable as an integer (-SET &VAL='123';) because when it calls the function, the variable looks like a number. This is a NO NO for these functions. I'm surprised Web focus let you get away with the '1 ' or the '123' case.

When I modified the code to:

DEFINE FUNCTION FN1(MYINPUT/A3)
MYLEN/I2 = ARGLEN(3, MYINPUT, 'I2');
MYCHR/A2 = EDIT(MYLEN);
MYVAL/D3 = ATODBL(MYINPUT,MYCHR, 'D3');
FN1/A50 = 'MYINPUT:{' | MYINPUT | '} MYLEN:' | EDIT(MYLEN) |
' MYVAL:' | EDIT(MYVAL) |' MYCHR:'| MYCHR;
END
-RUN
-SET &INVAL1 = '1 X';
-SET &OUTVAL1 = FN1(&INVAL1);
-TYPE &OUTVAL1
-SET &INVAL2 = '12 X';
-SET &OUTVAL2 = FN1(&INVAL2);
-TYPE &OUTVAL2
-SET &INVAL3 = '123X';
-SET &OUTVAL3 = FN1(&INVAL3);
-TYPE &OUTVAL3
-SET &INVAL4 = '1234X';
-SET &OUTVAL4 = FN1(&INVAL4);
-TYPE &OUTVAL4
-TYPE Above results are expected.
-TYPE Below results are FUBAR.
-SET &INVAL5 = '1 X';
-SET &OUTVAL5 = FN1(&INVAL5);
-TYPE &OUTVAL5
-SET &INVAL6 = '12 X';
-SET &OUTVAL6 = FN1(&INVAL6);

it produced the following results:

MYINPUT::1 : MYLEN:01 MYVAL:001 MYCHR:01
MYINPUT::12 : MYLEN:02 MYVAL:012 MYCHR:02
MYINPUT::123: MYLEN:03 MYVAL:123 MYCHR:03
MYINPUT::123: MYLEN:03 MYVAL:123 MYCHR:03
Above results are expected.
Below results are FUBAR.
MYINPUT::1 : MYLEN:01 MYVAL:001 MYCHR:01
MYINPUT::12 : MYLEN:02 MYVAL:012 MYCHR:02

This puts character data where the subroutines expect to see it. I also noticed that MYLEN was computed, but not used in the original code. It also needs to be character, and apparently the maximum length the subroutine will use is two bytes. See the ATODBL documentation for the specifications of what data type the arguments need to be.

Dialogue Manager is tricky when subroutines are used. You need to use 'tricks' to control data type. You can't use a short variable because you need to keep the alpha portion of the string from being included in the processing.

Hope this helps.

 
If I'm not mistaken, everything is an alpha to Dialogue Manager.

I came up with a solution (thanks to focwizard's call-by-reference comment). &VAR.EVAL is the solution, and here's why:
This goes for every language I can think of, and FOCUS seems to follow these general principles. When you pass a variable by value, it creates a new memory location and copies the value into it. When you pass a variable by reference, it uses the location already in memory. If the memory location is too short, you get one of three things: warning, error, or garbage values (whatever is in memory past that location). Luckily in a language where call-by-reference is the default (like FOCUS), a non-variable will be called by value rather than reference. There's no real memory location holding the value, so one is created during the function call. If you evaluate the amper variable before passing it, then a memory location of the correct length is created.

Don't believe me? Try this:
-SET &INVAL5 = '1';
-SET &OUTVAL5 = FN1(&INVAL5.EVAL);
-TYPE &OUTVAL5
-SET &INVAL6 = '12';
-SET &OUTVAL6 = FN1(&INVAL6.EVAL);
-TYPE &OUTVAL6

Not only does it return the correct value, it gets rid of the PARAMETER LENGTH CONFLICT warning. 8^)

Thanks for all your help.
-Michael
 
You are mistaken! Everything in Dialogue Manager is NOT alpha.

Try the following:

-SET &WORK1=123;
-SET &WORK2=456;
-SET &WORK3=&WORK2 - &WORK1;
-TYPE &WORK3

You should get 333. How is this alpha?

Since when does Fortran understand everything in terms of alpha? When you are calling subroutines, they do NOT always deal with things in terms of alpha!

By the way, where did .EVAL come from? I thought 'short' values were what caused the problem. I also thought you were using 'CTRAN(3, MYINPUT, 0, 32, 'A3');'. .EVAL does convert the & variable to 'character' which proves my original point. You have to pass the variable in character format. If you do, Focus will understand what you are trying to do.

Incidentally, it would be helpful if you showed your results. Apparently, not all Focus platforms produce the same results in all cases.
 
I stand corrected. I wasn't aware that DM accepts non-alphas. Thanks for the info. 8^)

Here's more code:
DEFINE FUNCTION FN1(MYINPUT/A3)
MYLEN/I3 = ARGLEN(3, MYINPUT, 'I3');
MYVAL/D3 = ATODBL(MYINPUT, '03', 'D3');
FN1/A40 = 'MYINPUT:{' | MYINPUT | '} MYLEN:' | EDIT(MYLEN) | ' MYVAL:' | EDIT(MYVAL);
END
-RUN
-TYPE These results are expected.
-SET &INVAL1 = '1 ';
-SET &OUTVAL1 = FN1(&INVAL1);
-TYPE &OUTVAL1
-SET &INVAL2 = '12 ';
-SET &OUTVAL2 = FN1(&INVAL2);
-TYPE &OUTVAL2
-SET &INVAL3 = '123';
-SET &OUTVAL3 = FN1(&INVAL3);
-TYPE &OUTVAL3
-SET &INVAL4 = '1234';
-SET &OUTVAL4 = FN1(&INVAL4);
-TYPE &OUTVAL4
-TYPE These results are undesirable.
-SET &INVAL5 = '1';
-SET &OUTVAL5 = FN1(&INVAL5);
-TYPE &OUTVAL5
-SET &INVAL6 = '12';
-SET &OUTVAL6 = FN1(&INVAL6);
-TYPE &OUTVAL6
-TYPE These show the effects of the previous 2 using .EVAL
-SET &INVAL7 = '1';
-SET &OUTVAL7 = FN1(&INVAL5.EVAL);
-TYPE &OUTVAL7
-SET &INVAL8 = '12';
-SET &OUTVAL8 = FN1(&INVAL6.EVAL);
-TYPE &OUTVAL8
-TYPE These show the effects using string literals
-SET &OUTVAL9 = FN1('1');
-TYPE &OUTVAL9
-SET &OUTVAL0 = FN1('12');
-TYPE &OUTVAL0

And here are my results:
These results are expected.
MYINPUT:{1 } MYLEN:001 MYVAL:001
MYINPUT:{12 } MYLEN:002 MYVAL:012
MYINPUT:{123} MYLEN:003 MYVAL:123
MYINPUT:{123} MYLEN:003 MYVAL:123
These results are undesirable.
(FOC36335) PARAMETER LENGTH CONFLICT IN FUNCTION "FN1 ", ARG 1.
MYINPUT:{1} MYLEN:003 MYVAL:000
(FOC36335) PARAMETER LENGTH CONFLICT IN FUNCTION "FN1 ", ARG 1.
MYINPUT:{12} MYLEN:003 MYVAL:000
These show the effects of the previous 2 using .EVAL
MYINPUT:{1 } MYLEN:001 MYVAL:001
MYINPUT:{12 } MYLEN:002 MYVAL:012
These show the effects using string literals
MYINPUT:{1 } MYLEN:001 MYVAL:001
MYINPUT:{12 } MYLEN:002 MYVAL:012


kiddpete, I never claimed that Fortran understands everything in terms of alpha. If you'll notice, I started a new paragraph, which means I was starting a new idea. The "This goes for every language..." refers to the fundamental programming language concepts which follow in that paragraph.

You'd like to know where .EVAL came from? Here's a quote from the WF help files about .EVAL syntax:
[&]&variable.EVAL
where:
variable - Is a local or global variable.
When the command procedure is executed, the expression is replaced with the value of the specified variable before any other action is performed. Without the .EVAL operator, a variable cannot be used in place of some commands.

I'm not using the variable in place of any commands in this case, but it does help my situation. As you can see from my results and the explanation below, WebFOCUS (a call-by-reference language) is creating a new memory location upon execution of the function call when .EVAL is used. Looking at cases 5, 7, & 9 will demonstrate this. When the DM variable containing '1' is passed to the function without first evaluating it, there is a PARAMETER LENGTH CONFLICT warning. Whatever undesirable information is in the 2 bytes of memory after that variable are appended to the desired value of '1'. In most cases, it was a null character, but I did get some other garbage at times. For this reason, CTRAN(3, MYINPUT, 0, 32, 'A3') didn't work as a general solution. It only converted null characters to spaces.

Consequently, I was forced to pursue a different solution. I apologize for pursuing this solution without your consent, kiddpete. Actually, I tried a few different solutions, and .EVAL seemed to be the most robust. It takes advantage of the fact that call-by-reference languages don't always pass function parameters by reference.

In the case of a string literal (or using a .EVAL), the function call creates a new memory location of the correct length (in this case 3 bytes), and copies the value into that location. The result is a variable in the function which:
1) is the desired length
2) contains the desired value, exactly the desired value, and nothing but the desired value
3) behaves desirably.

In summary, .EVAL works for my purposes. I have a solution, and do not need further assistance on this topic. I cannot attest to the functionality of this solution for non-WebFOCUS environments nor for non-WF5.2.1 environments, for that matter.

Thank you for your help.
Regards,
Michael
 
Michael,

I'm sorry you didn't learn much from the exercise. I get a little testy when someone is blaming Focus and IBI for their own lack of understanding.
 
kiddpete,

Let's call a truce.
It's apparent to me that we're both highly intelligent people trying to outdo each other, and it's getting us nowhere fast. I am a firm believer that no single person can know everything about any given topic, and we've seen hints of that in this thread.

I thank you for your input, and hope that I can apply it to my further projects.

Respectfully,
Michael
 
Just for clarification, ALL variables in Dialogue Manager ARE stored as alpha strings. When used, if the 'context' is a numeric function, the strings are converted to double precision, the computation done, and the result converted back to alpha containing digits. That's why, if you do the following:

-SET &VAL1 = 3;
-SET &VAL2 = 2;
-SET &VAL3 = &VAL1/&VAL2;

The value of &VAL3 is 1. For the division, the strings are converted to 'D' format, and the computation done. Then the result is converted BACK to an alpha integer (since we don't know how many decimal places to use). This is also why the following works:

-SET &VAL1 = 3;
-SET &VAL2 = 2;
-SET &VAL3 = IF &VAL1/&VAL2 EQ 1.5 THEN 'YES' ELSE 'NO';

Sets &VAL3 to 'YES', because the comparison is ALSO done in double precision.

When dealing with subroutines,we don't KNOW what format the subroutine expects. So, if the variable LOOKS like a numeric (&field.TYPE is 'N'), we automatically convert to double precision.

Historically, when passing a string, we pass a length, so we know whewre it ends. With numerics, we always pass as 'D' format, so we know it's 8 bytes.
 
If I recall correctly, some subroutines do not return alpha numbers when used in DM. If so, you need to be aware of these and compensate. I don't remember which ones do this, but I'm pretty sure I have observed this in the past.
 
That's why, in the Dialogue Manager, the LAST argument is specified as the 'format' of the return argument, so we know what is being returned, and can convert to the appropriate alpha string.
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top