cCMDL. Commandline Class.

Share your advanced PureBasic knowledge/code with the community.
User avatar
Hroudtwolf
Addict
Addict
Posts: 803
Joined: Sat Feb 12, 2005 3:35 am
Location: Germany(Hessen)
Contact:

cCMDL. Commandline Class.

Post by Hroudtwolf »

Hi,

I made a little commandline tool-class for one of my projects.
With it, it is very easy to get/check arguments of the current commandline.

@Procedural developers
It is not necessary to start flamewars against sense of OOP ;-)


Code: Select all

; PureBasic-Lounge.de
; Author: Hroudtwolf
; Date: 03. November 2007
; OS: Windows, Linux, Mac
; Demo: Yes


Interface IcCMDL
   IsArg       (sKey.s)
   GetArg.s    (lIndex.l)
   GetArgIndex (sKey.s)
   CountArgs   ()
   Done        ()
EndInterface

Structure cCMDL 
   *VTABLE
   
   *ArgList
   lArgs    .l
EndStructure

Structure tCMD
   sValue .s
   lHash  .l
EndStructure

; **************************************************************
; * Internal function
; **************************************************************

Procedure.l _cCMDL_Hash (sString.s)
   Protected *Source    .CHARACTER = @sString
   Protected lHash      .l         = #Null
   Protected lX         .l         = #Null
   
   While *Source\c
      lHash = (lHash << 4) + *Source\c
      lX = lHash & $F0000000
      *Source + SizeOf (CHARACTER)
   Wend
   
   ProcedureReturn lHash
EndProcedure 

; **************************************************************
; * *Obj\IsArg (sKey.s)
; **************************************************************
; * Checks whether a key exist in commandline arguments.
; * 
; * sKey             - In Example "/debug".
; **************************************************************
; * Returns:
; * True/False
; **************************************************************
Procedure cCMDL_IsArg (*This.cCMDL , sKey.s)
   Protected lCount     .l
   Protected lKeyHash   .l = _cCMDL_Hash (sKey)
   Protected *CurrentKey.tCMD
   
   
   For lCount = 0 To *This\lArgs - 1
      *CurrentKey = *This\ArgList + (lCount * SizeOf (tCMD))
      If lKeyHash = *CurrentKey\lHash
         ProcedureReturn #True
      EndIf
   Next lCount
   
   ProcedureReturn #False
EndProcedure

; **************************************************************
; * *Obj\GetArg (lIndex.l)
; **************************************************************
; * Retrieves an argument from given index.
; * 
; * lIndex           - The index of the argument you want to get
; **************************************************************
; * Returns:
; * Argument string
; **************************************************************
Procedure.s cCMDL_GetArg (*This.cCMDL , lIndex.l)
   Protected *CurrentKey.tCMD
   
   If lIndex < 0 Or lIndex > *This\lArgs - 1
      ProcedureReturn  ""
   EndIf
   
   *CurrentKey = *This\ArgList + (lIndex * SizeOf (tCMD))
   ProcedureReturn *CurrentKey\sValue
EndProcedure

; **************************************************************
; * *Obj\GetArgIndex (sKey.s)
; **************************************************************
; * Retrieves the index of a given argument key.
; * 
; * sKey             - In Example "/debug".
; **************************************************************
; * Returns:
; * LONG | Index number
; **************************************************************
Procedure cCMDL_GetArgIndex (*This.cCMDL , sKey.s)
   Protected lCount     .l
   Protected lKeyHash   .l = _cCMDL_Hash (sKey)
   Protected *CurrentKey.tCMD
   
   
   For lCount = 0 To *This\lArgs - 1
      *CurrentKey = *This\ArgList + (lCount * SizeOf (tCMD))
      If lKeyHash = *CurrentKey\lHash
         ProcedureReturn lCount
      EndIf
   Next lCount
   
   ProcedureReturn -1
EndProcedure

