Diese Seite mit anderen teilen ...

Informationen zum Thema:
Forum:
WinDev Forum
Beiträge im Thema:
25
Erster Beitrag:
vor 1 Jahr, 1 Monat
Letzter Beitrag:
vor 4 Monaten
Beteiligte Autoren:
Ola, Donald Montaine, Alexandre Leclerc, Bart VDE, steve erts, André Labuschagné, DerekT, Arekusei Timakobu, Danny Lauwers, Paul Turner, GuenterP

[WD21] Internal procedures - very disappointing!

Startbeitrag von Ola am 07.10.2016 10:05

Hi all,

I was rather enthusiastic to hear that Windev will -- at last! -- have internal procedures within procedures. After all, I had used them extensively in my good old Clarion Professional Developer days. In CPD they were called Routines. With Windev I have been forced to do without it them, which has made coding more difficult, slower and uglier.

The Internal procedure was one of the major "killer features", that urged me to "upgrade" from WD19.

Now I have tried to use them a few of times, and it has been very disappointing. The worst thing is that in the internal procedure you cannot use the parameters given to the main procedure! And this makes the internal procedures practically useless.

And when I combine to this the general worse quality and instability I have experienced with WD21, I feel that I have been badly cheated by PCSoft. My money was good, but their product is [beep].

Best regards
Ola

Antworten:

I don't understand what are you talking about. I use internal procedures and I didn't have problems with them. The internal procedure is usable only inside process in which it declared. All local variables declared in the same process (and in initial code of window) are visible for it. So what is you problem?

von Arekusei Timakobu - am 07.10.2016 13:12
Hi,

I also use internal procedures without a problem !!
Could you give us example code of what you are trying to do ?

Danny

von Danny Lauwers - am 07.10.2016 14:25
Internal Procedures work great for me too. Post some code so we can see where your problem lies...

von Paul Turner - am 07.10.2016 22:32
I am in the process of removing internal procedures. I have code that has worked fine for months. I have moved some code blocks into internal procedures to simulate top down structured programming techniques.

Upon moving the code into an internal procedure, it will randomly throw an error after running for hours and being called repeatedly. These errors have shown up in calls to hWrite(), API(), and procedures declared global to the whole project. In all cases the error indicates that an improper parameter was passed to the procedure. In many cases, but not all, these parameters are hard coded, not in variables. Again, it is not that the procedure will fail immediately, but after several hours where it has been called successfully and then suddenly fails.

von Donald Montaine - am 08.10.2016 00:33
Hi all

The problem is the parameters given to the (main) procedure by reference. The (main) procedure's parameters cannot be used in the internal procedure (IP).

Of course you can first move the parameters to local variables and then use them in the IP, but that is an unnecessary complication.

So you cannot change the value of the parameters.

I haven't yet tested how it works, If I change a local variable in the IP and then move the local variable back to the parameter in the (main) procedure's code, but that would be another unnecessary complication.

It seems that the internal procedures are somehow handled differently from the main procedure's code. IMO they shouldn't. IP calls should be handled just like any other code in the procedure, for instance like the GOTO command, but with automatic return to behind the place where they were called from.

I don't see what are the benefits of blocking the use of the parameters in the IP other than complicating things? Maybe there are some? I'd like to know.

Example:


Procedure MyProc(parameter1)

Blaah
IF Parameter1 = Something THEN
GosubIP1
ELSE
GosubIP2
END
Blaah
Return

Internal Procedure GoSubIP1
Blaah
Parameter1 = Blaah //Creates error: "A parameter by reference cannot
//be used in an internal procedure..."!
Blaah
END //Internal procedure 1

Internal Procedure GoSubIP2
Blaah
Parameter1 = BlaahBlaah //Creates error: "A parameter by reference..."!
Blaah
END //Internal procedure 2


Best regards
Ola

von Ola - am 08.10.2016 08:57
Ola
Had not noticed this - just tried and found that what you say here is correct.

