News:

MASM32 SDK Description, downloads and other helpful links
MASM32.com New Forum Link
masmforum WebSite

How to define overloadable proc :?

Started by Kernel, March 20, 2008, 09:12:08 PM

Previous topic - Next topic

Kernel

Hi,

well, I did search, but I didn't know how this is exactly called
.. sry then, if there are several topics about that.

What I need is coding a procedure, which can handle an undefined amount of parameters.
You know like for instance WSPRINTF does:
invoke wsprintf,addr szOutput,addr szFormat,11111111h,22222222h,33333333h,... (x times)
Or was this a bad example, coz the amount is handled by the number of '%' in the end :?
Anyway somehow this function also has to correctly deal with the stack, right ?

SomeProc PROTO :DWORD,:DWORD,??????....

SomeProc proc :DWORD,:DWORD,??????....

    ??????

   (my code)

    leave (?????)
    ret

SomeProc endp


Thank you :)

Kernel

Only now got the idea to look at the proc definition for wsprintf in the user32.inc.. DOOH  :red
You're looking for a longer time, and once you start asking, you find it yourself after some mins  :green

'C' was the thingy !

SomeProc PROTO C :VARARG

SomeProc proc c :VARARG

Ok, the stack prob is solved. But what would be the smartest way to check how many args
have been pushed (e.g. need to be handled) ??

'Thanks again' ^^

jj2007

Quote from: Kernel on March 20, 2008, 10:46:35 PM
But what would be the smartest way to check how many args
have been pushed (e.g. need to be handled) ??
Push a magic 7654321 as last value... or push the number of args as first value.

GregL

Kernel,

I have always just passed another argument with the count.

Here is the example from the "MASM Programmer's Guide" converted to 32-bit code, see "Using VARARG" in Chapter 7.


addup3 PROC C numargs:DWORD, args:VARARG

   xor     eax, eax        ; clear work registers
   xor     edx, edx

   .WHILE  numargs > 0     ; numargs has number of arguments
       add eax, args[edx]  ; args has the first argument
       dec numargs         ; point to next argument
       add edx, 4
   .ENDW

   ret                      ; result is in eax
addup3 ENDP


Kernel

@both:
Thanks for the replies :)

Yea passing the numer of args also came to my mind, coz I can't
'Push a magic 7654321 as last value'... due to needing a possible range
from 0 to FFFFFFFF :\

I was again stepping through 'wsprintf' to find out the trick (coz it doesn't use any numArgs),
but got too confused after the sh!tload of code there.

I thought I could calculate it somehow by substracting [ESP] from EBP+8 or something,
but that would have been to easy I guess :\
If I got LOCALS in the procedure, which calls my c-routine, then such a calculation is messed up already...

Jimg

Here's part of a proc I wrote:

GridMsg proc SYSCALL Public uses esi edi ebx GridHandle:dword,Request:dword,Parms:vararg
    maxparm = 6 ; ignore any excess entries greater than this
    ; locals are saved in reverse order on stack to be in same order as call
    local Parm6,Parm5,Parm4,Parm3,Parm2,Parm1

    mov eax,[ebp+4]  ; get the return address (address after the call in the calling procedure)
    ;  the instruction at the return address will be   ADD ESP,nnnn  where nnnn is number of bytes

    movzx ecx,byte ptr [eax+2]  ; which will contain the number of bytes to remove from the stack
    shr ecx,2   ; convert to dword count
    dec ecx     ; skip 2 fixed arguments before the vararg
    dec ecx
    lea edi,Parm1       ; address of local parms
    lea esi,Parms       ; address of input
    m2m edx,maxparm     ; available parm spots
    .repeat
        xor eax,eax     ; default value for parm
        jecxz usedefault ; check number of input parms left
        lodsd ; pick up next parm
        dec ecx
      usedefault:
        stosd   ; save
        dec edx
    .until Zero?

Works for me, and I don't have to count parms every time I call it.

u

There's no way to know how many parameters were passed.
wsprintf uses the format-string as an indicator of the count.

That's why, generally you should specify a "numArgs" parameter of the proc.
Please use a smaller graphic in your signature.

Kernel

Quote from: Ultrano on March 21, 2008, 06:39:00 AMwsprintf uses the format-string as an indicator of the count.
I saw that all it does is calling 'wvsprintf', but I missed reading about wvsprintf  :red
arglist
   ... The number, type, and interpretation of the arguments depend on
   the corresponding format-control specifications in the lpFmt parameter.


@Jimg:
Thank you ! I will try it :)
One question remains: Is that approach safe enough ?
Coz this function is going to be part of a static library, which I want to release...

Thanks to all

zooba

Jimg,

Nice code :U

Kernel,

Jimg's code is safe enough if a standard calling convention is followed (ie. if invoke is used). If the function is called in a non-standard way (example below) then it may not be robust. It is best to make the caller responsible for telling the function how many parameters they are passing; the most common method is passing an integer as the parameter before the vararg parameter.

Cheers,

Zooba :U

(An example of a useful non-standard calling optimisation is as follows:)
push value2
push value1
push value0
push OFFSET szFormat1
push OFFSET szOutput1
; first call with a particular format string
call wsprintf
mov  [esp+4], OFFSET szFormat2
mov  [esp+4], OFFSET szOutput2
; call again with same arguments but different format string and different output
call wsprintf
add  esp, 4*5

Kernel

Yea it works like a charm for me.
I love that code, coz I gotta deal with up to 20 possible args.
Thanks a bunch  :bg

Jimg

#10
As zooba says, it will break if it isn't called with invoke.  For a general purpose library, there's no way to force the user to do so.
The way I get around it is to require the user to use a macro I provide in the general include file-
GridMsg Proto SYSCALL GridHandle:dword,Request:dword,Parameters:vararg
; if an error occurs, ecx will contain the error number, else zero
gmsg macro Handle:req,Parameters:vararg
invoke GridMsg,Handle,Parameters
or ecx,ecx ;; test for error
EndM

a sample of usage to set up a grid of buttons:
    gmsg hGridb,SetGridSize,eax,edx ;bcols,brows
    jnz DoError
    gmsg hGridb,SetColVisible,0,FALSE
    gmsg hGridb,SetRowVisible,0,FALSE
    gmsg hGridb,SetColWidth,gAll,90
    gmsg hGridb,SetRowHeight,gAll,15
    gmsg hGridb,SetCell3D,gAll,gAll,TRUE
    inv GetSysColor,COLOR_BTNFACE
    gmsg hGridb,SetCellBackColor,gAll,gAll,eax
    gmsg hGridb,SetCellReadOnly,gAll,gAll,TRUE
    gmsg hGridb,SetCellTextAlignment,gAll,gAll,DT_CENTER
    gmsg hGridb,SetDrawBorder,FALSE
    gmsg hGridb,SetColWidth,1,70
    gmsg hGridb,SetColWidth,2,22
    gmsg hGridb,SetColWidth,5,80
    gmsg hGridb,SetColWidth,6,70


of course you could just make the macro count the params and pass the count.