; **************************************************************
; * *Obj\CountArgs ()
; **************************************************************
; * Retrieves the number if commandline arguments
; * 
; **************************************************************
; * Returns:
; * LONG
; **************************************************************
Procedure cCMDL_CountArgs (*This.cCMDL)
   ProcedureReturn *This\lArgs 
EndProcedure

; **************************************************************
; * *Obj\Done ()
; **************************************************************
; * Releases the whole object.
; * 
; **************************************************************
; * Returns:
; * null
; **************************************************************
Procedure cCMDL_Done (*This.cCMDL)
   FreeMemory (*This\ArgList)
   FreeMemory (*This)
   ProcedureReturn #Null
EndProcedure
   
; **************************************************************
; * Constructor
; **************************************************************
ProcedureDLL CreateObject_cCMDL ()
   Protected *This   .cCMDL = AllocateMemory (SizeOf (cCMDL))
   Protected lCountOf.l     = CountProgramParameters ()
   Protected lCount  .l
   Protected *TempLst.tCMD
   Static    *VTABLE_cCMDL
    
   If Not lCountOf
      FreeMemory (*This)
      ProcedureReturn #Null
   EndIf 
      
   If Not *VTABLE_cCMDL
      *VTABLE_cCMDL = AllocateMemory (SizeOf (IcCMDL ))
      If Not *VTABLE_cCMDL
         ProcedureReturn #Null
      EndIf
      PokeL (*VTABLE_cCMDL + OffsetOf (IcCMDL\IsArg       ()) , @cCMDL_IsArg         ())
      PokeL (*VTABLE_cCMDL + OffsetOf (IcCMDL\GetArg      ()) , @cCMDL_GetArg        ())
      PokeL (*VTABLE_cCMDL + OffsetOf (IcCMDL\GetArgIndex ()) , @cCMDL_GetArgIndex   ())
      PokeL (*VTABLE_cCMDL + OffsetOf (IcCMDL\CountArgs   ()) , @cCMDL_CountArgs     ()) 
      PokeL (*VTABLE_cCMDL + OffsetOf (IcCMDL\Done        ()) , @cCMDL_Done          ())  
   EndIf
   
   *This\VTABLE = *VTABLE_cCMDL
   
   For lCount = 0 To lCountOf - 1
      If Not *TempLst
         *This\ArgList   = AllocateMemory (SizeOf (tCMD))
         *TempLst        = *This\ArgList
         *TempLst\sValue = ProgramParameter (lCount)
         *TempLst\lHash  = _cCMDL_Hash (*TempLst\sValue)
         Else
         *TempLst = ReAllocateMemory (*This\ArgList , (lCount + 1) * SizeOf (tCMD))
         If Not *TempLst
            If *This\ArgList
               FreeMemory (*This\ArgList)
            EndIf
            FreeMemory (*This)
            ProcedureReturn #Null
         EndIf
         *This\ArgList = *TempLst
         *TempLst      = *This\ArgList + (lCount * SizeOf (tCMD))
         *TempLst\sValue = ProgramParameter (lCount)
         *TempLst\lHash  = _cCMDL_Hash (*TempLst\sValue)
      EndIf
   Next lCount
   
   *This\lArgs = lCountOf
   
   ProcedureReturn *This
EndProcedure

;-------------- Test -------------------------------------------------

; Put '/debug /load "myfile.txt"' to your commandline for example.

*test.IcCMDL = CreateObject_cCMDL ()
OpenConsole ()
   If *test\IsArg ("/debug")
      PrintN ("Debug mode: On")
      Else
      PrintN ("Debug mode: Off")
   EndIf
   If *test\IsArg ("/load")
      lIndex = *test\GetArgIndex ("/load")
      PrintN ("Loading file: " + *test\GetArg (lIndex + 1))
   EndIf
   Input ()
CloseConsole ()
Best regards

