Some simple macros to help make code more readable

Share your advanced PureBasic knowledge/code with the community.
BorisTheOld
Enthusiast
Enthusiast
Posts: 542
Joined: Tue Apr 24, 2012 5:08 pm
Location: Ontario, Canada

Some simple macros to help make code more readable

Post by BorisTheOld »

Even though the PB macro support is quite primitive, we've made use of it to help implement a simplified OOP (SOOP). However, we also use macros for more mundane things.

Here are a few simple macros to make code a little more readable. These are some of the 100 or so macros that we use in our shop to simplify the coding process and make the code less opaque.

In this first example we change the Select block so that it has a similar look to the If block:

Code: Select all


Macro CaseElse
  Default
EndMacro

Select ......

   Case .....

   Case .....

   CaseElse

EndSelect


Although While/Wend is a looping contruct related to Repeat and For, the use of the word Wend makes it look more like a non-looping block, like If/EndIf. So we change things a little to make the looping more obvious:

Code: Select all


Macro Loop
  Wend
EndMacro

While .....

Loop


Lastly, here are some examples related to the Break statement. In complex code, the Break Level doesn't make it obvious which loops are involved. Our macros make this clearer by including the loop types in the macro name. Here are a few examples:

Code: Select all


Macro ExitFor
  Break
EndMacro

Macro ExitRepeat
  Break
EndMacro

Macro ExitWhile
  Break
EndMacro

Macro ExitForFor   ; this breaks from 2 nested For loops
  Break 2
EndMacro

Macro ExitForWhile  ; this breaks from a For loop and its outer While loop 
  Break 2
EndMacro

Repeat

  ExitRepeat

Until .....

While .....

   ExitWhile

   For .....

      ExitForWhile

      For .....

        ExitFor
        ExitForFor

      Next

   Next

Loop

For ten years Caesar ruled with an iron hand, then with a wooden foot, and finally with a piece of string.
~ Spike Milligan
User avatar
Frarth
Enthusiast
Enthusiast
Posts: 241
Joined: Tue Jul 21, 2009 11:11 am
Location: On the planet
Contact:

Re: Some simple macros to help make code more readable

Post by Frarth »

While/Wend and Do/Loop have been part of QuickBASIC and old VisualBasic from the beginning, so your suggestion will probably look somewhat strange to those with this -BASIC background.

In general, using too many such macros may in time conflict with language adjustments or shared code; it may become difficult to share your code or use shared code from others.

Leaving language statements as they are, is probably best practice IMHO.
PureBasic 5.41 LTS | Xubuntu 16.04 (x32) | Windows 7 (x64)
BorisTheOld
Enthusiast
Enthusiast
Posts: 542
Joined: Tue Apr 24, 2012 5:08 pm
Location: Ontario, Canada

Re: Some simple macros to help make code more readable

Post by BorisTheOld »

These are all valid points, but there are counter arguments.

PB already looks strange to anyone, like myself, who has used BASIC for a long time. Besides, PB does not have a Do/Loop construct, so combining a While/Wend and Do/Loop into a While/Loop isn't going to confuse people too much.

Macros are part of the PB instruction set, so in using them there's always the risk of future name conflicts. But as with life in general, programming languages change, and like the Borg we have to adapt.

Our programs have evolved over the years, and during that time I have implemented very strict coding standards. The aim being to create bug free code that is easy to maintain, while at the same time being able to take advantage of new programming techniques.

The following code snippet may not look much like PB code, but everything about it is pure PB. It's a method from one of our code generator classes. Like all our code it makes use of macros to force rigid coding standards, make the code easy to read, and hide some of the quirky features of the PB language.

Code: Select all

;
;-----------------------------------------------
;
PrivateSubroutine(DDMod, MethodBody)   (ByVal(Me, strDDMod))
;
;  MethodBody                          generate a method
;
;
;--} local data

  Local(sDataParmType, typString)                                              ; 15080 : parameter data type
  Local(sWork1, typString)                                                     ; 15084 : string work area
  Local(sWork2, typString)                                                     ; 15085 : string work area
  Local(sWork3, typString)                                                     ; 15086 : string work area
  Local(sDataFuncType, typString)                                              ; 15485 : function data type

