Initialize pointer-to-struct declaration to an unnamed struct?

E

Ehud Shapira

Is it possible to have a declaration of a struct pointer initialized
to an unnamed struct?
(I'm only concerned with static/global variables, if it matters.)

I'm trying to do something like:

struct st_a {
int i, j;
};

struct st_b {
int k, l;
st_a *m;
} b[] = {
1, 2, {0, 1},
3, 4, {0, 2}
};

It could be done by declaring each "sub-struct" with a name, then
pointing to these,
but besides being more work it's also more difficult to read when the
idea is a hierarchy.
 
R

Rolf Magnus

Ehud said:
Is it possible to have a declaration of a struct pointer initialized
to an unnamed struct?
(I'm only concerned with static/global variables, if it matters.)

I'm trying to do something like:

struct st_a {
int i, j;
};

struct st_b {
int k, l;
st_a *m;
} b[] = {
1, 2, {0, 1},
3, 4, {0, 2}

{0, 1} is not a valid initializer for a pointer.
};

It could be done by declaring each "sub-struct" with a name, then
pointing to these,
but besides being more work it's also more difficult to read when the
idea is a hierarchy.

It's not quite clear to me what you actually want.
 
E

Ehud Shapira

{0, 1} is not a valid initializer for a pointer.
I know. What *can* be valid? You can see what sort of initialization
I'm trying to do.
 
R

Rolf Magnus

Ehud said:
I know. What *can* be valid? You can see what sort of initialization
I'm trying to do.

Actually, I can't. Why does it have to be a pointer? Can't you just store a
struct directly?
 
E

Ehud Shapira

Actually, I can't. Why does it have to be a pointer? Can't you just store a
struct directly?
What I want to do involves a hierarchy of variable length struct
arrays. But regardless of the why, the question is whether it's
possible at all to do something like ( char *pc = "something" ) for a
struct and not char.
 
V

Victor Bazarov

Ehud said:
What I want to do involves a hierarchy of variable length struct
arrays. But regardless of the why, the question is whether it's
possible at all to do something like ( char *pc = "something" ) for a
struct and not char.

Usually not. There is a huge difference between a string literal (that
has a very very very long lifetime) and a struct temporary. You could
create a static object of the struct type, and then declare a pointer
and initialise it with the address of the static object:

static somestruct something = { blah };
somestruct *ps = &something;

V
 
E

Ehud Shapira

There is a huge difference between a string literal and a struct temporary.
The struct is just as static -- it's a global definition.
You could create a static object of the struct type, and then declare a pointer
and initialise it with the address of the static object:
That's what I'm trying to avoid; naming every linked array of structs.
 
V

Victor Bazarov

Ehud said:
The struct is just as static -- it's a global definition.

No, a temporary created for the purposes of initialising something is
definitely not static. It's temporary.
That's what I'm trying to avoid; naming every linked array of structs.

<shrug> Don't name them. Have an array and get pointers to its
elements:

struct st_a {
int i, j;
} a[] = { { 0, 1 }, {0, 2 } };

struct st_b {
int k, l;
st_a *m;
} b[] = {
{ 1, 2, a+0 },
{ 3, 4, a+1 }
};

You won't have "every" element named, only one.

Another solution is to create those dynamically:

struct st_a {
int i, j;
};

st_a* make_st_a(int i, j) {
st_a a = {i, j};
return new st_a(a);
}

struct st_b {
int k, l;
st_a *m;
} b[] = {
1, 2, make_st_a(0, 1),
3, 4, make_st_a(0, 2)
};

If 'b' is static, you don't have to care about disposing of
those dynamic objects, a small leak like that isn't going to
hurt much. Just be aware of it.

V
 
J

James Kanze

Is it possible to have a declaration of a struct pointer initialized
to an unnamed struct?
(I'm only concerned with static/global variables, if it matters.)
I'm trying to do something like:
struct st_a {
int i, j;
};
struct st_b {
int k, l;
st_a *m;} b[] = {
1, 2, {0, 1},
3, 4, {0, 2}
};

It can't be done in C++. In C you can write something like:

typedef struct
{
int i, j ;
} st_a ;

typedef struct
{
int k, l ;
st_a* m ;
} st_b ;

st_b b[] = {
{ 1, 2, &(st_a){ 0, 1 }, },
{ 3, 4, &(st_a){ 0, 2 }, },
} ;

but this was added to C after C++ was standardized. (Possibly
some C++ compilers support it anyway. I don't know.)
It could be done by declaring each "sub-struct" with a name, then
pointing to these,
but besides being more work it's also more difficult to read when the
idea is a hierarchy.

In such cases, I usually define the data in a simple text file,
and then write a small program to convert it into legal C++.
 
E

ehudshapira

No, a temporary created for the purposes of initialising something is
definitely not static. It's temporary.

I don't understand that. Maybe we are talking about different things?
The struct will be initialized at compile-time, and so will live
"forever". How is that temporary?
<shrug> Don't name them. Have an array and get pointers to its
elements:

Bookkeeping array indexes isn't much better. And dynamically creating
them would add overhead, etc.

In such cases, I usually define the data in a simple text file,
and then write a small program to convert it into legal C++.

Yeah, if that's the case, for big definitions I'll probably go with a
custom preprocessor, possibly outputting binary directly (which would
also rid me of unclear handling of union initialization by VC6... but
maybe that's the problem; using an old compiler.)

Too bad C/++ doesn't offer (in many cases) such terseness as that
possible by, say, JavaScript.

Thanks all for the feedback.
 
V

Victor Bazarov

I don't understand that. Maybe we are talking about different things?
The struct will be initialized at compile-time, and so will live
"forever". How is that temporary?

In the statement

T obj = someexpression;

(provided it appears at the namespace scope) 'obj' has static storage
duration, but whatever 'someexpression' returns does NOT have static
storage duration, it's a temporary. That's significantly different
from

char const* p = "blah";

where the string literal ("blah") has static storage duration.
Bookkeeping array indexes isn't much better. And dynamically creating
them would add overhead, etc.



Yeah, if that's the case, for big definitions I'll probably go with a
custom preprocessor, possibly outputting binary directly (which would
also rid me of unclear handling of union initialization by VC6... but
maybe that's the problem; using an old compiler.)

Too bad C/++ doesn't offer (in many cases) such terseness as that
possible by, say, JavaScript.

Huh? Terseness?

V
 
E

Ehud Shapira

T obj = someexpression;

(provided it appears at the namespace scope) 'obj' has static storage
duration, but whatever 'someexpression' returns does NOT have static
storage duration, it's a temporary.
If (someexpression) is (a + b) then I can see how it's temporary, but
what's temporary about a struct defined at compile-time? Just like a
string, it would have a memory address and content defined before any
instruction is executed.
Huh? Terseness?
Stuff like:

b = [
{ k:1, l:2, m:[ { i:0, j:1 } ] }
];
 
V

Victor Bazarov

Ehud said:
If (someexpression) is (a + b) then I can see how it's temporary, but
what's temporary about a struct defined at compile-time? Just like a
string, it would have a memory address and content defined before any
instruction is executed.

There is no such concept in C++ as "struct defined at compile time".
Taking an address requires an l-value. To define an l-value, you need
to define a *named* object or an element/subobject of an array/another
object.
Huh? Terseness?
Stuff like:

b = [
{ k:1, l:2, m:[ { i:0, j:1 } ] }
];

How does that relate to your problem? You're trying to initialise
a pointer with an address of a temporary.

V
 
R

Rolf Magnus

Ehud said:
If (someexpression) is (a + b) then I can see how it's temporary, but
what's temporary about a struct defined at compile-time?

The C++ standard says that it is one.
Just like a string, it would have a memory address and content defined
before any instruction is executed.

When will the destructor be executed?
 
J

James Kanze

I don't understand that. Maybe we are talking about different things?
The struct will be initialized at compile-time, and so will live
"forever". How is that temporary?

At present, an object created as part of an initialization
expression is a temporary, it's destructor is called and the
memory it allocates is freed as soon as the initialization is
finished. Thus, for example, you could get the code to pass the
compiler by declaring constructors for the referred to struct's,
and defining an operator&() in them which returned the this
pointer. All you'd end up with, however, is dangling pointers.

Of course, if C++ added some special syntax to support such
things, a la C, then the lifetime of the objects would be
whatever C++ decided for those types of objects---in C, they
have static lifetime. (But C isn't C++, and such compound
initializers for a static object must have static initialization
themselves.)
Bookkeeping array indexes isn't much better. And dynamically creating
them would add overhead, etc.
Yeah, if that's the case, for big definitions I'll probably go with a
custom preprocessor, possibly outputting binary directly (which would
also rid me of unclear handling of union initialization by VC6... but
maybe that's the problem; using an old compiler.)

I'm not sure what the problem is in VC++, but C++ doesn't have
designaged initializers either, and so you can only initialize
the first element of a union.
Too bad C/++ doesn't offer (in many cases) such terseness as that
possible by, say, JavaScript.

The languages are designed to meet different goals. I'd
certainly not want to develop anything really large in
JavaScript. A bit of redundancy is necessary when trying to get
different people, or even just separately compiled modules, to
work together.
 
E

Ehud Shapira

At present, an object created as part of an initialization
expression is a temporary, it's destructor is called and the
memory it allocates is freed as soon as the initialization is
finished.

Thanks for clearing that up a bit. So the scope of the object/
sub-struct would be the initialization block, and since that
doesn't make sense, anonymous recursive objects aren't allowed?

How's that behavior useful for anything? I'd expect the scope
to be that of the containing parent.
I'm not sure what the problem is in VC++, but C++ doesn't have
designaged initializers either, and so you can only initialize
the first element of a union.

Yes, it expects the type of the first union member. (Which I
find an unclear limitation; the whole idea of a union is to
hold any of its types. [I also see the {.union_member = 1}
syntax unnecessarily verbal]).

But what's odd is that the first array element does accept a
non-first type, and only later array elements result in
compiler complaints.
The languages are designed to meet different goals. I'd
certainly not want to develop anything really large in
JavaScript.

I'm not suggesting to use JS for "real" programming, but some
syntax possiblities are nice, and I think could work in C++
without conflicting with its syntax.

I guess C++ does pick up some things from here and there (like
C), and has quite possibly improved already; maybe I'm just not
seeing it w/ VC6.

Victor Bazarov:
How does that relate to your problem? You're trying to
initialise a pointer with an address of a temporary.

No. I was trying to initialize a hierarchial piece of data
in a concise and clean way.

Rolf Magnus:
When will the destructor be executed?

The scope of the parent initialization is global, so I didn't
expect any freeing to be necessary.
 
E

Ehud Shapira

At present, an object created as part of an initialization
expression is a temporary, it's destructor is called and the
memory it allocates is freed as soon as the initialization is
finished.

Thanks for clearing that up a bit. So the scope of the object/
sub-struct would be the initialization block, and since that
doesn't make sense, anonymous recursive objects aren't allowed?

How's that behavior useful for anything? I'd expect the scope
to be that of the containing parent.
I'm not sure what the problem is in VC++, but C++ doesn't have
designaged initializers either, and so you can only initialize
the first element of a union.

Yes, it expects the type of the first union member. (Which I
find an unclear limitation; the whole idea of a union is to
hold any of its types. [I also see the {.union_member = 1}
syntax unnecessarily verbal]).

But what's odd is that the first array element does accept a
non-first type, and only later array elements result in
compiler complaints.
The languages are designed to meet different goals. I'd
certainly not want to develop anything really large in
JavaScript.

I'm not suggesting to use JS for "real" programming, but some
syntax possiblities are nice, and I think could work in C++
without conflicting with its syntax.

I guess C++ does pick up some things from here and there (like
C), and has quite possibly improved already; maybe I'm just not
seeing it w/ VC6.

Victor Bazarov:
How does that relate to your problem? You're trying to
initialise a pointer with an address of a temporary.

No, I was after initializing a hierarchial piece of data
concisely and cleanly.

Rolf Magnus:
When will the destructor be executed?

The scope of the parent initialization is global, so I didn't
expect any freeing to be necessary.
 
J

James Kanze

Thanks for clearing that up a bit. So the scope of the object/
sub-struct would be the initialization block, and since that
doesn't make sense, anonymous recursive objects aren't allowed?

I don't think that's the issue. If I write something at
namespace scope like:

std::string s( str1 + str2 ) ;

where str1 and str2 are also strings, the operator+ returns a
temporary, which will be destructed once the initialization has
finished (supposing no RVO, which would merge the return value
and the object being constructed).
How's that behavior useful for anything? I'd expect the scope
to be that of the containing parent.

See above. The problem is that we're discussing something that
isn't currently present in C++, and trying to assimilate it to
features that are. For example, one way to get your code to
compile would be to provide constructors for the sub-object,
and---since you can't use the built-in operator & on a
temporary, a user defined operator & which returns this. The
problem is that as far as the compiler is concerned, this is
exactly like the case with string, above---the constructed
object et al. is just there to get the initial value, which will
end up in the actual declared object. And all of the
intermediate values will be destructed, and the memory for them
freed.

One possibility would be to give the hierarchial elements
constructors, then write something like:

st_b c =
{
{ 1, 2, new st_a( 0, 1 ) },
{ 3, 4, new st_a( 0, 2 ) },
// ...
} ;
Yes, it expects the type of the first union member. (Which I
find an unclear limitation; the whole idea of a union is to
hold any of its types. [I also see the {.union_member = 1}
syntax unnecessarily verbal]).

So how else would you choose. C90 says you can only initialize
the first element. C99 decided that this was overly
restrictive, and (for that and other reasons) introduced
designated initializers. What other syntax would you suggest?

In C++, you can initialize the element you want by using a
constructor.
But what's odd is that the first array element does accept a
non-first type, and only later array elements result in
compiler complaints.

You've confused me here. Could you give an example.

[...]
Victor Bazarov:
No, I was after initializing a hierarchial piece of data
concisely and cleanly.

Yes and no. You were trying to define several distinct objects
of different types in a single definition statement. That just
doesn't exist in C++; you can only define a single object with
static lifetime in each declarator. Any other objects created
by the initialization expression are temporaries, or if you use
the operator new, dynamically allocated.
Rolf Magnus:
The scope of the parent initialization is global, so I didn't
expect any freeing to be necessary.

It probably isn't. I don't delete my singletons either.

The C solution gives static initialization and static lifetime.
Using new in C++ gives dynamic initialization and dynamic
lifetime. If the objects have a POD type, you never delete
them, and they are created during initialization of static
objects, their effective lifetime is the same as if they were
static. But the initialization remains dynamic; if you use the
objects in the constructors of other static objects, you are
likely to run into order of construction problems.
 
E

Ehud Shapira

I don't think that's the issue. If I write something at
namespace scope like:

std::string s( str1 + str2 ) ;

But here there's a ctor (and computation), so a temporary would make
sense. My deceleration had neither.
The problem is that we're discussing something that
isn't currently present in C++,

That's what I now understand. What I used was initializing multiple
flat arrays and linkings them by their names. I don't want dynamic
initialization; I'd rather have static data static.
Yes, it expects the type of the first union member. (Which I
find an unclear limitation; the whole idea of a union is to
hold any of its types. [I also see the {.union_member = 1}
syntax unnecessarily verbal]).
So how else would you choose...
What other syntax would you suggest?

The most logical thing would be to accept any type the union can hold.
If it's both an int and float, I shouldn't have to cast either.

As for .member, it seems I was mistaken. Naming members on
initialization isn't required, it's just another option. (Looks like
it might also be allowed to initialize a single object with a mix of
named and unnamed, for easy skipping of members, which is nice. But I
can't check all that on my compiler).
You've confused me here. Could you give an example.

Actually, I misinterpreted the reason why an initializer of a union
(which is part of an array) worked even though its type was not the
same as the first union member. It wasn't about being the first in
array, it seems. The compiler somehow accepts offsetof(st, m) as a
pointer when m is the first member of st.
Using new in C++ gives dynamic initialization and dynamic
lifetime. If the objects have a POD type, you never delete
them, and they are created during initialization of static
objects, their effective lifetime is the same as if they were
static. But the initialization remains dynamic;

Do you mean ones declared without "new"? If they're practically
static, why would they have to be created dynamically?

Checking the compiled code of a simple test, it seems a global
initialized struct is really static; it has a static address (no
indirection) and the data is preinitialized.
 
J

James Kanze

On Jun 29, 12:11 pm, James Kanze <[email protected]> wrote:

[...]
Yes, it expects the type of the first union member. (Which I
find an unclear limitation; the whole idea of a union is to
hold any of its types. [I also see the {.union_member = 1}
syntax unnecessarily verbal]).
So how else would you choose...
What other syntax would you suggest?
The most logical thing would be to accept any type the union can hold.
If it's both an int and float, I shouldn't have to cast either.

But that doesn't help choose which element is being initialized.
Consider:

union U
{
double d1 ;
double d2 ;
} u = 3.1415 ;

Which element is being initialized? Or

union U
{
long l ;
double d ;
} u = 42 ;

Which element is being initialized?
Do you mean ones declared without "new"? If they're practically
static, why would they have to be created dynamically?

At namespace scope:

int* p = new int ;

That's dynamic initialization, even if p has static lifetime.
(I'm talking here about the variable p, not about the object
created by new.) In general, unless the initializer is a
constant expression and the type of the data is POD,
initialization will be dynamic. So:

int i1 = 42 ; // static initialization...
std::string s( "abc" ) ; // dynamic initialization...
extern int f() ;
int i2 = f() ; // dynamic initialization...
Checking the compiled code of a simple test, it seems a global
initialized struct is really static; it has a static address (no
indirection) and the data is preinitialized.

If the type is a POD (plain ordinary data---roughly speaking,
something you could do in C), and the initialization expression
is a constant, then initialization should be static.

The distinction can be important. Static initialization takes
place before any code you write is executed, and because it
doesn't involve code, it is automatically independant of
ordering considerations. Dynamic initialization occurs in an
undefined order (when in different translation units), and you
can get into trouble quickly counting on it. (Imagine in the
above that f() uses some static variable which is dynamically
initialized.)
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,744
Messages
2,569,484
Members
44,903
Latest member
orderPeak8CBDGummies

Latest Threads

Top