Our full technical support staff does not monitor this forum. If you need assistance from a member of our staff, please submit your question from the Ask a Question page.


Log in or register to post/reply in the forum.

Passing function arguments of undetermined data type


crs Apr 4, 2018 07:22 PM

I want to write a CRBasic function that takes an input parameter of any data type (string, float, long, ...), similar to what is done with the SprintF instruction, which can accept up to 10 different arguments of different types.  

Is this possible?  Alternately, I can call SprintF ahead of each call to my function, and just pass a string argument, but it would be cleaner if I didn't need this extra line of code for each call.  

On a related note, how do I pass an array of unspecified length to a function/subroutine?  


JDavis Apr 4, 2018 07:47 PM

To ultimately do what you are asking to do, you will need to use data pointers. 

There is an article on our blog that can get you started:

https://www.campbellsci.com/blog/pointers-make-crbasic-programs-efficient 

If you have used pointers in other programming languages, you need to understand that pointers in CRBasic point to the start of a 4 byte data chunk.

There are also some issues with pointers to string variables, but I don't remember the details of that.

The TypeOf() function was added for this use as well. You can use it to check what the data type is of the variable the pointer points to.


crs Apr 7, 2018 09:23 PM

Thanks for that advice.  I have been working on a solution using pointers and TypeOf(), but the help file isn't terribly clear on all this, and I couldn't find anything relating to pointers and strings, or specific to manipulating arrays.  

I wrote a function that accepts a single parameter pointer (As Long) and returns As String.  Inside the function, I use TypeOf() on the pointer, and then SprintF() to write the value of the passed variable as a string, which is then returned by the function.  There is more to my implementation, but below shows the relevant bits to my problem.  

Function ToString (ptr_Variable As Long) As String * 20

    Dim str_fmt As String * 10
    Dim str_temp As String * 20

    Select Case TypeOf(!ptr_Variable)
        Case 6         ' Long
            str_fmt = "%d"
        Case 9         ' IEEE4
            str_fmt = "%f"
        Case 11        ' String
            str_fmt = "%s"
        Case 28        ' Boolean
            str_fmt = "%d"
    EndSelect

    SprintF (str_temp,str_fmt,!ptr_Variable)
    Return str_temp

EndFunction

Calls to the function are of the form:

my_string_variable = ToString (@my_float_variable))

Unfortunately this is not working properly, and there are a couple of things that are confusing me.  

If I assign the pointer to a separate Long variable in the main program (ptr = @my_float_variable), then !ptr gives the value of the original variable.  When I de-reference the pointer variable inside the function the value is not correct.   

Furthermore, de-referencing the pointer in TypeOf (!ptr_Variable) returns a zero -- not the type of the original variable as I would expect.  I can get a valid output for TypeOf(ptr_Variable), but of course this for the pointer variable itself, which should always be Long. 

How can I determine what type of variable has been passed into the function?  How can I access the value of that variable from within the function?

Also, I'm not sure on how this relates, but I noted the following lines from the CRBasic help file that seem as they might have some relevance. 

By default, pointer variables are of type Long, but they can be typed using Float!, Long!, Boolean!, or String! (e.g., Dim P as Float!). If pointers are not typed, the summation of the values pointed to truncates the floats before the addition.


Sam Apr 17, 2018 04:36 AM

Charles,

You are experiencing two issues.

(1) TypeOf(!Input_Pointer) is not working as expected in the current released OS when Input_Pointer is passed into a subroutine or function. This has been changed in the current beta OS that is under test here at CS.

(2) SprintF() is not accepting a dereferenced pointer as an input argument. This needs to be looked at and may or may not be changed.

I don't know if your example is actual, but it seems like you could accomplish this much easier by just using the auto-casting behavior of CRBasic.

For example 

my_string_variable = my_float_variable

 

Or use the CType() instruction available in some of the dataloggers, such as 

my_string_variable = CTYPE(my_float_variable,STRING)

If this function is the best way to solve the problem at hand, then for now you will have to create some work arounds. Something like

 

Public my_float_variable As Float
Public my_string_variable As String * 20

Function ToString (ptr_Variable As Long,var_type As Long) As String * 20

  Dim str_temp As String * 20
  Dim l As Long, f As Float
  Dim s As String * 20, b As Boolean

  Select Case var_type
  Case Long
    l = !ptr_Variable
    Sprintf (str_temp,"%d",l)
  Case Float
    f = !ptr_Variable
    Sprintf (str_temp,"%f",f)
  Case String
    s = !ptr_Variable
    Sprintf (str_temp,"%s",s)
  Case Boolean
    b = !ptr_Variable
    Sprintf (str_temp,"%d",b)
  EndSelect

  Return str_temp

EndFunction

BeginProg
  Scan (1,Sec,0,0)
    my_string_variable = ToString (@my_float_variable,TypeOf(my_float_variable))
  NextScan
EndProg

 


crs Apr 17, 2018 07:24 PM

Thanks for that.  I have been trying to learn about CRBasic pointers using materials that are based on C/C++, and aside from the syntax differences there are a few other things that don't make for an easy comparison.  So it is at least some consolation that I had the concepts right, and that the issue is out of my hands.  

My example was actually just a simplification of what I have really been trying to do, which essentially involves maintaining a "stack" of variables (of any data type) that I can use to perform comparisons or calculations against the current value of that variable.  I was using SprintF() to store a representation of the value in a global string variable array, rather than maintaing separate global variable arrays for each data type.  Maybe there is a better way to accomplish this.  

Since the pointers are not working properly I won't copy the complete (non-working) code here, but the algorithm consists of

1) checking whether the variable is currently on the stack,

Do While (Not Found)
    i = i + 1
    If (ptr_Stack(i,1)=ptr_Variable) Then Found = True
    If (ptr_Stack(i,1)=0) Then ExitDo
Loop

 2) returning that existing value representation if it does,

If (Found) Then str_LastValue = str_Stack(i)

 3) and then updating or adding the new variable and its value representation to the stack.  

If (Not Found) OR (Found AND !ptr_Stack(i,2)<>str_temp) Then
    Changed = True
    str_Stack(i) = str_temp
    ptr_Stack(i,1) = ptr_Variable
    ptr_Stack(i,2) = @str_Stack(i)
EndIf

Where ptr_Stack(n,2) holds pointers to the original variable and to the string representation, and str_Stack(n) holds the string representation of the variable.  

As I said, I am new to pointers so I don't know if this would have worked, but I have set it aside for now.  If anyone has thoughts on how to make something like this work I would love to hear ideas.  

On a related note, it would be great if someone with a solid understanding of using pointers in CRBasic could provide a brief tutorial similar to those that exist for C -- perhaps just taking one of the C tutorials and making edits as appropriate.  

Log in or register to post/reply in the forum.