Scoped enumerations with added features

Share your advanced PureBasic knowledge/code with the community.
Mistrel
Addict
Addict
Posts: 3415
Joined: Sat Jun 30, 2007 8:04 pm

Scoped enumerations with added features

Post by Mistrel »

This is similar to features provided by the Enum class in Java. It allows enumerations to hold additional information and for their type to be queried for validity.

Code: Select all

DeclareModule ENUM_CARDINAL_DIRECTION
  Structure Class
    *vTable
    name.s
    ordinal.i
  EndStructure
  
  ; --
  
  Macro POINTER
    i
  EndMacro
  
  Interface Public
    name.s()
    ordinal.i()
  EndInterface
  
  ;/ Static
  Declare new(ordinal.i, name.s)
  Declare.POINTER fromOrdinal(ordinal.i)
  Declare.POINTER valueOf(name.s)
  
  NewMap values.Public()
  
  NONE.Public=new(1,"NONE")
  NORTH.Public=new(2,"NORTH")
  EAST.Public=new(3,"EAST")
  SOUTH.Public=new(4,"SOUTH")
  WEST.Public=new(5,"WEST")
EndDeclareModule

Module ENUM_CARDINAL_DIRECTION
  Structure VTable
    *name
    *ordinal
  EndStructure
  
  Declare setVTable(*vTable.Class)
  
  ;/ Static
  Global vTable.VTable
  setVTable(@vTable)
  
  Procedure new(ordinal.i, name.s)
    Protected *instance.Class
    
    *instance=AllocateStructure(Class)
    *instance\vTable=@vTable
    
    *instance\name.s=name.s
    *instance\ordinal=ordinal
    
    ENUM_CARDINAL_DIRECTION::values(Str(ordinal))=*instance
    
    ProcedureReturn *instance
  EndProcedure
  
  Procedure.POINTER fromOrdinal(ordinal.i)
    ProcedureReturn ENUM_CARDINAL_DIRECTION::values(Str(ordinal))
  EndProcedure
  
  Procedure.POINTER valueOf(name.s)
    ForEach ENUM_CARDINAL_DIRECTION::values()
      If ENUM_CARDINAL_DIRECTION::values()\name()=name.s
        ProcedureReturn ENUM_CARDINAL_DIRECTION::values()
      EndIf
    Next
    
    ProcedureReturn #Null
  EndProcedure
  
  ;/ Public
  Procedure.s name(*this.Class)
    ProcedureReturn *this\name.s
  EndProcedure
  
  Procedure.i ordinal(*this.Class)
    ProcedureReturn *this\ordinal
  EndProcedure
  
  ;/ Private
  Procedure setVTable(*vTable.VTable)
    *vTable\name=@name()
    *vTable\ordinal=@ordinal()
  EndProcedure
EndModule

Interface ENUM_CARDINAL_DIRECTION Extends ENUM_CARDINAL_DIRECTION::Public
EndInterface
Demonstration:

Code: Select all

;/ Enums can have names (for debug and serialization)
Debug ENUM_CARDINAL_DIRECTION::NONE\name()

Debug "--"

;/ Query the enum for all values
ForEach ENUM_CARDINAL_DIRECTION::values()
  With ENUM_CARDINAL_DIRECTION::values()
    Debug Str(\ordinal())+" "+\name()
  EndWith
Next

Debug "--"

;/ Deserialize and validation
If Not ENUM_CARDINAL_DIRECTION::valueOf("UNKNOWN")
  Debug "No enum by that name"
EndIf

Debug "--"

;/ From ordinal
Define result.ENUM_CARDINAL_DIRECTION

reuslt=ENUM_CARDINAL_DIRECTION::fromOrdinal(3)

Debug result\name()
Output:

Code: Select all

NONE
--
1 NONE
2 NORTH
3 EAST
4 SOUTH
5 WEST
--
No enum by that name
--
EAST
Mistrel
Addict
Addict
Posts: 3415
Joined: Sat Jun 30, 2007 8:04 pm

Re: Scoped enumerations with added features

Post by Mistrel »

Improved base class module "Enum" which removes a lot of code duplication:

https://github.com/codespunk/libcodespu ... st/enum.pb

Code: Select all

XIncludeFile #CODESPUNK_HOME+"/purebasic/mod_Assertion.pb"
XIncludeFile #CODESPUNK_HOME+"/purebasic/mod_SAL.pb"
XIncludeFile #CODESPUNK_HOME+"/purebasic/mod_Type.pb"

EnableExplicit

