[comp.lang.c++] structures with variable length elements.

monardo@cshl.org (Pat Monardo) (05/18/91)

i am only superficially aware of C++, so....
i recall that PL1 would allow a structure to contain variable length
elements (depending on a length parameter). With C i would
have to declare a pointer and allocate the memory.
Did PL1 allocate the vector inline? Does C++ do this?
Maybe a little visual aid would clarify:

C:

struct {

int vector_len;
int *vector_val;

};

C++:?
struct {

int vector_len;
int vector_val[0];
...
int vector_val[vector_len-1];
...
};

monardo@cshl.org (Pat Monardo) (05/19/91)

In article <1991May18.010928.16619@cshl.org> monardo@cshl.org (Pat Monardo) writes:
>i am only superficially aware of C++, so....
whew, doing my own research led me into templates.
i dont think i can deal with that now.  what i want is
an "APL runtime" written in C++. we could design
some base classes for example:

class hier sketch:

JType
JShape
JData

and somehow wrap this into a template/virtual function
scheme and viola *APL to go*. Somwhere in the JType and
JShape subclasses would have to be converters/streamers/etc.
we would have to support Dictionary APL concepts of
virtual accessors and dynamic ranking (reshaping?)

! right.
<*> :-(

? x

steve@taumet.com (Stephen Clamage) (05/21/91)

monardo@cshl.org (Pat Monardo) writes:

>i am only superficially aware of C++, so....
>i recall that PL1 would allow a structure to contain variable length
>elements (depending on a length parameter). With C i would
>have to declare a pointer and allocate the memory.
>Did PL1 allocate the vector inline? Does C++ do this?

There is a "trick" in C to make variable-length structures:
	struct phony {
	    ...
	    int i[1];	/* must be last field in the struct */
	};
Then you create an instance like
	phony *p = (phony*)malloc(sizeof(phony) + 511*sizeof(int));
getting a version of a phony with an array of 512 ints at element 'i'.
This is very poor programming practice, in my view.

Another way, which also works in C++, is to make 'i' a pointer, then
allocate an array for 'i' to point to whenever you create an instance of
the struct.  Then the order of the fields doesn't matter.  In C, this
technique requires some careful bookkeeping, but in C++ it all goes in
the constructor and destructor, and you don't have to worry about it
in the rest of the code.
	class var_int {
	    ...
	    int *ip;
	    int array_size;	// size of the ip array
	public:
	    var_int(int sz);	// constructor allocates array of sz ints
	    ~var_int();		// destructor deallocates the array
	    size();		// return number of elements
	    int& operator[](int index);	// access array element
	};
-- 

Steve Clamage, TauMetric Corp, steve@taumet.com

niklas@appli.se (Niklas Hallqvist) (05/22/91)

steve@taumet.com (Stephen Clamage) writes:

>monardo@cshl.org (Pat Monardo) writes:

>>i am only superficially aware of C++, so....
>>i recall that PL1 would allow a structure to contain variable length
>>elements (depending on a length parameter). With C i would
>>have to declare a pointer and allocate the memory.
>>Did PL1 allocate the vector inline? Does C++ do this?

>There is a "trick" in C to make variable-length structures:
>	struct phony {
>	    ...
>	    int i[1];	/* must be last field in the struct */
>	};
>Then you create an instance like
>	phony *p = (phony*)malloc(sizeof(phony) + 511*sizeof(int));
>getting a version of a phony with an array of 512 ints at element 'i'.
>This is very poor programming practice, in my view.

>Another way, which also works in C++, is to make 'i' a pointer, then
>allocate an array for 'i' to point to whenever you create an instance of
>the struct.  Then the order of the fields doesn't matter.  In C, this
>technique requires some careful bookkeeping, but in C++ it all goes in
>the constructor and destructor, and you don't have to worry about it
>in the rest of the code.
>	class var_int {
>	    ...
>	    int *ip;
>	    int array_size;	// size of the ip array
>	public:
>	    var_int(int sz);	// constructor allocates array of sz ints
>	    ~var_int();		// destructor deallocates the array
>	    size();		// return number of elements
>	    int& operator[](int index);	// access array element
>	};

In a C++ compiler supporting templates the extra new-delete pair
can go away, if the size of the buffer is known at compile time.  Consider:

class var_int_base {
  ...
  int* ip;
  int array_size;
public:
  var_int_base(int* vec, int sz) : ip(vec), array_size(sz) {}
  int size() { return array_size };
  int& operator[](int index) { return ip[index]; }
};

template<int size> class var_int : var_int_base {
  int buf[size];
public:
  var_int(int sz) : var_int_base(buf, sz) {};
};

void foo()
{
  var_int<256> vec256;
  var_int<128> vec128;
  // and if you want to treat them as if they were of the same type, use:
  var_int_base* vecp;
  vecp = &vec256;
  vecp = &vec128;
}

The code above has NOT been tested, but I think it explains the idea
even if it might be slightly incorrect.  I would use references instead
of pointers, if I were sure of the semantics of references to arrays
passed as parameters, which I'm not for the moment.  Maybe I'll
test this tonight...  In real life the element type should of course
be a template argument too.

						Niklas

davidm@uunet.UU.NET (David S. Masterson) (05/22/91)

>>>>> On 20 May 91 17:23:36 GMT, steve@taumet.com (Stephen Clamage) said:

Stephen> Another way, which also works in C++, is to make 'i' a pointer, then
Stephen> allocate an array for 'i' to point to whenever you create an instance
Stephen> of the struct.  Then the order of the fields doesn't matter.  In C,
Stephen> this technique requires some careful bookkeeping, but in C++ it all
Stephen> goes in the constructor and destructor, and you don't have to worry
Stephen> about it in the rest of the code.

Stephen> 	class var_int {
Stephen> 	    ...
Stephen> 	    int *ip;
Stephen> 	    int array_size;	// size of the ip array
Stephen> 	public:
Stephen> 	    var_int(int sz); // constructor allocates array of sz ints
Stephen> 	    ~var_int();		// destructor deallocates the array
Stephen> 	    size();		// return number of elements
Stephen> 	    int& operator[](int index);	// access array element
Stephen> 	};

Hmmm, you're beginning to hit a problem that is close to home (and one I'm
still having trouble with).   In my system, we are moving events in and out of
the system.  Events come in two flavors, fixed-length and variable-length
(variable-length is often used for shipping arbitrary lists of information).
So Events might look like:

class Event { ... };
class FixedEvent : public Event { ... };
class VarEvent : public Event { ... };

The question I have is what should VarEvent look like internally such that a
pointer to it could be handed off to:

boolean EventShipper(Event*);	// assuming Event knows how big it is.

and EventShipper could just copy it into a "mailbox" for shipment.  Obviously,
if this is just a "straight" copy, then Events really can't have virtual
capabilities (a problem in the current design).  Given that, though, a natural
way of arranging this is as above and memcpy() the Event from place to place.
The problem, though, is that the VarEvent needs the ability to grow to some
given size.  What is the best way of handling this?
--
====================================================================
David Masterson					Consilium, Inc.
(415) 691-6311					640 Clyde Ct.
uunet!cimshop!davidm				Mtn. View, CA  94043
====================================================================
"If someone thinks they know what I said, then I didn't say it!"