Velocity Reviews - Computer Hardware Reviews

Velocity Reviews > Newsgroups > Programming > C Programming > struct by value

Reply
Thread Tools

struct by value

 
 
ImpalerCore
Guest
Posts: n/a
 
      10-13-2010
When creating an API for a struct in C, one of the questions that
recently came up is how to pass that struct or return a struct from a
function. Often the answer is obvious, to use pointers, particularly
for large structs for performance reasons.

However, as the struct size shrinks, the choice of passing struct by
value or by pointer becomes less clear to me. Let me use a simple
struct as an example.

struct greg_ymd
{
int16_t year;
int8_t month;
int8_t day;
};

I use this struct to represent a date in the gregorian calendar
(fields not offset to start from 0). Let's say that I want to have a
function that adds a certain number of days to this ymd struct. There
are a couple of options that come to mind.

1. struct greg_ymd add_days( const struct greg_ymd ymd, int days );
2. <return type> add_days( struct greg_ymd* pymd, int days );

First of all, what is the performance implications of using struct
pass-by-value for smallish structs?
Is there a rule of thumb of struct size for an interface API that you
convert all struct parameter passing to use pointers?
Have you used struct by value parameter passing or return value at all
in your API design experience?

The struct pass-by-value version of the interface avoids the pesky
NULL pointer argument issue, but still I can't get away from it
completely since I use 'int (*compare_function)( const void* p, const
void* q )' to define the sorting property in my generic containers.

Thanks for your time.
 
Reply With Quote
 
 
 
 
Keith Thompson
Guest
Posts: n/a
 
      10-13-2010
ImpalerCore <(E-Mail Removed)> writes:
> When creating an API for a struct in C, one of the questions that
> recently came up is how to pass that struct or return a struct from a
> function. Often the answer is obvious, to use pointers, particularly
> for large structs for performance reasons.
>
> However, as the struct size shrinks, the choice of passing struct by
> value or by pointer becomes less clear to me. Let me use a simple
> struct as an example.
>
> struct greg_ymd
> {
> int16_t year;
> int8_t month;
> int8_t day;
> };


I'd say that "small" structs can sensibly be passed by value (unless of
course the function needs to modify them), and "large" structs
should be passed by pointer for performance reasons.

I have no particular guidance to offer about the dividing line between
"small" and "large". I'd say that anything no larger than a pointer is
certainly "small", but beyond that ...

I know that's not very helpful.

> I use this struct to represent a date in the gregorian calendar
> (fields not offset to start from 0). Let's say that I want to have a
> function that adds a certain number of days to this ymd struct. There
> are a couple of options that come to mind.
>
> 1. struct greg_ymd add_days( const struct greg_ymd ymd, int days );


The "const" doesn't really serve any purpose here, any more than
"const int days" would.

> 2. <return type> add_days( struct greg_ymd* pymd, int days );


Here a "const" would be helpful:
<return type> add_days( const struct greg_ymd *pymd, int days );
since it guarantees to the caller that the object pointed to by
the argument won't be modified.

> First of all, what is the performance implications of using struct
> pass-by-value for smallish structs?


It depends on the compiler.

[...]

--
Keith Thompson (The_Other_Keith) http://www.velocityreviews.com/forums/(E-Mail Removed) <http://www.ghoti.net/~kst>
Nokia
"We must do something. This is something. Therefore, we must do this."
-- Antony Jay and Jonathan Lynn, "Yes Minister"
 
Reply With Quote
 
 
 
 
ImpalerCore
Guest
Posts: n/a
 
      10-13-2010
On Oct 13, 2:29*pm, Keith Thompson <(E-Mail Removed)> wrote:
> ImpalerCore <(E-Mail Removed)> writes:
> > When creating an API for a struct in C, one of the questions that
> > recently came up is how to pass that struct or return a struct from a
> > function. *Often the answer is obvious, to use pointers, particularly
> > for large structs for performance reasons.

>
> > However, as the struct size shrinks, the choice of passing struct by
> > value or by pointer becomes less clear to me. *Let me use a simple
> > struct as an example.

>
> > struct greg_ymd
> > {
> > * int16_t year;
> > * int8_t month;
> > * int8_t day;
> > };

>
> I'd say that "small" structs can sensibly be passed by value (unless of
> course the function needs to modify them), and "large" structs
> should be passed by pointer for performance reasons.
>
> I have no particular guidance to offer about the dividing line between
> "small" and "large". *I'd say that anything no larger than a pointer is
> certainly "small", but beyond that ...
>
> I know that's not very helpful.