DeclareModule Enum
  UseModule SAL
  
  Macro ORDINAL_TYPE
    Type::INT_32
  EndMacro
  
  Interface IObject
    name.s()
    ordinal.ORDINAL_TYPE()
  EndInterface
  
  Macro OBJECT_POINTER
    Type::POINTER
  EndMacro
EndDeclareModule

Module Enum
  UseModule SAL
EndModule

DeclareModule _Enum
  UseModule SAL
  UseModule Enum
  
  Structure VTable
    *name
    *ordinal
  EndStructure
  
  Structure Class
    *vTable
    name.s
    ordinal.ORDINAL_TYPE
  EndStructure
  
  ;/ Static
  Global vTable.VTable
  
  Declare.OBJECT_POINTER new(_IN_OUT Map values.IObject(), _IN ordinal.ORDINAL_TYPE, _IN name.s)
  Declare.OBJECT_POINTER fromOrdinal(_IN_OUT Map values.IObject(), _IN ordinal.ORDINAL_TYPE)
  Declare.OBJECT_POINTER valueOf(_IN_OUT Map values.IObject(), _IN name.s)
EndDeclareModule

Module _Enum
  UseModule Assertion
  UseModule SAL
  
  Procedure.OBJECT_POINTER new(_IN_OUT Map values.IObject(), _IN ordinal.ORDINAL_TYPE, _IN name.s)
    Protected *instance.Class
    
    *instance=AllocateStructure(Class)
    *instance\vTable=@vTable
    
    *instance\name.s=name.s
    *instance\ordinal=ordinal
    
    values(Str(ordinal))=*instance
    
    ProcedureReturn *instance
  EndProcedure
  
  Procedure.OBJECT_POINTER fromOrdinal(_IN_OUT Map values.IObject(), _IN ordinal.ORDINAL_TYPE)
    ASSERT_MAP_KEY(values(),Str(ordinal))
    
    ProcedureReturn values(Str(ordinal))
  EndProcedure
 
  Procedure.OBJECT_POINTER valueOf(_IN_OUT Map values.IObject(), _IN name.s)
    ForEach values()
      If values()\name()=name.s
        ProcedureReturn values()
      EndIf
    Next
    
    ASSERT(#False,"Invalid name")
    
    ProcedureReturn #Null
  EndProcedure
  
  ;/ IObject methods
  Procedure.s name(_IN *this.Class)
    ProcedureReturn *this\name.s
  EndProcedure
  
  Procedure.ORDINAL_TYPE ordinal(_IN *this.Class)
    ProcedureReturn *this\ordinal
  EndProcedure
  
  vTable\name=@name()
  vTable\ordinal=@ordinal()
EndModule

DisableExplicit
Example:

Code: Select all

XIncludeFile #CODESPUNK_HOME+"/purebasic/mod_Enum.pb"
XIncludeFile #CODESPUNK_HOME+"/purebasic/mod_SAL.pb"
XIncludeFile #CODESPUNK_HOME+"/purebasic/mod_Type.pb"


DeclareModule _ENUM_CARDINAL_DIRECTION
  UseModule SAL
  
  #ENUM_CARDINAL_DIRECTION_NONE=1
  #ENUM_CARDINAL_DIRECTION_NORTH=2<<0
  #ENUM_CARDINAL_DIRECTION_EAST=2<<1
  #ENUM_CARDINAL_DIRECTION_SOUTH=2<<2
  #ENUM_CARDINAL_DIRECTION_WEST=2<<3
  #ENUM_CARDINAL_DIRECTION_NORTHEAST=#ENUM_CARDINAL_DIRECTION_NORTH|#ENUM_CARDINAL_DIRECTION_EAST
  #ENUM_CARDINAL_DIRECTION_SOUTHEAST=#ENUM_CARDINAL_DIRECTION_SOUTH|#ENUM_CARDINAL_DIRECTION_WEST
  #ENUM_CARDINAL_DIRECTION_SOUTHWEST=#ENUM_CARDINAL_DIRECTION_NORTH|#ENUM_CARDINAL_DIRECTION_EAST
  #ENUM_CARDINAL_DIRECTION_NORTHWEST=#ENUM_CARDINAL_DIRECTION_SOUTH|#ENUM_CARDINAL_DIRECTION_WEST
  #ENUM_CARDINAL_DIRECTION_UP=2<<4
  #ENUM_CARDINAL_DIRECTION_DOWN=2<<5
EndDeclareModule

Module _ENUM_CARDINAL_DIRECTION
EndModule

DeclareModule ENUM_CARDINAL_DIRECTION
  UseModule _ENUM_CARDINAL_DIRECTION
  UseModule SAL
  
  Macro OBJECT_POINTER
    Type::POINTER
  EndMacro
  
  Interface IObject Extends ENUM::IObject
  EndInterface
  
  ;/ Static
  Global NewMap values.ENUM::IObject()
  
  ;/ Static methods
  Declare.OBJECT_POINTER fromOrdinal(_IN ordinal.Type::INT_32)
  Declare.OBJECT_POINTER valueOf(_IN name.s)
  
  ;/ Static variables
  NONE.IObject=_Enum::new(values(),#ENUM_CARDINAL_DIRECTION_NONE,"CARDINAL_DIRECTION_NONE")
  NORTH.IObject=_Enum::new(values(),#ENUM_CARDINAL_DIRECTION_NORTH,"CARDINAL_DIRECTION_NORTH")
  EAST.IObject=_Enum::new(values(),#ENUM_CARDINAL_DIRECTION_EAST,"CARDINAL_DIRECTION_EAST")
  SOUTH.IObject=_Enum::new(values(),#ENUM_CARDINAL_DIRECTION_SOUTH,"CARDINAL_DIRECTION_SOUTH")
  WEST.IObject=_Enum::new(values(),#ENUM_CARDINAL_DIRECTION_WEST,"CARDINAL_DIRECTION_WEST")
  NORTHEAST.IObject=_Enum::new(values(),#ENUM_CARDINAL_DIRECTION_NORTHEAST,"CARDINAL_DIRECTION_NORTHEAST")
  SOUTHEAST.IObject=_Enum::new(values(),#ENUM_CARDINAL_DIRECTION_SOUTHEAST,"CARDINAL_DIRECTION_SOUTHEAST")
  SOUTHWEST.IObject=_Enum::new(values(),#ENUM_CARDINAL_DIRECTION_SOUTHWEST,"CARDINAL_DIRECTION_SOUTHWEST")
  NORTHWEST.IObject=_Enum::new(values(),#ENUM_CARDINAL_DIRECTION_NORTHWEST,"CARDINAL_DIRECTION_NORTHWEST")
  UP.IObject=_Enum::new(values(),#ENUM_CARDINAL_DIRECTION_UP,"CARDINAL_DIRECTION_UP")
  DOWN.IObject=_Enum::new(values(),#ENUM_CARDINAL_DIRECTION_DOWN,"CARDINAL_DIRECTION_DOWN")
  
  Declare.OBJECT_POINTER fromOrdinal(_IN ordinal.Type::INT_32)
  Declare.OBJECT_POINTER valueOf(_IN name.s)
EndDeclareModule

Module ENUM_CARDINAL_DIRECTION
  Procedure.OBJECT_POINTER fromOrdinal(_IN ordinal.Type::INT_32)
    ProcedureReturn _Enum::fromOrdinal(values(), ordinal)
  EndProcedure
  
  Procedure.OBJECT_POINTER valueOf(_IN name.s)
    ProcedureReturn _Enum::valueOf(values(), name.s)
  EndProcedure
EndModule

;/ Enums can have names (for debug and serialization)
Debug ENUM_CARDINAL_DIRECTION::NONE\name()

Debug "--"

;/ Query the enum for all values
ForEach ENUM_CARDINAL_DIRECTION::values()
  With ENUM_CARDINAL_DIRECTION::values()
    Debug Str(\ordinal())+" "+\name()
  EndWith
Next

Debug "--"

;/ From ordinal
Define result.ENUM_CARDINAL_DIRECTION::IObject

result=ENUM_CARDINAL_DIRECTION::fromOrdinal(4)

Debug result\name()

Debug "--"

;/ Deserialize and validation
If Not ENUM_CARDINAL_DIRECTION::valueOf("UNKNOWN")
  Debug "No enum by that name"
EndIf
Output:

Code: Select all

CARDINAL_DIRECTION_NONE
--
0 CARDINAL_DIRECTION_NONE
2 CARDINAL_DIRECTION_NORTH
4 CARDINAL_DIRECTION_EAST
6 CARDINAL_DIRECTION_SOUTHWEST
8 CARDINAL_DIRECTION_SOUTH
16 CARDINAL_DIRECTION_WEST
24 CARDINAL_DIRECTION_NORTHWEST
32 CARDINAL_DIRECTION_UP
64 CARDINAL_DIRECTION_DOWN
--
CARDINAL_DIRECTION_EAST
--
<Assertion>
Dependencies:
https://github.com/codespunk/libcodespu ... sertion.pb
https://github.com/codespunk/libcodespu ... od_Enum.pb
https://github.com/codespunk/libcodespu ... mod_SAL.pb
https://github.com/codespunk/libcodespu ... od_Type.pb
Post Reply