There's an interesting blog post by Dennis Vorst gone up on the Uniface.info blog, titled Where to put your code. As the title suggests, it's about which places are best for different types of code, based on their content.
There are a number of options; conceptual at the entity or field level, locally at the entity or field level, within the component, a local procedure or a global procedure. All of these have their merits.
I've posted my response, defending the mighty global procedure, but why don't you have a read and add your opinion in the comments?
Summary: Check out this blog post... http://unifaceinfo.com/where-to-put-your-code
Showing posts with label entity. Show all posts
Showing posts with label entity. Show all posts
Friday, 24 July 2015
Tuesday, 17 June 2014
What are handles and should I be using them?
The Uniface manuals describes a handle as...
If you're used to pointers, you may at first glance think that these are the same, but they're not. A pointer contains the address of an item in memory and is therefore fixed, whereas a handle is an abstract reference, which means the address can be changed without breaking the reference. This makes them very useful.
In Uniface you can have "public" or "partner" handles, which allow you to define which types of operations can access them. They are also datatypes available for local, component and global variables, depending on the scope required. I'm not going to discuss either of these points in detail, but I think it's handy to know.
As the description in the manual states, you can create a handle that references most Uniface objects, and there are different methods for creating these handles: Component instances ($instancehandle), entity collections ($colhandle), entity occurrences ($occhandle), fields ($fieldhandle) and also OCX controls ($ocxhandle).
I'm going to focus on component instances. More specifically, I'm going to focus on creating new component instances, using newinstance and handles.
As you might expect, creating a new instance of a form has a little lead time. Using a handle doesn't stop this lead time, but what it does do, is mean that it only has to happen once. This means that using a handle for a single form activation is not going to give you any advantage (unless you just love handles!) but when calling operations on the same form multiple times, handles could give you a performance improvement.
This is something like how you could activate a form...
activate "TEST_FORM".test_oper("Test")
In fact this does an implicit newinstance as well (if it can't already find an instance with the same name as the form) and so explicitly your code could look like this...
If you were to replace this with a handle then you may use something like this...
Notice that in this case we have used a handle instead of an instance name in the newinstance call, and then we can use this handle in place of the activate command. The -> is referred to by Uniface as the "dereference" operator, and is processed with a precedence of 2 (with only field indirection being higher).
So let's try these two different ways of activating an operation (in this case a very simple operation that simply returns out immediately). The newinstance command is done before the loop, and the activate (or equivalent handle call) is done inside the loop, 200,000 times...
As you can see, using a handle is marginally quicker, although you'd have to be making a lot of calls before you really noticed the difference.
I thought that this would be even more useful when the handle references a component instance that is running on another server, such as when a form is mapped to run elsewhere, as the I thought the initial creation of the new instance would take longer here. So I ran the same operation 20,000 times (after mapping the form)...
Not much difference here either. Having said that, maybe on a chatty network the difference would be greater.
Please note: You must return out of the operation that you call, if you exit out then this will destroy the handle and future calls to the operation will fail with a $status of -1. To clean up a handle you can either set it to 0 or "" (empty string).
Summary: Handles are abstract references to pretty much any Uniface object. They can be useful, especially if you want to call the same object multiple times, but they don't seem to have the performance benefits that I was expecting, not when it comes to component instances anyway.
A handle is a reference to an object, such as a component instance, entity, occurrence, or field. Handles can be used to call operations on the referenced object, or to pass these objects as variables and parameters.
If you're used to pointers, you may at first glance think that these are the same, but they're not. A pointer contains the address of an item in memory and is therefore fixed, whereas a handle is an abstract reference, which means the address can be changed without breaking the reference. This makes them very useful.
In Uniface you can have "public" or "partner" handles, which allow you to define which types of operations can access them. They are also datatypes available for local, component and global variables, depending on the scope required. I'm not going to discuss either of these points in detail, but I think it's handy to know.
As the description in the manual states, you can create a handle that references most Uniface objects, and there are different methods for creating these handles: Component instances ($instancehandle), entity collections ($colhandle), entity occurrences ($occhandle), fields ($fieldhandle) and also OCX controls ($ocxhandle).
I'm going to focus on component instances. More specifically, I'm going to focus on creating new component instances, using newinstance and handles.
As you might expect, creating a new instance of a form has a little lead time. Using a handle doesn't stop this lead time, but what it does do, is mean that it only has to happen once. This means that using a handle for a single form activation is not going to give you any advantage (unless you just love handles!) but when calling operations on the same form multiple times, handles could give you a performance improvement.
This is something like how you could activate a form...
activate "TEST_FORM".test_oper("Test")
In fact this does an implicit newinstance as well (if it can't already find an instance with the same name as the form) and so explicitly your code could look like this...
newinstance "TEST_FORM","TEST_FORM"
activate
"TEST_FORM".test_oper("Test")
If you were to replace this with a handle then you may use something like this...
newinstance "TEST_FORM",theHandle
theHandle->test_oper("Test")
Notice that in this case we have used a handle instead of an instance name in the newinstance call, and then we can use this handle in place of the activate command. The -> is referred to by Uniface as the "dereference" operator, and is processed with a precedence of 2 (with only field indirection being higher).
So let's try these two different ways of activating an operation (in this case a very simple operation that simply returns out immediately). The newinstance command is done before the loop, and the activate (or equivalent handle call) is done inside the loop, 200,000 times...
- Using activate: 2.56, 2.61, 2.60 (about 2.6 seconds)
- Using a handle: 2.39, 2.35, 2.39 (about 2.4 seconds)
As you can see, using a handle is marginally quicker, although you'd have to be making a lot of calls before you really noticed the difference.
I thought that this would be even more useful when the handle references a component instance that is running on another server, such as when a form is mapped to run elsewhere, as the I thought the initial creation of the new instance would take longer here. So I ran the same operation 20,000 times (after mapping the form)...
- Using activate: 13.39, 14.35, 12.99 (about 13.5 seconds)
- Using a handle: 13.56, 13.12, 12.91 (about 13.0 seconds)
Not much difference here either. Having said that, maybe on a chatty network the difference would be greater.
Please note: You must return out of the operation that you call, if you exit out then this will destroy the handle and future calls to the operation will fail with a $status of -1. To clean up a handle you can either set it to 0 or "" (empty string).
Summary: Handles are abstract references to pretty much any Uniface object. They can be useful, especially if you want to call the same object multiple times, but they don't seem to have the performance benefits that I was expecting, not when it comes to component instances anyway.
Labels:
$colhandle,
$fieldhandle,
$instancehandle,
$occhandle,
$ocxhandle,
abstract,
activate,
component,
entity,
exit,
field,
handle,
instance,
newinstance,
occurrence,
partner,
performance,
public,
return,
uniface
Friday, 20 July 2012
Types of for loops - part two
In part one I talked about the forlist statements and how these could be used to iterate through Uniface lists for easily. The next thing I want to talk about is looping through entity occurrences. Personally I've always done this using a combination of setocc and $curocc, something like this...
setocc "ent",1
while ( $status > 0 )
;do something
setocc "ent",$curocc(ent)+1
endwhile
This has the advantage of not needing to use any variables for the loop. However, it may be better for performance if a similar loop was used, but using variables to control it...
Another alternative would be to use the new statement forentity, which was also added in Uniface 9.5...
count = 0
stat = $hits(ent)
while ( count < stat )
count = count+1
setocc "ent",count
;do something
endwhile
Another alternative would be to use the new statement forentity, which was also added in Uniface 9.5...
forentity "ent"
;do something
endfor
As you can see, the code is much more concise. There is no need to initialise the count variable or $status, everything is done as part of the forlist statement, and the incrementing and extracting are done automatically. The "count" variable is optional, if you don't need it then you don't need to include it.
So let's test these three blocks of code over 65,000 iterations...
- while ($curocc) = 00:00.47, 00:00.47, 00:00.47 (about half a second)
- while (variables) = 00:04.62, 00:04.25, 00:05.10 (about 5 seconds)
- forentity = 00:00.37, 00:00.34, 00:00.35 (about a third of a second)
As you can see, the new forentity is more concise
code and also performs better, fairly significantly over some alternatives.
I was surprised by how slow the
second method was compared to the first. The only explanation I have for
this is that $hits is taking a long time to complete the hit-list before
the loop starts, rather than completing the hit-list as it goes through the
loop. Turns out I’ve been doing it a pretty efficient way all along, but
I like the simplicity of the new forentity statement. Which leads me to an almost identical summary as in part one.
Summary: Whilst I have previously always used while loops, I shall now be considering switching the forentity loops, for iterating through entity occurrences.
Wednesday, 30 May 2012
Undocumented feature - error stack
This is something which has previously never been documented by Compuware. In the Uniface 9.5 manuals it is used in a code example, but isn't explained at all.
We first found it in 2008, when we were on Uniface 9.2. I've called it an "error stack", because that's the context we use it in, but really it's accessing the process stack, so you can determine where you are within the code at runtime. And it's as simple as this...
stack = $proccontext("STACK")
I could just leave it there, but that seems a little mean, so I'll continue :)
At this point the "stack" variable is populated with a Uniface list. Each item in this list contains another list, which contains the following items...
- TYPE - The type of item on the stack ("TRG" for trigger, for example)
- INSNAM - The instance name of the form running (or application shell)
- TRIG - The trigger of the form running ("execute", for example)
- ENTITY - The entity name of the item (if you're in an entity or field trigger)
- FIELD - The field name of the item (if you're in a field trigger)
- MODEL - The model name of the item (if you're in an entity or field trigger)
- MODNAM - The name of the item (the entry or global procedure name, for example)
- LNR - The line number within the item
- LIN - The line of code itself
So you can loop through this list (forwards if you want it like a stack, or backwards if you want it to be more readable) to determine how you managed to end up where you currently are, down to the line number. This is really useful for outputting when errors occur, especially in global procedures, as you can see what form was running at the time, right back to the application shell.
Thanks to Mark R for telling me about this one!
Summary: It is possible to determine exactly where you are within code at runtime, which is really useful for error messaging.
Labels:
$proccontext,
entity,
error,
field,
instance,
line,
model,
runtime,
stack,
trigger,
undocumented,
uniface
Subscribe to:
Posts (Atom)