Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]
Groups > comp.lang.c > #163814 > unrolled thread
| Started by | Oğuz <oguzismailuysal@gmail.com> |
|---|---|
| First post | 2021-12-13 02:49 -0800 |
| Last post | 2021-12-15 12:38 +0300 |
| Articles | 13 — 8 participants |
Back to article view | Back to comp.lang.c
About struct fields that are assigned once and never changed again Oğuz <oguzismailuysal@gmail.com> - 2021-12-13 02:49 -0800
Re: About struct fields that are assigned once and never changed again Oğuz <oguzismailuysal@gmail.com> - 2021-12-13 02:50 -0800
Re: About struct fields that are assigned once and never changed again Malcolm McLean <malcolm.arthur.mclean@gmail.com> - 2021-12-13 03:05 -0800
Re: About struct fields that are assigned once and never changed again David Brown <david.brown@hesbynett.no> - 2021-12-13 12:12 +0100
Re: About struct fields that are assigned once and never changed again Oğuz <oguzismailuysal@gmail.com> - 2021-12-13 09:01 -0800
Re: About struct fields that are assigned once and never changed again David Brown <david.brown@hesbynett.no> - 2021-12-13 18:37 +0100
Re: About struct fields that are assigned once and never changed again scott@slp53.sl.home (Scott Lurndal) - 2021-12-13 18:06 +0000
Re: About struct fields that are assigned once and never changed again Ben Bacarisse <ben.usenet@bsb.me.uk> - 2021-12-13 16:12 +0000
Re: About struct fields that are assigned once and never changed again James Kuyper <jameskuyper@alumni.caltech.edu> - 2021-12-13 12:09 -0500
Re: About struct fields that are assigned once and never changed again Guillaume <message@bottle.org> - 2021-12-13 19:15 +0100
Re: About struct fields that are assigned once and never changed again James Kuyper <jameskuyper@alumni.caltech.edu> - 2021-12-13 13:25 -0500
Re: About struct fields that are assigned once and never changed again Tim Rentsch <tr.17687@z991.linuxsc.com> - 2021-12-15 00:25 -0800
Re: About struct fields that are assigned once and never changed again Oğuz <oguzismailuysal@gmail.com> - 2021-12-15 12:38 +0300
| From | Oğuz <oguzismailuysal@gmail.com> |
|---|---|
| Date | 2021-12-13 02:49 -0800 |
| Subject | About struct fields that are assigned once and never changed again |
| Message-ID | <56b509db-c5ba-4020-b1e4-6250d41a706cn@googlegroups.com> |
I have an array of structs as follows:
struct {
struct {
int value;
bool in_use;
} values[MAX_INPUTS_SIZE];
size_t size;
} inputs;
and a function that initializes it with user-supplied data. After the function is run, nothing in the program alters `inputs.size' or `inputs.values[N].value'. Now I have two questions about this that I can't answer myself:
i) Is there a way to treat size and value fields as `const' outside that function?
ii) If there is, would it make any difference performance-wise? Would it allow the compiler to optimize more?
[toc] | [next] | [standalone]
| From | Oğuz <oguzismailuysal@gmail.com> |
|---|---|
| Date | 2021-12-13 02:50 -0800 |
| Message-ID | <f6e5e404-1336-40b6-a38a-a63bc7548fbdn@googlegroups.com> |
| In reply to | #163814 |
On Monday, December 13, 2021 at 1:49:20 PM UTC+3, Oğuz wrote: > I have an array of structs as follows: Sorry, I meant "a struct", not "an array of structs".
[toc] | [prev] | [next] | [standalone]
| From | Malcolm McLean <malcolm.arthur.mclean@gmail.com> |
|---|---|
| Date | 2021-12-13 03:05 -0800 |
| Message-ID | <95e8b1c9-b1a4-4743-afb5-478f18b93ea2n@googlegroups.com> |
| In reply to | #163814 |
On Monday, 13 December 2021 at 10:49:20 UTC, oguzism...@gmail.com wrote:
> I have an array of structs as follows:
>
> struct {
> struct {
> int value;
> bool in_use;
> } values[MAX_INPUTS_SIZE];
> size_t size;
> } inputs;
>
> and a function that initializes it with user-supplied data. After the function is run, nothing in the program alters `inputs.size' or `inputs.values[N].value'.
> Now I have two questions about this that I can't answer myself:
> i) Is there a way to treat size and value fields as `const' outside that function?
> ii) If there is, would it make any difference performance-wise? Would it allow the compiler to optimize more?
>
i) C doesn't allow that. It doesn't have a concept of a "constructor".
ii) It's unlikely to make any difference to performance. If a "variable" is known at compile time,
then the compiler can propagate it, and often do some significant optimisations. But if it is
read in from a parameter list, then this isn't possible.
[toc] | [prev] | [next] | [standalone]
| From | David Brown <david.brown@hesbynett.no> |
|---|---|
| Date | 2021-12-13 12:12 +0100 |
| Message-ID | <sp79qr$uop$1@dont-email.me> |
| In reply to | #163814 |
On 13/12/2021 11:49, Oğuz wrote:
> I have an array of structs as follows:
>
> struct {
> struct {
> int value;
> bool in_use;
> } values[MAX_INPUTS_SIZE];
> size_t size;
> } inputs;
>
(Google's broken interface messes up formatting, especially indentation.
If you are going to use Usenet a lot then I strongly recommend getting
a proper newsreader client and server. They are freely available, and
work much better.)
> and a function that initializes it with user-supplied data. After the function is run, nothing in the program alters `inputs.size' or `inputs.values[N].value'. Now I have two questions about this that I can't answer myself:
> i) Is there a way to treat size and value fields as `const' outside that function?
> ii) If there is, would it make any difference performance-wise? Would it allow the compiler to optimize more?
>
Your friend here is the online compiler at <https://godbolt.org>. You
can pick your language, your compiler, your flag set (for experimenting
with gcc, I'd recommend something like "-std=c11 -Wall -Wextra
-Wpedantic .O2"). Then you put in your test code and see the effect and
the error messages immediately. It's great for trying out different things.
In C, you can think of two kinds of "const". One is objects that are
defined as "const" - these must be given initial values, and you are not
allowed to change them later. That is not suitable here, as you don't
want to write out an initialiser for the whole inputs, you want to fill
it with a loop.
The other is to use a const-qualified lvalue (such as a "pointer to
const T") to access the data. This will not let the compiler do much
extra in the way of optimisation, but it can reduce the risk of mistakes
in your code (or rather, it can increase the likelihood of certain
mistakes being caught by the compiler.
As for performance, this all depends on what you are doing with the
data. If you hare using it heavily and might be doing so from different
cpu cores, then it can make sense to separate the fixed data from the
the variable data in different arrays. This will make it possible to
have shared read-only cache lines for the read-only data, rather than
more costly exclusive read-write cache lines. But don't worry about
that unless you are actually measuring performance and see that it is
slower than you want.
[toc] | [prev] | [next] | [standalone]
| From | Oğuz <oguzismailuysal@gmail.com> |
|---|---|
| Date | 2021-12-13 09:01 -0800 |
| Message-ID | <221ac97e-0500-45dc-867a-b469f3816712n@googlegroups.com> |
| In reply to | #163817 |
On Monday, December 13, 2021 at 2:12:39 PM UTC+3, David Brown wrote: > (Google's broken interface messes up formatting, especially indentation. > If you are going to use Usenet a lot then I strongly recommend getting > a proper newsreader client and server. They are freely available, and > work much better.) Sorry. I tried Pan before but didn't like it. Which one do you use?
[toc] | [prev] | [next] | [standalone]
| From | David Brown <david.brown@hesbynett.no> |
|---|---|
| Date | 2021-12-13 18:37 +0100 |
| Message-ID | <sp80dm$v96$1@dont-email.me> |
| In reply to | #163820 |
On 13/12/2021 18:01, Oğuz wrote: > On Monday, December 13, 2021 at 2:12:39 PM UTC+3, David Brown wrote: >> (Google's broken interface messes up formatting, especially indentation. >> If you are going to use Usenet a lot then I strongly recommend getting >> a proper newsreader client and server. They are freely available, and >> work much better.) > > Sorry. I tried Pan before but didn't like it. Which one do you use? > I don't want to start yet another thread or branch about newsreaders - if my answer here doesn't suit you, there are many previous ones in this group and others. (And if there is one thing google groups is good for, it is searching old posts.) My email is also valid. I use Thunderbird for news (and email, and calender), on Windows and Linux. I don't know if it is the "best" Usenet client - that will depend on personal preferences. But it has been fine for me since about version 1. I use news.eternal-semptember.org as the server - it's free, and has all the newsgroups I need. (I also use news.gmane.org as an alternative interface to a number of mailing lists.)
[toc] | [prev] | [next] | [standalone]
| From | scott@slp53.sl.home (Scott Lurndal) |
|---|---|
| Date | 2021-12-13 18:06 +0000 |
| Message-ID | <wYLtJ.26162$G996.16069@fx31.iad> |
| In reply to | #163820 |
=?UTF-8?B?T8SfdXo=?= <oguzismailuysal@gmail.com> writes: >On Monday, December 13, 2021 at 2:12:39 PM UTC+3, David Brown wrote: >> (Google's broken interface messes up formatting, especially indentation. >> If you are going to use Usenet a lot then I strongly recommend getting >> a proper newsreader client and server. They are freely available, and >> work much better.) > >Sorry. I tried Pan before but didn't like it. Which one do you use? What's not to like about PAN, particularly when compared with GG? I use 'xrn'.
[toc] | [prev] | [next] | [standalone]
| From | Ben Bacarisse <ben.usenet@bsb.me.uk> |
|---|---|
| Date | 2021-12-13 16:12 +0000 |
| Message-ID | <87fsqw32mi.fsf@bsb.me.uk> |
| In reply to | #163814 |
Oğuz <oguzismailuysal@gmail.com> writes:
> I have a struct as follows:
(corrected)
>
> struct {
> struct {
> int value;
> bool in_use;
> } values[MAX_INPUTS_SIZE];
> size_t size;
> } inputs;
>
> and a function that initializes it with user-supplied data. After the
> function is run, nothing in the program alters `inputs.size' or
> `inputs.values[N].value'. Now I have two questions about this that I
> can't answer myself:
>
> i) Is there a way to treat size and value fields as `const' outside
> that function?
Hmm... You could have two versions, a public one and a private one (the
pre-processor is your friend here). The public one has const on value
and size, the private one, visible to the function that sets up the
data, does not.
I have a feeling this is not 100% permitted, but I don't have time to
check.
> ii) If there is, would it make any difference performance-wise? Would
> it allow the compiler to optimize more?
Pass.
--
Ben.
[toc] | [prev] | [next] | [standalone]
| From | James Kuyper <jameskuyper@alumni.caltech.edu> |
|---|---|
| Date | 2021-12-13 12:09 -0500 |
| Message-ID | <sp7uo8$spv$2@dont-email.me> |
| In reply to | #163814 |
On 12/13/21 5:49 AM, Oğuz wrote:
> I have an array of structs as follows:
>
> struct {
> struct {
> int value;
> bool in_use;
> } values[MAX_INPUTS_SIZE];
> size_t size;
> } inputs;
>
> and a function that initializes it with user-supplied data. After the function is run, nothing in the program alters `inputs.size' or `inputs.values[N].value'. Now I have two questions about this that I can't answer myself:
> i) Is there a way to treat size and value fields as `const' outside that function?
Sort-of. What you can do is use access functions to give a const
interface to the data, with the file containing the initialization
routine and access functions being the only place which has non-const
access to the data. The key to making this work is to declaring an
opaque struct type for use outside of that, with those functions being
the only ones that know the actual struct layout:
mystruct.h:
#ifndef H_MYSTRUCT
#define H_MYSTRUCT
struct mystruct;
int get_value(const struct mystruct*, size_t);
size_t get_size(const struct mystruct*);
const struct mystruct *initialize_mystruct
(size_t size, int[static size]);
#endif
mystruct.c:
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include "mystruct.h"
// Since this approach requires that mystruct objects be dynamically
// allocated, I decided to save some space by moving size to the
// beginning, allowing values to be a flexible array member. As a result
// you don't have to impose any arbitrary upper limit on the size;
// you're limited only by the amount of availale memory. You don't have
// to do this.
struct mystruct {
size_t size;
struct {
int value;
bool in_use;
} values[];
};
int get_value(const struct mystruct *inputs, size_t index)
{
// I'm not sure how you want to use in_use, but it should probably
// have some role to play in this function.
if(inputs == NULL || index >= inputs->size)
{
// Error handling
return 0;
}
return inputs->values[index].value;
}
size_t get_size(const struct mystruct *inputs)
{
if(inputs == NULL)
{
// Error handling
return 0;
}
return inputs->size;
}
const struct mystruct *initialize_mystruct(size_t size, int values[size])
{
struct mystruct *inputs = malloc(sizeof(*inputs) +
size * sizeof(inputs->values[0]));
if(inputs == NULL)
{
// Error handling
return NULL;
}
inputs->size = size;
memcpy(inputs->values, values, size*sizeof(values[0]));
for(size_t val=0; val<size; val++)
inputs->values[val].in_use = false;
return inputs;
}
The above code compiles on my desktop without error messages, but I have
not tested it. My apologies for any errors I might have made.
Note that the members of mystruct are completely unknown outside of
mystruct.c, and code outside of mystruct.c never sees anything other
than const-qualified pointers to struct mystruct.
> ii) If there is, would it make any difference performance-wise? Would it allow the compiler to optimize more?
I wouldn't expect major benefits from this approach other than making
the contents of struct mystruct inaccessible.
[toc] | [prev] | [next] | [standalone]
| From | Guillaume <message@bottle.org> |
|---|---|
| Date | 2021-12-13 19:15 +0100 |
| Message-ID | <sp82kn$66h$1@gioia.aioe.org> |
| In reply to | #163814 |
Le 13/12/2021 à 11:49, Oğuz a écrit :
> I have an array of structs as follows:
>
> struct {
> struct {
> int value;
> bool in_use;
> } values[MAX_INPUTS_SIZE];
> size_t size;
> } inputs;
>
> and a function that initializes it with user-supplied data. After the function is run, nothing in the program alters `inputs.size' or `inputs.values[N].value'. Now I have two questions about this that I can't answer myself:
> i) Is there a way to treat size and value fields as `const' outside that function?
There is an 'ugly' way of doing it.
You can qualify the struct itself (if it's going to be entirely const),
or any member of it 'const'.
Now, to initialize members at run-time, you would use pointers and
casts. For instance:
*(size_t *)(&inputs.size) = 10;
This will "override" the const qualifier. Clunky way of accessing struct
members, but it "works".
Note that this is likely undefined behavior, strictly speaking (I'll
have to check in the standard.) Depending on implementation, anything
qualified "const" may not even be put in a writable memory area. (I
think it's more likely to happen if the whole struct is qualified
"const" rather than individual members, but those are just assumptions...)
> ii) If there is, would it make any difference performance-wise? Would it allow the compiler to optimize more?
I doubt it. Maybe in some specific cases.
But anyway, the only right way of initializing "const" objects is to
initialize them at compile time (meaning, with initializers.)
If you want to "protect" struct members outside of specific functions,
nothing strictly conforming in C will work. You may try the above
approach, which is kind of messy and not guaranteed to be correct except
if you're lucky.
[toc] | [prev] | [next] | [standalone]
| From | James Kuyper <jameskuyper@alumni.caltech.edu> |
|---|---|
| Date | 2021-12-13 13:25 -0500 |
| Message-ID | <sp836b$m7i$1@dont-email.me> |
| In reply to | #163826 |
On 12/13/21 1:15 PM, Guillaume wrote: ... > You can qualify the struct itself (if it's going to be entirely const), > or any member of it 'const'. > > Now, to initialize members at run-time, you would use pointers and > casts. For instance: > > *(size_t *)(&inputs.size) = 10; > > This will "override" the const qualifier. Clunky way of accessing struct > members, but it "works". > > Note that this is likely undefined behavior, strictly speaking (I'll > have to check in the standard.) Correct: "If an attempt is made to modify an object defined with a const qualified type through use of an lvalue with non-const-qualified type, the behavior is undefined." (6.7.3p6). *(size_t*)(&inputs.size) creates an lvalue of type "size_t", which is not const-qualified. Also note that attempting to modify an object through an lvalues that IS const-qualified is a constraint violation. Depending on implementation, anything > qualified "const" may not even be put in a writable memory area. (I > think it's more likely to happen if the whole struct is qualified > "const" rather than individual members, but those are just assumptions...)
[toc] | [prev] | [next] | [standalone]
| From | Tim Rentsch <tr.17687@z991.linuxsc.com> |
|---|---|
| Date | 2021-12-15 00:25 -0800 |
| Message-ID | <864k7a5l6q.fsf@linuxsc.com> |
| In reply to | #163814 |
Oğuz <oguzismailuysal@gmail.com> writes:
> I have an array of structs as follows:
>
> struct {
> struct {
> int value;
> bool in_use;
> } values[MAX_INPUTS_SIZE];
> size_t size;
> } inputs;
>
> and a function that initializes it with user-supplied data. After
> the function is run, nothing in the program alters `inputs.size'
> or `inputs.values[N].value'. Now I have two questions about this
> that I can't answer myself:
>
> i) Is there a way to treat size and value fields as `const'
> outside that function?
Here is one well-known technique. Step one: write a .h file
that defines the struct type and declares an extern pointer
variable and a function to do the initializing. Note that the
type of the pointer variable says it points to a 'const' struct:
/* file provide-struct.h */
#ifndef H_PROVIDE_STRUCT_H
#define H_PROVIDE_STRUCT_H
#include <stddef.h>
enum { MAX_INPUTS_SIZE = 10000 };
typedef struct {
struct {
int value;
_Bool in_use;
} values[ MAX_INPUTS_SIZE ];
size_t size;
} Values_Array_Struct;
extern const Values_Array_Struct *const inputs;
extern void initialize_inputs_struct( const char * );
#endif/*H_PROVIDE_STRUCT_H*/
Step two: write a .c file that defines a static instance of the
struct, along with a definition for the pointer variable and the
initializing function. Because the static struct instance is
declared (and defined) without using 'const', the initialization
function can write its values with no problem:
/* file provide-struct.c */
#include "provide-struct.h"
static Values_Array_Struct inputs_w;
const Values_Array_Struct *const inputs = &inputs_w;
void
initialize_inputs_struct( const char *data ){
inputs_w.size = 3;
inputs_w.values[0].value = 3;
inputs_w.values[2].value = 4;
inputs_w.values[0].in_use = 1;
inputs_w.values[2].in_use = 1;
/* ... etc ... */
}
Step three: write client code in a different .c file, which can
initialize the struct by calling the initialization function, and
access the struct using the declared pointer variable. Because
the pointer variable's type says it points to a 'const' struct,
the struct cannot be modified by client code:
/* file provide-struct-client.c */
#include "provide-struct.h"
#define inputs (*inputs)
unsigned
example_client( const char *data ){
unsigned i;
unsigned total = 0;
initialize_inputs_struct( data );
/* Any attempt to modify a field of 'inputs' will
be flagged by the compiler. */
for( i = 0; i < inputs.size; i++ ){
if( inputs.values[i].in_use ){
total += inputs.values[i].value;
}
}
return total;
}
Incidentally, notice the #define in this file. This macro
definition uses an idiom so the struct can be accessed using '.'
rather than '->'. Doing that lets the code look a little nicer
but isn't an essential element of the basic idea.
[toc] | [prev] | [next] | [standalone]
| From | Oğuz <oguzismailuysal@gmail.com> |
|---|---|
| Date | 2021-12-15 12:38 +0300 |
| Message-ID | <spcd24$5i3$1@oguzismail.eternal-september.org> |
| In reply to | #163840 |
On 12/15/21 11:25 AM, Tim Rentsch wrote:
> Here is one well-known technique. Step one: write a .h file
> that defines the struct type and declares an extern pointer
> variable and a function to do the initializing. Note that the
> type of the pointer variable says it points to a 'const' struct:
>
> /* file provide-struct.h */
>
> #ifndef H_PROVIDE_STRUCT_H
> #define H_PROVIDE_STRUCT_H
>
> #include <stddef.h>
>
> enum { MAX_INPUTS_SIZE = 10000 };
>
> typedef struct {
> struct {
> int value;
> _Bool in_use;
> } values[ MAX_INPUTS_SIZE ];
> size_t size;
> } Values_Array_Struct;
>
> extern const Values_Array_Struct *const inputs;
>
> extern void initialize_inputs_struct( const char * );
>
> #endif/*H_PROVIDE_STRUCT_H*/
>
> Step two: write a .c file that defines a static instance of the
> struct, along with a definition for the pointer variable and the
> initializing function. Because the static struct instance is
> declared (and defined) without using 'const', the initialization
> function can write its values with no problem:
>
> /* file provide-struct.c */
>
> #include "provide-struct.h"
>
> static Values_Array_Struct inputs_w;
>
> const Values_Array_Struct *const inputs = &inputs_w;
>
> void
> initialize_inputs_struct( const char *data ){
> inputs_w.size = 3;
> inputs_w.values[0].value = 3;
> inputs_w.values[2].value = 4;
> inputs_w.values[0].in_use = 1;
> inputs_w.values[2].in_use = 1;
> /* ... etc ... */
> }
>
> Step three: write client code in a different .c file, which can
> initialize the struct by calling the initialization function, and
> access the struct using the declared pointer variable. Because
> the pointer variable's type says it points to a 'const' struct,
> the struct cannot be modified by client code:
>
> /* file provide-struct-client.c */
>
> #include "provide-struct.h"
>
> #define inputs (*inputs)
>
> unsigned
> example_client( const char *data ){
> unsigned i;
> unsigned total = 0;
>
> initialize_inputs_struct( data );
>
> /* Any attempt to modify a field of 'inputs' will
> be flagged by the compiler. */
>
> for( i = 0; i < inputs.size; i++ ){
> if( inputs.values[i].in_use ){
> total += inputs.values[i].value;
> }
> }
> return total;
> }
>
> Incidentally, notice the #define in this file. This macro
> definition uses an idiom so the struct can be accessed using '.'
> rather than '->'. Doing that lets the code look a little nicer
> but isn't an essential element of the basic idea.
>
This and others all worked fine and I learned a lot from this thread,
thank you all. And for the record, neither making those fields const nor
separating `inputs' into two arrays made the program any faster than it
already was :/
[toc] | [prev] | [standalone]
Back to top | Article view | comp.lang.c
csiph-web