Wolf
Last edited by Hroudtwolf on Tue Nov 06, 2007 5:24 pm, edited 3 times in total.
srod
PureBasic Expert
PureBasic Expert
Posts: 10589
Joined: Wed Oct 29, 2003 4:35 pm
Location: Beyond the pale...

Post by srod »

Very nice indeed. This was to be one of the next classes I was going to write in my conversion to oop, but you've saved me the bother. :)

I'll add this to my collection of utility classes.

Thanks.
I may look like a mule, but I'm not a complete ass.
akj
Enthusiast
Enthusiast
Posts: 668
Joined: Mon Jun 09, 2003 10:08 pm
Location: Nottingham

Post by akj »

Please forgive ny ignorance, but what are the advantages in using this commandline tool class rather than the built-in PB commands CountProgramParameters() and ProgramParameter() ?
Anthony Jordan
User avatar
Hroudtwolf
Addict
Addict
Posts: 803
Joined: Sat Feb 12, 2005 3:35 am
Location: Germany(Hessen)
Contact:

Post by Hroudtwolf »

@srod

Thx.
Your'e welcome :)

Please forgive ny ignorance,...
I don't see any ignorance.
I think you doesn't look in the source. ^^ *Joke*

Sure..., it's better to show you the little advantage of this class in two examples.

Maybe, you want to look whether keys are set in the commandline (in Example: /debug & /color)....
Procedure with ProgramParameter ():

Code: Select all

If Not CountProgramParameters ()
   End
EndIf
While lCount < CountProgramParameters () -1
   If ProgramParameter (lCount) = "/debug"
      Debug "Tadarata. Key '/debug' was set."
   EndIf
   If ProgramParameter (lCount) = "/color"
      Debug "Tadarata. Key '/debug' was set."
   EndIf
   lCount + 1
Wend
Procedure with my class:

Code: Select all

*test.IcCMDL = CreateObject_cCMDL ()
If *test\IsArg ("/debug")
   Debug "Tadarata. Key '/debug' was set."
EndIf
If *test\IsArg ("/color")
   Debug "Tadarata. Key '/color' was set."
EndIf 
In addition, it is very simple to determine the index of any key.
It's just a little assistance.


Best regards

Wolf
akj
Enthusiast
Enthusiast
Posts: 668
Joined: Mon Jun 09, 2003 10:08 pm
Location: Nottingham

Post by akj »

@Hroudtwolf
Thanks for your reply. However your COM example is bugged as it crashes with "Invalid memory access" if no command line parameters are supplied. I think it should be changed to:

Code: Select all

*test.IcCMDL = CreateObject_cCMDL ()
If *test
  If *test\IsArg ("/debug")
     Debug "Tadarata. Key '/debug' was set."
  EndIf
  If *test\IsArg ("/color")
     Debug "Tadarata. Key '/color' was set."
  EndIf
EndIf
Now the code length is 9 lines, very similar in length to your non-COM version of 12 lines, and I'm finding it hard to see the advantage of the formar, as it more complex and presumably much slower.

Incidently, there are bugs in lines 4 and 9 of your non-COM example.
Anthony Jordan
User avatar
Hroudtwolf
Addict
Addict
Posts: 803
Joined: Sat Feb 12, 2005 3:35 am
Location: Germany(Hessen)
Contact:

Post by Hroudtwolf »

Yes. To check the object pointer is important. 'Cause the object will only be created if a commandline does exist.

Sorry. But if ya don't see the advantage, you are just blind.
Not only the count of lines are the advantage.
It is the handy usability of this class.

For ME, it is very useful.
And I didn't create this little class unfounded.

Best regards

Wolf

PS: Your'e free to use it, or not ;-)
srod
PureBasic Expert
PureBasic Expert
Posts: 10589
Joined: Wed Oct 29, 2003 4:35 pm
Location: Beyond the pale...

Post by srod »

