Assigning Structure Values

QUESTION: I've been beating my head on this and can't find anything that answers my problem. (Which is usually a sign to me that I am trying to do something really wrong/inefficiently/stupidly, and yet in this instance I persist.)

For reasons that aren't important to my question, I want to have a pointer to an array of structures. I've stripped the problem down to the bare essentials here. (Which was the first thing I did to try to make sure I wasn't making some other larger error.) Making this pointer is no problem. Reading the variables is no problem. It's writing to the variables I'm having a problem with. The best explanation of my problem is a few lines of code:

   ; Set up our crazily designed ptr to an array of structures
   a = ptr_new( replicate( {x:0d}, 5 ) )

   ; We *can* set the whole variable at once, e.g.:
   (*a).x = dindgen(5)
   print,(*a).x    ; print everything
   print,((*a)[0]).x   ; print just the first element

   ; That's all good, but what if you want to set just the first element
   ; equal to something?
   ((*a)[0]).x = 99d

   ; This gives the following error:           
   % Attempt to store into an expression: Structure reference.

One way around this is to do something dumb, like:

   temp = (*a).x
   temp[0] = 99d
   (*a).x = temp

But, I can see no other way around this, aside from redesigning how I'm storing the information. Does anyone see how to do the equivalent of "((*a)[0]).x = 99d" in the above example? (Without the awkward three line dumb hack I've shown.) I'm sure I'm just not seeing something simple....

ANSWER: What the user was looking for is this expression:

    (*a)[0].x = 99d 

This, natually, caused the user deep consternation, with subsequent wailing and knashing of teeth over the ease with which the answer was pointed out to him. But, in fact, it is a common problem and one that can often be solved by a slow reading of the operator precedence tutorial, which provides a useful rule of thumb on when and where parentheses are needed in this sort of situation.

A further problem is using too many parentheses, as pointed out by another reader of the IDL newsgroup:

I tried the OP's method and got his error.

IDL> ((*a)[0]).x = 99d
% Attempt to store into an expression: Structure reference.
% Execution halted at: $MAIN$

and thought that the "correct" syntax would be something like ((*a).x)[0].

This is what I get:

IDL> a = ptr_new( replicate( {x:0d}, 5 ) )
IDL> (*a).x = dindgen(5)
IDL> help, ((*a).x)[0]
<Expression>    DOUBLE    =        0.0000000
IDL> ((*a).x)[0] = 99d
% Internal error: The Interpreter stack is not empty on exit.
IDL> help, ((*a).x)[0]
<Expression>    DOUBLE    =        99.000000

So that works too..... sort of.

JD Smith (as usual) explained the problem to us.

Actually, it's pretty much as expected. What you wanted is:

IDL>  (*a)[0].x = 99D

Doing it your way first creates a temporary array ((*a).x) and then indexes and assigns a member, which is slow, and leads to the reported error. The original error was even more severe: you can't (usually) include temporary expressions on the LHS of the assignment.

Since “[]” and “.” are at the same level of precedence, all you need to remember is to use parentheses to group “*” with all the pointers in the expression. No other parentheses are needed, and in fact using any other parentheses will likely create temporary variables, which is not usually what you want (and can lead to errors of the type mentioned).

Imagine a bizzarely deeply nested data structure like this:

IDL> a=ptr_new(replicate({b:ptr_new([ptr_new(replicate(1,5)), $
	    ptr_new(replicate({c:ptr_new(14.)},3))])},4))

Suppose we want the “c” field of the last of that final array of structures. How to approach this? One way is first to pretend there is no such thing as operator precedence, and just write it out without thinking:

   val = ****a[1].b[1][2].c

This of course, won't work. Now go through and group, using (), all pointers together with their leading dereference operator “*”, starting with the innermost. There are a total of four pointers we need to consider. Here is the result:

   val = *(*(*(*a)[1].b)[1])[2].c

See how “a” is a pointer, so “(*a)” is the group, “(*a)[1].b” is a pointer so “(*(*a)[1].b)” is the group, etc.? You can leave off any “outer ”parentheses that are not followed by anything (and probably should if you are assigning to the element, just to get in the habit).

Google
 
Web Coyote's Guide to IDL Programming