Yeah, I didn't really know how to answer the question either. I could
try to perform some experiments, but I don't know how useful it would
be. The two structs I use pass-by-value in their interface is the
greg_ymd above, and a timeval like struct. I provide my own simply
because struct timeval doesn't seem to be standard as far as I can
tell.

struct c_timeval
{
long int sec;
long int usec;
};

> > I use this struct to represent a date in the gregorian calendar
> > (fields not offset to start from 0). *Let's say that I want to have a
> > function that adds a certain number of days to this ymd struct. *There
> > are a couple of options that come to mind.

>
> > 1. *struct greg_ymd add_days( const struct greg_ymd ymd, int days );

>
> The "const" doesn't really serve any purpose here, any more than
> "const int days" would.


Good point.

> > 2. *<return type> add_days( struct greg_ymd* pymd, int days );

>
> Here a "const" would be helpful:
> * * <return type> add_days( const struct greg_ymd *pymd, int days );
> since it guarantees to the caller that the object pointed to by
> the argument won't be modified.


Actually, in this case, the pymd value would be modified, and the
return value would be used to indicate some error status or void if I
ignore pymd == NULL error issues.

The difference in usage would look like the following.

\code example
int main(void)
{
struct c_greg_ymd ymd = { 2000, 1, 1 };

#ifdef PASS_STRUCT_BY_VALUE
ymd = add_days( ymd, 7 ); /* ymd is now { 2000, 1, 8 } */
#else /* PASS_STRUCT_BY_POINTER
add_days( &ymd, 7 ); /* ymd is now { 2000, 1, 8 } */
#endif
}
\endcode

> > First of all, what is the performance implications of using struct
> > pass-by-value for smallish structs?

>
> It depends on the compiler.


And maybe the compiler optimization flags too.

Best regards,
John D.
 
Reply With Quote
 
Gene
Guest
Posts: n/a
 
      10-14-2010
On Oct 13, 1:04*pm, ImpalerCore <(E-Mail Removed)> wrote:
> When creating an API for a struct in C, one of the questions that
> recently came up is how to pass that struct or return a struct from a
> function. *Often the answer is obvious, to use pointers, particularly
> for large structs for performance reasons.
>
> However, as the struct size shrinks, the choice of passing struct by
> value or by pointer becomes less clear to me. *Let me use a simple
> struct as an example.
>
> struct greg_ymd
> {
> * int16_t year;
> * int8_t month;
> * int8_t day;
>
> };
>
> I use this struct to represent a date in the gregorian calendar
> (fields not offset to start from 0). *Let's say that I want to have a
> function that adds a certain number of days to this ymd struct. *There
> are a couple of options that come to mind.
>
> 1. *struct greg_ymd add_days( const struct greg_ymd ymd, int days );
> 2. *<return type> add_days( struct greg_ymd* pymd, int days );
>
> First of all, what is the performance implications of using struct
> pass-by-value for smallish structs?
> Is there a rule of thumb of struct size for an interface API that you
> convert all struct parameter passing to use pointers?
> Have you used struct by value parameter passing or return value at all
> in your API design experience?
>
> The struct pass-by-value version of the interface avoids the pesky
> NULL pointer argument issue, but still I can't get away from it
> completely since I use 'int (*compare_function)( const void* p, const
> void* q )' *to define the sorting property in my generic containers.


I have done some tests in the past with 32-bit gcc and Visual C. Both
would move structs to registers for call-by-value params, local
assignment (as in swapping struct values) and return values if the
size was 4 bytes or less.

In practice though the cost difference between allocating a struct in
the caller and passing a pointer to it vice accepting a return value
must be vanishingly small even if the struct does fit in a register.
When the struct doesn't fit in a register, the two return mechanisms
would be all but identical.

I've always used caller allocates and passes pointer for APIs. This
is for at least 5 reasons. (1) It's efficient enough in all cases
I've ever encountered. (2) A single convention means the caller must
remember only a single convention. (3) You get "in", "out", and "in
out" parameters all with the same mechanism. (4) You also get
"optional" parameters with the same mechanism by allowing NULL to mean
"not needed." This works for all 3 kinds. (5) It's clean in the
implementation to uniformly use -> for all (or nearly all) element
access rather than a mix of . and ->.

A convention I find very useful is this

typedef struct foo_s {

... big collection of fields ...

} FOO;

// Prepares the raw memory of the foo for initialization.
// Often just zeros or sets a flag so that later functions can
// see it hasn't been set up yet. May even do nothing. Declare anyway
// as a placeholder for the convention. Don't allocate anything here.
// Can't fail, so a void function.
void init_foo(FOO *foo);

