C/C++ question about dynamic "static struct"

G

gus gassmann

Hi,

I have a static struct, defined in a C header file as something like

typedef struct
SomeStruct {
char *name;
int kind;
int value;
}

and I need to supply (in C++) values to an array of this type. I
understand that I can set *static* values by

static SomeStruct
myStruct[] = {
{"name1", 1, 17},
{"name2", 7, 25},
{"name3", 3, 6},
{"name4", 0,117}
};

However, I need to set dynamic values along these lines (I know that the
code does not work):

static SomeStruct
myStruct[] = {
{name[0], kind[0], val[0]},
{name[1], kind[1], val[1]},
...
{name[n], kind[n], val[n]},
};

where both the values and the number of values are only known at
runtime. I cannot change the definition in the header file; I am not
even sure that what I want to do is possible. Any ideas?

Thanks

gus
 
N

Noob

gus gassmann wrote:

First of all, it is not clear what "C/C++" means.
There is C, and there is C++.
I have a static struct, defined in a C header file as something like

typedef struct
SomeStruct {
char *name;
int kind;
int value;
}

Nit: you should copy/paste the EXACT code. The above does not make sense.
(warning: useless storage class specifier in empty declaration)

I think you meant:

typedef struct { char *name; int kind; int value; } SomeStruct;

which defines an anonymous struct, and defines "SomeStruct"
as a synonym. (AFAIU, this is done "automatically" in C++)
and I need to supply (in C++) values to an array of this type. I
understand that I can set *static* values by

static SomeStruct
myStruct[] = {
{"name1", 1, 17},
{"name2", 7, 25},
{"name3", 3, 6},
{"name4", 0,117}
};

However, I need to set dynamic values along these lines (I know that the
code does not work):

static SomeStruct
myStruct[] = {
{name[0], kind[0], val[0]},
{name[1], kind[1], val[1]},
...
{name[n], kind[n], val[n]},
};

where both the values and the number of values are only known at
runtime. I cannot change the definition in the header file; I am not
even sure that what I want to do is possible. Any ideas?

If the size of the array is not known at compile-time, then you
need dynamic memory allocation.

In C, you could write:

#include <stdlib.h>
typedef struct { char *name; int kind; int value; } SomeStruct;
static SomeStruct *array;
void array_init(int n, char **name, int *kind, int *value)
{
int i;
array = malloc(n * sizeof *array);
for (i = 0; i < n; ++i)
{
SomeStruct temp = { name, kind, value };
array = temp;
}
}

In C++, they use new and new[] instead of malloc.
 
J

James Kuyper

gus gassmann wrote:

First of all, it is not clear what "C/C++" means.
There is C, and there is C++.

In context, it means mixed-language programming. He has a C header file
declaring a struct type, and an array of that type which will be filled
in by calling a C++ function. The C++ standard defines how mixing C and
C++ works; the C standard does not, so this is fundamentally a C++ question.
 
J

James Kuyper

Hi,

I have a static struct, defined in a C header file as something like

typedef struct
SomeStruct {
char *name;
int kind;
int value;
}

That's not a valid typedef; it's not clear what you intended, but given
the code below, you should probably have moved 'SomeStruct' after the
right curly bracket, and then appended a semicolon.
and I need to supply (in C++) values to an array of this type. I
understand that I can set *static* values by

static SomeStruct
myStruct[] = {
{"name1", 1, 17},
{"name2", 7, 25},
{"name3", 3, 6},
{"name4", 0,117}
};

However, I need to set dynamic values along these lines (I know that the
code does not work):

static SomeStruct
myStruct[] = {
{name[0], kind[0], val[0]},
{name[1], kind[1], val[1]},
...
{name[n], kind[n], val[n]},
};

where both the values and the number of values are only known at
runtime. I cannot change the definition in the header file; I am not
even sure that what I want to do is possible. Any ideas?

It isn't possible to do this in the initializer of a static object. That
initializer is evaluated at the time the object is created, which for
objects with static storage duration, occurs at the start of the
program. If you want the values to be set at run time, you'll have to
use assignment expressions to fill it in, not initialization.

