Live data from Hacker News

Principles for C programming

drewdevault.com

71–80 of 149 posts

Re: Principles for C programming

#71
post #59

Earlier quoted context omitted.

To be honest I would just have your library offer a function that does it your fancy way. It's not possible to have a single function do this. Not possible to have any finite number of functions do this if, like me, you want to support all floating-point and integer types. Self-contained routines that are completely unmaintainable and unintelligible to anyone but you, though. All of my macros are intelligible to anyo…

> More to the point, why do they need to be maintainable? When was the last time you maintained the strtof function in your C library? A few years ago. https://sourceware.org/bugzilla/show_bug.cgi?id=15744 Acting like you can get anything done right in C simply because it's self-contained is proven wrong every day. It's good practice, yes, but doesn't magically (we like this word now) make us immune to error. Everyth…

> we can't just replace broken pieces of code with the same simplicity we can replace a broken fridge.

Sure we can. Recompile glibc, dynamic linking, pow.

I wouldn't say that bug you referenced is a strike against C; it could happen in any language that was locale-aware and parsing floats.

For what it's worth, these macros in libcperciva are perfectly readable and maintainable:

  #define ELASTICARRAY_DECL(type, prefix, rectype)			\
  	static inline struct prefix##_struct *				\
  	prefix##_init(size_t nrec)					\
  	{								\
  		struct elasticarray * EA;				\
  									\
  		EA = elasticarray_init(nrec, sizeof(rectype));		\
  		return ((struct prefix##_struct *)EA);			\
  	}								\
  ...
The advice should really be "be careful, it's really easy to write shitty macros, so don't".

Re: Principles for C programming

#72
post #63

I'll wade in a little. > Don't use macros. > Don't use the inline keyword > Never put code into a header I disagree with these. It's often critical to avoid a function call in hot paths, and if you don't use macros or inline you have to resort to copy/paste, which is error-prone and hampers maintainability. It's also the case that C is a rather inflexible language. Macros can be extraordinarily helpful in reducing bo…

>I disagree with these. It's often critical to avoid a function call in hot paths, and if you don't use macros or inline you have to resort to copy/paste, which is error-prone and hampers maintainability.

This advice is to be taken with a grain of salt, as with all programming advice. If you have a hot path, you should do whatever is necessary to meet the requirements, including macros or inline functions. The caviat, though, is that performance critical code that demands that is rare.

>It's also the case that C is a rather inflexible language. Macros can be extraordinarily helpful in reducing boilerplate: in unit testing, for example. I won't argue that macro definitions are the easiest things to read (usually they're OK but they can get pretty arcane), but I do think something that cuts a file down from 8,000 lines to 1,000 is at least worth considering.

I'm dissatisfied with all unit test frameworks for C that I've encountered. I briefly gave an example of how it might be done better in an unrelated blog post, would like to hear your thoughts: https://drewdevault.com/2016/07/19/Using-Wl-wrap-for-mocking...

>Macros can also help you maintain type safety. Colin Perciva demonstrated this a little with his elasticarray, but khash is another example of using macros to dynamically create type safe data structures. It can also help the compiler optimize your code.

I mentioned in response to cperciva's post that this is a tricky one. I acknowledge both sides of this discussion as valid but fall on the "just use void*" side. Not sure how it helps the optimizer out, though.

>Everything is a fixed-size buffer until you change its size. If you malloc a buffer of 1024 bytes and read 1025 bytes of user input into it, you overflowed anyway. The principle ought to be "check your bounds", which applies whether your buffer is stack/heap.

Well, I didn't say to just use fixed size buffers on the heap. I said to measure what you need and allocate that much. I probably should have phrased this more about just checking bounds in general, though.

>I'm with you on pointer hiding (looking at you FreeType), but "struct" is just far too verbose. You don't accidentally pass things by value because the compiler will tell you you're passing the wrong type. You won't think it's actually a scalar because you have a grand total of 3 scalars in C (bool/int/float), and if you don't know the types you're working with in your functions you should probably look them up.

Addressed in other comments.

>You probably don't think this is a big issue because you don't adhere to 80 columns in your code (I looked @ your GitHub briefly), but let me tell you you run out of space real quick, and "struct" is practically meaningless.

I actualy do, but I use 4 wide tabs, and as in all things I permit the occasional exception to the rule.

    static void set_background(struct wl_client client,
        struct wl_resource resource, struct wl_resource _output,
        struct wl_resource surface) {
I don't mind adding the extra newlines. It's not a big deal. These standards also evolve over time, and I've become more strict (check out chopsui for more a recent C example). I'm also lenient on columns from pull requests. I actually code on a VT220 sometimes, I do value width :)

No comment regarding GNU.

>Unrelated: aerc looks great! I've been thinking about moving off gmail and moving more of my life back into the terminal (I used to be all mutt and IRC and now I'm gmail and hangouts :/ ), and I really like aerc's well-organized code. Nice work.

Glad you like it! It's not ready for prime time, but maybe you'd be interested in contributing? I rely heavily on contributors to get so many projects done.

Re: Principles for C programming

#73
post #61

> GNU is a blight on this Earth, do not let it infect your code. Can someone explain this sentiment to me? I know about licensing and philosophical criticisms, but are there any _technical_ faults?

Lots of GNU code flies in the face of these principles. A lot of GNU software encourages bad behaviors like using non-standard features (which makes for non-portable software). GNU software is also often very bloated and overengineered, and often found in critical places like glibc. Their coding styles are highly questionable and I disagree with a lot of their design decisions.

Thanks for the reply.

> GNU software is also often very bloated and overengineered, and often found in critical places like glibc.

My memory is rusty - aren't those features "protected" against accidental usage, with something like "#define GNU_SOURCE" needed before you include the headers? Or is that protection insufficient?

> Their coding styles are highly questionable and I disagree with a lot of their design decisions.

On the coding styles I agree. On the design decisions... I realize that this is a big and hard question (and that answering it probably amounts to another blog post) but could you please explain that, maybe with an example?

Re: Principles for C programming

#74
post #70
post #50

Earlier quoted context omitted.

The reality is that if you are creating a library you probably should prefix your types and functions anyway. And rely on the prefix to minimize collision probability. So it doesn't really matter if you put _t and the end of your type aliases. You will probably not get the collisions anyway. Unless POSIX is going to suddenly introduce mylib_array_t or something.

No, but your compiler MIGHT decide in a future release that it's a whole lot faster to ignore the header files for standards types and definitions and just copy a pre parsed version of the struct into the symbol table when the header is included. It might look at the _t and decide nope, I don't have a definition for this so it's an error, despite your own definitions. This probably won't happen. But if it does you do…

The compiler to do that would also need to drop C standard compatibility (section 7.1.3 of C99). Which is probably a good reason to complain and to just stop using that version of this purely theoretical compiler.

Re: Principles for C programming

#75
post #73

Earlier quoted context omitted.

Lots of GNU code flies in the face of these principles. A lot of GNU software encourages bad behaviors like using non-standard features (which makes for non-portable software). GNU software is also often very bloated and overengineered, and often found in critical places like glibc. Their coding styles are highly questionable and I disagree with a lot of their design decisions.

Thanks for the reply. > GNU software is also often very bloated and overengineered, and often found in critical places like glibc. My memory is rusty - aren't those features "protected" against accidental usage, with something like "#define GNU_SOURCE" needed before you include the headers? Or is that protection insufficient? > Their coding styles are highly questionable and I disagree with a lot of their design deci…

>My memory is rusty - aren't those features "protected" against accidental usage, with something like "#define GNU_SOURCE" needed before you include the headers? Or is that protection insufficient?

They are "protected", yes, but their mere presence encourages people to use them. There's no reason to use asprintf, but glibc makes it available so some software uses it. That software is now non-portable.

>On the coding styles I agree. On the design decisions... I realize that this is a big and hard question (and that answering it probably amounts to another blog post) but could you please explain that, maybe with an example?

Maybe in a blog post someday.

Re: Principles for C programming

#76
1) Learn Compiler Design

2) Write a Compiler for a better language

