[comp.lang.c] Request for Comments: Aggregate Assignment in C ...

mayer@iuvax.cs.indiana.edu (Mayer Goldberg) (12/11/90)

Pascal programmers who learn C, frequently complain of C's
lack of a "with" construct. We propose an alternative to
adding such a keyword, through a natural extention of C's
aggregate initialization statement.

Consider the following program fragment:

struct ag {
  int a, b, c;
};

struct ag my_vec = {1, 2, 3};

This form of an "assignment" is permitted only during the
initialization of a variable (at compile time). Any other way
of assignment to fields in my_vec will have to be done by routing
through my_vec, as in:

my_vec.a = 4; my_vec.c = 7; /* etc. */

We submit that a better way of doing this would be to allow a
kind of assignment that we allow during initialization.
Something like:

my_vec = {4,, 7};

If for example we wished to assign 2 and 3 to the 'a' and 'b'
fields of my_vec, any one of the following should be ok:

my_vec = {2, 3}; 

or 

my_vec = {2, 3,};

This mechanism should work for both structures and arrays.

This solves the "with" problem in C. It also opens a delicious
can of worms: Should the order of evaluation of the aggregated 
expressions be specified? Perhaps not, for this would be in
line with C's philosophy of not specifying the order of
evaluation of parameters to a function, or alternately, we
could demand a left-to-right order of evaluation as implied by
the comma operator.

Mayer Goldberg 		 	Nick Cline
mayer@iuvax.cs.indiana.edu	cline@silver.ucs.indiana.edu

gwyn@smoke.brl.mil (Doug Gwyn) (12/11/90)

In article <77546@iuvax.cs.indiana.edu> mayer@iuvax.cs.indiana.edu (Mayer Goldberg) writes:
>This solves the "with" problem in C.

Doing nothing is even better.  "with" is insufficiently general, in
that it cannot discriminate between similarly named members of
different structsures that you might want to be working "with"
simultaneously.

"Not looking like Pascal is not a language deficiency."

