Android Question Help! Getting and using the name of a variable/array?

Status
Not open for further replies.

wonder

Expert
Licensed User
Longtime User
Hi guys!

This might be hard to explain, but I'll do my best. Consider the following pseudo-code:
B4X:
Dim a, b, c, d, e, f, g, ... As Int
GetUserInput("Which variable? ", VarName)
SetVar(VarName, 10)

'Output:
'(c = 0)
'Which variable? c
'(c = 10)

How can I do this without having to do this with lots of IF's or CASE statements?
B4X:
Sub SetVar(VarName as String, Value as Int)
    If VarName = "a" Then a = value
    If VarName = "b" Then b = value
    If VarName = "c" Then c = value
    If VarName = "d" Then d = value
    ...
End Sub

I would like to do more advanced stuff like creating variables on the fly:
B4X:
GetUserInput("Create which variable? ", VarName)
SetVar(VarName, 10)

'Output:
'(in the beginning, the variable "c" doesn't exist.)
'Which variable? c
'(the "c" variable is created through something like:
' Dim !VarName! As Int
' !VarName! = 10)

Is there anyone who can help me? :)
 
Last edited:

sorex

Expert
Licensed User
Longtime User
this

B4X:
Sub Globals
Dim id As InputDialog
Dim m As Map
End Sub

Sub Activity_Create(FirstTime As Boolean)
m.Initialize
m.Put("a",1)
m.Put("b",2)
m.Put("c",3)
m.put(getuserinput("which variable"),10)
Log(m.Get("a"))
Log(m.Get("b"))
Log(m.Get("c"))
End Sub

Sub getuserinput(t As String) As String
id.Show(t,t,"ok","","",Null)   
Return id.Input
End Sub

outputs

** Activity (main) Create, isFirst = true **
10
2
3
** Activity (main) Resume **


after entering "a" in the dialog
 
Upvote 0

sorex

Expert
Licensed User
Longtime User
it also suits your "advanced" request.

use this
B4X:
Sub Activity_Create(FirstTime As Boolean)
m.Initialize
m.put(getuserinput("which variable"),10)
Log(m.Get("a"))
Log(m.Get("b"))
Log(m.Get("c"))
End Sub

and it will generate them on the fly and output

B4X:
** Activity (main) Create, isFirst = true **
10
null
null
** Activity (main) Resume **
 
Upvote 0

wonder

Expert
Licensed User
Longtime User
@sorex
First of all, thanks for posting.
However, according to your example, all the "variables" are stored in a map.
I see that how it's a proper solution for my request, but only when writing new code.
The problem with such implementation is that, at this point, I would have to restructure the whole program.
 
Upvote 0

sorex

Expert
Licensed User
Longtime User
well, that's your problem then ;)

no, maybe you should've given an example that reflected you structure as you didn't mention any limitation or something like that.

what keeps you from adding another map?
 
Upvote 0

wonder

Expert
Licensed User
Longtime User
I wanted to implement a small scripting language on my engine. While using maps is a great idea for a fresh new project, I'm trying to work with what I already have created. :) I will analyze all the options and chose the most efficient one.
 
Upvote 0

JordiCP

Expert
Licensed User
Longtime User
You can do it without maps, at least for simple variables

This will work for Process Globals and Activity Globals.

Even if in B4A the variable names are uppercase or mixed, you must refer to them in lowercase, and with a leading underscore ("_")
Also, you must change the initting string for OO according to your app package name and module name (perhaps there is another way)

B4X:
Sub Process_Globals
    Dim OO As JavaObject
    Dim ccc As Int = 4444
End Sub

Sub Globals
    Dim aaA1 As Int =5555    'Example variables.
    Dim AAa2 As Int =6666
    Dim AaA3 As Int =7777
End Sub