The issue however is always going to depend on your programming style I guess.
Checking through my Internal Procedures (I have 20+) I obviously found no instances where I had attempted to use the passed params - indeed I rarely pass params to a local procedure.

If, as you seem to be suggesting, I needed to update the params to return to the calling window I would use be using global variables on the window (or getters,setters if a class) to handle this.

von DerekT - am 08.10.2016 11:37
Hi DerekT

Yes, there are always ways to get done what needs to be done, but in this case they again are just workarounds, just complicating things. And of course there are simple cases where this limitation does not apply.

When I first tried the WD internal procedures, they all were cases where I needed to update the parameter, and I thought, what the... ...again just another half-baked feature added only to fill the sales brochure!

Best regards
Ola

von Ola - am 08.10.2016 14:17
Hi Donald,
to me this smells like a) a "memory leak" or b) a non-threaded and thus unprotected re-calling of a procedure in too short a time interval. Both maybe even caused by WD-code and not by yours. First I'd look whether memory usage is growing in order to be able to judge about the reason ...

von GuenterP - am 08.10.2016 15:53
I solved most of my problems by removing the Internal Procedures. I was not passing parameters, just moving sections of code into the internal procedures to structure the code better (in effect using them as a GOSUB). They were always calls to Windev procedures such as hWrite() and as I said only happened after several hours of use.

The API() problem was more difficult. And since the API was called in a loop perhaps 100 times per second, and was causing a GPF in the OS, I guessed that it was something like a memory leak. Again, this would happen about once every 2 hours.

I reduced the frequency of the calls by 80% and it did not help. I tried adding threadpause(1) and it made the code unusable. I changed the threadpause(1) to wait(1,waitmouseandkeyboard) and it seems to have solved the problem. I know that using wait() in threads is not recommended. But in this case it worked.

I know that PCSoft wants to increase sales in the US. However, the IT culture here is rather brutal toward products that do not have adequate QC or at least the availability of a list of known problems. Releasing features as production that eventually get fixed in releases two or three years later is not a good way to get market penetration here.

The calls to an external API may very well not be a problem with Windev. But Windev procedure call failures and the undocumented problems with passing of parameters should have been caught and at least documented in the help files before the feature was released.

von Donald Montaine - am 08.10.2016 16:43
Except when Windows throws a GFE, the error message I am getting is shown below. The line of code causing this error is:
API(USBDLL$Name,"USBm_WriteB",BBDevice1,bb$$Svc)

Where USBDLL$Name and bb$$Svc are constants and BBDevice1 is an integer variable. The call will work for an hour or two and then throw the error. wd201vm.dll is always the Windev module throwing the error.

-------------------------------

08/10/2016 09:38:51:60 Program Restarted - Error Condition - GP_Error - Error Description Follows
WL call:
Process of 'Global Procedure GP_ButtonBlockThread' (GlobalProcedures.GP_ButtonBlockThread), line 45, thread 0
'API' function, syntax 0

What happened?
'

Error code: 2803
Level: fatal error

System error code: 127
System error message:
The specified procedure could not be found.

Dump of the error of 'wd210vm.dll' module (21.0.317.0).
Identifier of detailed information (.err): 2803
Debugging information:
Fonction (0,90)
Additional Information:
EIT_PILEWL :
Global Procedure GP_ButtonBlockThread (GlobalProcedures.GP_ButtonBlockThread), line 45
EIT_DATEHEURE : 08/10/2016 09:38:51
EIT_TYPE_WDFILE :
EIT_IDCODE :

von Donald Montaine - am 08.10.2016 17:51
I have gone through several testing loops to make my project more stable.

I have removed internal procedures.
I have added delays to loops in threads to slow down how fast they cycle.
I have reduced the calls the API procedures that write values so that they happen once every 10 cycles.
I have gone through the project and wrapped every api() call to the third party DLL in

WHEN EXCEPTION IN ...
DO
ExceptionEnable()
END

since it doesn't matter if the IO handled in the loop is delayed.

I have also wrapped an hWrite() call in the same type of exception code.