I'm with you wolfie! :wink:
I may look like a mule, but I'm not a complete ass.
User avatar
Hroudtwolf
Addict
Addict
Posts: 803
Joined: Sat Feb 12, 2005 3:35 am
Location: Germany(Hessen)
Contact:

Post by Hroudtwolf »

Thx :)
Let us build a OOP gang to accroach the world domination. ^^
#NULL
Addict
Addict
Posts: 1499
Joined: Thu Aug 30, 2007 11:54 pm
Location: right here

Post by #NULL »

but that said advantage is not really oop-related. actually for the IsArg()-thing i would prefer a simple function like this:
(not well tested)

Code: Select all

Procedure isArg(arg.s)
  Static NewList args.s()
  Static init.l = 0
  Static i.l
  Protected ret.l = #False
  
  If Not init
    init=1
    For i=0 To CountProgramParameters()-1
      AddElement(args())
      args() = ProgramParameter()
      Debug args()
    Next
  EndIf
  
  ForEach args()
    If args() = arg
      ret = #True
      Break
    EndIf
  Next
  
  ProcedureReturn ret
EndProcedure
..but maybe i'm not having the right overview about the context, and neither did i fully check you class.
but thanks for sharing good code. i like oop style and i'm using it often, but in a very loose way.
the biggest advantage is the oop-interface for usage, but that's only an advantage for people who like it :P .

..i just see now for my code, if i would want to access the arg-list from different procedures for different purposes (as you do), there come the first complications. so for that oop is much better maybe.
[sorry, probably you will never a class posted without those "do you need that"s :lol: ]
User avatar
Hroudtwolf
Addict
Addict
Posts: 803
Joined: Sat Feb 12, 2005 3:35 am
Location: Germany(Hessen)
Contact:

Post by Hroudtwolf »

Hi,

I never said, "this is only possible in OOP style" ;-)
All my codes are capsuled in classes. That is my unique codingstyle.
I don't think, I've to explain more about this.
'Cause everybody should know the advantages of such a method.

Let us assume, a objectmanager is our central and global interface to all of our objects and our cCMDL interface haves two methode more...
(All my classes does have such a standard interface)

Code: Select all

Interface IcCMDL
   ; Standard
   SendMessage (lMsgID.l , *ParamA , *ParamA)
   PostBox         (*lptrMsg.LONG , *lptrParamA.LONG , *lptrParamA.LONG)
   ; Class internal
   IsArg       (sKey.s)
   GetArg.s    (lIndex.l)
   GetArgIndex (sKey.s)
   CountArgs   ()
   Done        ()
EndInterface

With such a method, this is possible--->

Code: Select all

If *MyOM\SendMessage ("cmdl" , #CMDL_MSG_ISARG , @"/load" , #Null)
   *MyOM\SendMessage ("otherobject" , #OOBJ_MSG_DOANYTHING , #Null , #Null)
   Else
   *MyOM\SendMessage ("errorhandler" , #OOBJ_MSG_POSTERROR , @"Wrong arguments." , #Null)
EndIf
'Cause the objectmanager can control other objects by ONE interface.

Code: Select all

Interface IcSTDINTERFACE
   SendMessage (lMsgID.l , *ParamA , *ParamA)
   PostBox         (*lptrMsg.LONG , *lptrParamA.LONG , *lptrParamA.LONG)
EndInterface

And this is for ME, elysium of a proper programingstyle.

I hope you see the facts in spite of my funny english. ^^

Best regards :)

Wolf
#NULL
Addict
Addict
Posts: 1499
Joined: Thu Aug 30, 2007 11:54 pm
Location: right here

Post by #NULL »

that makes sense and can be useful indeed.
thanks for the explanations and ideas :)
User avatar
pdwyer
Addict
Addict
Posts: 2813
Joined: Tue May 08, 2007 1:27 pm
Location: Chiba, Japan

Post by pdwyer »

Forcing OOP in this situation still seems like an overkill to me. The code below achieves the same thing but (to me) it's easier to read and understand (and debug). If the language was OOP then you wouldn't have this under-the-hood OOP code there in front of you but it's not so I probably wouldn't bother unless the task was really lending itself to being easier to understand as instanciated objects. If you had a swarm algorithm OOP would probably make it easier to encapsulate complex movements for each member, Neurons in an ANN, stuff like that.

But hey, I'm one of those anti java kind of people so it's just my opinion 8)

