7

I have read the writing on the wall, Avoid Globals. Which leads to the obvious question, how best to do it?

I obviously want to do it with a current project. The goal is to let a distant PC send "keystrokes" to applications that already have an stdin reader. The idea is to let the existing code detect there is no pending keystroke, then check if there is a "udp keystroke" and, if so, stuff it in so it appears to be keyboard input. Minimally invasive and won't require gobs of retro-fitting in other people's code.

So I cobbled up a small UDP socket reader that uses a setup() function to open and bind the port, then a service() function in a loop that uses a nonblocking select() once, no loop, just check if there is anything to read right now. If so, read the data from the socket and do something with it, else return a 0.

// pseudo c
char c;

setup();
while (1)
{
   c = check_for_keyboard_entry();
   if ( c == 0 )
      c = service();
   handle_keypress( c );
   do_a_bunch_of_other_stuff();
}

The obvious way to do this is with a few globals to transfer the port, timeout, sockaddr's etc. between the two functions. But, thou shalt avid globals, right?

So what is a preferred way to transfer the six or eight vars between the functions?

If I were to use static vars in the setup(), would they be accessible by the service() routine?

I suppose a struct that gets malloc-ed and passed around would work. I'd ought to have a cleanup() to close the socket and free the memory.

REMEMBER, THIS IS A C QUESTION. NO C++!

justin
  • 104,054
  • 14
  • 179
  • 226
Wes Miller
  • 2,191
  • 2
  • 38
  • 64
  • 2
    _``I suppose a struct that gets malloc-ed and passed around would work_'' — You got it! – James M Jul 08 '13 at 21:45
  • +1 for raw speed! Any other approaches? – Wes Miller Jul 08 '13 at 21:46
  • 1
    Please fix \0 to 0 or '\0' – sasha.sochka Jul 08 '13 at 21:49
  • 3
    I don't think there is anything wrong with using global variables (when they're simply needed in many places). The important thing is not to to avoid globals, but to not give variables a larger scope than what they're used for. – Will Jul 08 '13 at 21:50
  • 1
    Note that you can also use function `static` variable. This way you can provide some sort of "getter" interface that may make handling the global variable easier. – moooeeeep Jul 08 '13 at 21:53
  • A struct containing data shared by two or more functions is fine. But be sure the struct is logically coherent. If the clearest description of the structure is "stuff that happens to be needed by both `foo()` and `bar()`", you should probably re-think your design. – Keith Thompson Jul 08 '13 at 21:54
  • 1
    @sasha.sochka +1 for paying attention! – Wes Miller Jul 08 '13 at 21:58
  • @KeithThompson Sadly, other than stuff I need to pass between functions, there isn't much else here. – Wes Miller Jul 08 '13 at 21:59
  • I think you want object oriented C: http://stackoverflow.com/questions/351733/can-you-write-object-oriented-code-in-c – moooeeeep Jul 08 '13 at 22:05

1 Answers1

4

You can just pack all that information into a struct. The struct can be visible to the client or an opaque type. An opaque type may be a better choice so you can hide the data (encapsulation) for complex types.

typedef struct {
 port_t port;
 timeout_t timeout;
 sockaddr_t sockaddr;
 etc_t etc;
} my_stuff;

then pass it around by reference:

void Foo(my_stuff*);

I suppose a struct that gets malloc-ed and passed around would work. I'd ought to have a cleanup() to close the socket and free the memory.

Generally, a struct would not always need to be malloc'ed. But yes, this may be a case where it is required.

justin
  • 104,054
  • 14
  • 179
  • 226
  • But doesn't that make the struct a global? Or did you mean to have `foo()` return a pointer to `my_stuff`? If so, how would `my_stuff` persists when `foo()`'s scope closed? Or would you make `my_stuff` static in `foo()`? – Wes Miller Jul 08 '13 at 22:06
  • 2
    @WesMiller: You can put it on stack in outer scope. So `int main() {my_stuff stuff; stuff.port = 80; ...; Foo(&stuff); ...; return 0; }`. Static variables in functions ARE de-facto global and share similar problems. – Maciej Piechotka Jul 08 '13 at 22:24
  • @MaciejPiechotka +1. A well deserved, self-administered dope slap applied. The answer is so obvious once you see it. – Wes Miller Jul 08 '13 at 22:30
  • 1
    @WesMiller a) no, the struct would not need to be global. b) no, that function prototype just illustrates how you pass it around by reference. the prototype specifies/suggests nothing about the storage of the parameter. c) the instance is owned by the caller (or perhaps the caller of the caller or…). d) nope - function local is still a global mutable storage. a function local static can reduce external access, but it is not so different from a file-local static. -- **but** it looks like Maciej's explanation +1 caused this all to 'click' :) – justin Jul 08 '13 at 22:57
  • 1
    If one uses `typedef struct MY_STUFF_GUTS MY_STUFF;` within the header file, one can define `struct MY_STUFF_GUTS {...};` in the module with its associated code, without making the internals available to outside code. The biggest limitation with doing that is that outside code will be able to declare pointers to the struct, but will not be able to declare individual instances. Instead, it will have to call a function to receive an instance (probably supplied via `calloc` or other such means). – supercat Jul 08 '13 at 23:11
  • 2
    One stylistic note - don't use a `struct` to collect otherwise unrelated attributes just to avoid passing multiple parameters. In this case gathering attributes like port, timeout, sockaddr, and other connection-related parameters into a single `struct` type makes sense because they all relate to the same operation. But if you had a bunch of attributes that weren't somehow related, it would be better to pass them as separate parameters (and maybe take a hard look at what your function is doing; it may make sense to split it up into several smaller functions instead). – John Bode Jul 09 '13 at 05:11