Android Question Is it possible to reference class by name?

Sandman

Expert
Licensed User
Is there a way to turn this...
B4X:
Public Sub Setup
    Dim myInstance as myClass
    myInstance.Initialize
    myInstance.Execute

...into something this...
B4X:
Public Sub Setup(className As String)
    Dim myInstance as xui.GetClassFromName(className)
    myInstance.Initialize
    myInstance.Execute

The end goal would be that the project would have no actual mention of the class myClass, other than as a string.


(Why? Because I'm making a framework that will be used in more than one app, and the "myInstance" could refer to different classes, depending on what app I'm compiling. I really want to keep the framework library identical for all the apps, and then the "outer" app just defines what should be used.

To illustrate: Let's say I'm making a game-app AppA with four small mini-games in it, where the game classes are called G1, G2, G3 and G4. I'm also making game-app AppB, that has G1, G2 but then it has G5 and G6. I would like to keep the framework completely clueless about the classes, it's just supposed to init them and call the standardized subs I provide in them all.

I also would like to only have to include the actually used classes in each game. So if at all possible I'd like to avoid adding G5 and G6 to AppA, as they actually aren't used in that app.)
 

Erel

B4X founder
Staff member
Licensed User
Longtime User
Upvote 0

Sandman

Expert
Licensed User
The way I would have done it is with a factory class that creates the correct class instance.
But wouldn't that class basically just contain this sub?
(Which would functionally be the same as xui.GetClassFromName that I used in #1 above.)
B4X:
Public Sub Factory(className As String) As Object

    If className = "G1" Then

        Dim myG1 As G1
        myGi.Initialize
        Return myG1

    Else If className = "G2" Then

        Dim myG2 As G2
        myG2.Initialize
        Return myG2

    End If

End Sub

As it returns a generic Object, wouldn't it have to be cast to its proper class every time it's used?

And also, wouldn't the outside code have to have knowledge of what class it's actually casting?

And also, wouldn't this factory class also reference all G1, G2, G3, G4, G5, G6 to be able to create instances for all of them? And in that process pull in dependencies for them all?

I suspect my factory sub is all wrong, and the forum search didn't produce any examples to look at. If I'm completely off in my factory sub, could you give a pointer to how to do it?

(I haven't looked yet at the conditional compilation symbols - will do that now.)
 
Upvote 0

Erel

B4X founder
Staff member
Licensed User
Longtime User
ut wouldn't that class basically just contain this sub?
Yes.

You can use conditional compilation to avoid adding unwanted dependencies.
A bit ugly but is probably the best option:
B4X:
Public Sub Factory(className As String) As Object

    If className = "G1" Then
        #If SDN_G1
        Dim myG1 As G1
        myGi.Initialize
        Return myG1
         #Else
          Log("SDN_G1 is missing from the build configuration")
         #END IF
    Else If className = "G2" Then

        Dim myG2 As G2
        myG2.Initialize
        Return myG2

    End If

As it returns a generic Object, wouldn't it have to be cast to its proper class every time it's used?
B4X:
Dim g As G1 = Factory1.Factory("G1")
This deserves a question, what is the purpose of all of this, if you pass the class name?

And also, wouldn't the outside code have to have knowledge of what class it's actually casting?
It depends.
You can use CallSub without knowing the type:
B4X:
Dim SomeObjectThatBarks As Object = Factory1.Factory("some string that will create the correct class")
CallSub(SomeObjectThatBarks, "Bark")
 
Upvote 0

Sandman

Expert
Licensed User
There's a lot of good information in your post, I need a little time to unpack and consider the implications. But I think I probably have everything I need to move forward. Thanks!

This deserves a question, what is the purpose of all of this, if you pass the class name?
Well, my example in #1 was simplified to be bare-bones. This gives a slightly fuller picture:

- There are games G1..G100
- Each app (AppA, AppB etc) can contain up to 20 games (so, for instance, G1..G20 for AppA)
- At each given moment, only a subset of the games are available to the player
- Information about what games to make available to the player is received from my server - as a string
- Why would the strings (and therefore the games) change over time? Because there's also a website where the user has an account. And if they choose to enable a game for their account, that game will be available in the web - and also the mobile app.
 
Upvote 0

max123

Well-Known Member
Licensed User
Longtime User
Maybe doesn't exactly answer to your question, but see this discussion:
https://www.b4x.com/android/forum/threads/select-a-class-at-runtime.138929/
See last post.

I cannot use conditionals because the class to be used is selected at runtime by user.

Not super elegant way because I wanted to declare just the used class instead of both, but worked for me.
Hope this help
B4X:
Sub Class_Globals
    Private Viewer2D As GCodeViewer2D
    Private Viewer3D As GCodeViewer3D
 
    Private mUse3D As Boolean
End Sub

'Inizializes the gcode viewer object to the given Pane with a selected Zoom factor for grid.
'Zoom factor 1.0 is a non scaled grid.
Public Sub Initialize (Use3D As Boolean, Pnl As Panel, Zoom As Float, Antialiasing As Boolean)
    mUse3D = Use3D
    If mUse3D Then
        Viewer3D.Initialize(Pnl, Zoom, Antialiasing)
    Else
        Viewer2D.Initialize(Pnl, Zoom, Antialiasing)
    End If
End Sub

'Show the gcode viewer.
Public Sub Show(X As Int, Y As Int, Width As Int, Height As Int, InnerPaneWidth As Int, InnerPaneHeight As Int)
    If mUse3D Then
        Viewer3D.Show(X, Y, Width, Height, InnerPaneWidth, InnerPaneHeight)
    Else
        Viewer2D.Show(X, Y, Width, Height, InnerPaneWidth, InnerPaneHeight)
    End If
End Sub

'Resize the gcode viewer panel.
Public Sub Resize (X As Float, Y As Float, Width As Float, Height As Float)
    If mUse3D Then
        Viewer3D.Resize(X, Y, Width, Height)
    Else
        Viewer2D.Resize(X, Y, Width, Height)
    End If
End Sub

'Clear the viewer with specified color.
Public Sub Clear (Color As Int)
    If mUse3D Then
        Viewer3D.Clear(Color)
    Else
        Viewer2D.Clear(Color)
    End If
End Sub

'Fill the working plane with a specified color.
Public Sub FillPlane(Color As Long)
    If mUse3D Then
        Viewer3D.FillPlane(Color)
    Else
        Viewer2D.FillPlane(Color)
    End If
End Sub

'Sets the current drawing plane size of CNC machine expressed in Units (Millimeters or Inches).
'
'Example:
'<code>
'Dim Viewer As GCodeViewer3D
'Viewer.SetDrawingPlaneSize(200,200)   'Setup drawings for a machine that has plane 200x200 millimeters.  </code>
Public Sub SetDrawingPlaneSize(PlaneWidth As Float, PlaneHeight As Float)
    If mUse3D Then
        Viewer3D.SetDrawingPlaneSize(PlaneWidth, PlaneHeight)
    Else
        Viewer2D.SetDrawingPlaneSize(PlaneWidth, PlaneHeight)
    End If
End Sub

'Draw a grid based on CNC working plane dimensions (Units) you previousely setup with SetDrawingPlaneSize method.
'You can set Canvas background color, grid color and line width.
'
'The grid has separate settings for MinorTicks (millimeters), MediumTicks (centimeters) and MajorTicks (every 5 centimeters)
'
'Example:
'<code>Viewer.DrawGrid(Colors.Black, Colors.ARGB(100,255,255,255), Colors.ARGB(100,255,255,255), Colors.ARGB(100,255,255,255), .2, .4, .8)</code>
'
'Parameters:
'BackColor:  the viewer background color (NOTE: this is not a grid background only but full viewer background)
'MinorTicksColor:  the color of MinorTicks lines
'MediumTicksColor:  the color of MediumTicks lines
'MajorTicksColor:  the color of MajorTicks lines
'MinorTicksLineWidth:  the width of MinorTicks lines
'MediumTicksLineWidth:  the width of MediumTicks lines
'MajorTicksLineWidth:  the width of MajorTicks lines
Public Sub DrawPlaneGrid (BackColor As Int, MinorTicksColor As Int, MediumTicksColor As Int, MajorTicksColor As Int, MinorTicksLineWidth As Float, MediumTicksLineWidth As Float, MajorTicksLineWidth As Float)
    If mUse3D Then
        Viewer3D.DrawPlaneGrid(BackColor, MinorTicksColor, MediumTicksColor, MajorTicksColor, MinorTicksLineWidth, MediumTicksLineWidth, MajorTicksLineWidth)
    Else
        Viewer2D.DrawPlaneGrid(BackColor, MinorTicksColor, MediumTicksColor, MajorTicksColor, MinorTicksLineWidth, MediumTicksLineWidth, MajorTicksLineWidth)
    End If
End Sub

'Draw a plane border (squared or rounded) with the specified color and line width.
'Use SetStartPoint method to set a start position that by default is X = 0 , Y = 0, Z = 0.
Public Sub DrawPlaneBorders(RoundPlane As Boolean, Color As Int, LineWidth As Float)
    If mUse3D Then
        Viewer3D.DrawPlaneBorders(RoundPlane, Color, LineWidth)
    Else
        Viewer2D.DrawPlaneBorders(RoundPlane, Color, LineWidth)
    End If
End Sub

'Sets first drawing start point position from origin.
'By default X = 0, Y = 0, Z = 0 and assumed as CNC Home position.
'
'Z not used on 2D class (see 3D class), you can pass any numeric value like 0, the library just ignore it.
Public Sub SetStartPoint(X As Float, Y As Float, Z As Float)
    If mUse3D Then
        Viewer3D.SetStartPoint(X, Y, Z)
    Else
        Viewer2D.SetStartPoint(X, Y, Z)
    End If
End Sub

'Force the viewer redraw a full view.
Public Sub Redraw
    If    mUse3D Then Viewer3D.Redraw Else Viewer2D.Redraw
End Sub

Public Sub ResetRedrawingArea
    If mUse3D Then
        Viewer3D.ResetRedrawingArea
    Else
        Viewer2D.ResetRedrawingArea
    End If
End Sub

'Force the viewer redraw the working area rectangle.
Public Sub RedrawWorkingArea
    If mUse3D Then
        Viewer3D.RedrawWorkingArea
    Else
        Viewer2D.RedrawWorkingArea
    End If
End Sub

' PROPERTIES

'Gets current X Axis position.
Public Sub CurrentX As Float
    If mUse3D Then
        Return Viewer3D.CurrentX
    Else
        Return Viewer2D.CurrentX
    End If
End Sub

'Gets current Y Axis position.
Public Sub CurrentY As Float
    If mUse3D Then
        Return Viewer3D.CurrentY
    Else
        Return Viewer2D.CurrentY
    End If
End Sub

'Gets current Z Axis position.
Public Sub CurrentZ As Float
    If mUse3D Then
        Return Viewer3D.CurrentZ
    Else
        Return Viewer2D.CurrentZ
    End If
End Sub

'Gets or sets the current viewer zoom factor.
Public Sub setZoom(Zoom As Float)
    If mUse3D Then Viewer3D.Zoom = Zoom Else Viewer2D.Zoom = Zoom
End Sub
Public Sub getZoom As Float
    If mUse3D Then
        Return Viewer3D.Zoom
    Else
        Return Viewer2D.Zoom
    End If
End Sub

'Get round plane property.
'You can set it with DrawPlaneBorders method
Public Sub getRoundPlane As Boolean
    If mUse3D Then
        Return Viewer3D.RoundPlane
    Else
        Return Viewer2D.RoundPlane
    End If
End Sub

' VIEWS

'Returns a reference of viewer canvas object.
'You can use this to change any properties you like, add a style etc...
Public Sub getCanvas As Canvas
    If mUse3D Then
        Return Viewer3D.Canvas
    Else
        Return Viewer2D.Canvas
    End If
End Sub

'Returns a reference of viewer panel object.
'You can use this to change any properties you like, add a style etc...
Public Sub getPanel As Panel
    If mUse3D Then
        Return Viewer3D.Panel
    Else
        Return Viewer2D.Panel
    End If
End Sub

'Returns a reference of viewer ScrollPane object.
'You can use this to get InnerNode instance or change any properties you like, add a style etc...
Public Sub getScrollPane As ScrollView2D
    If mUse3D Then
        Return Viewer3D.ScrollPane
    Else
        Return Viewer2D.ScrollPane
    End If
End Sub

' //////////// DRAWING FUNCTIONS ////////////

'Draw a point in the specified position, color and line with.
'
'Z not used on 2D class (see 3D class), you can pass any numeric value like 0, the library just ignore it.
Public Sub DrawPoint(X As Float, Y As Float, Z As Float, Color As Int, PointWidth As Float)
    If mUse3D Then
        Viewer3D.DrawPoint(X, Y, Z, Color, PointWidth)
    Else
        Viewer2D.DrawPoint(X, Y, Z, Color, PointWidth)
    End If
End Sub

'Draw a line from X1,Y1 to X2,Y2 position with the specified color and width.
'
'Z1 and Z2 not used on 2D class (see 3D class), you can pass any numeric value like 0, the library just ignore it.
Public Sub DrawLine(X1 As Float, Y1 As Float, Z1 As Float, X2 As Float, Y2 As Float, Z2 As Float, Color As Int, LineWidth As Float, StartCup As Boolean, EndCup As Boolean)
    If mUse3D Then
        Viewer3D.DrawLine(X1, Y1, Z1, X2, Y2, Z2, Color, LineWidth, StartCup, EndCup)
    Else
        Viewer2D.DrawLine(X1, Y1, Z1, X2, Y2, Z2, Color, LineWidth, StartCup, EndCup)
    End If
End Sub

'Draw a line from last X,Y,Z position to the new X,Y,Z position with the specified color and width.
'Use SetStartPoint method to set a first start position that by default is X=0 Y=0 Z=0 (Home).
'
'Z not used on 2D class (see 3D class), you can pass any numeric value like 0, the library just ignore it.
Public Sub DrawLineTo(X As Float, Y As Float, Z As Float, Color As Int, LineWidth As Float, StartCup As Boolean, EndCup As Boolean)
    If mUse3D Then
        Viewer3D.DrawLineTo(X, Y, Z, Color, LineWidth, StartCup, EndCup)
    Else
        Viewer2D.DrawLineTo(X, Y, Z, Color, LineWidth, StartCup, EndCup)
    End If
End Sub

'Draw a circle in the specified position, radius, color, fill and stroke width.
'
'Z not used on 2D class (see 3D class), you can pass any numeric value like 0, the library just ignore it.
Public Sub DrawCircle(X As Float, Y As Float, Z As Float, Radius As Float, Color As Int, Filled As Boolean, StrokeWidth As Float)
    If mUse3D Then
        Viewer3D.DrawCircle(X, Y, Z, Radius, Color, Filled, StrokeWidth)
    Else
        Viewer2D.DrawCircle(X, Y, Z, Radius, Color, Filled, StrokeWidth)
    End If
End Sub

'Draw a text (2D) with the specified properties.
Public Sub DrawText(Text As String, X As Float, Y As Float, Typ As Typeface, Size As Float, Color As Int, Alignment As String)
    If mUse3D Then
        Viewer3D.DrawText(Text, X, Y, Typ, Size, Color, Alignment)
    Else
        Viewer2D.DrawText(Text, X, Y, Typ, Size, Color, Alignment)
    End If
End Sub

Public Sub TakeSnapshot
    If mUse3D Then    Viewer3D.TakeSnapshot Else Viewer2D.TakeSnapshot
End Sub
 
Last edited:
Upvote 0
Top