Can I simplify this code - somewhat like I could with Python

The first snippet here is taken from an untested program that I am writing to update some servos. (If there is an error in it, please ignore it, it is not relevant to my question).

The second snippet illustrates very roughly how that sort of code could be implemented in a Python style. The principal difference is the long-winded and error prone activeServos[n] can be replaced by as. And, for me at least, the second version is much easier to read..

I know the second snippet won't work in C++ but I'm wondering if there is something similar that can be done?

for (byte n = 0; n < numActiveServos; n++) {
	if (activeServos[n].curState == 'M') {
		activeServos[n].servo.attach(activeServos.servoPin);
		activeServos[n].curMicros = activeServos[n].startMicros;
		activeServos[n].curState = 'm';
	}
	if ((activeServos[n].curState != 'F') and (activeServos[n].curMicros < activeServos[n].endMicros)) {
		activeServos[n].curMicros += servoStep;
		activeServos[n].servo.writeMicroseconds(activeServos[n].curMicros);
	}
	else {
		activeServos[n].servo.detach();
		activeServos[n].curState = 'F';
	}
}
for (byte n = 0; n < numActiveServos; n++) {
	as = activeServo[n];
	if (as.curState == 'M') {
		as.servo.attach(as.servoPin);
		as.curMicros = as.startMicros;
		as.curState = 'm';
	}
	if ((as.curState != 'F') and (as.curMicros < as.endMicros)) {
		as.curMicros += servoStep;
		as.servo.writeMicroseconds(as.curMicros);
	}
	else {
		as.servo.detach();
		as.curState = 'F';
	}
}

...R

TheStructType& as = activeServo[n];
or
auto as = activeServo[n];

edit:
auto& as = activeServo[n];

auto without & would make a copy

Make activeServos an array of pointers or references to the servo objects, or simply create a pointer or reference from the desired element of the current array as the first line of the for loop.

Regards,
Ray L.

Juraj:
TheStructType& as = activeServo[n];
or
auto as = activeServo[n];

Thanks. That looks neat - I will try it.

I especially like the "auto" which makes a complete nonsense of all the strict data typing :slight_smile:

...R

Robin2:
Thanks. That looks neat - I will try it.

I especially like the "auto" which makes a complete nonsense of all the strict data typing :slight_smile:

...R

so here is no doubt about the data type. and it is determined by compiler, not at runtime

for (<StructType>& Servo : activeServo)
{
   Servo.currentState = 'M';
}

Also,

auto as = activeServo[n];

auto does not resolve to a reference. You need to use:

auto& as = activeServo[n];

arduino_new:

for (<StructType>& Servo : activeServo) {

Does that iterate over all the elements of the array activeServo in the same way that this Python code does

for Servo in activeServo:

If so that is even better

...R

Robin2:
Does that iterate over all the elements of the array activeServo in the same way that this Python code does

for Servo in activeServo:

If so that is even better

...R

Yes.

The concept in Reply #5 works very nicely. I can see myself using it regularly.

For other readers here is a short program that illustrates it

int testArray[] = {12,33,11,47,9,1234};

struct testStruct {
    int val1;
    char str[12];
};

testStruct stArray[2] = {{19, "hello"}, {23, "world"}};

void setup() {
    Serial.begin(115200);

    //~ for (int& aR : testArray) { // this also works
    for (auto &aR : testArray) {
        Serial.println(aR);
    }
    Serial.println();

    //~ for (testStruct& tS : stArray) { // this also works
    for (auto& tS : stArray) {
        Serial.println(tS.str);
    }
}

void loop() {
}

...R

In "plain C", I would've used something like:

for (byte n = 0; n < numActiveServos; n++) {
	Servo_t *as = &activeServo[n];
	if (as->curState == 'M') {
		as->servo.attach(as.servoPin);
		as->curMicros = as.startMicros;
		as->curState = 'm';
	}
   :

westfw:
In "plain C", I would've used something like:

for (byte n = 0; n < numActiveServos; n++) {

Servo_t *as = &activeServo[n];
if (as->curState == 'M') {
as->servo.attach(as.servoPin);
as->curMicros = as.startMicros;
as->curState = 'm';
}
  :

Another option using old-school 'C':

Servo_t *as = activeServo + n;

westfw:
In "plain C", I would've used something like:

	Servo_t *as = &activeServo[n];

Just out of curiosity why is the & on the other side in your code compared to the version at the bottom of Reply #5

auto& as = activeServo[n];

...R

@westfw's code uses the "Address Of" operator.

Reply #5 is specifying that 'as' is a reference to the type determined by 'auto'.

gfvalvo:
@westfw's code uses the "Address Of" operator.

Reply #5 is specifying that 'as' is a reference to the type determined by 'auto'.

That sounds as if "&" has a different meaning depending on which side of the "=" it finds itself.

In my limited knowledge I thought it meant "address of" on all occasions. In my own code I styled it like this

auto &tD = turnoutData[n];

which I have been reading as "tD has the address of turnoutData[n]"

(It's bad enough that "*" has multiple meanings)

...R

& can be either "address of" or "reference", depending on context.

char s = "hello";

char *p = &s[3];    // &s[3] is a pointer to the third character of s

char &q = s;          // q is a reference to the string s

char& q = s;          // identical to above

Regards,
Ray L.

auto &tD = turnoutData[n];

Means that tD is a reference to turnoutData[n]. The 'auto' lets the compiler determine the type for you. Otherwise you'd have to do:

uint16_t &tD = turnoutData[n];

Or, whatever data type the turnoutData array is.

I like TypeName& variableName with & at type name because the type is a reference type. In TypeName* ptr the variable is of pointer type so I write * next to the type.
In &variable as right side or parameter assignment the & is an operator.

Juraj:
I like TypeName& variableName with & at type name because the type is a reference type.

I have never been able to get my head around that style. When I see it I can't read it.

When I see TypeName &variableName I can immediately see that variableName holds an address

It's all the fault of the lunatic that chose to use "&" and "*" to identify pointers and their content

...R

& and * were "address of" and "pointer to" from day one of the original K&R c language, back on the '70s. The double usage of & came with c++ in the mid/late '80s. c++ is an after-the-fact retrofit of OOP into c, which will always come with some compromises in the syntax. In this case, there are simply a limited number of operator characters to work with, and "breaking" the traditional c syntax was NOT an option. So, the new syntax had to be a super-set of the old, not a change to existing syntax.

The meaning is quite simple, if you simply look at the syntax - look at WHERE the operator is, in context: There can be no confusion.

If the & appears in a new variable or function argument definition, between athe typename (on the left), and the variable name (on the right), then it is the reference operator.

If the & appears outside a variable or function argument definition, immediately preceding a variable name, it is the address of operator.

It doesn't get much simpler than that. Might as well complain that {} are used to delimit scope when you think [] should have been used. Learn the language, and it makes perfect sense. But it's silly to the language just because you won't take the time to understand it.

Regards,
Ray L.

RayLivingston:
If the & appears in a new variable or function argument definition, between athe typename (on the left), and the variable name (on the right), then it is the reference operator.

If the & appears outside a variable or function argument definition, immediately preceding a variable name, it is the address of operator.

Your description is very clear but in my Neanderthal brain I can see no reason why they aren't both called "address of" - after all, that's what it does.

...R