[HN Gopher] The C23 edition of Modern C
___________________________________________________________________
The C23 edition of Modern C
Author : bwidlar
Score : 492 points
Date : 2024-10-15 16:06 UTC (1 days ago)
(HTM) web link (gustedt.wordpress.com)
(TXT) w3m dump (gustedt.wordpress.com)
| belter wrote:
| Important reminder just in the Preface :-)
|
| Takeaway #1: "C and C++ are different: don't mix them, and don't
| mix them up"
| pjmlp wrote:
| Specially relevant to all those folks that insist on "Coding C
| with a C++ compiler", instead of safer language constructs, and
| standard library alternatives provided by C++ during the last
| decades.
| Spivak wrote:
| I mean as long as your goal is specifically to do that I
| think it's fine. Using a C++ compiler to compile a C program
| isn't that rare.
| com2kid wrote:
| Perfectly valid to do if you need to interface with a large C
| code base and you just want to do some simple OO here and
| there. Especially if you cannot have runtime exceptions and
| the like.
|
| This is how I managed to sneak C++ into an embedded C
| codebase. We even created some templates for data structures
| that supported static allocation at compile time.
| f1shy wrote:
| What would be an example of "simple OO here and there" that
| cannot be done cleanly in plain C?
| raluk wrote:
| RAII
| f1shy wrote:
| Is RAII Object orientation? I thought it was an idiom of
| C++ by Stroustrup.
| runevault wrote:
| It doesn't necessarily have to be OO no. Rust uses RAII
| and it uses traits instead of traditional OO style
| inheritance etc. You do need something like
| destructors/drop trait for it to work as far as I know
| though.
| tjoff wrote:
| The killer feature of RAII is when combined with
| exceptions. But sneaking in exceptions in an embedded C
| project isn't something I'd encourage or recommend.
|
| C++ imo doesn't offer anything compelling for the
| embedded usecase. Especially not considering all the
| footguns and politics it brings.
|
| You can of course be strict and diligent about it but if
| you are you are pretty much just writing C anyway. Better
| to do it explicitly.
|
| Allowing the use of the C++ standard library has been one
| of my biggest regrets (not that it was my decision to
| make, I fought it).
| AlotOfReading wrote:
| C++ offers lots of compelling things for embedded use
| cases, like enum classes (finally fixed in C23),
| constexpr, std::optional, namespaces, and
| atomics/generics that are much smaller dumpster fires.
|
| There's an effort to extract the good parts and make it
| work for embedded use cases or even bring them into C.
| Khalil Estelle on WG21 has been working on an
| experimental, deterministic runtime for exception
| handling, to give one example. Constexpr is an example of
| the latter that's now showing up in C23.
| tjoff wrote:
| I don't disagree, but these are in the ~2% convenience at
| most. With the huge baggage of including C++ in a
| project. The cost of learning C++ easily outweighs all
| those benefits. If you happen to have a proficient C++
| team (that actually know embedded), go for it!
| AlotOfReading wrote:
| Speaking more broadly than just the std implementation,
| but result types like optional shouldn't be a 2%
| convenience, they should be used in most function calls
| that return errors. Rust is the obvious example here.
| tjoff wrote:
| If you argue for Rust I'm all for it, arguably much less
| of a learning curve than C++ too.
| Ragnarork wrote:
| Strong disagree on that one. Even though C++ has a lot of
| features that take a while to learn, getting started with
| C++ is simpler by miles than getting started with Rust.
|
| One exception to that in my experience: dependencies,
| although I think that's a bit deceiving as yes, it's
| easier to get dependencies in Rust but in some areas
| they're way less mature and can sometimes be a pain to
| work with (usually when dealing with big C or C++
| libraries that have been quickly glued to a Rust
| interface to be available in Rust).
| tjoff wrote:
| Agree, writing bad C++ is easy. But being competent in
| C++ requires much more than being competent in rust.
|
| And not being competent in C++ is not great. You are
| going to be much more productive in C. The feedback loop
| is much faster, that is, feedback from your designs.
|
| Contrast with Rust which is harder to get going but
| doesn't require nearly as much to be decent.
| kccqzy wrote:
| There are a lot of large C++ shops that purposefully
| disable exceptions and yet still use RAII usefully. It's
| so useful that in many C codebases you see people using
| RAII. For example Gtk has g_autoptr and g_autofree.
|
| One of the most compelling things C++ offers to embedded
| use case is moving runtime initialization to compile-time
| initialization by liberally using constexpr functions.
| You literally ask the compiler to do work that would
| otherwise be done at runtime.
| tjoff wrote:
| RAII is useful without exceptions yes. I guess it is the
| other way around. Exceptions are not useful without RAII
| (sorry not sorry most garbage collected languages ;)).
|
| But without exceptions it is mostly syntactic sugar
| anyway.
|
| If compile time initialization is the most compelling
| usecase I'll rest my case. Good feature, yes! Hardly
| worth switching language for.
| adamrezich wrote:
| Namespaces, methods.
| f1shy wrote:
| Namespaces is not object orientation, is it? Am I missing
| something? You can place functions (methods) inside of
| structs in C23, can't you?
| staunton wrote:
| On a high level, "object orientation" means you think of
| your code as representing the state and interactions of
| objects. You can equally well do this in assembly. If you
| think of some namespace as a "singleton object" then
| that's what it is.
|
| I guess what you're really asking is what are the best or
| most common ways to do OO in C?
| f1shy wrote:
| Oh. I learned that object orientation is primarily a way
| to structure data and code, such that the data is
| encapsulated with the code that works on it, in so called
| objects. So an Object is the Data, plus the functions
| that work on the data, an ensure that some invariants are
| kept. In OO parlance, that code gets executed by sending
| messages (calling methods).
|
| Where can I find something about objects being "think of
| your code as representing the state and interactions of
| objects" honesty totally new to me.
|
| So no, certainly I'm not asking ways to do OO in C. But
| it seems to be more definitions of object orientation as
| I thought...
| int_19h wrote:
| There's no clear definition of what OO is, so the best
| you can do pragmatically is look at mainstream languages
| that are broadly recognized as OO and try to deduce the
| commonalities.
|
| If you do that, you'll notice that, for example,
| encapsulation is _not_ a part of that de facto
| definition, because languages like Python and (until
| recently) JavaScript lack it, despite being considered
| OO.
|
| Indeed, the only two things that appear to be
| consistently present in all OO languages are: 1) some
| notion of object identity as distinct from object state,
| and 2) runtime polymorphic dispatch.
| epcoa wrote:
| > Where can I find something about objects being "think
| of your code as representing the state and interactions
| of objects" honesty totally new to me.
|
| I'm scratching my head how you think this is materially
| different than what you described in your first para.
| s/state/data and s/interactions/methods.
|
| If anything though I would say the GP is more aligned
| with the classic definition as it highlights the focus is
| more on the messages (interactions) themselves rather
| than the implementation.
| adamrezich wrote:
| Correct, and you did ask specifically for OO things, but
| I thought I'd list namespaces too as far as "C++ things
| you might use when writing C-like C++ code".
|
| Another big one that I always forget C still doesn't
| support is function overloading.
| uecker wrote:
| Function overloading is a feature that makes code less
| self-documenting without providing any meaningful value.
| Operator overloading is more interesting, because you can
| build you domain language with nice syntax. But I also
| tend to think that this is not really worth it.
| adamrezich wrote:
| In C++ where you have methods? Sure. It would be nice to
| have in C, though. But, alas, ABI compatibility.
| listeria wrote:
| Function overloading enables the building of powerful
| idioms like swap, operator== and this [1] proposal for
| composable hashing to name a few. And when combined with
| templates (and ADL to provide some encapsulation with
| namespaces) you can build some interesting abstractions
| akin to the io module in go, like this implementation of
| io.ReadFull(): template<typename R>
| constexpr ssize_t io::ReadFull(R reader, char
| *buf, size_t len) { ssize_t recvd = 0,
| ret; do { ret = read(reader,
| &buf[recvd], len - recvd); if (ret > 0)
| recvd += ret; else break;
| } while (recvd < len); return recvd;
| }
|
| ---
|
| 1: https://open-
| std.org/jtc1/sc22/wg21/docs/papers/2014/n3980.h...
| int_19h wrote:
| You can handcode vtables in C, just as you can handcode
| loops in assembly (i.e. it works but it's verbose, not
| particularly readable, and brings more footguns).
|
| But why would you do that if you have an instrument that
| lets you work at the same level as C, but with methods
| provided as a proper abstraction that maps exactly to
| what you'd have written yourself anyway?
| uecker wrote:
| I don't know, I never found the "proper abstraction" be
| more than irrelevant syntactic sugar. And the cost of C++
| is that you end up putting everything in the header (IMHO
| the biggest design flaw of the language) and then compile
| time start to get long....
| bobmcnamara wrote:
| Templating on pixel classes so that a blitter builds all
| supported pixel paths separately and inlines them.
|
| Yes you can do it less cleanly with macros or inline
| functions. But you can't do it performantly with struct
| and function pointers.
| cozzyd wrote:
| CRTP?
| com2kid wrote:
| You can do anything in C that you want to. Of course one
| can make v-tables and all of that, and even do
| inheritance.
|
| But having the "class" keyword is nice. Having built in
| support for member functions is nice.
|
| Sometimes a person just wants the simplicity of C++ 2003.
|
| (In reality I was working on a project where our compiler
| only supported C++ 2003 and we had a UI library written
| in C++ 2003 and honestly pure C UI libraries kind of suck
| compared to just sprinkling in a bit of C++ sugar.)
| throwaway2037 wrote:
| > You can do anything in C that you want to.
|
| How about destructors?
| com2kid wrote:
| You can obviously build any runtime system you desire in
| C, including one that parses and executes C code with
| additional features added in. The wisdom of doing this is
| questionable.
|
| Though I've actually seen macro systems that do things
| akin to destructors, although less automatically.
| pjmlp wrote:
| Yeah, but one should provide C++ type safe abstractions on
| top.
|
| Just like one doesn't use Typescript to keep writing plain
| old JavaScript, then why bother.
| flohofwoe wrote:
| Funny because for a long time the Microsoft MSVC team
| explicitly recommended compiling C code with a C++ compiler
| because they couldn't be arsed to update their C frontend for
| over two decades (which thankfully has changed now) ;)
|
| https://herbsutter.com/2012/05/03/reader-qa-what-about-vc-
| an...
| rdtsc wrote:
| That thing always baffled me, this huge company building a
| professional IDE couldn't figure out how to ship updates to
| the C compiler.
|
| > it is hard to say no to you, and I'm sorry to say it. But
| we have to choose a focus, and our focus is to implement
| (the standard) and innovate (with extensions like everyone
| but which we also contribute for potential standardization)
| in C++.
|
| I mean, yeah if it came from a two member team at a
| startup, sure focus on C++, understandably. But Microsoft,
| what happened to "Developers! Developers! Developers!"?
| Jtsummers wrote:
| It's not baffling, it's remarkably consistent. They
| implemented Java as J++ and made their version
| incompatible in various ways with the standard so it was
| harder to port your code away from J++ (and later J#).
| They implemented things in the CSS spec almost exactly
| opposite the specification to lock people into IE (the
| dominant browser, if you have to make your site work with
| 2+ incompatible systems which will you focus on?). Not
| supporting C effectively with their tools pushed
| developers towards their C++ implementation, creating
| more lock-in opportunities.
| AlotOfReading wrote:
| Funnily enough, the intellisense parser _does_ support C
| syntax because it 's using a commercial frontend by
| edison under the hood. MSVC's frontend doesn't.
| pjmlp wrote:
| It was on purpose, Microsoft was done with C, the
| official message was to move on to C++.
|
| The change of heart was the new management, and the whole
| Microsoft <3 FOSS.
| rdtsc wrote:
| > It was on purpose, Microsoft was done with C
|
| Indeed, and yet here we are with C23
|
| > The change of heart was the new management, and the
| whole Microsoft <3 FOSS.
|
| Yeah, agree. To me the turning point was when they
| created WSL.
| kragen wrote:
| Microsoft didn't create C23 and they don't <3 FOSS.
| They're accepting that they have to deal with FOSS, but
| installing Windows will still make your Linux system
| unbootable until you fix it with a rescue disk, among
| numerous other unfriendly things they do.
| alexvitkov wrote:
| I haven't seen Windows fuck up the EFI partition or
| delete the other entries in a while now. After installing
| it the machine will usually boot directly into it, but it
| should be just a toggle in the firmware to switch back to
| GRUB.
| kragen wrote:
| That's an improvement! When did they fix that?
| chithanh wrote:
| > installing Windows will still make your Linux system
| unbootable until you fix it with a rescue disk
|
| This is no longer true. On UEFI systems the only thing
| you have to do normally is fix the boot order. In fact
| installing Linux first and Windows second tends to be the
| better dual-boot strategy nowadays.
|
| Fixing the boot order can be done from UEFI setup, and
| even from Windows command line bcdedit
| /enum firmware bcdedit /set {fwbootmgr}
| displayorder {yourlinuxuuid} /addfirst
|
| (Put single quotes around {} if you use PowerShell
| instead of CMD.exe)
| kragen wrote:
| Thank you for the correction and the howto! How long have
| I been wrong about this?
| pjmlp wrote:
| Microsoft doesn't take part on WG14, and MSVC only does
| up to C17 nowadays.
| pjmlp wrote:
| Yeah, 12 years ago, when governments couldn't care less
| about nation state cyberattacks, and Microsoft was yet to
| be called by the Congress to testify on their failures.
| jpcfl wrote:
| Bjarne should have called it ++C.
| card_zero wrote:
| Because people choose to use pre-increment by default instead
| of post-increment?
|
| Why is that?
| jejdjdbd wrote:
| Why would you use post increment by default? The semantics
| are very particular.
|
| Only on very rare occasions I need post increment
| semantics.
|
| And in those cases I prefer to use a temporary to make the
| intent more clear
| card_zero wrote:
| People seem to mostly write a typical for loop ending
| with ; ++i){
|
| But I write ; i++){ and seeing it the other way round
| throws me off for a minute, because I think, as you put
| it, why would you use those very particular semantics?
|
| But I guess this is only a semantic argument.
| johannes1234321 wrote:
| > why would you use those very particular semantics?
|
| The difference is that i++ has to keep a copy to the
| original around as the return value is the pre-increment
| value, while with ++i that isn't needed as the resulting
| value is being returned.
|
| In the for loop that shouldn't matter as a) for an
| integer it is essentially for free (it is just reordering
| when the relevant register is set) and b) that value is
| hopefully optimized out anyways by the compiler, however
| as there are cases where it matters some people prefer
| the ++i style, some just think it looks better.
| layer8 wrote:
| In C++ the semantics can differ, in that copying an
| object for post-increment might require a memory
| allocation internally (for example in the case of a
| BigInt class), which may fail and throw an exception. For
| consistency, using pre-increment by default and unless
| you _really_ need post-increment, is a good habit.
| Quekid5 wrote:
| I've seen either style, but it the argument about which
| is proper is pointless. Any modern compiler will optimize
| either equally well, unless you're doing something that
| actually depends on the order of the increment.
| kanbankaren wrote:
| No, for fundamental datatypes pre/post-increment doesn't
| matter, but for classes that overload those operators,
| the postfix form creates a temporary object hence people
| write
|
| for(auto it = begin(v); it != end(v); ++it)
| secondcoming wrote:
| It makes no difference if the increment is done on an
| int, but it can make a different if your `i` is some
| object with its own ++ operator.
| codr7 wrote:
| If you're used to the idiom, the intent couldn't be
| clearer.
|
| I miss it when switching between C/++ and other
| languages.
| flohofwoe wrote:
| I rarely use pre-increment tbh, but post-increment all
| the time for array indices (since typically the array
| should be indexed with the value before the increment
| happens).
|
| If the pre- or post-increment behaviour isn't actually
| needed, I prefer `x += 1` though.
| tialaramex wrote:
| Why use this operator? Like most C and C++ features the
| main reason tends to be showing off, you learned a thing
| (in this case that there are four extra operators here) and
| so you show off by using it even if it doesn't make the
| software easier to understand.
|
| This is not one of those beginner -> journeyman -> expert
| cycles where coincidentally the way you wrote it as a
| beginner is identical to how an expert writes it but for a
| very different reason. I'd expect experts are very
| comfortable writing either { x = k; k += 1; } or { k += 1;
| x = k; } depending on which they meant and don't feel an
| itch to re-write these as { x = k++; } and { x = ++k; }
| respectively.
|
| I'm slightly surprised none of the joke languages add
| equally frivolous operators. a%% to set a to the remainder
| after dividing a by 10, or b** to set b as two to the power
| b or some other silliness.
| cozzyd wrote:
| It's more useful for pointers than for values, IMO
| layer8 wrote:
| The idiomatic void strcpy(char *s, char
| *t) { while (*s++ = *t++)
| ; }
|
| (straight from K&R) wouldn't work without it.
| n_plus_1_acc wrote:
| Which many people find unreadable compared to other
| versions.
| beeforpork wrote:
| And for several reasons. * is it (*s)++
| or *(s++)? * it is not *++s nor ++*s
|
| And I have seen *(*s)++
|
| in some places!
|
| It is concise syntax but very confusing.
| chongli wrote:
| K&R actually teaches this as a desirable idiom? People
| should not be recommending K&R to beginners today!
| trealira wrote:
| Kernighan spends a page whittling strcpy down to just
| that, with various intermediate versions. After showing
| you that version, he describes it like this:
|
| _Although this may seem cryptic at first sight, the
| notational convenience is considerable, and the idiom
| should be mastered, because you will see it frequently in
| C programs._
| trealira wrote:
| They can be useful when adding things to an array in a
| loop. A trivial example which removes a character from a
| null terminated string: void
| remove_char(char *s, char c) { size_t i, j;
| for (i = j = 0; s[i] != '\0'; i++) if (s[i] !=
| c) s[j++] = c; s[j] = '\0'; }
|
| This might be better expressed with a higher order filter
| function, but C is too low level for things like that.
|
| There are also idioms for stack manipulation using them:
| "stack[sp++] = pushed" and "popped = stack[--sp]".
|
| C code does a lot of incrementing and decrementing by
| one, and so having dedicated syntax for it is convenient.
| jancsika wrote:
| Note that in your example there appear to be three
| distinct meanings:
|
| 1. prefix incr/decr precedence: "stack[--sp]"
|
| 2. postfix incr/decr precedence: "s[j++]"
|
| 3. i have no particular preference for the precedence and
| am just using a shorthand I inherited from my ancestors
| whose use cases are no longer relevant to me: "i++" in
| your for loop
|
| My rank speculation is that C programmers get in a habit
| of #3 and then forget to consider precedence in an
| expression where it matters.
|
| In any case, it would be interesting to do a scan of
| github to see how often prefix and suffix incr/decr had
| to get switched up in a bugfix patch.
| int_19h wrote:
| It should be ++C because with C++ the value you get from
| the expression is the old one.
|
| If you're asking why people use pre-increment by default
| instead of post-increment, it's mostly historical. The
| early C compilers on resource-constrained platforms such as
| early DOS were not good at optimization; on those, pre-
| increment would be reliably translated to a simple ADD or
| INC, whereas code for post-increment might generate an
| extra copy even if it wasn't actually used.
|
| For C++ this was even worse with iterators, because now it
| depended on the compiler's ability to inline its
| implementation of postfix ++, and then prove that all the
| copies produced by that implementation have no side effects
| to optimize it to the same degree as prefix ++ could.
| Depending on the type of the underlying value, this may not
| even be possible in general.
|
| The other reason is that all other unary operators in C are
| prefix rather than postfix, and mixing unary prefix with
| unary postfix in a single expression produces code that is
| easy to misunderstand. E.g. *p++ is *(p++), not (*p)++,
| even though the latter feels more natural, reading it left-
| to-right as usual. OTOH *++p vs ++*p is unambiguous.
| card_zero wrote:
| K&R seems to use pre-increment early on, then post-
| increment consistently (or a lot, anyway, I haven't done
| a thorough check) after chapter 3, in situations where
| either would do. In fact, after introducing post-
| increment at 2.8.
| jpcfl wrote:
| > It should be ++C because with C++ the value you get
| from the expression is the old one.
|
| You get it!
| wpollock wrote:
| The PDP-11 that C originally targeted had address modes to
| support the stack. Pre-increment and post-decrement
| therefore did not require a separate instruction; they were
| free. After the PDP-11 went the way of the dodo, both forms
| took a machine cycle so it (mostly) became a stylistic
| issue. (The two operators have different semantics, but the
| trend to avoid side-effects in expressions means that both
| are most often used in a single expression statement like
| "++x;" or "x++;", so it comes down to your preferred
| style.)
| zabzonk wrote:
| Please explain what you mean by "a separate instruction".
| spc476 wrote:
| Some idiomatic C code to copy a string (I'm not saying
| this is good C code, but it's just an example):
| while(*d++ = *s++) ;
|
| On the Motorola 68000 (based somewhat on the PDP-11) the
| code would look like: loop:
| move.b (a0)+,d0 move.b d0,(a1)+
| bne loop
|
| while on the x86 line, it would be:
| loop: mov al,[rsi] mov
| [rdi],al inc rsi ; extra
| instruction! inc rdi ; extra
| instruction! cmp al,0
| jne loop
|
| Yes, there are better ways to write that code for both
| the 68K and x86, but I hope this gets the point across.
| wang_li wrote:
| > loop: move.b (a0)+,d0 move.b d0,(a1)+
|
| ...
|
| > loop: mov al,[rsi] mov [rdi],al
|
| This hurts my brain. When we invent time machines I'm
| going to use it to go back and slap whoever at intel came
| up with that operand order.
| wpollock wrote:
| A MOV and an INC, as opposed to just the MOV.
| wnoise wrote:
| Nah. It's just the natural semantics -- he added stuff to C,
| but returned something that wasn't actually more advanced...
| f1shy wrote:
| A couple of months ago, in the company I work, there was a talk
| from HR, where they explained how to make a good CV (the
| company is firing lots of people). She say: "if you have
| experience in programming C, you can writing just that, or, if
| you have lots of experience in C, is customary to write ``C++
| Experience'' "
|
| Sooo... yeah... I should definitely change company!
| kstrauser wrote:
| That literally made me do a spit take, and it was fizzy water
| and it burned.
|
| My god. That's amazing.
| thenipper wrote:
| How many pluses until you should just say you have D
| experience?
| varjag wrote:
| Possibly three. Four pluses is naturally C#.
| jasode wrote:
| _> Takeaway #1: "C and C++ are different: don't mix them, and
| don't mix them up"_
|
| Where "mixing C/C++" is helpful:
|
| - I "mix C in with my C++" projects because "sqlite3.c" and
| ffmpeg source code is written C. _C++ was designed to
| interoperate with C code._ C++ code can seamlessly add
| _#include "sqlite3.h"_ unchanged.
|
| - For my own code, I take advantage of "C++ being _mostly_ a
| superset of C" such as using old-style C printf in C++ instead
| of newer C++ cout.
|
| Where the _" C is a totally different language from C++"_
| perspective is helpful:
|
| - knowing that compilers can compile code in "C" or "C++" mode
| which has ramifications for name mangling which leads to "LINK
| unresolved symbol" errors.
|
| - knowing that C99 C23 has many exceptions to "C++ is a
| superset of C" :
| https://en.wikipedia.org/wiki/Compatibility_of_C_and_C%2B%2B...
| accelbred wrote:
| C++ can seamlessly include C89 headers.
|
| The C library headers for libraries I write often include
| C11/C99 stuff that is invalid in C++.
|
| Even when they are in C89, they are often incorrect to
| include without the include being in an `extern "C"`.
| nuancebydefault wrote:
| Extern "C" around the prototypes is mandatory, otherwise
| your linker will search for C++ symbols, which cannot be
| found in the C libraries you pass it.
| Conscat wrote:
| Clang supports C11 - 23 in C++, as well as some future C
| features like fixed-point integers. The main pain points
| with Clang are just the fundamental differences like void*
| and char, which don't typically matter much at an
| interoperability layer.
| flohofwoe wrote:
| There's a lot of subtle differences between 'proper' C
| and the C subset of C++, since C++ uses C++ semantics
| everywhere, even for its C subset.
|
| Many C++ coders are oblivious to those differences
| (myself included before I switched from 'mainly C++' to
| 'mainly C') because they think that the C subset of C++
| is compatible with 'proper' C, but any C code that
| compiles both in a C++ and C compiler is actually also a
| (heavily outdated) subset of the C language (so for a C
| coder it takes extra effort to write C++ compatible C
| code, and it's not great because it's a throwback to the
| mid-90s, C++ compatible C is potentially less safe and
| harder to maintain).
|
| For instance in C++ it's illegal to take the address of
| an 'adhoc-constructed' function argument, like:
| sum(&(bla_t){ .a = 1, .b = 2, .c = 3, .d = 4 });
|
| (godbolt: https://www.godbolt.org/z/r7r5rPc6K)
|
| Interestingly, Objective-C leaves its C subset alone, so
| it is always automatically compatible with the latest C
| features without requiring a new 'ObjC standard'.
| pjmlp wrote:
| Because Objective-C initial proposal is that everything
| that isn't touched by Smalltalk like code, clearly in
| brackets or @annotarions, is plain C.
|
| The pre-processor original compiler, before the GCC fork,
| would leave everything else alone, blindly copying into
| the generated C file.
| kccqzy wrote:
| Yeah plenty of headers first have `#ifdef __cplusplus` and
| then they add `extern "C"`. And of course even then they
| have to avoid doing things unacceptable in C++ such as
| using "new" as the name of a variable.
|
| It takes a little bit of an effort to make a header work on
| C and C++. A lot less effort than making a single Python
| file work with Python 2 and 3.
| flohofwoe wrote:
| The '#ifdef __cplusplus extern "C" { }' thing only
| removes C++ name mangling from exported symbols, it
| doesn't switch the C++ language into "C mode"
| (unfortunately).
| tialaramex wrote:
| The entire I/O streams (where std::cout comes from) feature
| is garbage, if this was an independent development there is
| no way that WG21 would have taken it, the reason it's in C++
| 98 and thus still here today is that it's Bjarne's baby. The
| reason not to take it is that it's contradictory to the
| "Don't use operator overloading for unrelated operations"
| core idea. Bjarne will insist that "actually" these operators
| somehow always meant streaming I/O but his evidence is
| basically the same library feature he's trying to justify. No
| other language does this, and it's not because they can't
| it's because it was a bad idea when it was created, it was
| still a bad idea in 1998, the only difference today is that
| C++ has a replacement.
|
| The modern fmt-inspired std::print and std::println etc. are
| much nicer, preserving all the type checking but losing
| terrible ideas like stored format state, and localisation by
| default. The biggest problem is that today C++ doesn't have a
| way to implement this for your own types easily, Barry
| illustrates a comfortable way this could work in C++ 26 via
| reflection which on that issue closes the gap with Rust's
| #[derive(Debug)].
| pjmlp wrote:
| Perfectly iostreams happy user since 1993.
| codr7 wrote:
| Same, as long as I stay the hell away from
| locales/facets.
|
| Type safe input/output stream types and memory backed
| streams served on a silver plate is a pretty decent
| improvement over C.
| Dwedit wrote:
| int a;
|
| cin >> a;
|
| Then the program goes berserk as soon as the first non-
| number is read out of standard input. All the other "cin
| >> integer" lines are immediately skipped.
|
| Yes, I know about error checking, clearing error
| condition, discarding characters. But it's a whole lot of
| stuff you need to do after every single "cin>>" line. It
| makes the simplicity of cin not worth it.
| tightbookkeeper wrote:
| You're holding it wrong. Like nan, the point is you don't
| have to error check every operation.
|
| You check error for the whole batch.
| eMSF wrote:
| How could you ever continue after the second statement
| without checking if you actually read an integer or not?
| How would you know what you can do with a?
| jvanderbot wrote:
| You couldn't or wouldn't. but why have a read statement
| like cin>> which looks so nice and clean when you then
| have to go and check everything with flags and boolean
| casts on stateful objects.
|
| I agree. It's lunacy. just be explicit and use functions
| or equivalent like literally every other language.
| chongli wrote:
| Well in a language like Haskell you could solve this with
| monads and do-notation. The general idiom in Haskell is
| to use a Maybe or Either monad to capture success/failure
| and you assume you're on the happy path. Then you put the
| error handling at the consumer end of the pipeline when
| you unwrap the Maybe or Either.
|
| I believe Rust has adopted similar idioms. I've heard the
| overall idea referred to as Railway-oriented programming.
|
| In C++ you could implement it with exceptions, though
| they bring in a bunch of their own baggage that you don't
| have to deal with when using monads.
| PaulDavisThe1st wrote:
| fscanf (STDIN, "%d", &a);
|
| the program goes beserk as soon as the first non-number
| is read out of standard input.
|
| in both cases, you need error checking (which you "know
| about").
| unwind wrote:
| No actual C programmer who has been around the block more
| than halfway should do that. The mantra is: "read into a
| character buffer, then parse that".
|
| It's more code, sure, but it buys you a lot of good
| things. I/O is hard.
| PaulDavisThe1st wrote:
| No C++ programmer who has been around the block more than
| halfway should do cin >> a;
|
| and assume that it works.
|
| But also ... sscanf (the_buffer, "%d",
| &a);
|
| doesn't help the problem in any substantive way.
| tightbookkeeper wrote:
| Yep, it's very clean once you get the hang of it.
| einpoklum wrote:
| Then I suppose you don't care about:
|
| * Performance
|
| * Support for localization (as the format string and
| positions of values to format differ between languages).
|
| * Code reuse & dogfooding - the data structures used in
| iostreams are not used elsewhere, and vice-versa
|
| * C and OS interoperability - as you can't wrap a stream
| around a FILE* / file descritor
|
| * bunch of other stuff...
|
| iostreams work, but are rather crappy.
| pjmlp wrote:
| I care about performance, when it actually matters to
| acceptance testing.
|
| The less C the merrier.
|
| If you care about correct use of localisation, standard C
| and C++ libraries aren't really what you're looking for,
| or even C and C++ to start with.
| wakawaka28 wrote:
| C and C++ are the bedrock of operating systems with the
| best performance and extensive support for all languages.
|
| The only reason why iostreams are slow is because of its
| incompatible buffering scheme, and the fact that C and
| C++ need to stay in sync when linked together. And that
| brand of slow is still faster than other languages,
| except sometimes those that delegate i/o to pure C
| implementations.
| pjmlp wrote:
| Historical baggage, they weren't the first system
| programming languages, got lucky with UNIX's license
| allowing for widespread adoption, and won't be the last
| one standing either.
| wakawaka28 wrote:
| Considering that they are evolving, I think they are more
| likely than not to stay standing. There might be other
| similar languages developed in parallel, but after over
| 30 years of whining C and C++ are still popular. I don't
| expect that to change.
| throwaway2037 wrote:
| > If you care about correct use of localisation, standard
| C and C++ libraries aren't really what you're looking
| for, or even C and C++ to start with.
|
| What do you recommend instead?
| PaulDavisThe1st wrote:
| _("some text") ... aka gettext and friends.
| maccard wrote:
| QT, unfortunately.
| throwaway2037 wrote:
| Why do you "unfortunately"?
| maccard wrote:
| It's like using a sledgehammer for a picture hook. I
| think QT is a great tool, and it solves this problem
| nicely, but it's a full blown framework, and if you're
| already using something else - particularly if it's
| something "light" like SDL, or just a platform specific
| library like Win32, it's an awful lot to pull in (plus
| compile times, licensing, etc).
| fra wrote:
| This was a tip my hatn excellent to you
| johnisgood wrote:
| Why?
| lugu wrote:
| Thank you.
| dieortin wrote:
| > The biggest problem is that today C++ doesn't have a way
| to implement this for your own types easily
|
| I'm not sure about the stdlib version, but with fmtlib you
| can easily implement formatters for your own types.
| https://fmt.dev/11.0/api/#formatting-user-defined-types
| tialaramex wrote:
| I think the problem is that your idea of "easy" is
| "Here's a whole bunch of C++ you could write by hand for
| each type" while the comparison was very literally
| #[derive(Debug)]. I wasn't abbreviating or referring to
| something else, that's literally what Rust programmers
| type to indicate that their type should have the obvious
| boilerplate implementation for this feature, in most
| types you're deriving other traits already, so the extra
| work is literally typing out the word Debug.
| tightbookkeeper wrote:
| What's wrong with it?
| wakawaka28 wrote:
| >No other language does this, and it's not because they
| can't it's because it was a bad idea when it was created,
| it was still a bad idea in 1998, the only difference today
| is that C++ has a replacement.
|
| Hindsight is 20/20, remember that. Streams are not that bad
| of an idea and have been working fine for decades. You
| haven't named a problem with it other than the fact the
| operators are used for other stuff in other contexts. But
| operator overloading is a feature of C++ so most operators,
| even the comma operator, can be something other than what
| you expect.
|
| >The biggest problem is that today C++ doesn't have a way
| to implement this for your own types easily, Barry
| illustrates a comfortable way this could work in C++ 26 via
| reflection which on that issue closes the gap with Rust's
| #[derive(Debug)].
|
| You can trivially implement input and output for your own
| types with streams.
|
| You appear to be a Rust guy whose motive is to throw shade
| on C++ for things that are utterly banal and subjective
| issues.
| PaulDavisThe1st wrote:
| What they mean is this: struct Foo {
| int a; float b; std::string c;
| }; Foo foo; std::cout << foo;
|
| with no extra code. It's called reflection, where the
| compiler can generate good-enough code to generate a
| character-stream serialization of an object without any
| human intervention.
| wakawaka28 wrote:
| I know what reflection is of course. C++ makes it easy to
| implement IO. If you're asking for a reflection-based
| solution with less effort, you are practically asking for
| zero extra code. Anyway, C++ does not yet have reflection
| but who's to say how anyone wants any particular data to
| be dumped? A default implementation is nice but less
| useful than you make it sound. In any case, there are
| libraries approximating what you described (usually with
| macros and stuff) and reflection is totally coming at
| some point.
| spacechild1 wrote:
| Remember that C++ originally didn't have variadic
| templates, so something like std::format would have been
| impossible back in the day. Back in the day, std::iostream
| was a very neat solution for _type safe_ string formatting.
| As you conceded, it also makes it very easy to integrate
| your own types. It was a _big_ improvement over printf().
| Historic perspective is everything.
| vitaut wrote:
| {fmt} used to support pre-C++11 compilers that didn't
| have variadic templates. It was a pain to emulate
| variadics but it wasn't impossible.
| spacechild1 wrote:
| Wow, I don't know that! I would be curious to know how
| they did it.
| throwaway2037 wrote:
| Over the years, I have heard numerous complaints about C++
| I/O streams. Is there a better open source replacement? Or
| do you recommend to use C functions for I/O?
| steveklabnik wrote:
| https://en.cppreference.com/w/cpp/utility/format for
| C++20 and https://en.cppreference.com/w/cpp/io/print for
| C++23
|
| https://github.com/fmtlib/fmt is what it's based on, for
| C++11 and up support.
| alexvitkov wrote:
| > Don't use operator overloading for unrelated operations
|
| This disn't stop with <iostream>, they keep doing it - the
| latest example I can think of is std::ranges operations
| being "piped" with |.
| chipdart wrote:
| > The entire I/O streams (where std::cout comes from)
| feature is garbage, if this was an independent development
| there is no way that WG21 would have taken it, the reason
| it's in C++ 98 and thus still here today is that it's
| Bjarne's baby.
|
| I think this is a very lazy and somewhat conspiratorial
| take.
|
| C++'s IO stream library, along with C++'s adoption of
| std::string, is a response to and improvement over C's
| standard library support for IO. That alone makes it an
| invaluable improvement. It's easy and very lazy to look
| back 30 years ago and badmouth things done back then.
|
| It's also easy to complain about no one proposing changes
| when literally anyone, including you, can propose changes.
| The only need to do the legwork and put their money where
| their mouth is. The funny part is that we see frameworks
| putting together their own IO infrastructure and it ends up
| being not good, such as Qt's take on IO.
|
| But talk is cheap and badmouthing doesn't require a pull
| request.
| Someone wrote:
| > C++ code can seamlessly add #include "sqlite3.h" unchanged.
|
| Almost seamlessly. You have to do extern "C"
| { #include "sqlite3.h" }
|
| (https://isocpp.org/wiki/faq/mixing-c-and-cpp#include-c-
| hdrs-...)
| cornstalks wrote:
| If we're nitpicking then sqlite3.h already has `#ifdef
| __cplusplus` and `extern "C" {`. So yes, from the user's
| perspective it is seamless. They do not need to play the
| `extern "C" {` game.
| rramadass wrote:
| Yep; I think of it as "C/C++" and not "C" and/or "C++" i.e.
| one "multi-paradigm" language with different sets of mix-and-
| match.
| varjag wrote:
| kinda like python and ruby
| MathMonkeyMan wrote:
| My brief foray into microcontroller land has taught me that C
| and C++ are very much mixed.
|
| It's telling that every compiler toolchain that compiles C++
| also compiles C (for some definition of "C"). With compiler
| flags, GCC extensions, and libraries that are kinda-sorta
| compatible with both languages, there's no being strict about
| it.
|
| _My_ code might be strict about it, but what about tinyusb?
| Eventually you'll have to work with a library that chokes on
| `--pedantic`, because much (most?) code is not written to a
| strict C or C++ standard, but is "C/C++" and various
| extensions.
| rramadass wrote:
| > because much (most?) code is not written to a strict C or
| C++ standard, but is "C/C++" and various extensions.
|
| Absolutely true. I generally insist on folks learning C and
| C++ interoperability before diving in to all the "Modern C or
| C++" goodness. It helps them in understanding what actually
| is going on "under the hood" and makes them a better
| programmer/debugger.
|
| See also the book _Advanced C and C++ Compiling by Milan
| Stevanovic._
| emmelaich wrote:
| If you want a language with a great C FFI, C++ is hard to beat!
| sim7c00 wrote:
| can't believe so many people are arguing against this honestly.
| you don't mix them in the sense the author means. I take it
| these people didn't read the paragraphs this was the 'takeaway'
| from.
|
| For example, the primary reason for the sentence seems to be
| from the text: "Many code examples in this book won't even
| compile on a c++ compiler, So we should not mix sources of both
| languages".
|
| It's not at all about the ability to use c libraries in c++
| projects or vice versa :S.... c'mon guys!
| bitbasher wrote:
| Really looking forward to #embed, once the compilers catch up.
| Until then, Golang.
| enriquto wrote:
| This is not how C standards work. If it appears in the
| standard, it means that it is already implemented in some
| compilers (in that case, at least in gcc and clang).
| pjmlp wrote:
| That isn't really how it goes, that is how it used to be up
| to C99.
| enriquto wrote:
| Thanks for the correction! Do you know if there is a
| document from the standards body explaining the change in
| philosophy?
| MathMonkeyMan wrote:
| I've heard something along the lines of "the standard is
| to define facilities that will be used in most programs,
| and to codify widespread existing practice." That was in
| the context of "I don't like this proposed feature,"
| though. This was for C++, not C.
|
| A lot of stuff in the C++11 standard library was based on
| widespread use of Boost. Since then, I don't know. Also,
| were things like templates and lambdas implemented as
| compiler extensions before standardization? I don't know,
| but I doubt it. Maybe "we're a committee of people who
| will decide on a thing and we hope you like it" was
| always the norm in many ways.
| JonChesterfield wrote:
| It's a nuisance to implement the thing you want to add to
| the standard yourself. It's easier to ship it in the
| language and then complain at compiler devs that they're
| running behind the edge of progress.
|
| This interacts in the obvious way with refusing to
| correct mistakes after the fact for fear of breaking user
| code.
|
| I don't believe anyone has written a paper along the
| lines of "let's not bother with the existing practice
| part anymore", it's more an emergent feature of people
| following local incentive structures.
| rfl890 wrote:
| Clang 19 has it.
| jpcfl wrote:
| Or xxd --include <file>
|
| :)
| Keyframe wrote:
| The anti-Rust approach!
| f1shy wrote:
| I do that with ld and objcopy:
|
| https://stackoverflow.com/questions/58815959/include-binary-...
| accelbred wrote:
| I end up using a .S asm file with .incbin directives to embed
| files.
|
| #embed would be much nicer
| JonChesterfield wrote:
| Incbin works just fine from inline asm fwiw
| flohofwoe wrote:
| Inline assembly isn't supported for x86-64 and ARM on MSVC
| which unfortunately also means the incbin trick can't be
| used there anymore.
| enriquto wrote:
| So happy that we still get the dinosaur mascots! This is a good
| book.
| auggierose wrote:
| Table of contents in the sidebar doesn't work properly for me
| when I click on an entry (in macOS Preview).
| bwidlar wrote:
| I just test some links in the table of content, works fine for
| me. Using zathura pdf reader.
| Jtsummers wrote:
| Also works in Adobe and Firefox, but doesn't work in Chrome
| and Edge.
| f1shy wrote:
| Doesn't work for me either... but I will not dismiss the book
| because of that.
| soegaard wrote:
| Same here.
| channel_t wrote:
| Table of contents is definitely broken right now.
| jumpman_miya wrote:
| in example 1.1 i read that as 'tits_square' until i saw the
| output
| glass-z13 wrote:
| that's by design
| ykonstant wrote:
| It's a booby trap.
| zkirill wrote:
| I was going to ask if there is a good list of C books and then
| answered my own question. It categorizes _Modern C_ as
| Intermediate level.
|
| https://stackoverflow.com/questions/562303/the-definitive-c-...
| emmanueloga_ wrote:
| Note that this is not a complete list, fwiw. For example, I
| doesn't include "Effective C." [1].
|
| I like "Effective C" over "Modern C" because it's more engaging
| ... "Modern C" is super rigorous and feels a bit like reading
| an annotated spec of the language, which is what an expert may
| need, but makes for a dull read for a casual C user like me.
|
| --
|
| 1: https://nostarch.com/effective-c-2nd-edition
| xbar wrote:
| I agree, but I think Modern C has good, structured
| recommendations that make it worth getting through at least
| once.
| rramadass wrote:
| Also see _Fluent C: Principles, Practices and Patterns_ by
| Christopher Preschern.
| xbar wrote:
| I like Modern C. I have reviewed it favorably in several
| places. I agree it is intermediate.
|
| I think 21st Century C by Ben Klemens and C Programming a
| Modern Approach by King are both more approachable alternatives
| as a modern C companions to K&R.
| ralphc wrote:
| How does "Modern" C compare safety-wise to Rust or Zig?
| renox wrote:
| You'd be surprised: Zig has one UB (Undefined Behaviour) that C
| doesn't have!
|
| In release fast mode, unsigned overflow/underflow is undefined
| in Zig whereas in C it wraps.
|
| :-)
|
| Of course C has many UBs that Zig doesn't have, so C is far
| less safe than Zig, especially since you can use ReleaseSafe in
| Zig..
| uecker wrote:
| UB is does not automatically make things unsafe. You can have
| a compiler that implements safe defaults for most UB, and
| then it is not unsafe.
| ahoka wrote:
| By definition UB cannot be safe.
| marssaxman wrote:
| this depends on your choice of definition for "safe"
| Maxatar wrote:
| The definition given by the C standard allows for safe
| undefined behavior.
| umanwizard wrote:
| Something can be UB according to the standard, but
| defined (and safe) according to a particular
| implementation. Lots of stuff is UB according to the C or
| C++ standard but does something sensible in gcc and/or
| clang.
| duped wrote:
| That's implementation defined behavior, not undefined
| behavior. Undefined behavior explicitly refers to something
| the compiler does not provide a definition for, including
| "safe defaults."
| fuhsnn wrote:
| Compilers are not prohibited to provide their own
| definition for UB, that's how UBsan exists.
| Maxatar wrote:
| The C standard says, and I quote:
|
| >Possible undefined behavior ranges from ignoring the
| situation completely with unpredictable results ... or
| program execution in a documented manner characteristic
| of the environment (with or without the issuance of a
| diagnostic message)
|
| So a compiler is absolutely welcome to make undefined
| behavior safe. In fact every compiler I know of, such as
| GCC, clang, MSVC has flags to make various undefined
| behavior safe, such as signed integer overflow, type
| punning, casting function pointers to void pointers.
|
| The Linux kernel is notorious for leveraging undefined
| behavior in C for which GCC guarantees specific and well
| defined behavior.
|
| It looks like there is also the notion of unspecified
| behavior, which gives compilers a choice about the
| behavior and does not require compilers to document that
| choice or even choose consistently.
|
| And finally there is what you bring up, which is
| implementation defined behavior which is defined as a
| subset of unspecified behavior in which compilers must
| document the choice.
| renox wrote:
| Well Zig has ReleaseSafe for this.. ReleaseFast is for
| using these UBs to generate the fastest code.
| secondcoming wrote:
| Does C automatically wrap? I thought you need to pass
| `-fwrapv` to the compiler to ensure that.
| greyw wrote:
| Unsigned overflow wraps. Signed overflow is undefined
| behavior.
| kbolino wrote:
| This distinction does not exist in K&R 2/e which
| documents ANSI C aka C89, but maybe it was added in a
| later version of the language (or didn't make it into the
| book)? According to K&R, all overflow is undefined.
| wahern wrote:
| I don't have my copy of K&R handy, but this distinction
| has existed since the initial codification. From C89:
| 3.1.2.5 Types [...] A computation involving
| unsigned operands can never overflow, because a result
| that cannot be represented by the resulting unsigned
| integer type is reduced modulo the number that is one
| greater than the largest value that can be represented by
| the resulting unsigned integer type.
|
| Source: C89 (draft) at
| https://port70.net/~nsz/c/c89/c89-draft.txt
| renox wrote:
| -fwrapv is for _signed_ integer overflow not _unsigned_.
| sp1rit wrote:
| Yes, as unsigned overflow is fine by default. AFAIK the
| issue was originally that there were still machines that
| used ones complement for describing negative integers
| instead of the now customary twos complement.
| WalterBright wrote:
| Modern C still promptly decays an array to a pointer, so no
| array bounds checking is possible.
|
| D does not decay arrays, so D has array bounds checking.
|
| Note that array overflow bugs are consistently the #1 problem
| with shipped C code, by a wide margin.
| layer8 wrote:
| > no array bounds checking is possible.
|
| This isn't strictly true, a C implementation is allowed to
| associate memory-range (or more generally, pointer
| provenance) metadata with a pointer.
|
| The DeathStation 9000 features a conforming C implementation
| which is known to catch all array bounds violations. ;)
| uecker wrote:
| Right. Also it might it sound like array-to-pointer decay
| is forced onto the programmer. Instead, you can take the
| address of an array just fine without letting it decay. The
| type then preserves the length.
| codr7 wrote:
| Nice, when you know the length at compile time, which is
| rarely from my experience.
|
| The holy grail is runtime access to the length, which
| means an array would have to be backed by something more
| elaborate.
| uecker wrote:
| Oh, it also work for runtime length:
|
| https://godbolt.org/z/PnaWWcK9o
| pjmlp wrote:
| Now try that on a compiler without -fsanitize=bounds, yet
| full ISO C compliant.
| uecker wrote:
| You can still access the size which is what the parent
| was asking for. And please tell me how you would try this
| on an ISO compliant compiler for D.
| pjmlp wrote:
| D has bounds checking, and isn't a ISO language.
| WalterBright wrote:
| C: int foo(int a[]) { return a[5]; }
| int main() { int a[3]; return
| foo(a); } > gcc test.c >
| ./a.out
|
| Oops.
|
| D: int foo(int[] a) { return a[5]; }
| int main() { int[3] a; return
| foo(a); } > ./cc array.d >
| ./array
| core.exception.ArrayIndexError@array.d(1): index [5] is
| out of bounds for array of length 3
|
| Ah, Nirvana!
|
| How to fix it for C:
|
| https://www.digitalmars.com/articles/C-biggest-
| mistake.html
| uecker wrote:
| You need to take the address of the array instead of
| letting it decay and then size is encoded in the type:
| int foo(int (*a)[6]) { return a[5]; } int main() {
| int a[3]; return foo(&a); }
|
| Or for run-time length: int foo(int n,
| int (*a)[n]) { return (\*a)[5]; } int main() {
| int a[3]; return foo(ARRAY_SIZE(a), &a); }
| /app/example.c:4:38: runtime error: index 5 out of bounds
| for type 'int[n]'
|
| https://godbolt.org/z/dxx7TsKbK\*
| WalterBright wrote:
| int foo(int n, int (*a)[n]) { return (\*a)[5]; }
| int main() { int a[3]; return
| foo(ARRAY_SIZE(a), &a); }
|
| That syntax is why array overflows remain the #1 problem
| with C bugs in shipped code. It isn't any better than:
| int foo(size_t n, int* a) { assert(5 < n); return a[5]; }
| int main() { int a[3]; return
| foo(ARRAY_SIZE(a), a); }
|
| as the array dimension has to be handled separately from
| the pointer.
|
| Contrast with how simple it is in D:
| int foo(int[] a) { return a[5]; } int main() {
| int[3] a; return foo(a); }
|
| and the proof is shown by array overflow bugs in the wild
| are stopped cold. It can be that simple and effective in
| C.
| ryao wrote:
| This should be caught by CHERI.
| TZubiri wrote:
| "The DeathStation 9000"
|
| The what now?
| layer8 wrote:
| Google it.
| TZubiri wrote:
| Yeah, why have any type of human interaction in a forum
| when you can just refer your fellow brethren to the
| automaton.
| layer8 wrote:
| I'm saying this because any explanation I could offer
| would provide less insight than the Google results.
| TZubiri wrote:
| Less insight, perhaps, but of higher quality, which is
| subjective.
|
| I personally find that googling stuff provides not much
| connection to the subject of study, very impersonal and
| try to avoid it.
|
| For example I did google the concept, and found this
| https://github.com/cousteaulecommandant/ds9k.
|
| Which is not trivial to parse, bing posited the answer as
| authoritative, and if you look at the code it is really
| nothing, it seems to be a folklore concept, and as such,
| it is much more aptly transmitted by speaking to a human
| and getting a live version than by googling an
| authoratitative static answer.
| bsder wrote:
| Nasal daemons for those of us of a slightly older vintage
| ...
| trealira wrote:
| > The DeathStation 9000 features a conforming C
| implementation which is known to catch all array bounds
| violations. ;)
|
| That actually really does exist already with CHERI CPUs,
| whose pointers are tagged with "capabilities," which catch
| buffer overruns at runtime.
|
| https://tratt.net/laurie/blog/2023/two_stories_for_what_is_
| c...
|
| https://msrc.microsoft.com/blog/2022/01/an_armful_of_cheris
| /
| Rusky wrote:
| A worked example: https://github.com/pizlonator/llvm-
| project-deluge/blob/delug...
| sdk77 wrote:
| The thing is though that even with array bounds checking
| built into the language, out of bounds access due to
| programming error can still be attempted. Only this time it's
| safer because an attacker can't use the bug (which still
| exists) to access memory outside of bounds. In any case, the
| program still doesn't work as intended (has bugs) because the
| programmer has attempted, or allowed the attempt, to access
| out of bounds memory.
|
| Writing safe code is better than depending on safety
| features. Writing safe code is possible in any programming
| language, the only things required are good design principles
| and discipline (i.e. solid engineering).
| WalterBright wrote:
| In practice in C, that does not work because array overflow
| bugs are still the #1 bug in shipped C code, by a wide
| margin.
| jandrese wrote:
| Modern C is barely any different than older C. The language
| committee for C is extremely conservative, changes tend to
| happen only around the edges.
| flohofwoe wrote:
| Except for C99 which added designated init and compound
| literals. With those it almost feels like a new language
| compared to C89 (and the C99 designated init feature is so
| well thought out that it still beats most similar
| initialization patterns in more recent languages, including
| C++, Rust and Zig - only Odin seems to "get it").
| pornel wrote:
| There's finally a way to safely add two signed numbers, without
| tricky overflow checks that may trigger UB themselves!
| zerr wrote:
| Do they still use 0-terminated strings/char* as the main string
| type?
|
| Is the usage of single linked lists still prevalent as the main
| container type?
| racingmars wrote:
| > Do they still use 0-terminated strings/char* as the main
| string type?
|
| Of course, it's still C.
|
| > Is the usage of single linked lists still prevalent as the
| main container type?
|
| As far as I can remember, the C standard library has never had
| any functions that used linked lists. Nor are there any
| container types, linked lists or otherwise, provided by C. So
| I'd say this is a question about how people teach and use C,
| not related to the language -- or language spec version --
| itself.
| eps wrote:
| Don't feed the trolls.
| zerr wrote:
| I don't mean the language spec but what is commonly used in
| the wild.
| KerrAvon wrote:
| The C standard library provides no recognizable container
| types, so there's no "main" anything.
| codr7 wrote:
| Embedded linked lists are pretty cool though.
| ithkuil wrote:
| aka intrusive linked lists
| EasyMark wrote:
| I don't think that will ever change. Will the possibly
| introduce a more modern string2 type? Maybe but it will
| probably be unlikely before 2050
| russellbeattie wrote:
| Wow, the use of attributes like [[__unsequenced__]],
| [[maybe_unused]] and [[noreturn]] throughout the book is really
| awful. It seems pretty pedantic of the author to litter all the
| code examples with something that is mostly optional. For a
| second I wondered if C23 _required_ them.
| amomchilov wrote:
| Such is the issue with bad defaults. Opting into the sensible
| thing makes most of your code ugly, instead of just the
| exceptions.
| nimish wrote:
| My kingdom for fully specified, well defined portable bitfields.
| leonheld wrote:
| One of my favorite books ever.
| einpoklum wrote:
| It's only been a few years since I've come to feel I can rely on
| C compilers all supporting C99, for a library I'm maintaing [1].
| And after a couple of years, sure enough - I get an issue opened
| asking for C89 compatibility because of some arcane embedded
| toolchain or what-not.
|
| So, C23? ... that's nice and all, but, let's talk about it in 20
| years or so T_T
|
| [1]: https://github.com/eyalroz/printf
| jhatemyjob wrote:
| Can someone link me to an article that explains why C is
| basically frozen at C99 for all practical purposes? Few projects
| worth talking about leverage features from C11 and newer
| pornel wrote:
| C99 is still _new_! Microsoft tried to kill C by refusing to
| implement anything that wasn 't also in C++. MSVC was 16 years
| late implementing C99, and implemented only the bare minimum.
| Their C11 implementation is only 11 years late.
|
| I suspect that decades of C being effectively frozen have
| caused the userbase to self-select to people who like C exactly
| the way it is (was), and don't mind supporting ancient junk
| compilers.
|
| Everyone who lost patience, or wanted a 21st century language,
| has left for C++/Rust/Zig or something else.
| uecker wrote:
| Most of us liking a good language just did not use MSVC. I do
| not think many people who appreciate C's simplicity and
| stability would be happy with C++ / Rust. Zig is beautiful,
| but still limited in many ways and I would not use it outside
| of fun projects.
| pornel wrote:
| I don't even use Windows, but I need to write portable
| libraries. Unfortunately, MSVC does strongly influence the
| baseline, and it's not my decision if I want to be
| interoperable with other projects.
|
| In my experience, Windows devs don't like being told to use
| a different toolchain. They may have projects tied to
| Visual Studio, dependencies that are MSVC-only or code
| written for quirks of MSVC's libc/CRT, or want unique MSVC
| build features.
|
| I found it hard to convince people that C isn't just C
| (probably because C89 has been around forever, and many
| serious projects still target it). I look like an asshole
| when I demand them to switch to whole another toolchain,
| instead of me adding a few #ifdefs and macro hacks for some
| rare nice thing in C.
|
| Honestly, paradoxically it's been easier to tell people to
| build Rust code instead (it has MSVC-compatible output with
| almost zero setup needed).
| uecker wrote:
| The good news is that MSVC has C17 support (still missing
| important optional features, but at least some progress).
| flohofwoe wrote:
| Microsoft basically sabotaged C99 by not implementing any of
| its features until around 2015 in the Visual Studio C compiler,
| and then still took until 2019 before they acknowledged their
| failure and started supporting more recent C versions again
| (MSVC is still reliably behind Clang and GCC when it comes to
| their C frontend though).
|
| And back around 2010 MSVC still mattered a lot (which sounds
| weird from today's pov where most developers appear to have
| moved to Linux).
|
| But OTH, few projects actually need C11 features (and C11
| actually took one thing away from C99: VLAs - nothing of value
| was lost though).
|
| C23 might be the first version since C99 that's actually worth
| upgrading to for many C code bases.
| spacechild1 wrote:
| C11 gave us one very important thing: a standardized memory
| model! Just like in C++11, you can finally write cross-
| platform multithreaded code with standardized atomics,
| synchronization primitives and threads. Unfortunately,
| compiler/library support is still lacking...
| kristianp wrote:
| GCC support has been around since gcc 11 apparently. See table at
| (1). This is available in ubuntu 22.04. The page below also shows
| support for C26!
|
| 1) https://gcc.gnu.org/projects/cxx-
| status.html#:~:text=C%2B%2B...
| bondant wrote:
| That's for C++ not C
| johnisgood wrote:
| Personally this[1] just makes C much more complicated for me, and
| I choose C when I want simplicity. If I want complicated, I would
| just pick C++ which I typically would never want. I would just
| pick Go (or Elixir if I want a server).
|
| "_BitInt(N)" is also ugly, reminds me of "_Bool" which is
| thankfully "bool" now.
|
| [1] guard, defer, auto, constexpr, nullptr (what is wrong with
| NULL?), etc. On top of that "constexpr" and "nullptr" just reeks
| of C++.
|
| That said, Modern C is an incredible book, I have been using it
| for C99 (which I intend to continue sticking to).
| cornstalks wrote:
| > _what is wrong with NULL?_
|
| For starters, you have to #include a header to use it.
| zik wrote:
| And it avoids the NULL == 0 ambiguity, allowing for better
| type checking.
| johnisgood wrote:
| Well, I always include stdio.h which includes stddef.h that
| defines NULL as (void *)0.
| dhhfss wrote:
| In my experience, hardly any source files require studio.h
|
| stddef.h on the other hand is required by most to get
| size_t
| johnisgood wrote:
| You are right. Hereby I correct my parent comment: I
| talked about my own personal experience[1], but yeah, as
| you said, stddef.h is often required (and yes, often I do
| not need stdio.h, stddef.h is what I need) which defines
| NULL, which was my point. If it is often required, then
| it does not matter whether you have to include a header
| file or not, IMO.
|
| Just include the stddef.h header if you want to use NULL,
| similarly to how you include a header file if you want to
| use anything else, e.g. bool from stdbool.h.
|
| [1] I am not entirely sure in retrospect, actually, as I
| might be misremembering, but my point stands with or
| without stdio.h!
| mmphosis wrote:
| NULL is not wrong. The things that I will do with NULL are
| nickelpro wrote:
| > what is wrong with NULL?
|
| One of the few advantages of ISO standardization is you can
| just read the associated papers to answer questions like this:
| https://wg21.link/p2312
|
| The quick bullet points:
|
| * Surprises when invoking a type-generic macro with a NULL
| argument.
|
| * Conditional expressions such as (1 ? 0 : NULL) and (1 ? 1 :
| NULL) have different status depending how NULL is defined
|
| * A NULL argument that is passed to a va_arg function that
| expects a pointer can have severe consequences. On many
| architectures nowadays int and void* have different size, and
| so if NULL is just 0, a wrongly sized argument is passed to the
| function.
| flohofwoe wrote:
| auto is mostly useful when tinkering with type-generic macros,
| but shouldn't be used in regular code (e.g. please no 'almost
| always auto' madness like it was popular in the C++ world for a
| little while). Unfortunately there are also slight differences
| between compilers (IIRC Clang implements a C++ style auto,
| while GCC implements a C style auto, which has subtle
| differences for 'auto pointers' - not sure if those differences
| have been fixed in the meantime).
|
| _BitInt(N) isn't typically used directly but typedef'ed to the
| width you need, e.g. typedef _BitInt(2) u2;
|
| The 'ugly' _B syntax is needed because the combination of
| underscore followed by a capital letter is reserved in the C
| standard to avoid collisions with existing code for every
| little thing added to the language (same reason why it was
| called _Bool).
|
| AFAIK defer didn't actually make it into C23?
|
| I'm also more on the conservative side when it comes to adding
| features to the C standard, but IMHO each of the C23 additions
| makes sense.
| humanrebar wrote:
| > IIRC Clang implements a C++ style auto, while GCC
| implements a C style auto, which has subtle differences for
| 'auto pointers' - not sure if those differences have been
| fixed in the meantime
|
| Both have compatibly implemented the standard C++ auto. Since
| 2011 or so.
| flohofwoe wrote:
| Well, not in C :)
|
| Here's an example where Clang and GCC don't agree about the
| behaviour of auto in C23:
|
| https://www.godbolt.org/z/WchMK18vx
|
| IIRC Clang implements 'C++ semantics' for C23 auto, while
| GCC doesn't.
|
| Last time I brought that up it turned out that both
| behaviours are 'standard compliant', because the C23
| standard explicitly allows such differing behaviour (it
| basically standardized the status quo even if different
| compilers disagreed about auto semantics in C).
|
| PS: at least Clang has a warning now in pedantic mode:
| https://www.godbolt.org/z/ovj5r4axn
| johnisgood wrote:
| This difference of implementation in two of the major C
| compilers leaves a bad taste in my mouth. :/
| cpeterso wrote:
| > PS: at least Clang has a warning now in pedantic mode:
| https://www.godbolt.org/z/ovj5r4axn
|
| Did you mean gcc? Your link shows a gcc error:
| <source>:3:5: error: 'auto' requires a plain identifier,
| possibly with attributes, as declarator 3 |
| auto* p = &i; | ^~~~
| flohofwoe wrote:
| No, GCC is right to error there, because the code uses a
| C++-ism (the '*' after 'auto' only makes sense in C++ but
| not in C).
| eqvinox wrote:
| > AFAIK defer didn't actually make it into C23?
|
| Correct, defer didn't make it into C23.
|
| It (in its __attribute__((cleanup())) form) is also one of
| the most useful extensions in GCC/clang -- but, again, for
| use in macros.
| josefx wrote:
| > (what is wrong with NULL?)
|
| The old definition did not even specify wether it was a pointer
| or an integer. So for platforms that did not follow the Posix
| ((void*)0) requirement it was a foot gun that had neither the
| type nor the size of a pointer.
|
| > On top of that "constexpr" and "nullptr" just reeks of C++.
|
| Probably because they where back ported from C++. You can still
| use NULL, since that was apparently redefined to be nullptr.
| johnisgood wrote:
| What platforms are those that are in use, and how widespread
| their use is?
| consteval wrote:
| > If I want complicated, I would just pick C++ which I
| typically would never want
|
| In my opinion, complexity doesn't scale linearly like this.
| Sometimes, in fact often times, having more complex tools means
| a simpler process and end result.
|
| It's like building a house. A hammer and screwdriver are very
| simple. A crane is extremely complex. But which simplifies
| building a house? A crane. If I wanted to build a house with
| only a hammer and screwdriver, I would have to devise
| incredibly complex processes to get it done.
|
| You see the same type of thing in programming languages. Making
| a generic container in C++ is trivial. It's very, very hard in
| C. You can make it kind of generic. You can use void * and do a
| bunch of manual casting. But it's cumbersome, error prone, and
| the code is more complex. It's counter-intuitive - how can C, a
| simpler language, produce code that is more complex than C++?
|
| Or look at std::sort vs qsort. The power of templates and
| functors makes the implementation much simpler - and faster! We
| don't have to pass around void * and dereference them at
| runtime, instead we can build in comparison into the definition
| of the function itself. No redirection, no passing on the
| stack, and we can even go so far as to inline the comparison
| function.
|
| There's really lots of examples of this kind of stuff. Point
| being, language complexity does not imply implementation
| complexity.
| israrkhan wrote:
| Most important aspect of C is its portability. From small
| microcontrollers to almost any computing platform. I doubt that
| any new version of C will see that much adoption.
|
| If I want to live on cutting edge I would rather use C++2x or
| Rust rather than C.
|
| Am I missing something? What benefit this supposedly modern C
| offers?
| vitaminka wrote:
| these features will eventually trickle down into the
| mainstream, kind of like C11 is doing at the moment
|
| also, unless you're targeting embedded or a very wide set of
| architectures, there's no reason why you couldn't start using
| C23 today
| bboygravity wrote:
| Or in other words, for embedded and existing code: most use
| c99, some use c11 and nobody uses c23 until at least 10 years
| from now.
| dhhfss wrote:
| This depends on the platform. Many embedded systems are
| based on arm these days and have modern toolchains
| available.
|
| I cannot remember the last time I saw C99 used. C codebases
| generally use C11 or C17, and C++ code bases use C++20
| pjmlp wrote:
| Unless you can vouch for the C++ compiler, the best C++
| portable code can offer today is C++17.
|
| Also 8 and 16 bit embedded toolchains are certainly not
| on C11 / C17, they can hardly afford full C89.
| flohofwoe wrote:
| SDCC is a niche C compiler for 8-bit CPUs and is more
| uptodate than MSVC ;P
|
| https://sdcc.sourceforge.net/
|
| That's the nice thing with C: it's much easier for small
| teams to fully support than the latest C++ standards.
| pjmlp wrote:
| Now try to use it on the embedded deployments that
| require certification.
| vitaminka wrote:
| most non-embedded and non-legacy codebases could use c23,
| that's not an insignificant set
| doe_eyes wrote:
| Of course they will, just like they did in the past with C11,
| GNU extensions, or some of the individual features that are now
| rolled into C23. For example, the 0b notation for binary
| numbers is widely used in the MCU world.
|
| The microcontroller toolchains are generally built on top of
| GCC, so they get the features for free. There are some
| proprietary C compilers that are chronically lagging behind,
| but they are not nearly as important as they used to be two
| decades ago.
| shakna wrote:
| The `thread_local` specifier is used on a few microcontroller
| platforms already, but would be absolutely illegal in C11 and
| before to use. However, it vastly simplifies memory management
| in a threaded context.
|
| Why would I rather step into the world of C++ just to deal with
| that?
| casenmgreen wrote:
| IIRC, performance and cost of thread local store varies
| greatly between platforms.
|
| You have to know what you're biting into, before you use
| that.
| flohofwoe wrote:
| One advantage of writing C code is that you don't have annoying
| discussions about what idiomatic code is supposed to look like,
| and what language subset is the right one ;)
|
| For the cutting edge I would recommend Zig btw, much less
| language complexity than both modern C++ and Rust.
|
| One good but less visible side effect of C23 is that it
| harmonizes more syntax with C++ (like ... = {} vs {0}) which
| makes it a bit less annoying for us C library maintainers to
| support the people how want to compile their C code with a C++
| compiler.
| pjmlp wrote:
| There is enough material in C, and related compiler
| extensions, to have similar discussions, starting from where
| to place brackets.
| rbanffy wrote:
| Maybe the C24 will define the One Right Way.
| Arch-TK wrote:
| > C library maintainers to support the people how want to
| compile their C code with a C++ compiler.
|
| Just tell them to go away.
|
| Trying to write the subset of C and C++ is a fool's errand.
| jstarks wrote:
| No inline functions in library headers, then.
| flohofwoe wrote:
| Inline is mostly pointless in C anyway though.
|
| But it might be a minor problem for STB-style header
| libraries.
|
| It's not uncommon for C++ projects to include the
| implementation of an STB-style header into a C++ source
| file instead of 'isolating' them in a C source file.
| That's about the only reason why I still support the
| common C/C++ subset in my C libraries.
| pragma_x wrote:
| I'm with you on this. The feature list reads like a subset of
| later C++ standards that fit within C's (deliberately)
| rudimentary feature set.
|
| You could, in theory, just use C++ and be done with it. But
| like any C++ project you'd need a pretty strict style guide or
| even a linter, but this time it would have to be extra
| restrictive lest you slide into full C++ territory. And maybe
| that's a major stumbling block for some people?
| BenjiWiebe wrote:
| Won't any llvm/gcc supported target get the new version of C
| automatically? You won't get it in the vendor-modified ancient
| gcc toolchain for some other arch though.
| musicale wrote:
| I kind of like some of Metaware's high C extensions.
|
| https://news.ycombinator.com/item?id=41647843
|
| https://news.ycombinator.com/item?id=38938402
| survivedurcode wrote:
| Continuing to use a memory-unsafe language that has no recourse
| for safety and is full of footguns and is frankly irresponsible
| for the software profession. God help us all.
|
| By the way, the US government did the profession no favors by
| including C++ as a memory-unsafe language. It is possible to
| write memory-safe C++, safe array dereferencing C++. But it's not
| obvious how to do it. Herb Sutter is working on it with CppFront.
| The point stands that C++ can be memory-safe code. If you make a
| mistake, you might write some unsafe code in C++. But you can fix
| that mistake and learn to avoid it.
|
| When you write C, you are in the bad luck shitter. You have no
| choice. You will write memory--unsafe code and hope you don't
| fuck it up. You will hope that a refactor of your code doesn't
| fuck it up.
|
| Ah, C, so simple! You, only you, are responsible for handling
| memory safely. Don't fuck it up, cadet. (Don't leave it all to
| computers like a C++ developer would.)
|
| Put C in the bin, where it belongs.
| purple-leafy wrote:
| Skill issue
| pornel wrote:
| It's been a skill issue for 40 years. How long are we going
| to continue searching for those programmers who don't make
| mistakes?
| worksonmine wrote:
| Programmers make stupid mistakes in the safest languages
| too, even more so today when software is a career and not a
| hobby. What does it matter if the memory allocation is safe
| when the programmer exposes all user sessions to the
| internet because reading Dockers' documentation is too much
| work? Even Github did a variant of this with all their
| resources.
| pornel wrote:
| Because memory vulnerabilities don't make programs immune
| to other dumb mistakes. You get these vulnerabilities _on
| top of_ everything else that can go wrong in a program.
|
| Manual checking of memory management correctness takes
| extra time and effort to review, debug, instrument, fuzz,
| etc. things that the compiler could be checking
| automatically and reliably. This misplaced effort wastes
| resources and takes focus away from dealing with all the
| other problems.
|
| There's also a common line of thinking that that because
| working in C is hard, C programmers must be smarter and
| more diligent, so they wouldn't make dumb mistakes like
| the easy-language programmers do. I don't like such
| elitist view, but even if true, the better programmers
| can allocate their smarts to something more productive
| than expertise in programs corrupting themselves.
| chongli wrote:
| _Because memory vulnerabilities don 't make programs
| immune to other dumb mistakes. You get these
| vulnerabilities on top of everything else that can go
| wrong in a program._
|
| The issue is that these great new tools don't just fix
| the old vulnerabilities, they also provide a lot of new,
| powerful footguns for people to play with. They're
| shipping 2000 feet of rope with every language when all
| we need is 6 feet to hang ourselves.
| pornel wrote:
| There has been a bunch of failed C killers, and C++ has
| massively shat the bed, so I understand that people are
| jaded.
|
| However, this pessimistic tradeoff is just not true in
| case of Rust -- it has been focused from the start on
| preventing footguns, and actually does a great job of it.
| You don't trade one kind of failure for another, you
| replace them with compilation errors, and they've even
| invested a lot of effort into making these errors clear
| and useful.
| fjfaase wrote:
| There are still applications (especially with embedded devices)
| where you do not dynamically allocate memory or might not even
| use pointers at all.
| dxuh wrote:
| You can't just put a language in the bin that has been used for
| 50 years and that a huge percentage the present day software
| infrastructure is built on.
|
| I see comments like yours everywhere all the time and I
| seriously think you have a very unhealthy emotional
| relationship with this topic. You should not have that much
| hate in your heart for a programming language that has served
| us very well for many decades and still continues to do so.
| Even if C was literally all bad (which imho isn't even
| possible), you shouldn't be that angry at it.
| uecker wrote:
| There good tools that help improving memory safety in C and I
| do not think Rust is a good language. Of course, the worst
| about Rust are its fans.
| tenderfault wrote:
| any chance of getting a responsive TOC in any pdf reader
| whatsoever?
| kamaal wrote:
| https://www.manning.com/books/modern-c-third-edition
| johanvts wrote:
| I payed for this on manning and they didn't even release the
| final version yet. I guess I didn't understand what I was buying,
| but I can't help feel a bit cheated.
| sylware wrote:
| I am worried where "official" C is going. Its syntax which is
| already too complex and already does too much, but that would
| require to "break" backward compatibility namely it would require
| "porting". But since it would be still "C" that amount of work
| should be close to "a bit" of "step by step" refactoring
|
| For instance, only sized types:u8...s64, f32, f64... no implicit
| casts except for void* and literals, no integer promotion, no
| switch, no enum, only one loop keyword (loop{}!), no anonymous
| code block, and no toxic attribute like "packed structure" which
| makes us lose sight of data alignment... no _generic, typeof,
| restrict, syntax based tls, etc...
|
| But we would need explicit atomics, explicit memory barriers,
| explicit unaligned memory access.
|
| Instead of adding and complexifying C to make writing a naive
| compiler more and more complex, long and a mouse and cat catchup
| "to the standard" tedious task, what should be done is exactly
| the other way around.
|
| In end, I don't trust C officials anymore, I tend to stick to
| C99, or even assembly (I am currently writing rv64 assembly I run
| an x86_64).
| eqvinox wrote:
| > I tend to stick to C99,
|
| > [...] But we would need explicit atomics, explicit memory
| barriers, [...]
|
| You should read a change summary before complaining about bits
| missing from C99 that have in fact been added to C11.
|
| > [...] no toxic attribute like "packed structure" which makes
| us lose sight of data alignment [...]
|
| And you should also familiarize yourself with what's in actual
| ISO C vs. compiler extensions before complaining about bits
| that are in fact compiler extensions.
| sylware wrote:
| I _TEND_ to stick to C99 = usually C99 with very few bits of
| c11+ (usually the missing bits) and even some extensions
| (often related to ELF /object format). But I really try hard
| to minimize their usage.
|
| The pb is in ISO c11+ we got some of the missing stuff for
| modern hardware architecture, but also tons of tantrums
| (_generic, typeof, restrict....)
| eqvinox wrote:
| > The storage order, the endianness, as given for my machine, is
| called little-endian. A system that has high-order representation
| digits first is called big-endian. Both orders are commonly used
| by modern processor types. Some processors are even able to
| switch between the two orders on the fly.
|
| Calling big endian "commonly used by modern processor types" when
| s390x is really the only one left is a bit of a stretch ;D
|
| (Comments about everyone's favorite niche/dead BE architecture in
| 3... 2... 1...)
| ondra wrote:
| MIPS is still quite alive in consumer networking hardware.
| eqvinox wrote:
| True - but at the same time, about half1 of it is mipsel,
| i.e. in little-endian mode :). It's also in decline, AFAICS
| there is very little new silicon development.
|
| 1 on the OpenWRT table of hardware
| tonetegeatinst wrote:
| Learning MIPS assembly currently using mars and QtSpim.
|
| Any recommended hardware I should use for bare metal
| development messing around? Hopefully priced like a SBC like
| the raspberry pi.
|
| Want to move from making basic programs like adding, messing
| with functions, etc and bring my MIPS assembly up to a real
| hardware environment.
| AntoniusBlock wrote:
| Many routers use the MIPS ISA and they can be rooted to get
| shell access. That's what I did with an old Netgear router,
| which was like a very low spec SBC. If you have a PS2 lying
| around, you could try that.
| throwaway19972 wrote:
| "Modern" doesn't mean "currently widespread".
| eqvinox wrote:
| Indeed, if it meant "currently widespread" there'd be a
| stronger argument for Big Endian with a lot of MIPS and PPC
| chugging away silently. But interpreting "modern" as recent
| development, BE is close to gone.
| throwaway19972 wrote:
| Is there some end to this criticism or do you have some
| stake in dismissing big endian architectures?
| eqvinox wrote:
| Uh, why so serious? I called it "a bit of a stretch ;D" -
| there was a reason for that smiley. I'm well aware BE is
| alive enough to be around.
|
| If you can't live without knowing, sure, my stake in
| dismissing big endian architectures is that I can't in
| fact dismiss BE architectures because I have users on it.
| And it's incredibly painful to test because while my
| users have such hardware, actually buying a good _test
| platform or CI system_ is close to impossible. (It ended
| up being Freescale T4240-QDS devkits off eBay. Not a good
| sign when the best system you can get is from a company
| that doesn 't exist anymore.)
|
| And at some point it's a question about network
| protocols/encodings being designed to a "network byte
| order" determined in the 80s to be big endian. When
| almost everything is LE, maybe new protocols should just
| stick with LE as well.
| pm215 wrote:
| To be fair to IBM, with s390x they do have a "community
| cloud" programme where open source projects can apply to
| get a Linux s390x VM to use for things like CI:
| https://community.ibm.com/zsystems/l1cc/ . But yeah, BE
| MIPS is super awkward because the target systems are all
| embedded things that are bad dev/CI machines.
| eqvinox wrote:
| Stupidly enough, "my" software is a routing control plane
| (FRRouting), so what I need to support are exactly those
| embedded things. I'm not sure anyone uses FRRouting on a
| s390x machine. But maybe we should go ask IBM anyway, a
| BE system is a BE system...
|
| qemu CPU emulation exists too, but that's painfully slow
| for an actual CI run, and I'm not sure I trust it enough
| with e.g. AF_NETLINK translation to use the "-user"
| variant on top of an LE host rather than booting a full
| Linux (or even BSD).
|
| And in the very best case, proper testing would pit BE
| and LE systems "against" each other; if I run tests on BE
| against itself there's a good risk of mis-encodings on
| send being mis-decoded back on receive and thus not
| showing as breakage...
|
| ... really, it's just a pain to deal with. Even the
| beauty (in my eyes) of these T4240 ppc64 systems doesn't
| bridge that :(
| throwaway19972 wrote:
| > there was a reason for that smiley
|
| Fair--my bad, I can fail at reading tone sometimes.
|
| Would you propose the C abstract machine abstracting away
| endianness entirely as an alternative? My understanding
| is that deprecating support for existing architectures is
| discouraged to every practical extent.
| eqvinox wrote:
| Maybe we failed to communicate because our brains have
| different endianness? :D
|
| To be honest, I don't think this is a solvable problem.
| (Changing the C machine concept doesn't do much if you
| need to process network traffic that uses both (e.g. IP
| packet [big endian] carrying protobuf [little endian]).
| It's already _mostly_ a question of data ingress
| /egress.)
|
| What is solvable though is making sure people are
| sufficiently aware. And the people who read a book like
| "Modern C" are probably a very good target audience,
| building low-level bindings and abstractions. They should
| know that LE and BE are _technically_ a free-floating
| design choice, but _practically_ the vast majority of
| systems is LE now. But at the same time, yes, BE isn 't
| extinct, and won't be any time soon... and it's left to
| them to make their best possible design given their
| environments.
| umanwizard wrote:
| Isn't Sparc big-endian?
| zifpanachr23 wrote:
| Is SPARC still seeing serious hardware development like
| s390x? I know it's still around but I can't recall the last
| time I heard of any new hardware.
| unilynx wrote:
| well in a way all processors commonly use them... as big-endian
| is also the network byte order
| flohofwoe wrote:
| ...x86 CPUs actually have special mov instructions now to
| load big endian data. Not sure since when though (on godbolt
| it needs `-march=native`:
|
| https://www.godbolt.org/z/bWfhGx7xh
|
| ...without -march=native it's a mov and bswap (so not too bad
| either).
| cmovq wrote:
| Looks like it was introduced with Haswell (2013). So it's
| safe to use if you're also compiling with AVX2.
| legends2k wrote:
| Arm is bi-endian and is alive in most phones.
|
| I agree with another GP's comment that modern doesn't mean
| popular/widely used.
| umanwizard wrote:
| I really doubt any mainstream smartphone runs their Arm chip
| in big-endian mode ever.
| legends2k wrote:
| That's besides the point. The book's author has a valid
| point. Being pedantic should be applied at all levels if
| you're going that route.
| eqvinox wrote:
| The problem about being pedantic is that you can choose
| different directions to be pedantic in. My "direction" is
| that code isn't written in a vacuum, it mixes with code
| millions of other people wrote and runs on machines
| millions of other people built. As such:
|
| My concern isn't that the phrasing in the book is _wrong_
| , and I have expressly not argued that. It's that it
| presents the issue as having no further depth, and these
| two choices as equivalent. They aren't. The "Some
| processors are even able to switch between the two orders
| on the fly." that follows makes it even worse, at least
| to me it really sounds like you needn't give any care.
|
| And the people reading this book are probably the people
| who should be aware of more real-world background on
| endianness, for the good of the next million of people
| dealing with what they produced.
| kelsey98765431 wrote:
| MipsBE is very common in edge devices on many networks. You
| may have 5 MipsBE devices in your home or office without
| realizing. It's almost never an issue so nobody cares, but
| they are common.
| unscaled wrote:
| The book does say "Both orders are commonly used by modern
| processor types". I'd say this sentence is quite misleading,
| since it would lead you to believe two falsehoods:
|
| 1. That both byte orders are equally prevalent in the wild,
| particularly in systems that are expected to run modern C
| code.
|
| 2. That both byte orders are equally likely to be found in
| "modern" (new or updated) processor design.
|
| It's not entirely incorrect, but a better phrasing could be
| used to clarify that little-endian is the more modern and
| common storage order, but you still cannot ignore big-endian.
| 3836293648 wrote:
| Don't a bunch of web protocols use big endian?
| Keyframe wrote:
| well, network byte order is a thing. Not a processor though.
| uvas_pasas_per wrote:
| I've been using modern C++ for a personal project (a language
| interpreter) for the last year+. I constantly think of switching
| to C, because of the mental burdens of C++, and because of the
| problems with tooling (Visual Studio's IntelliSense still barely
| works, because I use C++20 modules), and compile times get ugly
| because of the way the language failures force so much into
| interfaces (even with modules). But on the flip side I've gotten
| so used to classes, member functions, generic programming
| (templates), namespaces... I may be hooked.
| fluoridation wrote:
| I've been using C++ for the longest time, and I would never
| give up destructors to switch to C.
|
| For your particular use case, have you considered C#? VS works
| much more nicely with it.
| uvas_pasas_per wrote:
| Yeah, I did. I want something low level and cross platform,
| including mobile. I think when I tried the C# for iOS stuff,
| nothing worked. But it's probably too much VM/runtime for me
| anyway, for this project.
| fluoridation wrote:
| Fair enough. I would've used C++ as well.
| neonsunset wrote:
| iOS C# is more or less fine, there is quite a bit of work
| done in .NET to make this better still. .NET 9 gains native
| Swift Library Evolution ABI support even - you can
| literally declare DllImports against public Swift APIs by
| simply annotating them with [typeof(CallConvSwift)], it's
| not as convenient as it sounds but it's only a matter of
| time when the tools like
| https://github.com/royalapplications/beyondnet adopt this.
| It's going to get much better once MonoAOT is replaced with
| NativeAOT for all major publish modes for iOS.
| delduca wrote:
| I wish auto in C was similar to auto in C++.
| RantyDave wrote:
| Wait, C programmers now put the star on the left hand side?
|
| char* thing; // good
|
| char *thing; // bad
|
| This ... is awesome. As a C++ "native" I've always found the
| "star on the right" thing to be really horribly confusing.
| bloppe wrote:
| Ofc this has always been an option. In my C heyday I used to
| put a space on both sides of the star. It makes for a more
| consistent syntax when you have multi layer pointers with const
| at various layers. For example:
|
| // mutable pointer to mutable data:
|
| char * str;
|
| // Immutable pointer to immutable data:
|
| char const*const str;
|
| // Mutable pointer to an immutable pointer to a mutable pointer
| to immutable data:
|
| char const**const* strs;
___________________________________________________________________
(page generated 2024-10-16 23:01 UTC)