ok@goanna.cs.rmit.oz.au (Richard A. O'Keefe) (12/11/90)

In article <77546@iuvax.cs.indiana.edu>, mayer@iuvax.cs.indiana.edu (Mayer Goldberg) writes:
> We submit that a better way of doing this would be to allow a
> kind of assignment that we allow during initialization.
> Something like:
> my_vec = {4,, 7};

If you want Ada, you know where to find it.
It you want to make it easy for people to introduce subtle and
difficult-to-locate mistakes in their programs, keep it up.

A better approach is to write a function that fills in records
    struct ag mk_ag(int a, int b, int c)
	{ struct ag ans; ans.a = a, ans.b = b, ans.c = c; return ans; }

Then the struct declaration can have new fields added, or the fields
re-ordered, and
	my_vec = mk_ag(4, 0, 7);
will still work.  (Hint:  this is why Ada record constructors let you
use keywords.)

If your compiler supports inlining (gcc, for example) struct-building
functions are good candidates for inlining.

-- 
The Marxists have merely _interpreted_ Marxism in various ways;
the point, however, is to _change_ it.		-- R. Hochhuth.

steve@taumet.com (Stephen Clamage) (12/11/90)

mayer@iuvax.cs.indiana.edu (Mayer Goldberg) writes:

|Pascal programmers who learn C, frequently complain of C's
|lack of a "with" construct. We propose an alternative to
|adding such a keyword, through a natural extention of C's
|aggregate initialization statement.

|[example]
|struct ag {
|  int a, b, c;
|};
|struct ag my_vec = {1, 2, 3};

You could just use C++, which already allows such things via constructors.
-- 

Steve Clamage, TauMetric Corp, steve@taumet.com

dgil@pa.reuter.COM (Dave Gillett) (12/12/90)

In <77546@iuvax.cs.indiana.edu> mayer@iuvax.cs.indiana.edu (Mayer Goldberg) writes:

>Pascal programmers who learn C, frequently complain of C's
>lack of a "with" construct. We propose an alternative to
>adding such a keyword, through a natural extention of C's
>aggregate initialization statement.
     .
     .
     .
>This solves the "with" problem in C. It also opens a delicious
>can of worms: Should the order of evaluation of the aggregated 
>expressions be specified? Perhaps not, for this would be in
>line with C's philosophy of not specifying the order of
>evaluation of parameters to a function, or alternately, we
>could demand a left-to-right order of evaluation as implied by
>the comma operator.


     On the contrary, it solves a *piece* of the "with" problem, and
introduces a can of worms which is not only delicious, but not present
in the Pascal "with" construct.

     Mayer's suggestion covers the case of aggregate assignment *to* a struct,
but does not even attempt to address references to struct elements as anything
other than targets of assignment.  Since Pascal's "with" applies to all
element references, regardless of context, the suggested enhancement cannot
be mistaken for an equivalent feature.
     If the comma is to be used, it seems preferable for it to retain as
much of its normal semantics as possible.  Indeed, I think it is already a
linguistic flaw for it to have different semantics as an argument delimiter
than as an operator.  Introducing a third semantics for a single symbol is
clearly not a good idea.
     Using the operator semantics should, it seems, yield a single rvalue
for the expression within the {}s.  [No, I didn't quote Mayer's entire article.]
So that leaves the function parameter semantics.  (At the moment, {} have
different semantics depending on whether one is looking at a variable
declaration or at code; leaking the variable definition semantics of {}
over into code, where they already have their own semantics, seems dubious.)

     Suppose, having decided to use the argument delimiter semantics of the
comma, we also steal the accompanying () semantics.  So "(a, b, ...)" is a
"list of values" construct, which can be assigned to a structure (under
suitable constraints) or passed as an argument list to a function.  (So
"()" is the empty list.)  So far, this looks like a powerful generalization
that shouldn't break existing code.
     The problem is that, if we define the paren/comma construct this way,
then "funcname structname;" should call function "funcname" passing the values
from struct "structname" as arguments.  That might be nice, but it's not C.

     Note that "funcname structname" is *not* equivalent to
"funcname (structname)".  The latter function takes a single struct
as an argument, not the elements of the struct as list of arguments.
You can make them equivalent in the compiler, but that gives you no way
to have a struct as one of a list of arguments, and so breaks existing
code.

     Mayer's suggestion points in the direction of APL's nested arrays or
LISP's lists.  But it's not C, and it's not Pascal's "with", either.
                                                   Dave

bson@rice-chex.ai.mit.edu (Jan Brittenson) (12/13/90)

In article <77546@iuvax.cs.indiana.edu>
   mayer@iuvax.cs.indiana.edu (Mayer Goldberg) writes:

 >Consider the following program fragment:
 >
 >struct ag {
 >  int a, b, c;
 >};
 >
 >struct ag my_vec = {1, 2, 3};
 >
 >This form of an "assignment" is permitted only during the
 >initialization of a variable (at compile time).

   Perhaps you should take a look at GNU C. Its aggregate initializer
doesn't allow you to "skip" members, but I fid it much more useful
than Pascal's WITH clause.


/* Feed me to GCC! */
#include <stdio.h>

struct barf {
  int x, y;
};

void print_barf(barfstuff)
  struct barf barfstuff;
{
  printf("barf: x=%d, y=%d\n", barfstuff.x, barfstuff.y);
}

main()
{
  print_barf( (struct barf) {2, 3} );
}

heidi@ctk1.UUCP (Heidi de Wet) (12/14/90)

In <4479@goanna.cs.rmit.oz.au> ok@goanna.cs.rmit.oz.au (Richard A. O'Keefe) writes:
>In article <77546@iuvax.cs.indiana.edu>, mayer@iuvax.cs.indiana.edu (Mayer Goldberg) writes:
>> [Proposed alternative to 'with' construct]
>> We submit that a better way of doing this would be to allow a
>> kind of assignment that we allow during initialization.
>> Something like:
>> my_vec = {4,, 7};

As a staunch fan of Pascal, writing C for a living :-), this seems a
very uninteresting problem.  Pascal's 'with' doesn't solve this, anyway.
It _does_ reduce the text associated with multiple references to the
same record structure in one expression.  Try this for size:

   (*Buff)->Msg_Bytes [(*Buff)->Start_Index + (*Buff)->Byte_Count] = B;

Now try copying one 'Buff' to another...  Any suggestions about this?

If you want to make assignments of multiple elements in a structure
easier, how about the construct 'A = B', where A and B are identical,
complex structs or arrays?  Yes, you can use memcpy, but it would be
nice to see the compiler doing a _little_ bit of work for a change.


>If you want Ada, you know where to find it.
>It you want to make it easy for people to introduce subtle and
>difficult-to-locate mistakes in their programs, keep it up.

Well, that would be entirely in the spirit of C, wouldn't it?

--------------------
Heidi de Wet
University of Cape Town, South Africa
ddsw1!proxima!ctk1!heidi -or- proxima!ctk1!heidi@ddsw1.mcs.com

jmd@dlogics.COM (Jens M. Dill) (12/18/90)

In article <1990Dec14.130916.18447@ctk1.UUCP>, 
    heidi@ctk1.UUCP (Heidi de Wet) writes:
> >In article <77546@iuvax.cs.indiana.edu>, 
    mayer@iuvax.cs.indiana.edu (Mayer Goldberg) writes:
> >> [Proposed alternative to 'with' construct]
> >> We submit that a better way of doing this would be to allow ...
> >> Something like:    my_vec = {4,, 7};
> 
> As a staunch fan of Pascal, writing C for a living :-), this seems a
> very uninteresting problem.  Pascal's 'with' doesn't solve this, anyway.
> It _does_ reduce the text associated with multiple references to the
> same record structure in one expression.  Try this for size:
> 
>    (*Buff)->Msg_Bytes [(*Buff)->Start_Index + (*Buff)->Byte_Count] = B;

If you are willing to step outside of comp.lang.C for a moment, note that
C++ reference types handle this very nicely:

     {  BuffStructType  & BF = **Buff ;  /* Yes, you did have a double 
                                            ... indirection in your code */

        BF.Msg_Bytes [BF.Start_Index + BF.Byte_Count] = B ;
     }

