Access Cookbook, 2nd Edition [Electronic resources] نسخه متنی

اینجــــا یک کتابخانه دیجیتالی است

با بیش از 100000 منبع الکترونیکی رایگان به زبان فارسی ، عربی و انگلیسی

Access Cookbook, 2nd Edition [Electronic resources] - نسخه متنی

Ken Getz; Paul Litwin; Andy Baron

| نمايش فراداده ، افزودن یک نقد و بررسی
افزودن به کتابخانه شخصی
ارسال به دوستان
جستجو در متن کتاب
بیشتر
تنظیمات قلم

فونت

اندازه قلم

+ - پیش فرض

حالت نمایش

روز نیمروز شب
جستجو در لغت نامه
بیشتر
لیست موضوعات
توضیحات
افزودن یادداشت جدید










Recipe 7.2 Create a Global Procedure Stack



7.2.1 Problem


When you're writing an
application, you often need to know the name of the current procedure
from within your code. For example, if an error occurs,
you'd like to be able to have a generic function
handle the error and display the name of the procedure in which the
error occurred (and all the procedures that have been called on the
way to get there). VBA doesn't include a way to
retrieve this information. How can you accomplish this?


7.2.2 Solution


By maintaining a list of active procedures, adding the current name
to the list on the way into the procedure and removing it on the way
out, you can always keep track of the current procedure and the
procedure calls that got you there. There are many other uses for
this functionality (see the next solution, for example), but one
simple use is to retrieve the name of the current procedure in a
global error-handling procedure.

The
kind of data structure you'll need for maintaining
your list is called a

stack . As you enter a new
procedure, you "push" its name onto
the top of the stack. When you leave the procedure, you
"pop" the name off the stack. Figure 7-2 shows a graphical representation of a
procedure stack in action. The arrows indicate the direction in which
the stack grows and shrinks as you add and remove items.


Figure 7-2. The call stack and the sample routines to fill it


To see the procedure stack in action,
load

07-02.MDB . Open the module basTestStack in
design mode. Open the Immediate window (choose View
Immediate Window). In the Immediate window, type:

? A( )

to execute the function named A. Figure 7-2 shows A
and the procedures it calls. At each step, the current procedure
pushes its name onto the procedure stack and then calls some other
procedure. Once the calling procedure regains control, it pops its
name off of the stack. In addition, each procedure prints the name of
the current procedure (using the

acbCurrentProc
function, discussed later in this solution) to the Immediate window.
Once all execution has finished, you should see in the Immediate
window output like that shown in Figure 7-3.


Figure 7-3. The output from running the sample procedure


Follow these steps to incorporate this functionality into your own
applications:

  1. Import the module basStack into your application. This includes the
    procedures that initialize and maintain the procedure stack.

  2. Insert a call to the

    acbInitStack subroutine into code
    that's executed when your application starts up.
    Consider adding this procedure call to the code in your main
    form's Load event procedure. You'll
    want to call

    acbInitStack any time you restart
    your program during development, so you probably
    don't want to call it from the Autoexec macro, which
    is executed only when you first load the database. To call

    acbInitStack , either place its name alone on a
    line of code, like this:

    acbInitStack

    or use the Call construct, as follows:

    Call acbInitStack
  3. For each procedure in your application, place a call to

    acbPushStack as the first statement. This
    procedure will place the value it's passed on the
    top of the stack. As the single argument for each call, pass the name
    of the current procedure. Our example places a pair of parentheses
    after function names and nothing after subroutine names, as a matter
    of style. As the last line in each procedure add a call to

    acbPopStack , which will remove the current name
    from the top of the stack.

  4. You can retrieve the name of the currently executing procedure at any
    time by calling the

    acbCurrentProc function.
    This function looks at the top of the stack and returns the string it
    finds there. You can use this as part of an error handler or, as in
    the next solution, to help track procedure performance.



7.2.3 Discussion


The module you imported from

07-02.MDB ,
basStack, includes code for maintaining the procedure stack and a
module-local variable that is the stack itself. There are just six
entry points (nonprivate procedures) in the module. Table 7-1 lists those procedures. Since all the code for
the stack is encapsulated in that one module, you never really have
to know how it all works. However, it's quite
simple.

Table 7-1. The six entry points into basStack

Procedure name


Purpose


Parameters


 acbInitStack


Initialize the stack.


 acbPushStack


Add an item to the stack.


A string to push


 acbPopStack


Remove an item from the stack.


 acbCurrentProc


Retrieve the name of the current procedure.


 acbGetStack


Retrieve a specific item from the stack.


The item number to retrieve


 acbGetStackItems


Retrieve the number of items on the stack.

basStack includes two module-level variables:
mastrStack, the array of strings that is
the stack itself; and mintStackTop, an
integer that holds the array slot into which the next stack item will
be placed. When you begin your work with the stack,
mintStackTop must be 0, so the first item
will go in the slot numbered 0. The

