handmade.network » Forums » Help with stack, heap and inline functions best practices
nyeecola
Italo
13 posts
#11859 Help with stack, heap and inline functions best practices
6 months, 3 weeks ago Edited by Italo on May 7, 2017, 11:13 a.m.

Suppose I have something like the following:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
my_thing_t thing = {};
thing.a = 23;
thing.b = {5, 0};
thing.c = 25553;
thing.d = 23;
thing.e = 0.23;
thing.f = "abc";
thing.g = 23;
thing.h = 'k';
thing.i = 23;
thing.j = 0;
thing.k = thing.j + 4;
thing.l = {2341};
thing.m = 223;
thing.n = 2343;
do_something_with_thing(program_state, thing);


This functions gets called in about 40 different places ~5 times per second. (with different arguments)

I could pass all those struct members as parameters to the function instead of creating a struct for this, but imagine the struct is much bigger than that. This would make the code mode readable (at least I think so?).
Now, imagine that function is about 50 lines of code. What should I do here?

- Make the function inline and pass `thing` by value
- Make the function inline and pass `thing`'s reference? (probably never good, right?)
- Allocate `thing` on the heap and pass it's reference?
- Pass everything as arguments even though it could be more than 20 arguments?

Is this something I should not do?
Isn't this too much like OOP? (in the sense that I'm basically creating an object and calling its method)

What are better alternatives?
ratchetfreak
317 posts
#11860 Help with stack, heap and inline functions best practices
6 months, 3 weeks ago Edited by on May 7, 2017, 12:31 p.m.

The inline keyword says nothing about whether the function will be inlined or not. All it means is that the One Definition Rule doesn't apply to it (it's still UB to have the same inline function twice with differerent definitions though).

The only way to pick is the fastest is to try it and benchmark it. Also investigate the resulting code.

For readability I would suggest to split the struct up in logical parts and pass those.
mmozeiko
Mārtiņš Možeiko
1506 posts
1 project
#11861 Help with stack, heap and inline functions best practices
6 months, 3 weeks ago Edited by Mārtiņš Možeiko on May 7, 2017, 4:50 p.m.

My feeling is that you want to pass this large structure by address (or reference). Although it will not make difference if you are calling only 5 times per second. Even if you pass by value and compiler will need to make a copy every time, it will make no measurable impact on your performance.

Making function inline or not should not be decision you make based on how and what you pass in arguments. As rachetfreak already explained, it should be decision about what and where you declare and define.

And there is absolutely nothing OOP in passing large or small structures to function by value, address or reference.

CaptainKraft
Jeremiah
157 posts
2 projects

Father, husband, C programmer, and Linux apologist. Think before you code.

#11863 Help with stack, heap and inline functions best practices
6 months, 3 weeks ago

If you pass a reference, does it need to be on the heap?

Is there a reason *not* to keep it on the stack and pass a reference?

Build a man a fire, he'll be warm for a day.
Set a man on fire, he'll be warm for the rest of his life.
mmozeiko
Mārtiņš Možeiko
1506 posts
1 project
#11864 Help with stack, heap and inline functions best practices
6 months, 3 weeks ago Edited by Mārtiņš Možeiko on May 7, 2017, 10:14 p.m.

No, it doesn't be on the heap.
From compiler point of view it doesn't matter where is variable - on stack, heap or global. All it does it just passes address to argument and address can be anything. Reference is nothing else than fancy C++ a pointer which cannot be NULL, and you cannot change where it points to (not the value of element it points to, but actual address it contains). And because obviously you can have pointers that point to local variables, that means you can also have references that points to local variables;

Here's an example:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
// function that prints out integer from pointer, and changes it to 42
void ptr(int* a)
{
  printf("%d\n", *a);
  a = 42;
}

// function that prints out integer from reference, and changes it to 24
void ref(int& a)
{
  printf("%d\n", a);
  a = 24;
}

int global = 111;

int main()
{
  ptr(&global);           // will print 111, and change it to 42
  ref(global);            // will print out 42, and change it to 24
  printf("%d\n", global); // will print out 24

  int local = 999;
  ptr(&local);           // will print 999, and change it to 42
  ref(local);            // will print out 42, and change it to 24
  printf("%d\n", local); // will print out 24

  int& temp_ref = local; // reference that points to local variable
  temp_ref = 1212;       // change "local" variable to 1212
  printf("%d\n", local); // will print out 1212

  int* heap = malloc(sizeof(*heap));
  *heap = 444;
  ptr(heap);             // will print 444, and change it to 42
  ref(*heap);            // will print out 42, and change it to 24
  printf("%d\n", *heap); // will print out 24
}


CaptainKraft
Jeremiah
157 posts
2 projects

Father, husband, C programmer, and Linux apologist. Think before you code.

#11868 Help with stack, heap and inline functions best practices
6 months, 2 weeks ago

Sorry, I wasn't very clear. I meant for his use case, does he need to heap allocate instead of just leave his stuff on the stack.

Build a man a fire, he'll be warm for a day.
Set a man on fire, he'll be warm for the rest of his life.
mmozeiko
Mārtiņš Možeiko
1506 posts
1 project
#11871 Help with stack, heap and inline functions best practices
6 months, 2 weeks ago

No, he doesn't. That's exactly what my example shows. Imagine "my_thing_t thing" instead of "int local" in my example. Function ref will happily accept it.