// Make a foo fully ready for use. Allocate storage and other
resources.
// Return 0 on success else an error code.
int set_up_foo(FOO *foo, ...);

// Release all resources of a foo, returning it to the init state.
// Return 0 on success else an error code. It's okay to set_up
immediately
// after a clear.
int clear_foo(FOO *foo);

Then in code:

// A way to eliminate the clutter of &. YMMV.
FOO foo_instance[1];

// Always init after declaration even if the init does nothing.
init_foo(foo_instance);

.... yada yada ..

for (...) {

err = set_up_foo(foo_instance, ...);

... check for error.

... use the foo instance then release its resources

err = clear_foo(foo_instance);

... check for error.

... more processing that doesn't need the foo instance.
}

I've used this so often that it's a comfortable old friend. Once I
used setjmp/longjmp as poor-man's exceptions rather than returning
error codes. It's okay if error handling doesn't need any
granularity.
 
Reply With Quote
 
Eric Sosman
Guest
Posts: n/a
 
      10-14-2010
On 10/13/2010 1:04 PM, ImpalerCore wrote:
> When creating an API for a struct in C, one of the questions that
> recently came up is how to pass that struct or return a struct from a
> function. Often the answer is obvious, to use pointers, particularly
> for large structs for performance reasons.
>
> However, as the struct size shrinks, the choice of passing struct by
> value or by pointer becomes less clear to me. Let me use a simple
> struct as an example.
>
> struct greg_ymd
> {
> int16_t year;
> int8_t month;
> int8_t day;
> };
>
> I use this struct to represent a date in the gregorian calendar
> (fields not offset to start from 0). Let's say that I want to have a
> function that adds a certain number of days to this ymd struct. There
> are a couple of options that come to mind.
>
> 1. struct greg_ymd add_days( const struct greg_ymd ymd, int days );


What does `const' buy you? Or, why not `const int days'? Other
than that, this seems plausible.

> 2.<return type> add_days( struct greg_ymd* pymd, int days );


This seems plausible, too.

> First of all, what is the performance implications of using struct
> pass-by-value for smallish structs?


Mu.

> Is there a rule of thumb of struct size for an interface API that you
> convert all struct parameter passing to use pointers?


When writing software that others might blame you for, the basic
rule of thumb is "Leave no fingerprints."

> Have you used struct by value parameter passing or return value at all
> in your API design experience?


Yes. For parameters, I'd estimate that I use a struct pointer
more frequently than a struct value, maybe 90%-10% or even more
lopsided. For function values, leaving "lookup-ish" functions aside,
I'd guess my own ratio is closer to 70%-30%. YMMV.

> The struct pass-by-value version of the interface avoids the pesky
> NULL pointer argument issue, but still I can't get away from it
> completely since I use 'int (*compare_function)( const void* p, const
> void* q )' to define the sorting property in my generic containers.


Sorry; I can't make sense of this. If "the pesky ... issue" is
that a struct pointer might be NULL, well, that can often be a help
rather than a harm: You can provide a NULL for an "optional" struct
pointer argument, but you cannot do so with a struct value. As for
your comparison function, the relevance escapes me: You've already
chosen to pass pointers, so what are you asking about?

--
Eric Sosman
(E-Mail Removed)lid
 
Reply With Quote
 
Jon
Guest
Posts: n/a
 
      10-14-2010
ImpalerCore wrote:
> When creating an API for a struct in C, one of the questions that
> recently came up is how to pass that struct or return a struct from a
> function. Often the answer is obvious, to use pointers, particularly
> for large structs for performance reasons.
>
> However, as the struct size shrinks, the choice of passing struct by
> value or by pointer becomes less clear to me. Let me use a simple
> struct as an example.
>
> struct greg_ymd
> {
> int16_t year;
> int8_t month;
> int8_t day;
> };
>
> I use this struct to represent a date in the gregorian calendar
> (fields not offset to start from 0). Let's say that I want to have a
> function that adds a certain number of days to this ymd struct. There
> are a couple of options that come to mind.


I really can't see that being a performance bottleneck, but you are
surely asking about theory/practice (read, being ultra-tidy in your
programming).

>
> 1. struct greg_ymd add_days( const struct greg_ymd ymd, int days );
> 2. <return type> add_days( struct greg_ymd* pymd, int days );
>
> First of all, what is the performance implications of using struct
> pass-by-value for smallish structs?


Profile it and see.

