News:

Masm32 SDK description, downloads and other helpful links
Message to All Guests
NB: Posting URL's See here: Posted URL Change

Main Menu

Vararg in Colletion::ForEach

Started by HSE, October 10, 2023, 05:05:40 AM

Previous topic - Next topic

HSE

Hi Biterider!

Can you remind me what problem have vararg in ObjAsm methods?

My big projects still are ObjAsm32, and at some point that could be a problem. In these projects ForEach method is called with vararg.

Thanks in advance, HSE.
Equations in Assembly: SmplMath

Biterider

Hi HSE
Sorry for the late replay.
Quote from: HSE on October 10, 2023, 05:05:40 AMCan you remind me what problem have vararg in ObjAsm methods?
Vararg works in the same way as "invoke". When you declare method in an Object using, for example,
VirtualMethod MyMethod, DWORD, DWORD, ?
It means, that you pass 2 DWORDs and a variable number of additional arguments.

When you implemet the method, you have to follow to following syntax
Method MyMethod, uses xsi, Arg1:DWORD, Arg2:DWORD, Arg3:Vararg
Arg3 is only used to calculate where the additional args are on the stack. On method entry, eax holds the number of all arguments.

Quote from: HSE on October 10, 2023, 05:05:40 AMIn these projects ForEach method is called with vararg
ForEach and variants use 2 arguments, like wParam and lParam. In most cases this is more than enough, but if not, create a structure on the stack and pass a pointer to it to the iterantor.

Thanks for pointing this out. In the documentation is not mentioned anywhere.

Biterider




HSE

Hi Biterider!

Perfect. Then I was having a little confusion  :thumbsup:

It's just enough to redefine ForEach.

Thanks, HSE.
Equations in Assembly: SmplMath

Biterider

Hi
Using ForEach is not as fast as other coding methods, because you have to take some things into account when writing the code. 
This led me to write experimental macros to make the setup process a bit easier. 
The process starts when you have to deal with more than 2 variables to pass to the callback proc or method. The suggested way to do this is to set up a struct in the stack and pass a pointer to that struct in one of the 2 callback-proc arguments. Theoretically, you don't need the second argument anymore. 

The structure is created with the DefArgs macro, where a Struct-Name and the type of arguments needed are declared. 
To fill the structure before calling ForEach, $FillArgs moves the required values into the structure.
The disadvantage of this approach is the inflexible naming of the structure members (Arg0, Arg1, etc.), which is a bit ugly but gets the job done. 

Perhaps a different approach can be taken by using templates like TVector where the whole thing can be created on the fly...

Attached are the files of a testbed I used to experiment with this concept.

Biterider


HSE

Hi Biterider,

Quote from: Biterider on October 21, 2023, 07:27:03 PMThis led me to write experimental macros to make the setup process a bit easier.

Look interesting  :thumbsup:


Quote from: Biterider on October 21, 2023, 07:27:03 PMThe disadvantage of this approach is the inflexible naming of the structure members (Arg0, Arg1, etc.), which is a bit ugly but gets the job done.

That it's not a problem at all:
DefArgsName macro StructName, ArgNames:vararg
  $$Count = 0
    for ArgName, <ArgNames>
      @CatStr(StructName,<_Arg>, %$$Count, < textequ >) <ArgName>
      $$Count = $$Count + 1
    endm
endm

DefArgsType macro StructName, ArgTypes:vararg
  $$Count = 0
  StructName struct
    for ArgType, <ArgTypes>
      @CatStr(StructName,<_Arg>, %$$Count, < >, ArgType, < ?>)
      $$Count = $$Count + 1
    endm
  StructName ends
  @CatStr(<P>, StructName, < typedef ptr >, StructName)
endm

$FillArgs macro StructName, LocalVarName, ArgValues:vararg
  $$Count = 0
  for ArgValue, <ArgValues>
    @CatStr(<m2m >, LocalVarName,<.>, StructName,<_Arg>, %$$Count, <, >, ArgValue, <, xdx>)
    $$Count = $$Count + 1
  endm
  exitm <addr LocalVarName>
