General > General Technical Chat
How many people code in C these days, and if so, why?
engrguy42:
--- Quote from: Siwastaja on May 11, 2020, 10:37:35 am ---engrguy42, you are getting a bit boring. Repeating the joke gets old.
I see you have a mental issue seeing people disagree with each other, but if you have nothing to contribute, you can also stop repeatedly trying to be "funny" which you are failing at. Let the kids adults fight discuss.
If the awkwardness is getting at you, you are free to ignore the thread.
Thank you.
--- End quote ---
I'm sorry you're bored. But, honestly, I couldn't care less.
Siwastaja:
- good-enough-ness despite all the issues
- lack of anything else that demonstrably and actually works better in the real world cases where C is still used. (Wherever C is not the right tool, it has largely been replaced already, so where it remains, there usually are good reasons.)
Nominal Animal:
Considering the various ways success/error results have been passed from the callee to the caller in C, there are some that are "standard" in the sense of so common to not surprise anyone:
* Integer return value: 0 = Success, -1 = Error, with error code in a thread-local global variable
This is the interface used by most standard library functions, using errno for the error code.
* Pointer return value: non-NULL = Success, NULL = Error, with error code in a thread-local global variable
This is also used by standard library functions, with errno for the error code.
* Integer return value: 0 = Success, nonzero = Error code
This is the interface used by most POSIX thread functions (pthreads).
* Pointer return value: Small range of addresses reserved for errors
This is used internally by the Linux kernel, but requires intimate knowledge of the virtual memory available to userspace processes (see ERR_PTR(), PTR_ERR(), IS_ERR(), and IS_ERR_OR_NULL() macros in the linux kernel sources for details). A small range of addresses at the top of the virtual memory is used to make the check a simple comparison (IS_ERR_VALUE() macro).
In POSIX C, the mmap() function returns a pointer to the mapped region, or MAP_FAILED if an error occurs.
* Unrelated return value, often none: Final function parameter points to an integer result status variable
This is common in C bindings to Fortran libraries, like BLAS and LAPACK (very common linear algebra libraries still in use).
Typically, the result status variable is 0 for success, and an error code otherwise.
Each of these have their upsides and their downsides.
Even the standard C library has an exception to this pattern: strtol(), strtoul(), strtoll(), strtoull(), and strtod(). See the code snippet at the end of the man strtol man page to see the correct procedure. I personally wrap these inside a parse_type(src, base, to) function.
The first case has lead to many programmers to use (result < 0) instead of (result == -1) to determine if an error occurs, but that is wrong if the function can return valid negative values (other than -1); or, on LP64 architectures, when the return type is long, but is stored in an int, and a larger than 232 value happens to be truncated to 32-bit negative value. This yields a set of common bugs when porting "bad" C from ILP32 to LP64.
The second case tends to trip programmers whenever encountering functions of the fourth type, including POSIX C mmap().
The fifth case looks "odd" in C, especially if the function does not return any value. Some interfaces allow passing NULL for the pointer to the status variable, some do not, so it tends to be a bit cumbersome.
I personally prefer the third case, because it yields the following code pattern,
--- Code: --- result = some_function_call(parameters);
if (result) {
an_error_occurred();
}
--- End code ---
In cases where the error reason is uninteresting, it shortens to if (some_function_call(parameters)) an_error_occurred(); which does take a bit of getting used to, but unfortunately, it also allows the risky if (result = some_function_call(parameters)) an_error_occurred() pattern, which can easily trip up humans ("shouldn't that be == instead?").
Because of the variance in error/failure condition reporting methods, many C programmers develop an emotional distrust of the return values. (The example of comparing < 0 instead of == -1 is a typical one. Also, I am not excluding myself here; I'm no better than anybody else in this.) As I mentioned above, this has lead to bugs, so it is not a good thing; it is not a defensive strategy, but an emotional cause of further bugs. Paranoia is useful only up to a limit.
In some cases, it is even better to deliberately choose an odd error/failure reporting method (one not listed above, or one differing from the rest of the API), to ensure human programmers notice it and use it correctly; docstring comments just before the function are extremely useful for this.
Technically, it does not matter. It is true that there may be C compilers that generate incorrect code for one of the schemes, but I would throw such a buggy compiler to a bonfire, instead of letting it indoctrinate me into not trusting my tools at all. Compiler bugs are exceedingly rare compared to human bugs; whatever error/failure method you do use really does not (should not!) matter to the compiler. Some of them may map better to the underlying hardware architecture, but since C does not expose the ALU status flags basically all current processors and microcontrollers have, that is already kind of a moot point. (That is, most HW could have an ABI where one of the status flags, say Carry, is used to indicate an error/failure at return time, but current C would need to use some kind of a pseudo-variable or function kludge to expose it. Yet, it would be extremely efficient at the machine code level, if properly exposed.)
Some see this as versatility, some as a failure, of C. For example, the standard method of reporting error/failure in Python is raising an exception; it is simple and straightforward.
Why, then, would C need that versatility, instead of having just one simple way to pass errors like Python does?
That answer, and the answer to the question posed in the initial message in this thread, is that we just have situations where that kind of versatility is useful, and sometimes necessary.
(To repeat: they are necessary when implementing language bindings to other programming languages, and vice versa; consider the Fortran example above. I would not want to have to write those bindings in assembly; I much prefer writing them in C.)
Is this message, and my previous message, just singing praise for C?
Fuck no. I have tons of grievances against it, and tons more against the current direction of the C standards committee, so much so that I actually prefer C99 over C11. I haven't even bothered to read the text of C18 yet, and have not read anything about C2x thus far. Now that Microsoft is becoming saner with respect to FOSS, there could be an improvement coming, but standards folks move slowly, so I'm not holding my breath just yet.
C is just a tool. It has its use cases, like low-level systems programming and interfacing to other programming languages, where it is a better fit than anything else we currently have; one reason being its ubiquitous availability, and the size of the existing codebase. In my previous post in this thread, I listed the reasons why replacing C with C++ can be problematic in this niche.
Does that mean I think C++ has no use cases, then? No, of course not. I personally prefer using Python for user interfaces, because I find the ability of end users to modify the UI without recompiling or setting up any kind of a development environment to be worth it. User interfaces by their very nature are event driven, and object-oriented languages are best suited for those; so, if I was writing a proprietary GUI application where UI in Python would be unacceptable, C++ would be my choice; and then even the low-level stuff would naturally be written in C++, not C, because using C would only increase the complexity and maintenance cost.
(As far as I know, GTK+ is actually the only native C GUI toolkit currently in use. I do like it, but considering an entire software project with paid programmers and such, C++ and Qt or FLTK is just a better, more reasonable choice.)
What about Go, Rust, Julia, et cetera? They have all been touted as being better at what C is used for. Leaving marketing-speak and buzzwords alone, I want to see actual low-level libraries (plural! Not just a special case chosen for its particular quirks!) implemented and performing better, done by people who know these languages better, than to try and switch myself just because of the touted potential. History shows these "new" languages tend to be domain-specific, and quite high-level, and not at all suited for the low-level systems programming tasks I and many others use C for.
I really wonder how many of those who believe C++ does everything C does and better, are actually Windows-only developers? After all, Microsoft no longer has a C compiler at all, only a C++ compiler that supports compiling C too, and unless you have practical experience on other platforms, that sort of thing must affect ones experience and opinions. Nothing wrong in that perse (;)), but it is extrapolating assumptions from a small region to a larger whole, which rarely matches reality.
Karel:
--- Quote from: Nominal Animal on May 11, 2020, 11:21:31 am ---..., so much so that I actually prefer C99 over C11.
--- End quote ---
Can you give some examples of what you don't like about C11?
coppice:
--- Quote from: Nominal Animal on May 11, 2020, 11:21:31 am ---History shows these "new" languages tend to be domain-specific, and quite high-level, and not at all suited for the low-level systems programming tasks I and many others use C for.
--- End quote ---
History shows that all computer languages have been domain specific, and that very often the developers were completely unaware of this. You only have to look at early stuff pushing C++. It touted the generality of its OOP features, yet always showed examples in one of the few areas where is really excels.
Navigation
[0] Message Index
[#] Next page
[*] Previous page
Go to full version