C programming architecture

When creating a command-line app in C, what it's the idiomatic way to structure your source code?

Having played around with Go, I've been doing it like this:

Declaring a struct at the top of a header file, along with several functions like new, set, delete, along with any functions that take this struct as an argument. Also any relevant defines, like Maxes an mins for elements in the struct.

The header and source file are named after the struct, and so are all the functions. (user.h, struct User, new_user(), etc)

Go returns errors along side every function return, so the first struct element is int err, and the functions, which all return a struct, always return an error/status as well.

So after compiling I have a user.o file that contains all the user-related stuff.

A make file prevents unnecessary recompiling, and a test file contains my main, which calls and tests all the functions with assert().

Is this a good way to go about it?

Attached: c-program.jpg (1600x900, 220K)

Other urls found in this thread:

flak.tedunangst.com/post/to-errno-or-to-error
pabloariasal.github.io/2018/02/19/its-time-to-do-cmake-right/
youtube.com/watch?v=bsXLMQ6WgIk
twitter.com/NSFWRedditGif

Wait, Go has header files?

No, sorry, I meant that this is how I've been organizing my c code based on my experience with Go...

Seems okay, no need to exaggerate the number of header files, but no need to cram everything into one. Not sure why you'd return a struct for errors or have err as an member in the struct though.

You want to look at the „fork()“ command and depending on what you want to achieve, „pipe()“, sockets or posix-threads might come in handy..

Cool. Yeah, that's how go does it, so I'm used to having an err value returned along with what I actually want.

The only alternative I see is to pass in an extra variable to be populated with the status, or always have the function return a status and pass in an extra variable for the return value, or maybe use the global error value, but I don't know anything about that..

Just return the status as return value, and pass values to the function as references.

I drank the functional programming Kool-ade, so this gives me side effect anxiety

If you're looking to write code that focuses on correctness, don't write C or Go. If you're looking to write efficient code, then you have to deal with passing memory addresses. Anyway, you literally considered using a global state for error in the post above, so I'm not sure how sincere you are. Passing structs by copy and making new allocations and copies all the time sound extremely inefficient and cumbersome.

I suppose I just want to cut *with* the grain of the C language as much as possible, without making terrible blunders that newer languages prevent.

Passing by reference is not a "terrible blunder". I'm not even sure why you find it so objectionable, you're passing in the state that needs to be modified rather than relying on implicit state anyway.

>modifying state

You're either autistic or trolling at this point, I tried answer seriously but this is just inane shitposts.

If you can, split it into library and slim wrapper that just parses flags, decides on inputs/outputs (e.g. what files), and just runs the library.
>error handling
whatever you do, don't fall into errno pattern. it's a fucking bullshit that should not be replicated.
there are several ways you can do it:
>return integer
this is usable when you would return void otherwise or can return with out pointer
negative means error, zero means ok, positive if ok has some meaning
check for error by comparing the if result < 0 { handle error }
it's a simple way
popular pattern is a context struct. you can put some error member into it. this fits into structs that serve as a state of a process, not as a plain data representation.
bad patterns: reserving range from an actual return (e.g. ssize_t), out pointer for error
you can also just panic
>new_user()
rather user_new(), this closer to namespacing

Shitpost, not op

small inspiration: flak.tedunangst.com/post/to-errno-or-to-error

Lookup persistent data structures. That's a way to make copying not that inefficient.

This is good info, thanks! I'm glad you're encouraging me to avoid errno, I wasn't looking forward to using it.

I get the part about using a struct with an error member for state, not as an object, but the part that follows, the bad pattern, I don't understand. Just don't use a pointer for an error return?

Good quick read, of the choices I think I like this way best:

val = strtonum(buf, min, max, &errstr);
if (errstr) abort();

>aborting because of invalid input
Sure is fizzbuzz-tier in here.

google "cmake hello world"

nah I was pointing on this pattern: but idk, maybe I'm biased. imho error should be the return value and result should be returned through the pointer passed to the function

As for the project layout, you can also consider using CMake.
CMake is one of those very convoluted ways that has million ways to do badly and very few, not well described, to do well. Similar to C++. It also has the worst official tutorial I've ever seen.
Few resources on doing it well:
pabloariasal.github.io/2018/02/19/its-time-to-do-cmake-right/
youtube.com/watch?v=bsXLMQ6WgIk

Do library functions mostly work this way, clobbering the arguments and returning the result status?

It's very common.
But it's C. if there isn't some official way to do things, people will do it in every way possible. Maybe I'm retarded and wrong.

Try to decouple your structs and functions as much as possible.
It's fine to have constructors and destructors like struct_new() and struct_del() but if you end up with a bunch of functions all called struct_dosomething() that all returns void and all take as first and often only argument a struct pointer you're in for trouble.
This is sort of proto-OOP and should be avoided. Look at your functions and see which struct members they use and then pass them directly instead. Use pointers if you want to modify multiple members.
Your functions will become much more general and if they start taking too many arguments you'll know you have to split them. Farther into a project I often found myself wanting to use struct related functions without the struct.
This also avoids hidden states.

Hmm, good point. I've just realized I've essentially copied the same function for use with different structs.

But where do you put these general functions that still are kinda related to the struct/object?

maybe check Plan 9 (alternatively 9front) and OpenBSD codebases. it's a nice C and they use a lot of cmdline tools

You can still put them in a file called structname.c and call them structname_dostuff() but reading them will be much clearer if you can at a glance see what they return and take as input.
Also some other things:
Put includes primarily in the .c file and not in the .h file. This avoids circular includes.
Const everything. This will look strange to begin with but actually makes things more readable if you can tell which values are modified farther down and you will avoid bugs. This is something rust gets right.
If you want to split something into separate functions but can't figure out how you can use unnamed scopes as an option:
function() {
{
first thing this fuctions does
}
{
second thing this function does
}
}
Again this is primarily for readability. Note that variables declared inside unnamed scopes can't be used outside. You'll know variables declared outside are important. Kind of like lambdas.

Looks like today is "Refactor Day" for me. Best get to it. Thanks for all the help!