;--} local code
;
;  private file records on entry:
;
;    Me\prrR231 - modules
;    Me\prrR227 - module procedures
;
;  other private file records used:
;
;    Me\prrR354 - module procedure parms
;    Me\prrR233 - elements
;
;  local file records used:
;
;    none
;

  ObjectCall(gloDD, Txt1) (";")
  ObjectCall(gloDD, Txt1) (#sK_1)
  ObjectCall(gloDD, Txt1) (";")

  sWork2 = "ByVal(Me, str{prefix})"

  ObjectCall(Me\proF354, Empty) (Me\pruH354, Me\prrR354)                       ; module procedure parms
  Me\prrR354\nModuleKey = Me\prrR227\nModuleKey
  Me\prrR354\nModuleProcKey = Me\prrR227\nModuleProcKey
  ObjectCall(Me\proF354, First) (Me\pruH354, Me\prrR354, #iINDEX_1, #iPARTIAL_KEY_2)
  While Me\pruH354\iReturn = #iRETURN_OK

    ObjectCall(Me\proF233, Empty) (Me\pruH233, Me\prrR233)                     ; elements
    Me\prrR233\nElementKey = Me\prrR354\nElementNumber
    ObjectCall(Me\proF233, Read) (Me\pruH233, Me\prrR233, #iINDEX_1)

    sDataParmType = ObjectReturn(gloDD, GetDataParmType) (Me\prrR233\nElementKey)

    Select Me\prrR233\cDataType
      Case #cDATA_TYPE_STRUCTURE, #cDATA_TYPE_OBJECT, #cDATA_TYPE_UDT
        sWork1 = "ByRef(br"
      CaseElse
        sWork1 = "ByVal(bv"
    EndSelect

    sWork2 = sWork2 + ", " + sWork1 + Me\prrR233\cDataType + Me\prrR233\sElementName + ", " + sDataParmType + ")"

    ObjectCall(Me\proF354, Next) (Me\pruH354, Me\prrR354, #iINDEX_1, #iPARTIAL_KEY_2)
  Loop

  If Me\prrR227\cProcedureType = #sCODE_PROC_FUNCTION

    sDataFuncType = ObjectReturn(gloDD, GetDataFuncType) (Me\prrR227\cDataType)

    If Me\prrR227\cProcedureScope = #sCODE_SCOPE_EXTERNAL
      ObjectCall(gloDD, Txt2) ("ExternalFunction({prefix}, " + Me\prrR227\sProcedureName + ", " + sDataFuncType + ")", "(" + sWork2 + ")")
    Else
      ObjectCall(gloDD, Txt2) ("PrivateFunction({prefix}, " + Me\prrR227\sProcedureName + ", " + sDataFuncType + ")", "(" + sWork2 + ")")
    EndIf

  Else

    If Me\prrR227\cProcedureScope = #sCODE_SCOPE_EXTERNAL
      ObjectCall(gloDD, Txt2) ("ExternalSubroutine({prefix}, " + Me\prrR227\sProcedureName + ")", "(" + sWork2 + ")")
    Else
      ObjectCall(gloDD, Txt2) ("PrivateSubroutine({prefix}, " + Me\prrR227\sProcedureName + ")", "(" + sWork2 + ")")
    EndIf

  EndIf

  ObjectCall(gloDD, Txt1) (";")
  ObjectCall(gloDD, Txt2) (";  " + Me\prrR227\sProcedureName, Me\prrR227\sProcedureNote)
  ObjectCall(gloDD, Txt1) (";")

  ClassCall(DDMod, ParmComments) (Me)

  ObjectCall(gloDD, Txt1) (";")
  ObjectCall(gloDD, Txt1) (";--} local data")
  ObjectCall(gloDD, Txt0) ()

  ClassCall(DDMod, MethodVariables) (Me)

  ObjectCall(gloDD, Txt0) ()
  ObjectCall(gloDD, Txt1) (";--} local code")

  Select Me\prrR227\sProcedureName

    Case "Constructor"
      ClassCall(DDMod, CreateAutoConstructor) (Me)

    Case "Destructor"
      ClassCall(DDMod, CreateAutoDestructor) (Me)

    CaseElse
      ClassCall(DDMod, MethodCode) (Me)

  EndSelect

  If Me\prrR227\cProcedureType = #sCODE_PROC_FUNCTION
    ObjectCall(gloDD, Txt1) ("EndFunction")
  Else
    ObjectCall(gloDD, Txt1) ("EndSubroutine")
  EndIf

EndSubroutine
For ten years Caesar ruled with an iron hand, then with a wooden foot, and finally with a piece of string.
~ Spike Milligan
Post Reply