Sub Activity_Create(firsttime As Boolean)
   
    OO.InitializeContext

    'CAUTION: You should change the string below for <yourpackagename>.<main> (or the module name if not 'main')
    OO=OO.InitializeNewInstance("jcp.b4a.thirdlib.main",Null)

    '--------------------------------------------------    
    ' Do this To get the value of a variable
    '--------------------------------------------------
    Dim myVar as String = "_aaa1"  'Lowercase and with a leading "_" 
    Log(OO.GetField(myVar))  '--> This will return 5555

    '--------------------------------------------------    
    ' Try to set a new value
    '--------------------------------------------------
     Dim myNewVal As Int =1111
    'OO.SetField(myVar,Array(myNewVal))    'This doesn't work for me (crashes)
   
    Dim res As Int = OO.RunMethod("SetClassValue",Array(myVar, myNewVal)) ' 0:ok, 1:not such field, 2:could not assign value
      
    Log(OO.GetField( myVar )    'Both are the same. Should give 1111
    Log(aaA1)
End Sub

#If java

  import java.lang.reflect.Field;

    public int SetClassValue(String str, int val){
   
        Field aField;   
        try {
   
            aField= getClass().getDeclaredField(str);
        }   
        catch(Exception e){
            return(-1);    // Not such field
        }   
        try {
            aField.set(this, val);   
        }   
        catch(Exception e){
            return(-2); // Could not assign value
        }
        return(0);

    }
#End If

This will only work for ints, but you can change to whatever
 
Upvote 0

Ed Brown

Active Member
Licensed User
Longtime User
Hello @wonder

I think I understand what you are wanting to do. In .NET one would use Reflection to access variables in the way that you want. To date, I haven't found anything in B4A that comes anywhere near close to the .NET Reflection capabilities. The solutions provided by @sorex and @JordiCP are good options without running into potential obfuscation issues in your production release.
 
Upvote 0

sorex

Expert
Licensed User
Longtime User
my solution is 3 lines of code, how hard could it be to implement it?

What have you already created? some details might help us to give better advice.
 
Upvote 0

JordiCP

Expert
Licensed User
Longtime User
I "guess" the key here is that @wonder wants to have access to already existing variables, for instance, parameters of a phisics engine, which are spread through the code, and "test", in real time, how these changes work

Maps is a very elegant solution, but vars are copied by value, not by reference. So it would be necessary, for each already existing var, to get its new value each time the map is changed (or replace the lines where they are used by map.get(key)
 
Upvote 0

wonder

Expert
Licensed User
Longtime User
JordiCP said:
You can do it without maps, at least for simple variables
Thank you so much, I believe that's exactly what I'm after. Indeed sorex's solution is elegant, but for my current needs, you approach seem to be the right one.

I "guess" the key here is that @wonder wants to have access to already existing variables, for instance, parameters of a phisics engine, which are spread through the code, and "test", in real time, how these changes work

Maps is a very elegant solution, but vars are copied by value, not by reference. So it would be necessary, for each already existing var, to get its new value each time the map is changed (or replace the lines where they are used by map.get(key)
@sorex, here's your answer. @JordiCP, you're absolutely right! Especially in the "parameters of a phisics engine, which are spread through the code". :)
 
Upvote 0

cimperia

Active Member
Licensed User
Longtime User
I think your code needs refactoring. Too bad B4x editors lack this basic functionality.
 
Upvote 0

JordiCP

Expert
Licensed User
Longtime User
This one is more tricky.;)

Here the modified code to work also with arrays

B4X:
Sub Process_Globals
    Dim OO As JavaObject
    Dim ccc As Int = 4444
End Sub

Sub Globals
    Dim aaA1 As Int =5555
    Dim AAa2 As Int =6666
    Dim AaA3 As Int =7777
    Dim zzz(3) As Int
End Sub

Sub Activity_Create(firsttime As Boolean)
   
    OO.InitializeContext
   
    'You should change the string below for <yourpackagename>.<main> (or the module name if not 'main')
    OO=OO.InitializeNewInstance("jcp.b4a.thirdlib.main",Null)

    '--------------------------------------------------    
    ' Do this To get the value of a variable
    '--------------------------------------------------
    Log(OO.GetField("_aaa1"))  'Lowercase and with a leading "_"
   
    '--------------------------------------------------    
    ' Try to set a new value
    '--------------------------------------------------
    Dim newval As Int =1111
    ' Dim newval As String ="dddddd"
    'OO.SetField("_aaa1",Array(val))    'This doesn't work for me (crashes)
   
    Dim res As Int = OO.RunMethod("SetClassValue",Array("_aaa1",newval)) ' 0:ok, 1:not such field, 2:could not assign value
     
    Log(OO.GetField("_aaa1"))    'Both are the same. Should give 1111
    Log(aaA1)
   

    '------------------------
    ' Arrays
    '------------------------
    zzz(0)=10000:zzz(1)=20000:zzz(2)=30000
    Log("Array values before:"& zzz(0)&","&zzz(1)&","&zzz(2))
   
   
    For index=0 To 2
        Dim CurrentVal As Int
        'This function to get a value from an array
        CurrentVal = OO.RunMethod("GetIntArrayValue",Array("_zzz",index))
        'This one to modify it
        Dim res As Int = OO.RunMethod("SetIntArrayValue",Array("_zzz",index,CurrentVal+1)) ' 0:ok, 1:not such field, 2:could not assign value
    Next  
    Log("Array values after: "& zzz(0)&","&zzz(1)&","&zzz(2))
   
End Sub

#If java

  import java.lang.reflect.Field;

    public int SetClassValue(String str, int val){
   
        Field aField;  
        try {   
            aField= getClass().getDeclaredField(str);
        }  
        catch(Exception e){
            return(-1);    // Not such field
        }  
        try {
            aField.set(this, val);  

        }  
        catch(Exception e){
            return(-2); // Could not assign value
        }
        return(0);
    }
   
    public int GetIntArrayValue( String str, int index ){
   
        int a[];
        try{
            a = (int[]) getClass().getField(str).get(null);
        }
        catch(Exception e){
            return(-1);    // Not such field
        } 
        return (a[index]);        
    }
   
    public int SetIntArrayValue( String str, int index, int value ){
   
        int a[];
        try{
            a = (int[])getClass().getField(str).get(null);
        }
        catch(Exception e){
            return(-1);    // Not such field
        } 
        try {
            a[index]=value;  
        }  
        catch(Exception e){
            return(-2); // Could not assign value
        }
        return(0);

    }
   
   
   
#End If
 
Upvote 0

JordiCP

Expert
Licensed User
Longtime User
Thanks! Next step would be to have access to types/structs...:)

I think it could be really useful to put it as a library or class module so that it can be added to any project and do real-time var debugging /parameter tunning without being tied to the IDE

Let's see if I find some time or someone else does it!
 
Upvote 0
Status
Not open for further replies.
Top