> Is there a rule of thumb of struct size for an interface API that you
> convert all struct parameter passing to use pointers?


I pass all structs by reference (I use a C/C++ compiler but limit
severely the amount of C++ things I use) and primitives by value.

> Have you used struct by value parameter passing or return value at all
> in your API design experience?
>
> The struct pass-by-value version of the interface avoids the pesky
> NULL pointer argument issue,


References are nice in that regard, but I have a feeling it is not
guaranteed portable (sorry for the C++ chat).

> but still I can't get away from it
> completely since I use 'int (*compare_function)( const void* p, const
> void* q )' to define the sorting property in my generic containers.


Default arguments (C++ again) can help with that. You can have null to
mean do an object compare or non-null to use the passed-in function.
(Side-stepping the "default arguments are evil" debate).

>
> Thanks for your time.



 
Reply With Quote
 
Jon
Guest
Posts: n/a
 
      10-14-2010
Keith Thompson wrote:
> ImpalerCore <(E-Mail Removed)> writes:
>> When creating an API for a struct in C, one of the questions that
>> recently came up is how to pass that struct or return a struct from a
>> function. Often the answer is obvious, to use pointers, particularly
>> for large structs for performance reasons.
>>
>> However, as the struct size shrinks, the choice of passing struct by
>> value or by pointer becomes less clear to me. Let me use a simple
>> struct as an example.
>>
>> struct greg_ymd
>> {
>> int16_t year;
>> int8_t month;
>> int8_t day;
>> };

>
> I'd say that "small" structs can sensibly be passed by value (unless
> of course the function needs to modify them), and "large" structs
> should be passed by pointer for performance reasons.
>
> I have no particular guidance to offer about the dividing line between
> "small" and "large". I'd say that anything no larger than a pointer
> is certainly "small", but beyond that ...
>
> I know that's not very helpful.


How about this "rule": if it's a primitive, pass by value, else don't.
There are not stack frame guarantees that allow further portable
("overall") rules of thumb, as far as I know.



 
Reply With Quote
 
Jon
Guest
Posts: n/a
 
      10-14-2010
Gene wrote:
> On Oct 13, 1:04 pm, ImpalerCore <(E-Mail Removed)> wrote:
>> When creating an API for a struct in C, one of the questions that
>> recently came up is how to pass that struct or return a struct from a
>> function. Often the answer is obvious, to use pointers, particularly
>> for large structs for performance reasons.
>>
>> However, as the struct size shrinks, the choice of passing struct by
>> value or by pointer becomes less clear to me. Let me use a simple
>> struct as an example.
>>
>> struct greg_ymd
>> {
>> int16_t year;
>> int8_t month;
>> int8_t day;
>>
>> };
>>
>> I use this struct to represent a date in the gregorian calendar
>> (fields not offset to start from 0). Let's say that I want to have a
>> function that adds a certain number of days to this ymd struct. There
>> are a couple of options that come to mind.
>>
>> 1. struct greg_ymd add_days( const struct greg_ymd ymd, int days );
>> 2. <return type> add_days( struct greg_ymd* pymd, int days );
>>
>> First of all, what is the performance implications of using struct
>> pass-by-value for smallish structs?
>> Is there a rule of thumb of struct size for an interface API that you
>> convert all struct parameter passing to use pointers?
>> Have you used struct by value parameter passing or return value at
>> all in your API design experience?
>>
>> The struct pass-by-value version of the interface avoids the pesky
>> NULL pointer argument issue, but still I can't get away from it
>> completely since I use 'int (*compare_function)( const void* p, const
>> void* q )' to define the sorting property in my generic containers.

>
> I have done some tests in the past with 32-bit gcc and Visual C. Both
> would move structs to registers for call-by-value params, local
> assignment (as in swapping struct values) and return values if the
> size was 4 bytes or less.


OK, then, here is something I don't understand: if those compilers do
that, how am I supposed to write an assembly language prologue/epilogue
to interface such things if arguments are not passed on the stack?

>
> In practice though the cost difference between allocating a struct in
> the caller and passing a pointer to it vice accepting a return value
> must be vanishingly small even if the struct does fit in a register.
> When the struct doesn't fit in a register, the two return mechanisms
> would be all but identical.