If the array's length is not known until run time, you can't make it a
static array; the best you can do is a static pointer to a dynamically
allocated array, which should be initialized with a null pointer. Make
sure that your code is written in such a way as to guarantee that the
pointer is not dereferenced while null. You could use malloc() to
allocate the array, in either language. However, since the array will be
filled in by C++ code, you'd be better off using a 'new' expression,
which has better type safety.
 
R

Richard Damon

static SomeStruct
myStruct[] = {
{name[0], kind[0], val[0]},
{name[1], kind[1], val[1]},
...
{name[n], kind[n], val[n]},
};

where both the values and the number of values are only known at
runtime. I cannot change the definition in the header file; I am not
even sure that what I want to do is possible. Any ideas?

Thanks

gus

Fundamental problem! A static (or any global) object MUST have a
compiler known size, you can NOT have its size determined at run time.

If its size is not know at compile time, then its memory allocation must
be dynamic. (In C99, but not C++ except as an extension, it could also
be of storage type auto).

This is a fundamental issue, any static/global object has a fixed
address range, so a fixed size. you need something without a fixed
address to store something of variable size (unless you can cap the size
and just always allocate the most space it will ever need).

In C and C++, this is done with the heap.

You could possible change your definition of myStruct to

static SomeStruct *myStruct;

And then create the array of SomeStructs and assign its address to
myStruct.
 
J

James Kuyper

On 10/17/12 5:49 AM, gus gassmann wrote: ....
If its size is not know at compile time, then its memory allocation must
be dynamic. (In C99, but not C++ except as an extension, it could also
be of storage type auto).

Use of the 'auto' keyword is completely unnecessary in C, because it can
only be used in contexts where the object would have automatic storage
duration whether or not you used that keyword. In C++ the 'auto' keyword
cannot be used for this purpose, because it has been given a different,
much more useful meaning. However, automatic storage duration is the
default for objects declared with block scope in both C and C++.
 
J

Jorgen Grahn

.
In C++, they use new and new[] instead of malloc.

Actually, we usually use std::vector or other containers, so we don't
have to manage the memory manually. (It's unclear though if the OP is
allowed to to that or if he needs to keep a raw C array.)

/Jorgen
 
G

gus gassmann

gus gassmann wrote:

First of all, it is not clear what "C/C++" means.
There is C, and there is C++.
I have a static struct, defined in a C header file as something like

typedef struct
SomeStruct {
char *name;
int kind;
int value;
}

Nit: you should copy/paste the EXACT code. The above does not make sense.
(warning: useless storage class specifier in empty declaration)

I think you meant:

typedef struct { char *name; int kind; int value; } SomeStruct;

which defines an anonymous struct, and defines "SomeStruct"
as a synonym. (AFAIU, this is done "automatically" in C++)
and I need to supply (in C++) values to an array of this type. I
understand that I can set *static* values by

static SomeStruct
myStruct[] = {
{"name1", 1, 17},
{"name2", 7, 25},
{"name3", 3, 6},
{"name4", 0,117}
};

However, I need to set dynamic values along these lines (I know that the
code does not work):

static SomeStruct
myStruct[] = {
{name[0], kind[0], val[0]},
{name[1], kind[1], val[1]},
...
{name[n], kind[n], val[n]},
};

where both the values and the number of values are only known at
runtime. I cannot change the definition in the header file; I am not
even sure that what I want to do is possible. Any ideas?

If the size of the array is not known at compile-time, then you
need dynamic memory allocation.

In C, you could write:

#include <stdlib.h>
typedef struct { char *name; int kind; int value; } SomeStruct;
static SomeStruct *array;
void array_init(int n, char **name, int *kind, int *value)
{
int i;
array = malloc(n * sizeof *array);
for (i = 0; i < n; ++i)
{
SomeStruct temp = { name, kind, value };
array = temp;
}
}