Each of these steps has reduced the frequency of exceptions causing a program hald. The last test lasted 6 hours and there were no API() call exceptions that weren't handled in the program.
I did add the hWrite() exception code as that one showed up after I had fixed the API() exceptions. Continuing to test, making progress.

von Donald Montaine - am 09.10.2016 05:04
Hi all!

Another serious problem with WD21's internal procedures (IP) is that you cannot call another IP from inside an IP.

Now how stupid is that limitation?!?!?!?

I wonder whether the IP problems/limitations have been fixed in WD22?

Best regards
Ola

von Ola - am 01.07.2017 08:37

Re: [WD21] Internal procedures - very promising!

Hi Ola,

There is an easy way to avoid the limitation of internal procedure not being able to call another internal procedure. Just create a procedure variable and call the other internal procedure.


PROCEDURE Main()

INTERNAL PROCEDURE InternalProc1()
END

pProc1 is Procedure = InternalProc1

INTERNAL PROCEDURE InternalProc2()
pProc1()
END


As for the "limitation" of using parameters from the Main() procedure inside the local procedure this is not a real limitation. I think it is due to the support of "Function Closures" for the internal procedures. https://en.wikipedia.org/wiki/Closure_(computer_programming)

If you declare your procedure and internal procedure as so:


PROCEDURE Main(sMsg is string)

INTERNAL PROCEDURE Display()
Info(sMsg)
END

Display()


...this will fail. But if you declare your arguments as local variables, this will work just fine:


PROCEDURE Main(LOCAL sMsg is string)

INTERNAL PROCEDURE Display()
Info(sMsg)
END

Display()


What is the advantage of closures?
- If you use an internal procedure as a callback function, all the variables in the main procedure will still keep their values (pointers, etc). Very useful.
- This also means that local variables are persistant.

There was a recent blog post from PCSoft on this topic (in French) : http://blogs.pcsoft.fr/fr/wlangage-permet-exploiter-principe-closure-grace-procedures-internes-bon-savoir/281474976710679/read.awp

Here is an example of what closures allows you to do:


PROCEDURE GetCounterProc(LOCAL nCounter is int = 0)

INTERNAL PROCEDURE Counter(LOCAL nAdd is int = 1)
nCounter += nAdd
Trace(nCounter)
END

pCounter is Procedure = Counter
RESULT pCounter


// Usage:
pCounter is Procedure = GetCounterProc()
pCountAgain is Procedure = GetCounterProc()
pCounter() // Trace: 1
pCounter() // Trace: 2
pCountAgain(5) // Trace: 5
pCounter(2) // Trace: 4
pCountAgain() // Trace: 6
pSuperCount is Procedure = GetCounterProc(10)
pSuperCount() // Trace: 11
pCounter() // Trace: 5


Nice stuff.

Here is a last example:

PROCEDURE ParseOneCharAtTheTime(LOCAL s is string) : Procedure

n is int
nMax is int = Length(s)

INTERNAL PROCEDURE Parse() : string
n++
IF n>nMax THEN
RESULT EOT
ELSE
RESULT s[[n]]
END
END

p is Procedure = Parse
RESULT p


// Usage:
pParse is Procedure = ParseOneCharAtTheTime("This is a test")
s is string = pParse()
WHILE (sEOT)
Trace(s)
s = pParse()
END


So this is probably one explication as to why only local parameters can be used in internal procedures. I'm not an expert in this field.

Best regards!
Alexandre Leclerc

von Alexandre Leclerc - am 05.07.2017 13:17

Re: [WD21] Internal procedures - very promising!

Hi Alexandre,

Thank you for the lesson. I appreciate it!:spos:

1. Now I know how to call an internal procedure from another internal procedure.

2. So using only local parameters in a procedure allows to use these parameters also in the internal procedures. But the "closure" concept flies a little high for me. I have never missed it, and I wonder whether it's good enough reason to block using non-local parameters in internal procedures...

And what if I need to change a parameter's value so that is changes also in the calling procedure? Do I then have to use global variables?