acbInitStack
procedure does nothing other than initialize
mintStackTop:

Public Sub acbInitStack( )
' Resets the stack top to 0.
mintStackTop = 0
End Sub

You can add an item to the stack at any time by calling

acbPushStack . Pass to this subroutine the item
you want pushed. To push the item, the code places the item in the
array at the location stored in
mintStackTop and then increments the value
of mintStackTop. Its code is:

Public Sub acbPushStack(strToPush As String)
' Push a string onto the call stack.
' If the stack is full, display an error.
' Otherwise, add the new item to the call stack.
' Handle the error case first.
If mintStackTop > acbcMaxStack Then
MsgBox acbcMsgStackOverflow
Else
' Store away the string.
mastrStack(mintStackTop) = strToPush
' Set mintStackTop to point to the NEXT
' item to be filled.
mintStackTop = mintStackTop + 1
End If
End Sub

The only problem that might occur is that the stack might be full.
The constant acbcMaxStack is originally set to 20,
which should be enough levels. (Remember that
mintStackTop goes up one only when a
procedure calls another procedure. If you have 20 levels of procedure
calling, you might consider rethinking your application, instead of
worrying about procedure stacks!) If the stack is full,

acbPushStack will pop up an alert and will not
add the item to the stack.

When leaving a procedure, you'll want to remove an
item from the stack. To do so, call the

acbPopStack procedure:

Public Sub acbPopStack( )
' Pop a string from the call stack.
' If the stack is empty, display an error.
' Otherwise, set the current item to be the
' next one to be filled in. If you're logging,
' send the information out to the log file.
' Handle the error case first.
If mintStackTop = 0 Then
MsgBox acbcMsgStackUnderflow
Else
' Because you're removing an item, not adding one,
' set the stack top back to the previous row. Next time
' you add an item, it'll go right here.
mintStackTop = mintStackTop - 1
End If
End Sub

Just as in

acbPushStack , this code first checks
to make sure that the stack integrity hasn't been
violated; you can't remove an item from the stack if
there's nothing to remove! If you try,

acbPopStack will pop up an alert and exit. If
the stack is intact, the procedure will decrement the value of
mintStackTop. Decrementing that value sets
up the next call to

acbPushStack so that it will
place the new value where the old one used to be.

To retrieve the value at the top of the stack without pushing or
popping anything, call the

acbCurrentProc
function:

Public Function acbCurrentProc( ) As String
' Since mintStackTop always points to the next item to
' be filled in, retrieve the item from mintStackTop - 1.
If mintStackTop > 0 Then
acbCurrentProc = mastrStack(mintStackTop - 1)
Else
acbCurrentProc = "
End If
End Function

This function retrieves the value most recently placed on the stack
(at the location one less than
mintStackTop, because
mintStackTop always points to the next
location to be filled). You can't look at
mastrStack yourself, because
it's local to basStackand
that's the way it

ought to be.
Since the details of how the stack works are kept private, you can
replace basStack, using a different architecture for the stack data
structure, and the rest of your code won't have to
change at all.

To retrieve more information about what's in the
stack, you can call

acbGetStackItems , to find
out how many items there are in the stack, and

acbGetStack , which retrieves a specific item
from the stack. For example, write code like this to dump out the
entire stack (see subroutine

D , which does just
this, in the basTestStack module):

Debug.Print "Stack items currently:"
For intI = 0 To acbGetStackItems( ) - 1
Debug.Print , acbGetStack(intI)
Next intI

The

acbGetStackItems function is simple: it
returns the value of mintStackTop, because
that value always contains the number of items in the stack:

Public Function acbGetStackItems( ) As Integer
' Retrieve the number of items in the stack.
acbGetStackItems = mintStackTop
End Function

The

acbGetStack function is a little more
complex. It accepts an item number (requesting item 0 returns the
item at the top of the stack) and calculates the position of the item
to retrieve. Its source code is:

Public Function acbGetStack(mintItem As Integer) As String
' Retrieve the item that's mintItems from the top of the
' stack. That is,
' ? acbGetStack(0)
' would return the same value as acbCurrentProc.
' ? acbGetStack(3) would return the third value from the top.
If mintStackTop >= mintItem Then
acbGetStack = mastrStack(mintStackTop - mintItem - 1)
Else
acbGetStack = "
End If
End Function

For the procedure stack to
work, you have to place calls to

acbPushStack
and

acbPopStack on entry and exit from every
procedure call. Good coding practice supports the concept of only one
exit point from each procedure, but even the best programmer
sometimes breaks this rule. To use the call stack, however, you must
catch every exit point with a call to

acbPopStack . Keep this in mind as you retrofit
old code to use this mechanism and when you devise new code to use
it. You can always code for a single exit point, and you will find
code maintenance much easier if you do.


/ 232