In C++, they use new and new[] instead of malloc.


Thanks to all who responded. As Jorgen Grahn surmised, I am not allowed
to change the C header file. I like Noob's solution, because it seems
simple and uses an idiom that I am familiar with. I therefore tried to
incorporate it into my file, making some adjustments I thought
necessary, and ran into trouble I cannot fathom. I would be grateful for
further assistance.

First, the C header file test.h. (I tried to simplify as much as
possible, while being faithful to the functionality I will need.)

#ifndef test_processed
#define test_processed

typedef struct
MyStruct {
char *name;
int kind;
int nextra;
} MyStruct;

#endif

Then the C++ code.

#include "test.h"
#include <sstream>

std::string get_str();
int get_dim();
int get_int();


int main(){
int ndim;
MyStruct *myStruct = 0;

ndim = get_dim();

myStruct = new MyStruct[ndim];
for (int i=0; i < ndim; i++)
{
myStruct = {{(const)(get_str().c_str()), 0,
get_int()}};
}
return 0;
}

std::string get_str()
{
return "test";
}

int get_dim()
{
return 7;
}

int get_int()
{
return 2;
}

This gives the following error messages:

test.cpp: In function ‘int main()’:
test.cpp:18:34: error: ISO C++ forbids declaration of ‘type name’ with
no type
test.cpp:18:58: error: cast from ‘const char*’ to ‘const int’ loses
precision
test.cpp:18:74: warning: extended initializer lists only available with
-std=c++0x or -std=gnu++0x
test.cpp:18:74: warning: extended initializer lists only available with
-std=c++0x or -std=gnu++0x
test.cpp:18:74: error: no match for ‘operator=’ in ‘*(myStruct + ((long
unsigned int)(((long unsigned int)i) * 16ul))) = {{((const int)((long
int)get_str()().std::basic_string<_CharT, _Traits, _Alloc>::c_str [with
_CharT = char, _Traits = std::char_traits<char>, _Alloc =
std::allocator<char>]())), 0, get_int()}}’
test.h:2:10: note: candidate is: MyStruct& MyStruct::eek:perator=(const
MyStruct&)

The first two have me confused. (Not that the others are any clearer,
but there I do not even understand what the error message means.) Isn't
'name' defined in the header file? And why does the compiler think I am
trying to cast to 'const int'?
BTW, I first tried

void main()

but the compiler complained that main() must return an integer, so I
changed it. I thought a void main was the proper thing.

Can anyone help me? (As you see, I no speaka da C++ so good, so please
be gentle.)

gus
 
I

Ike Naar

Thanks to all who responded. As Jorgen Grahn surmised, I am not allowed
to change the C header file. I like Noob's solution, because it seems
simple and uses an idiom that I am familiar with. I therefore tried to
incorporate it into my file, making some adjustments I thought
necessary, and ran into trouble I cannot fathom. I would be grateful for
further assistance.

First, the C header file test.h. (I tried to simplify as much as
possible, while being faithful to the functionality I will need.)

#ifndef test_processed
#define test_processed

typedef struct
MyStruct {
char *name;
int kind;
int nextra;
} MyStruct;

#endif

Then the C++ code.

#include "test.h"
#include <sstream>

Nothing from <sstream> is used, so why include it?
Something from said:
std::string get_str();
int get_dim();
int get_int();

int main(){
int ndim;
MyStruct *myStruct = 0;
ndim = get_dim();
myStruct = new MyStruct[ndim];

Get rid of the bogus initializations, and simply write:

int ndim = get_dim();
MyStruct *myStruct = new MyStruct[ndim];
for (int i=0; i < ndim; i++)
{
myStruct = {{(const)(get_str().c_str()), 0, get_int()}};


Here you're casting away const, and that is often a danger sign.
In this case, myStruct.name is set to point to a temporary value that
no longer exists after this statement.
}
return 0;
}

std::string get_str()
{
return "test";
}

int get_dim()
{
return 7;
}

int get_int()
{
return 2;
}

This gives the following error messages:

test.cpp: In function ?int main()?:
test.cpp:18:34: error: ISO C++ forbids declaration of ?type name? with
no type
test.cpp:18:58: error: cast from ?const char*? to ?const int? loses
precision
test.cpp:18:74: warning: extended initializer lists only available with
-std=c++0x or -std=gnu++0x
test.cpp:18:74: warning: extended initializer lists only available with
-std=c++0x or -std=gnu++0x
test.cpp:18:74: error: no match for ?operator=? in ?*(myStruct + ((long
unsigned int)(((long unsigned int)i) * 16ul))) = {{((const int)((long
int)get_str()().std::basic_string<_CharT, _Traits, _Alloc>::c_str [with
_CharT = char, _Traits = std::char_traits<char>, _Alloc =
std::allocator<char>]())), 0, get_int()}}?
test.h:2:10: note: candidate is: MyStruct& MyStruct::eek:perator=(const
MyStruct&)

The first two have me confused. (Not that the others are any clearer,
but there I do not even understand what the error message means.) Isn't
'name' defined in the header file? And why does the compiler think I am
trying to cast to 'const int'?

It's not clear which line is line 34, but the first error may have to
do with std::string being undeclared because said:
BTW, I first tried
void main()

but the compiler complained that main() must return an integer, so I
changed it. I thought a void main was the proper thing.

It's not. What made you think so?
 
J

James Kuyper

On 10/17/2012 05:47 PM, gus gassmann wrote:
....
First, the C header file test.h. (I tried to simplify as much as
possible, while being faithful to the functionality I will need.)

#ifndef test_processed
#define test_processed

typedef struct
MyStruct {
char *name;
int kind;
int nextra;
} MyStruct;

#endif

Then the C++ code.

#include "test.h"
#include <sstream>

std::string get_str();
int get_dim();
int get_int();