Best regards
Ola

P.S. It seems that I am, still today, so totally spoiled by the problem- and bug-free Clarion CPD language, which I used for over 20 years before committing myself to Windev, that I sometimes find it difficult having to accept all the limitations that Windev is putting in my way.

von Ola - am 07.07.2017 08:24
Hi Ola

Coming from a variety of other languages including Clarion I have to say that in my experience if you think that WX cannot do it then you are probably not looking hard enough or looking in the wrong place.

Cheers
André

von André Labuschagné - am 08.07.2017 07:36
Hi André,

You are right, and this is my experience too. I also played around with some languages before finding the DOS-goldnugget of the mid-eighties, Clarion CPD.

Too often Windev makes me work harder than some other languages, eg. Clarion CPD, simply because it is more complicated, and the complications and the solutions to them are not documented clearly enough in the help system and sample codes.

Let's take for instance the problem of not being able to call an internal procedure from inside another internal procedure: I don't know whether this limitation existed in Clarion CPD. If it did, it was handled internally by the language. I did not have to know if there is some kind of a problem. Clarion handled it for me. All I had to do was to code straightforward what I wanted to do, and not pay any attention to trivial low-level problems of the lower level languages.

In Windev I have to know myself how to handle this unnecessary extra complication, and this is something I am not expecting from a modern "4th generation" language.

I may not always be not looking hard enough, but I am looking in the right place, this forum:-).

So thanks to all.

Best regards
Ola

von Ola - am 08.07.2017 10:51

Re: [WD21] Internal procedures - very promising!

Hi Ola,

Quote
Ola
And what if I need to change a parameter’s value so that is changes also in the calling procedure? Do I then have to use global variables?


This is a good question. In fact I do not know how to answer because I have difficulty to imagine a case where I would need such a behaviour. I might lack imagination here. For me, functions are used for straightforward calls. When I need more complex behaviour with parameters that can change indirectly, I use class objects. Changing a member or property then influences the behaviour of methods (or internal functions).

You could mimic a class object using a structure and achieve what you describe, but for me it becomes a little bit too abstract (complex) for my poor brain, but can be very efficient in some use case. A guy made a blog post on this subject (in French, again) using a structure element to store two procedure variables linked to internal procedures. Such a complex assembly would allow, in your example, a way to have a function that changes the values of a parameter. http://blog.ytreza.org/windev-place-aux-pseudo-classes/

On this, I agree with you that some stuff could be handled by WinDev itself at lower levels. (Like an internal procedure calling an internal procedure. That should work.) I sent couple suggestions in the past for other behaviour like that to PC Soft. So feel free to send the suggestion! Who knows, in version 23 it might be possible!

Best regards,
Alexandre

Edit 1: Typo.

von Alexandre Leclerc - am 11.07.2017 12:50

Re: [WD21] Internal procedures - very promising!

Hi Alexandre,

I also have problems with some high-flying programming concepts:-)

Where would I need to be able to change a main procedure's non-local parameter in an internal procedure?

Relaying a text message back to the caller would be a typical case, for problem reporting or logging etc...

Two simple solutions come to my mind, when it needs to be done but cannot be done because of the limitation in Windev's internal procedures:

1. a global variable
2. a return value from a function, containing several variables if necessary.

But it's an unnecessary complication.

Concerning suggestions to Windev: Internal procedures, like "routines" in Clarion CPD, was actually one of my suggestions to them. I kept noise about it for several years. Had they been wise, they would have checked how Clarion does it and then copy it the best they could. But no, that was too simple and straightforward for them, so they decided to climb to the tree with their ass first:eek:

Best regards
Ola

von Ola - am 11.07.2017 15:20

Re: [WD21] Internal procedures - very promising!

Hi Ola,

Not the best way to climb a tree, I agree. ;-)

For a string message to be passed back to a caller, well, it depends how you implement. But I understand better what you are speaking about now. For that kind of stuff I would have used a classe that collect error messages. So the internal procedure (or a class method) would simply receive the message and log it into the class object.