(Case insensitive)

Code: Select all

Declare.s GetCMDParam(Param.s)
Declare.l GetCMDSwitch(Switch.s)

;using exe commandline: -i infile -o outfile -switch  (you could catch /switch too I suppose)

; Returns
    ; outfile
    ; infile
    ; 1
    ; 0

 
Debug GetCMDParam("-O")
Debug GetCMDParam("-i")
Debug GetCMDSwitch("-switch")
Debug GetCMDSwitch("-MissingSwitch")

;===========================================

Procedure.s GetCMDParam(Param.s)

    Param = LCase(Param)
    
    For i = 0 To CountProgramParameters() -1
        If  LCase(ProgramParameter(i)) = Param
            ProcedureReturn ProgramParameter(i+1)
        EndIf   
    Next   

    ProcedureReturn ""
    
EndProcedure

;===========================================

Procedure.l GetCMDSwitch(Switch.s)

    Switch = LCase(Switch)
    
    For i = 0 To CountProgramParameters() -1
        If  LCase(ProgramParameter(i)) = Switch
            ProcedureReturn #True
        EndIf   
    Next   

    ProcedureReturn #False   

EndProcedure

;===========================================
Paul Dwyer

“In nature, it’s not the strongest nor the most intelligent who survives. It’s the most adaptable to change” - Charles Darwin
“If you can't explain it to a six-year old you really don't understand it yourself.” - Albert Einstein
User avatar
Hroudtwolf
Addict
Addict
Posts: 803
Joined: Sat Feb 12, 2005 3:35 am
Location: Germany(Hessen)
Contact:

Post by Hroudtwolf »

I'll post OOP code never more, here. ^^
User avatar
pdwyer
Addict
Addict
Posts: 2813
Joined: Tue May 08, 2007 1:27 pm
Location: Chiba, Japan

Post by pdwyer »

:lol:

Sorry if that sounded a bit strong.

I tried to pepper my response with "to me" and "my opinion". I'm sure there's plenty of people who prefer your method to mine and I wasn't looking to start an OOP war, just some discussion.

Actually I'm not anti-oop, I just think that classes should be created for instanciating objects. I've just never understood why people (Mainly Java heretics :twisted: ) do OOP for something that won't ever have more than one object created for it. You only ever have one cmdline object created though (I think).

Anyway, didn't mean to rain on your parade :wink:
Paul Dwyer

“In nature, it’s not the strongest nor the most intelligent who survives. It’s the most adaptable to change” - Charles Darwin
“If you can't explain it to a six-year old you really don't understand it yourself.” - Albert Einstein
srod
PureBasic Expert
PureBasic Expert
Posts: 10589
Joined: Wed Oct 29, 2003 4:35 pm
Location: Beyond the pale...

Post by srod »

Whilst I agree with your point about instantiation and that creating a class for an object which will only be instantiated once in an application is, in a sense, 'overkill', there is still the encapsulation aspect of it all.

Wrapping up such functionality as offered here in a simple object which can be simply used and abused and otherwise forgotten about is quite nice, particularly in large projects. I now find myself doing this for more than a few classes in my applications, as much for portability as anything else as I can simply 'plug' these albeit simple classes into other programs without thinking about it. These include classes for managing preferences, language files etc.

Of course you can achieve all this quite readily with non-oop methods and so, like everything, it comes down to a matter of personal preference and mine is most definitely to use oop even in simple cases. I just find it a very tidy way of programming.

Yep, I likes it! :)
I may look like a mule, but I'm not a complete ass.
Post Reply