3) In new language, write a compiler for your new language

4) Retire from C programming, occasionally come to Hacker News to reminisce about C programming and ways to avoid shooting yourself in the foot

Re: Principles for C programming

#77
post #67
post #65

Earlier quoted context omitted.

It's "All the world's a VAX" in its new form, where you depend on some language/library feature that's actually not in the standard (IIRC alloca and preprocessor extensions are common culprits). And then suddenly you're on a different platform and discover that you can't rely on that -- pretty bad if it's in a central part of your system (like trampolining functions for your toy lisp). On the other hand, you might ru…

So in your opinion the problem is "only" one of portability to non-gnu systems? I thought the author also implied that GNU was technologically inferior and/or problematic. That would interest me...

Probably the ubiquitous "bloat" issue some die-hard C-heads have. But that's his prerogative, I was just pointing out that the specific context points towards portability issues.

Re: Principles for C programming

#79
>>Avoid magic. Do not use macros

What a put off!!!

If you are programming in C in the 21st century then you better know what you are doing. And this whole advice is for dilettantes (no offense).

C is no longer a choice language to demonstrate high level programming principles (not that you can't do it but it's not for the lazy), there's a host of other languages that do that better. But if you are interested to reach close to the machine (eg: you program needs a direct view of memory) then C is 'the' choice even today.

Look at the kernel list.h [0], it's a beautiful piece of code, and how concisely it uses macros. So the real advice to those starting out in C is to be bold and get immersed in all the things that people say you should not do and then let simplicity emerge.

In other words, 'simplicity' of the novice and of the experienced share the same word but are two different concepts from two different points of view.

[0]https://github.com/torvalds/linux/blob/master/include/linux/...

Re: Principles for C programming

#80
post #73

Earlier quoted context omitted.

Thanks for the reply. > GNU software is also often very bloated and overengineered, and often found in critical places like glibc. My memory is rusty - aren't those features "protected" against accidental usage, with something like "#define GNU_SOURCE" needed before you include the headers? Or is that protection insufficient? > Their coding styles are highly questionable and I disagree with a lot of their design deci…

>My memory is rusty - aren't those features "protected" against accidental usage, with something like "#define GNU_SOURCE" needed before you include the headers? Or is that protection insufficient? They are "protected", yes, but their mere presence encourages people to use them. There's no reason to use asprintf, but glibc makes it available so some software uses it. That software is now non-portable. >On the coding…

> They are "protected", yes, but their mere presence encourages people to use them. There's no reason to use asprintf, but glibc makes it available so some software uses it. That software is now non-portable.

I think asprintf is useful - it replaces an ugly "malloc-realloc-snprintf-loop"...

On exposing non-portable functions/features:

  - OpenBSD does it (pledge)

  - Freebsd/NetBSD do it (kqueue)

  - ... I'm certain other systems do too
I think _exposing_ non-portable features is ok, as long as you can't use them _accidentally_. Now, _if_ glibc fulfills that, the blame should fall on the (lazy) developer. If on the other hand glibc makes accidental use possible, then that is... bad for portability.

> Maybe in a blog post someday.

I would like to read that. :-)

[edit: formatting...]

Post reply on HN