If you want to pass the "parameter" as reference so to return data right into it with a given event, then you need a different design. In that case, a solution is again to use a class object. You then pass the reference of the dynamic object to the procedure and the internal procedure use the object to return the message. Then even if it is a local variable, the internal procedure can interact with the object (pass the error string) and since it is still the same object (because you work with pointers) the caller of your proc would also have the data. (I hope you follow me there.)

This also means that if the original calling code is now completed and out of memory, the callback using the internal procedure will not fail because the object will be persistant in memory until there is a valid pointer reference.

The following example is useless, but demonstrate that even if a context is no more existent, an object will still work and it is passer by reference because it is a pointer (dynamic). To make an intelligent exemple, I should declare the "obj" in a context that would be more persistent so that I can check the object later for errors or results. But this is just to illustrate. In this example we understand that a simple text string would not work even if it was allowed for an internal procedure to use "by reference" parameters from the parent procedure. The original calling context does not exist anymore. But with an object, this still works even if the original context is gone.


// On Button click:
obj is MyClass
MyProc(obj)
// At this point the "OnButton click code and all its local variables no more exist, except the "obj" because we passed a reference.

PROCEDURE MyProc(LOCAL obj is MyClass dynamic)
IF obj Null THEN
MakeLongCheckInThread(obj,Callback)
END
INTERNAL PROCEDURE Callback(sMsg is string)
obj.CatchMsg(sMsg)
END


But as you said, using a global variable is a simple effective solution, depending of your coding style!

Best regards,
Alexandre Leclerc

von Alexandre Leclerc - am 11.07.2017 18:07

Re: [WD21] Internal procedures - very promising!

Hi Alexandre

Thanks again for a good explanation. You should write a Windev book.
But I'm an old-fashioned procedural thinker, and all the OOP stuff is a
bit too high-flying for me, so I'll stay on the simple side of the solutions:-).

Best regards
Ola

von Ola - am 12.07.2017 10:50
Did not test this, but what if you do it this way, does it creates an error too ?

Procedure MyProc(parameter1)

Blaah
IF Parameter1 = Something THEN
GosubIP1(parameter1)
ELSE
GosubIP2(parameter1)
END
Blaah
Return

Internal Procedure GoSubIP1(aParameter)
Blaah
aParameter = Blaah
// Parameter1 = Blaah //Creates error: "A parameter by reference cannot
//be used in an internal procedure..."!
Blaah
END //Internal procedure 1

Internal Procedure GoSubIP2(aParameter)
Blaah
aParameter = Blaah
//Parameter1 = BlaahBlaah //Creates error: "A parameter by reference..."!
Blaah
END //Internal procedure 2

von Bart VDE - am 12.07.2017 11:15
Hi Bart,

Yes, that seems to work!:spos:

Best regards
Ola

von Ola - am 15.07.2017 11:18
Hi all

There is one more disappointment in the internal procedures:(.

F2 is a very usable and efficient function key in the code editor; put the cursor on a procedure name, press F2, and voilà, you're in that procedure:hot:.

Exept if that procedure happens to be an internal procedure, then F2 cannot jump into it:rolleyes:

Best regards
Ola

von Ola - am 19.07.2017 17:14
Quote
Ola
Hi all

There is one more disappointment in the internal procedures:(.

F2 is a very usable and efficient function key in the code editor; put the cursor on a procedure name, press F2, and voilà, you're in that procedure:hot:.

Exept if that procedure happens to be an internal procedure, then F2 cannot jump into it:rolleyes:

Best regards
Ola


I never knew about that one. Neat!

von steve erts - am 19.07.2017 17:22
Zur Information:
MySnip.de hat keinen Einfluss auf die Inhalte der Beiträge. Bitte kontaktieren Sie den Administrator des Forums bei Problemen oder Löschforderungen über die Kontaktseite.
Falls die Kontaktaufnahme mit dem Administrator des Forums fehlschlägt, kontaktieren Sie uns bitte über die in unserem Impressum angegebenen Daten.