An old C programmer, I’m new to Processing (and not particularly skilled at C++, come to that…), so naturally I’m banging up against the stronger type discipline of Object Oriented languages, and could use a spot of help…
I’m writing code based on the Arduino/Processing example pair “SerialCallResponse”, which transfers bytes on request.
Instead of simple bytes/integers, want to transfer floats in my data. Sending them from Arduino is simple (a simple union of 4 bytes and a float was all I needed to feed Serial.write()), but how to I read four bytes from Processing’s receive buffer into a float?
Excellent, thank you.
In a related question - and please forgive my ignorance of what probably appears basic…
In C I could pass a pointer to an integer (i.e. a primitive, not an object) to a function and have that function increment it (fairly obviously I’m talking about an array index). As Java doesn’t work that way (as well described here
http://www.javadude.com/articles/passbyvalue.htm
how would I achieve the same functionalilty?
Either you return
the value and assign it to the variable you wanna mutate or… use an object, which can be freely mutated.
For the latter, you can for example pass an int[]
array instead of a vanilla int
.
P.S.: Almost forgot: Declare a global field, so it can be reassigned everywhere!
Ah, so simply making it an array - even if it’s a single element array - promotes it from primitive to object class, so that the “pass by reference” opportunity exists; I get it
Global - got that too.
Thanks again:-)
OK, so I seem to have misunderstood the concept of marking a post as a solution. GoToLoop provided my solution, I didn’t.
yes, you can change that by click on the correct post.
Just to clarify, you are still passing a value – the value is a pointer to an array (or an Object).
This matters because the pointer destination can be modified, but the receiving function can’t reassign things to the reference label that was used to pass that value, because it doesn’t have that reference – it only has the pointer value.
void setup() {
int[] a = new int[]{0, 0};
increase(a); // yes -- can modify by passing value of array pointer
println(a); // {0, 1}
replace(a); // no -- cannot point 'a' at new contents, reference not passed
println(a); // {0, 1} -- not {9, 9}
}
void increase(int[] i) { // succeeds, value is pointer
i[1] = i[1]+1;
}
void replace(int[] i) { // fails, not pass by reference so cannot modify the calling i
i = new int[]{9, 9};
}
The classic example of this difference in Java is a simple function to “swap” two arrays or Objects provided as arguments, which fails – one example with detailed discussion is here:
Yes, I do understand.
It seems to me that there are four distinct but related core issues that, when not explained together, contribute to and perpetuate the misunderstanding.
The first is a problem of semantics: ALL data items passed as arguments are “values” in that they are a copy of - the value - of the data items passed. The significance is in how that item is interpreted - i.e. what you can DO with that value. If it is a primitive, then it is merely a copy of - the value of - the original data item; and you can use the information contained in it, whereas if it is a pointer, then while it is still a copy of - the value of - the original data item - that original data item is a pointer to (“a means by which we gain direct access to”) another data object. So if we pass a pointer to a data item, we can reach across and manipulate the item to which the pointer points (I resist the temptation to say “refers”.)
So in fact, it doesn’t matter if the argument passed is a primitive or a pointer to a complex data item, then the original instance of that argument cannot be changed by the called function/method - the only thing that can be changed is the actual data item that argument points to, IF that argument is a pointer.
The second is the “new” command. Java is inconsistent in that to create a primitive variable one has only to declare the type and the name of the new item:
int myInt;
but for a complex data item, we use the “new” command to actually allocate the space…:
myThing = new Thing;
and in this case “myThing” is a pointer, a handle by means of which the new Thing may be manipulated (not an Apple “Handle” from back in the day - that’s a pointer to a pointer).
So if we say
Thing aThing;
we are creating only a pointer to a complex data item - a means by which we might manipulate some real complex data item inf the future - NOT an actual item. Old C or assembly language programmers like me need to make sure we’re not tripped up by that!
Anyway, when we pass “myThing” we are passing a copy of - the value of - the variable “myThing”, which is only a pointer, which refers to - gives us access to - the Thing data item we just created. If we pass “aThing”, then we are doing the same thing: passing only the the value of the pointer, except that in this case hasn’t been initialized to a real data item yet.
The third is our over-simplified English language. When we say “We can’t change what myThing points to”, or even “We can’t change the data item to which myThing points”, we are NOT necessarily saying “we can’t change the actual item to which myThing happens to be pointing”. Grammatically those expressions are NOT the same at all, but the first two are dangerously and ambiguously similar to the third, and it is anybody’s guess which of those two critically different meanings the reader will internalize when those words are spoken. It’s a classic “who’s on first”.
The fourth: the syntactical elements which are glaringly absent from Java. You cannot say
&myPassedValue
in Java; it is simply not allowed. You cannot “back up” and fiddle with the original of the passed value; and you cannot/have no need to say
*myPassedValue
because if it’s a primitive then that construct makes no sense, and if it isn’t then Java already knows to treat it as a pointer and to dereference it.
THAT, to me, is the core of the issue, and to simply label the problem as “by reference vs by value” is to miss the point.
I think 90% of programming problems are communications problems. Here’s another example of the English language communication problem I mentioned above.
“I want to change my car”. Does that mean “I want to modify the car I have - maybe paint it, or put stiffer suspension in it” or does it mean “I want to sell my car and buy another”?
In the first instance I’m modifying some characteristic of the same car, while in the second I’m replacing the target of the token expression “my car” with an entirely different object. Our language doesn’t provide any formal grammatical way to disambiguate between those two interpretations, and we would rely on common usage to know what is meant.
Context and custom figure heavily in speech: by way of example, and if you will please forgive the sexism, if a woman is heard to say “I want to change my husband”, it will likely be understood - for ugly historical and cultural reasons - to mean a different thing than if a man says “I want to change my wife”. Maybe “I want to change my hair” vs “I want to change my shirt” is a less provocative but equally valid example, and purposeful ambiguation might make for some passable humor. But what if my hair is in the form of a wig? Now what does “change” mean? What if I’m at my tailor, being fitted for a tux? What does “change the shirt” mean now? Alter? Replace?
The comical ambiguities above might easily be resolved by saying “replace”, “modify” or “alter” instead of “change”, but in software it isn’t as easy, because culture doesn’t apply: saying “change what myPtr points to” in a discussion of language behavior could legitimately mean either, yet the difference is monumental, and central to the issue. Personally, in this context I think of “change” and “modify” as in the “hair” example above, and “replace” as in the “shirt” example, but that’s just me.
In the discussions and arguments I’ve seen online, people seem to talk as if the terms are immutable, but they really aren’t, and I haven’t even begun to address the challenge experienced by people for whom English is not the primary language…
So, to add my own “old C programmer” take on this “by value vs. by reference” thing, I think of a function argument as C’s “rvalue”: you cannot change an rvalue, only use the information contained in it. Yes, technically, you can do it locally within the function, of course, but that changes only the local copy, not the “real” data item. The data object to which a pointer refers, however, is an lvalue, and can be changed.
A silly, illegal way of demonstrating the difference might be this:
myInt = new int(5); // yes, I know you can't do that.
then you could pass myInt as a pointer to the new int, and change the original somehow…
void myBumpFunc(int intPtr) {
intPtr.int += 5;
}
you can’t do that, of course…
but you CAN create a single element array of integers…
int[] myInt = new int[1];
“myInt” is a pointer to an array object containing a single integer, and we can pass it to a function to give access to the original:
void myBumpFunc(int[] iP) {
iP[0] += 5;
}
but hey, who’d want to do that?
IMO, most of this confusion arises b/c we’re taught to associate a variable to its data content as if they were the same thing.
When we declare a variable, we’re just reserving a tiny contiguous block of memory addresses (bytes), which in most languages they vary from 1 to 8 bytes.
The variable itself is merely a label for the 1st memory address of that memory block.
In Java, only the content of a variable’s memory block can be passed around to other variables or to function calls.
We can’t pass the memory address of the variable itself!
In some system languages like C, they have an &
unary operator, which forces a variable spit out its memory address instead of the value its memory address block stores.
So we need to mentally separate the variable from its content if we really wanna understand how things work internally.
I tried your code - both the single float and the multiple - but couldn’t get it to behave… synchronization, maybe? Seems I should set up a call/response mechanism to synchronize the start of a block of values?
- You should post your own attempt.
- That sketch example relies on the combo noLoop() / redraw().
- So it doesn’t need any synchronization at all.
I think you think I mean screen sync; what I mean is sync with the start of the data block. I see no mechanism to identify the beginning of the block. I’m probably missing something…
I don’t have the hardware. But I believe the Processing sketch should fully start 1st (go thru’ setup() till the end) before Arduino can start sending bytes to it.
Agreed, but I see no mechanism to force that. I’ll need to build your code in a call-response structure. As I said, I’m not very fluent with the “object.method(parameter/arg)” structure of Java, and the more dots there are the more difficult it is for me to decode. I can’t yet get my head around
FloatBuffer buf = ByteBuffer.wrap(bytes).asFloatBuffer();
or
buf.get(floats).rewind();
for example; I’m accustomed to the “verb(parameter/arg)” of traditional procedural programming. More than one dot confuses me !!
We use the dot .
access operator right after a reference in order to access 1 of its members:
Therefore we need to know the available members we can access from a reference before we can use the dot .
operator on it.
Let’s examine the expression ByteBuffer.wrap(bytes).asFloatBuffer() which is immediately assigned to declared field FloatBuffer buf.
ByteBuffer is a class
reference:
Docs.Oracle.com/en/java/javase/11/docs/api/java.base/java/nio/ByteBuffer.html
B/c it is a class
, and not an object derived from it (instance), we can only access static
members from it via the dot .
operator.
So we invoke its public static
member method wrap() via the dot .
operator:
Docs.Oracle.com/en/java/javase/11/docs/api/java.base/java/nio/ByteBuffer.html#wrap(byte[])
According to that method’s doc, it says it returns a new
ByteBuffer object.
So the 2nd dot .
operator acts upon what is returned by method wrap(), which is an instance of ByteBuffer, allowing us to access its non-static
members as well.
So now we invoke its instance method asFloatBuffer():
Docs.Oracle.com/en/java/javase/11/docs/api/java.base/java/nio/ByteBuffer.html#asFloatBuffer()
Which states it returns a new
FloatBuffer object:
Docs.Oracle.com/en/java/javase/11/docs/api/java.base/java/nio/FloatBuffer.html
We stop chaining dot .
operators; and that last returned reference (AKA memory address or pointer) is finally assigned to field buf of datatype FloatBuffer:
FloatBuffer buf = ByteBuffer.wrap(bytes).asFloatBuffer();
As you can see from its Java doc link above, FloatBuffer is an abstract class
that lacks a formal public
constructor.
Although we can also get an instance of it via its static
methods like allocate() & wrap():
Docs.Oracle.com/en/java/javase/11/docs/api/java.base/java/nio/FloatBuffer.html#allocate(int)
Docs.Oracle.com/en/java/javase/11/docs/api/java.base/java/nio/FloatBuffer.html#wrap(float[])
However, its static
method wrap() accepts a float[]
array only.
But we really need to pass a byte[]
array b/c method Serial::readBytes() doesn’t work w/ float[]
:
That’s why we had to indirectly get an instance of it via method ByteBuffer::asFloatBuffer().
Now let’s check the statement: buf.get(floats).rewind();
We use the dot .
operator in order to invoke method FloatBuffer::get() over field buf, which stores the reference of a FloatBuffer object:
Docs.Oracle.com/en/java/javase/11/docs/api/java.base/java/nio/FloatBuffer.html#get(float[])
That method informs us that it returns a FloatBuffer as “This buffer”.
It means it’s not a new
instance but the same object that had been used to invoke it.
That is, the very reference stored in our field buf.
Such methods are said to be chainable, b/c they return
itself, allowing us to keep on accessing members of the same reference in sequence via multiple dot .
usage.
So the 2nd dot .
operator invokes method rewind() over the same buf:
Docs.Oracle.com/en/java/javase/11/docs/api/java.base/java/nio/Buffer.html#rewind()
Notice though method rewind() comes from its superclass Buffer.
Obviously, we coulda split the expression in 2:
buf.get(floats);
buf.rewind();
Thank you for that excellent explanation