int main(){
int ndim;
MyStruct *myStruct = 0;

ndim = get_dim();

myStruct = new MyStruct[ndim];
for (int i=0; i < ndim; i++)
{
myStruct = {{(const)(get_str().c_str()), 0,
get_int()}};


That (const) is a syntax error. Perhaps you intended
const_cast<char*>(get_str().c_str())? That would, at least, be correct
syntax, and it would disable the warning message you would otherwise get
from assigning a const char* value in a char* object. However, it's not
a good idea to disable that warning message. Your "program shall not
alter any of the values stored in the character array" pointed at by the
pointer returned by c_str() (C++ 21.4.7.1p3), so I would not recommend
storing it in a char* variable. If your code (or any other code you pass
this struct to) makes the mistake of trying to write through
myStruct.name, your compiler won't warn you that you're making a
mistake.

That pointer may become invalid if at any time the std::string object
from which it cames gets modified (C++ 21.4.1p6). That's a general
warning that applies to any situation in which you call c_str(), though
it hardly matters in this case. This situation is much worse than that.
That's because the relevant string is the one returned by the call to
get_str(). Since that function returns the string by value, it is a
temporary object, whose lifetime will end as soon as the full-expression
containing that function call is finished being evaluated. In other
words, the pointer myStruct.name becomes invalid immediately after
being initialized.

You need to store the string returned by get_str(), allocate sufficient
memory for a copy of the contents of that string, and then copy the
string into that memory. I'm primarily a C programmer, so I may be
missing out some elegant C++ way of doing that, but the following
inelegant (and untested) code should do the job:

std::string s = get_str();
char *p = new char[s.size()];
s.copy(p, s.begin(), s.size());

Since you had to allocate this memory, you should make sure that it gets
deallocated by using the delete operator when you're done with it;
otherwise, you'll have a memory leak.

What Noob suggested corresponds to following this up with the following
code:

MyStruct temp = {s, 0, get_int()};
myStruct = temp;

You tried to simplify by eliminating temp, but that's going too far with
simplification. The whole point of the temporary variable is that
initialization syntax can only be used when defining an object. It can't
be used as the right-hand-side (RHS) of an an assignment expression.

....
This gives the following error messages: ....
The first two have me confused. (Not that the others are any clearer,
but there I do not even understand what the error message means.) Isn't
'name' defined in the header file? And why does the compiler think I am
trying to cast to 'const int'?

It's probably trying to interpret your use of "(const)". I'm not sure
what it made of that syntax error, but there's no point in wondering
about it. Not only is the syntax wrong, but what you were attempting to
do using that syntax was also wrong. Just fix along the lines indicated
above.
BTW, I first tried

void main()

but the compiler complained that main() must return an integer, so I
changed it. I thought a void main was the proper thing.

No, int main() is the proper thing; void main() is a popular extension
to both C and C++, but neither language guarantees that it is supported.
On an implementation where that extension is not supported, you'll
probably get a warning message, but in principle your entire program
could malfunction if main() is declared that way.
Can anyone help me? (As you see, I no speaka da C++ so good, so please
be gentle.)

Your code involved several nasty misconceptions. I tried to explain the
problems as precisely as possible; I hope that didn't seem excessively
rough.
 
I

Ian Collins

myStruct = new MyStruct[ndim];
for (int i=0; i< ndim; i++)
{
myStruct = {{(const)(get_str().c_str()), 0,
get_int()}};
}


As others have correctly pointed out, the mistyped cast from std::string
will end in tears. You need to convert the string to an char array and
keep track of that data for later disposal.

Something like this my help:

struct FromString
{
static std::vector<char*> stash;

char *p;

FromString( const std::string& s )
: p(new char[s.size()])
{
s.copy(p, s.size());
stash.push_back(p);
}

operator char*() const { return p; }
};

std::vector<char*> FromString::stash; // *free these later!!*

Then you could write:

for (int i=0; i < ndim; i++)
{
MyStruct temp = {FromString(get_str()), 0, get_int()};
myStruct = temp;
}

Note I kept the temporary variable to avoid the use C++11 extended
initialiser lists.
 
J

Juha Nieminen

In comp.lang.c++ Noob said:
If the size of the array is not known at compile-time, then you
need dynamic memory allocation.

Incorrect. In C you can use a variable-length array, such as:

void foo(int n)
{
SomeStruct s[n];
...
}

However, that's probably not what the OP meant because he explicitly
wanted to use an initializer list to initialize the array. Therefore
he probably didn't mean to imply that the size of the array is only
known at runtime.
 
J

Juha Nieminen

In comp.lang.c++ Richard Damon said:
If its size is not know at compile time, then its memory allocation must
be dynamic. (In C99, but not C++ except as an extension, it could also
be of storage type auto).

I think you are confusing variable-length arrays (in C) with the keyword
'auto', which has absolutely nothing to do with that (in either language).

(Btw, and I'm not talking about you specifically, but is it just me or
are most of the answers to the OP's question given in this thread
unusually vague, incorrect and outright incompetent?)
 
J

Juha Nieminen

In comp.lang.c++ Ike Naar said:
Get rid of the bogus initializations, and simply write:

int ndim = get_dim();
MyStruct *myStruct = new MyStruct[ndim];

What exactly would be the reason to use 'new' explicitly here rather than,
say, std::vector?

The above code makes the function error-prone and exception-unsafe.
 
S

Stuart

On 10/18/12 James Kuyper wrote:
[snip]
I'm primarily a C programmer, so I may be
missing out some elegant C++ way of doing that, but the following
inelegant (and untested) code should do the job:
[snip]

I'm intrigued. I have never ever met someone who described himself as a
C programmer. May I ask what kind of business you are in? My guess is
your line of work includes either device drivers, embedded devices,
kernel code or something else that forces you to write C instead of C++.
In all these years I have never met someone who used C instead of C++
unless he was forced to do so.

@OP: Sorry for interrupting your thread.

Regards,
Stuart
 
J

jacob navia

Le 18/10/12 09:55, Stuart a écrit :
On 10/18/12 James Kuyper wrote:
[snip]
I'm primarily a C programmer, so I may be
missing out some elegant C++ way of doing that, but the following
inelegant (and untested) code should do the job:
[snip]

I'm intrigued. I have never ever met someone who described himself as a
C programmer.

C++ people think that their extremely complex language is the best of
all worlds. They have so little consideration for other views that
they really think, as you say:

I have never met someone who used C instead of C++
unless he was forced to do so.

I have never used C++ unless I was forced to.

C++ is a mistake. It has produced a language that nobody understands,
not even Stroustrup himself.


But let's come back to reality. I will not convince you that simplicity
outperforms complexity in software engineering.

So, be assured that C++ is the best invention since sliced bread.
 
J

Juha Nieminen

In comp.lang.c++ jacob navia said:
C++ is a mistake. It has produced a language that nobody understands,
not even Stroustrup himself.

To make such an inflammatory post on a C++ group you are either incredibly
stupid or the worst kind of troll. (And that's an inclusive 'or'.)
 
I

Ian Collins

But let's come back to reality. I will not convince you that simplicity
outperforms complexity in software engineering.

The complexity is in the problem. Whether the burden of implementing
that complexity is shared with the compiler or shouldered solely by the
programmer is the programmer's choice.
 
J

jacob navia

Le 18/10/12 10:23, Juha Nieminen a écrit :
To make such an inflammatory post on a C++ group you are either incredibly
stupid or the worst kind of troll. (And that's an inclusive 'or'.)
To say in a C group (comp.lang.c)
I have never met someone who used C instead of C++
unless he was forced to do so.

is NOT inflammatory, of course.

I did NOT treat anyone of "stupid" or "troll" Mr Nieminen.

Just some facts to put this discussion in a less polemic tone:

1) Mr Stroustrup gave up on "concepts" after several YEARS of work. He
could not (as he said) understand all the implications that the change
could have, nor could the people in the standards committee so they just
balked out.

For me, in my stupid little mind, it means that nobody could really
understand the consequences of a modification of the language and that
the languagve has gotten too complex for humans to understand. You need
a machine to understand c++

2) Further proof that C++ is a language humans can't fully understand is
the fact that the specifications for operator overloading (for instance)
go for pages and pages of the C++ standard and require in the last
resort a topological sort of all possible classes involved. This means
that it can only be done in a moderately complex application by a
machine (i.e. a compiler) that will do the sort and produce its result
that can be (maybe) followed by humans in a super human effort but
can't be done in a daily basis by the C++ programmers that can only
GUESS that their code is right.

3) And yet another proof is that when somebody asks something in the
C++ group the answer is in MOST cases

"I compiled it with compiler XXX and the answer was YYY"

and NOT

"The correct answer should be YYY because the standard specifies ..." etc.

People give as a ROUTINE answer the answer the machine proposes them,
not the answer they deduced since understanding a statement can be
an impossible task.

The language has grown SO complex that nobody knows ALL the subtleties
of it. Most programmers know a small subset that they use (30-40%), and
ignore the rest. Experts may know 60-70%, but nobody really understands
it ALL.

Is it necessary for humans to understand completely a machine language?

I do not know. Yes, my message was polemic but it was an ANSWER to
previous polemic.

Yours sincerely

jacob
 
N

Noob

Juha said:
In said:
If the size of the array is not known at compile-time, then you
need dynamic memory allocation.

Incorrect. In C you can use a variable-length array, such as:

void foo(int n)
{
SomeStruct s[n];
...
}

VLAs have automatic storage duration. They are therefore not appropriate
for the OP's purpose.
However, that's probably not what the OP meant because he explicitly
wanted to use an initializer list to initialize the array. Therefore
he probably didn't mean to imply that the size of the array is only
known at runtime.

Right, except for the fact that he wrote:

"I need to set dynamic values [...] where both the values and the number
of values are only known at runtime."
 

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

Forum statistics

Threads
473,968
Messages
2,570,149
Members
46,695
Latest member
StanleyDri

Latest Threads

Top