In short: the Pascal WITH statement allows you to completely elide a complex
qualifying expression on the front of a structure reference, at the cost of
only being able to do so for one structure of a given type at any one time.
C++ references allow you to replace a complex qualifying expression with what
looks like a simple reference to a field of a named structure.  You can't get
rid of that last qualifier, but you can make it short and you do get the 
freedom to do it to more than one structure of the same type.  By the way,
C purists can do almost the same thing with pointers:

     {  BuffStructure  * BP = *Buff ;
        BP->Msg_Bytes [BP->Start_Index + BP->Byte_Count] ;
     }

Don't let the cost of the "extra" variable fool you.  It's saving you time
by precomputing most of the common addressing information.  A smart compiler
will manage to keep the new variable in a register for its entire scope, if,
like this, it's VERY local and frequently used.  If you don't have a smart
compiler, you can declare it a register variable.
 
> Now try copying one 'Buff' to another...  Any suggestions about this?
> 
> If you want to make assignments of multiple elements in a structure
> easier, how about the construct 'A = B', where A and B are identical,
> complex structs or arrays?  ...

"A = B" works fine for structures, provided A and B are the same type of
structure.  It didn't work in the earliest versions of C, (and is not in
K&R 1), but unless you have a very old C compiler it should work for you.
Arrays are a whole different kettle of fish, as any regular reader of this
newsgroup will tell you.  You can get what you want by embedding the array
inside a structure and assigning the structure, however.

But the original poster didn't have two identical structures, but rather 
wanted to be able to initialize a structure dynamically with a constant
value for each of the fields.  C allows you to do this statically (at
compile time, into a global or static variable), but does not have an
equivalent run-time initialization for automatic structures.  Neither does
Pascal.  C++ allows you to fake it by writing a "constructor function" to
do the initialization on the fly, but that brings in a whole bunch of new
syntax and semantics that are not at all related to the static initialization
("= { ... }") idea.  The best solution is to define a static "constant"
structure that can be initialized using the "= { ... }" notation, and then
initialize your automatic variable by a simple assignment from the static
"constant":

     void  foo (args ...)
     {
         static struct MyStruct  Initializer = { ... } ;
         struct MyStruct         LocalStructVar = Initializer ;
         ...
     }

You may have to split the second declaration into a declaration and an
assignment statement if your compiler is overly rigid about not allowing 
any initialization of automatic variables.
  


*=====* TIME CANNOT BE WASTED *=====*       -- Jens M. Dill
 \ But it can be used for purposes /           jmd@dlogics.com
  \ other than what was intended. /
   *=============================*

smryan@garth.UUCP (Steven Ryan) (12/20/90)

>my_vec.a = 4; my_vec.c = 7; /* etc. */
>
>We submit that a better way of doing this would be to allow a  .  .  .
>
>my_vec = {4,, 7};

As I live and breathe--structure displays again, and after only 22 years.

Two points to consider:

(1) Can you handle single element structures? When Ada tried, they really
    screwed it up, even though a quick perusal of Algol 68 would have
    pointed this out early in the game.

(2) The mode of of structure display cannot be synthesised: given the
    types of the elements, you cannot generally determine the structure.
    In Algol a structure display can only occur in strong position. (A
    not-so-quick perusal of Algol will point out the dangers here.)

And for Ritchie's sake [*], don't screw up this ripof--err--borrowing the
same way +:= was screwed up to =+.
__________________________________________________
* I apologise to those who are offended by taking
the Lord's name in vain, bit I do get a teensy bit
irritated some times.
-- 
...!uunet!ingr!apd!smryan                                       Steven Ryan
...!{apple|pyramid}!garth!smryan              2400 Geng Road, Palo Alto, CA

mat@mole-end.UUCP (Mark A Terribile) (12/23/90)

In article <538@taumet.com>, steve@taumet.com (Stephen Clamage) writes:
> mayer@iuvax.cs.indiana.edu (Mayer Goldberg) writes:

> |Pascal programmers who learn C, frequently complain of C's
> |lack of a "with" construct. We propose an alternative to
> |adding such a keyword, ...

> |[example]
> |struct ag {
> |  int a, b, c;
> |};
> |struct ag my_vec = {1, 2, 3};


> You could just use C++, which already allows such things via constructors.


Note also that C++ has much less use for the `with' because sequences of
`` abc. ... '' and `` def-> ... '' tend to be absorbed into member functions
where they are not written when the function is called on behalf of the
structure object.

On the other hand, if someone is uncomfortable with C because he's just come
from another environment (such as one which will remain nameless except that
it's named after a fellow name Blaise Pascal, who did nothing at all to
deserve it) sending him to C++ is the LAST thing you want to do.  First,
because C++ has even a stronger flavor than C.  Second, because someone who
realizes that it's possible to do almost ANYTHING in C++, without learning
the C mindset as a stepping stone is liable to try some strange things.  They
might not work, but we can't count on it.
-- 

 (This man's opinions are his own.)
 From mole-end				Mark Terribile