endm


DefArgsName CALLBACK_ARGS, <Arg40, Arg51, Arg62>
DefArgsType CALLBACK_ARGS, <DWORD, XWORD, BYTE>


Regard, HSE.
Equations in Assembly: SmplMath

Biterider

Hi HSE
Thanks for showing this alternative  :thumbsup:

Not exactly a Vararg solution, but an alternative for the ForEach loop.
In this case, the macros .ColForEach and .ColNext are used to loop through.

It seems to me a cleaner solution than the previous one, but both have their uses.

Biterider



HSE

Quote from: Biterider on October 22, 2023, 07:49:15 AMThanks for showing this alternative  :thumbsup:

It's just the same you posted!


Quote from: Biterider on October 22, 2023, 07:49:15 AMIn this case, the macros .ColForEach and .ColNext are used to loop through.

That is efficient  :thumbsup:

But, in not so old programs, there is no problem to travel collection' descendents, for example:
   OCall _vector::Vector.GetFirst
   .if eax != NULL
      .while eax != NULL
     
         *****

         OCall _vector::Vector.GetNext
      .endw     
   .endif

Method Vector.GetFirst, uses xsi
SetObject xsi
.if [xsi].dCount > 0
mov [xsi].dCurrent, 0
ACall xsi.ItemAt, 0
.else
mov xax, NULL
.endif
MethodEnd

Method Vector.GetNext, uses xsi
SetObject xsi
@inicio:
mov xax, [xsi].dCurrent
inc xax
.if [xsi].dCount > xax
inc [xsi].dCurrent
ACall xsi.ItemAt, [xsi].dCurrent
.if xax == NULL
jmp @inicio
.endif
.else
xor xax, xax
.endif
MethodEnd

That can be improved  :cool: 

Older programs use ForEach :biggrin:
Equations in Assembly: SmplMath

jj2007

Quote from: HSE on October 22, 2023, 09:43:37 AM   OCall _vector::Vector.GetFirst
   .if eax != NULL
      .while eax != NULL
     
         *****

         OCall _vector::Vector.GetNext
      .endw     
   .endif

Shorter but equivalent:
   OCall _vector::Vector.GetFirst
      .while eax
     
         *****

         OCall _vector::Vector.GetNext
      .endw

HSE

Hi JJ,

Quote from: jj2007 on October 22, 2023, 11:14:23 AMShorter but equivalent:

No  :eusa_naughty:

Collection can be empty. Then a lot of problems running "*****" with a null address.

HSE
Equations in Assembly: SmplMath

jj2007

You don't need an .if eax if you are using a (rejecting) .While eax loop.

HSE

Quote from: jj2007 on October 22, 2023, 11:57:18 AMYou don't need an .if eax if you are using a (rejecting) .While eax loop.

 :thumbsup: I always forgot the jump. Thanks.
Equations in Assembly: SmplMath

Biterider

Hi
I have refined the syntax a little and have come to the following result:

  xor ecx, ecx
  .ColForEachRev MyColl
    mov xdi, xax
    .ColForEach [xdi]
      DbgDec [xax].$Obj(Primer).pOwner
      inc ecx
    .ColNext
  .ColNext
  DbgDec ecx

That seems intuitive enough to me.
Now .ColForEach(Rev) set up xax directly passing a pointer to the collection item.

I'll add these new macros to Collection.inc.

Biterider

HSE

Hi Biterider!

What is ItemReg?

Thanks, HSE.

Equations in Assembly: SmplMath

Biterider

Hi HSE
ItemReg in the first incarnation was the register that gets the item pointer. Later I realized that this is not necessary, since I use xax internally and xax keeps this pointer as a byproduct after the ForEach call.

In short, forget it  :biggrin:

Biterider

PS: TVector is another candidate for such macros...

HSE

Equations in Assembly: SmplMath