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?
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.
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.
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
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.