I read something recently (can't remember where) that passing arguments
in registers these days is more than likely "premature optimization", as
passing on the stack would be just as efficient given modern CPU designs.

>
> I've always used caller allocates and passes pointer for APIs.


That doesn't grok? You mean for structs as the OP asked?

> This
> is for at least 5 reasons. (1) It's efficient enough in all cases
> I've ever encountered. (2) A single convention means the caller must
> remember only a single convention. (3) You get "in", "out", and "in
> out" parameters all with the same mechanism. (4) You also get
> "optional" parameters with the same mechanism by allowing NULL to mean
> "not needed." This works for all 3 kinds. (5) It's clean in the
> implementation to uniformly use -> for all (or nearly all) element
> access rather than a mix of . and ->.


Agreed. I'll add that an uncomplicated call standard at the
implementation level is "tits".

>
> A convention I find very useful is this
>
> typedef struct foo_s {
>
> ... big collection of fields ...
>
> } FOO;
>
> // Prepares the raw memory of the foo for initialization.
> // Often just zeros or sets a flag so that later functions can
> // see it hasn't been set up yet. May even do nothing. Declare anyway
> // as a placeholder for the convention. Don't allocate anything here.
> // Can't fail, so a void function.
> void init_foo(FOO *foo);


Like a C++ constructor (with it's steroids taken away).

>
> // Make a foo fully ready for use. Allocate storage and other
> resources.
> // Return 0 on success else an error code.
> int set_up_foo(FOO *foo, ...);


Like a C++ constructor's steroidal effects.

>
> // Release all resources of a foo, returning it to the init state.
> // Return 0 on success else an error code. It's okay to set_up
> immediately
> // after a clear.
> int clear_foo(FOO *foo);


Symmetry lacking: you used 2 construction constituents but only one
destruction constituent. I think your "poor mans' OO" design is too
complex and probably missing something at the same time.

>
> Then in code:
>
> // A way to eliminate the clutter of &. YMMV.
> FOO foo_instance[1];


That is bizarre syntax.

>
> // Always init after declaration even if the init does nothing.
> init_foo(foo_instance);


A testament to C's defficiency over C++.

>
> ... yada yada ..
>
> for (...) {
>
> err = set_up_foo(foo_instance, ...);
>
> ... check for error.
>
> ... use the foo instance then release its resources
>
> err = clear_foo(foo_instance);
>
> ... check for error.
>
> ... more processing that doesn't need the foo instance.
> };


Or pass an "out" error argument to the functions. Or have an optional set
of functions that take an "out" error handler argurment. Or, learn, use,
abuse C++, the latter of which you are doing with the C code you
presented.

>
> I've used this so often that it's a comfortable old friend. Once I
> used setjmp/longjmp as poor-man's exceptions rather than returning
> error codes. It's okay if error handling doesn't need any
> granularity.



 
Reply With Quote
 
Jon
Guest
Posts: n/a
 
      10-14-2010
Eric Sosman wrote:
>> The struct pass-by-value version of the interface avoids the pesky
>> NULL pointer argument issue, but still I can't get away from it
>> completely since I use 'int (*compare_function)( const void* p, const
>> void* q )' to define the sorting property in my generic containers.

>
> Sorry; I can't make sense of this. If "the pesky ... issue" is
> that a struct pointer might be NULL, well, that can often be a help
> rather than a harm: You can provide a NULL for an "optional" struct
> pointer argument, but you cannot do so with a struct value.


Much more often than not, "the pesky issue" is the case at hand.
Asserting for null pointers is a PITA and an unnecessary one. Potential
language-level solutions: references that can't be null, a "not null" or
"can be null" keyword.


 
Reply With Quote
 
Nobody
Guest
Posts: n/a
 
      10-14-2010
On Wed, 13 Oct 2010 10:04:55 -0700, ImpalerCore wrote:

> First of all, what is the performance implications of using struct
> pass-by-value for smallish structs?


It depends upon the platform. On some platforms, a struct return is
implemented by passing in a pointer; the compiler will generate a
temporary in the caller if necessary.


 
Reply With Quote
 
 
 
Reply

Thread Tools

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are On
Pingbacks are On
Refbacks are Off


Similar Threads
Thread Thread Starter Forum Replies Last Post
Can *common* struct-members of 2 different struct-types, that are thesame for the first common members, be accessed via pointer cast to either struct-type? John Reye C Programming 28 05-08-2012 12:24 AM
Typedef A references struct B which references struct A which... DanielEKFA C++ 8 05-16-2005 10:26 AM
struct in struct Gunnar G C++ 14 06-02-2004 06:43 PM
struct my_struct *p = (struct my_struct *)malloc(sizeof(struct my_struct)); Chris Fogelklou C Programming 36 04-20-2004 08:27 AM
implementing a templated struct within a templated struct RA Scheltema C++ 3 01-06-2004 11:25 AM



Advertisments