[HN Gopher] The case for a modern language
___________________________________________________________________
The case for a modern language
Author : bshanks
Score : 185 points
Date : 2022-01-22 05:58 UTC (17 hours ago)
(HTM) web link (jeang3nie.codeberg.page)
(TXT) w3m dump (jeang3nie.codeberg.page)
| tragomaskhalos wrote:
| Either I'm going mad - in which case please set me straight - or
| the Rust example doesn't even compile: had to remove the odd-
| looking borrows on the method calls, and replace the type
| annotation in the final 'if let' with a turbofish on the call.
| tlamponi wrote:
| Nope, you're right, I need to apply the following diff (wrapped
| in a fn main() {}) to avoid rustc complaining:
| # diff -up test.rs test.fixed.rs --- test.rs
| 2022-01-22 16:03:57.302742242 +0100 +++ test.fixed.rs
| 2022-01-22 16:03:27.766250377 +0100 @@ -2,13 +2,13 @@
| fn main() { // pretend that this was passed in on
| the command line let my_number_string =
| String::from("42"); // If we just want to bubble
| up errors - let my_number: u8 =
| &my_number_string.parse()?; + let my_number: u8 =
| my_number_string.parse()?; assert_eq!(my_number,
| 42); // If we might like to panic! -
| let my_number: u8 = &my_number_string.parse().unwrap();
| + let my_number: u8 = my_number_string.parse().unwrap();
| assert_eq!(my_number, 42); // If we're a good
| Rustacean and check for errors before trying to use the data
| - if let Ok(my_number: u8) = &my_number_string.parse() {
| + if let Ok(my_number) = my_number_string.parse::<u8>() {
| assert_eq!(my_number, 42); } }
| tialaramex wrote:
| One of the best things to happen to C++ discussions was
| people starting to write godbolt links for their code.
| Immediately the code being discussed becomes code somebody
| actually _compiled_ and maybe _tried running_ and not just
| "oh, ignore the fact it's syntactically invalid - you know
| what I meant". No we don't.
|
| You can obviously write a godbolt link for Rust too, but
| Rust's playground is also a reasonable choice.
|
| I _think_ that code maybe makes more sense with a turbofish
| for each parse call and type inference, but maybe that 's
| just a matter of taste. If the author had used godbolt or
| playground or whatever they'd have written code that compiles
| and we'd not be guessing.
| futharkshill wrote:
| If a user wants to parse integers etc. from a string, the
| function snprintf and family is often applied. It is a neatly
| simple function. This article seems to invent a problem rather
| than an organic one.
| ogogmad wrote:
| The article argues that there is no easy way to detect whether
| the parsing finished successfully. As a consequence, the C
| standard library is unsafe when used normally.
|
| It's interesting how beginners are encouraged to use various
| string functions which are not safe to use with external input.
| skywhopper wrote:
| What a weird post. The examples from Rust and Zig don't fail
| gracefully, so they can't be considered complete. Panicking on
| bad user input is bad code, too. And the main complaint seems to
| be that the C stdlib could be improved. But where it has been
| improved, the author complains that it's really just doing the
| ugly stuff under the hood. What does the author think the Rust
| stdlib function is doing exactly?
| stncls wrote:
| From the article: char *forty_two_bee = "42b";
| char *end; errno = 0; // remember errno? long i
| = strtol(forty_two_bee, &end, 10);
|
| > This will return 0
|
| No, this will return 42. strtol() parses greedily until a
| character cannot be parsed, but then it returns the conversion of
| what it did parse.
|
| I guess the fact the author got this wrong... kind of proves
| their point that strtol()'s API is not great?
|
| On the other hand, while the article purports to criticize a
| language, it then proceeds to only cover its standard library.
| Sure, C's stdlib is old-fashioned, but there are many things in C
| that are _much worse_ than its standard library! (And I say that
| as someone who still likes the language.)
| PaulDavisThe1st wrote:
| strto*() is the wrong API to use if you care about errors.
| char* forty_two = 42; int i; if (sscanf (forty_two,
| "%d", &i) != 1) { /* error */ }
|
| Sometimes, there's more than one way to skin a cat, and one of
| them is more suited to the task at hand.
| JulianMorrison wrote:
| Now what happens if you pass it a string without a null byte
| terminator?
| alkonaut wrote:
| Wait is this article saying that there is no
| good/obvious/standard function to parse a string to a number and
| has the _two_ obvious outputs of such a function (the number, and
| a bool or error code)?
|
| Even a person in the 60s would realize that that's the api for
| conversion from a string to a number (or any conversion that
| might fail)! What happened? Why do these functions even exist?
| gengiskush wrote:
| How about leaving the old stuff you want to "replace" alone?
| People are using it.
| prirun wrote:
| PL/I, created in 1964, had strings. Real strings, where the
| compiler knows the length even when it gets passed around and is
| declared char(*) var in the receiving function. You can't have
| buffer overflows because the compiler and runtime know every
| string's current length and allocated length.
|
| This isn't a particularly hard problem. C just took a shitty
| shortcut to fake strings using byte arrays and the world glombed
| onto it. Now we're stuck with a crappy "standard" that people
| should have scoffed at when it first showed its ugly face.
| [deleted]
| grymoire1 wrote:
| Personally, I'm tired of people bitching about C. At the time,
| the choice was C or assembly language for embedded/operating
| systems. There was no other choice in the 1970's. In fact, it
| wasn't even an option for most of the 1970's.
|
| If you worked at a company and wanted a team of people to
| develop on a multi-user system, and port it to a single-user
| stand-alone system, you were out of luck. Our company sold test
| equipment based on the Data General minicomputers, and while DG
| had multi-user systems and single-user systems, they had no
| common programing language besides FORTRAN. It was so
| frustrating.
|
| And then Digital came to us and wanted to buy a lot of systems,
| but it had to be running on a PDP-11. Trouble is, our test
| system was written in Data General assembly language. We had to
| re-write the system in a portable high-level language that
| could run on RSX-11 OS. But how?
|
| We searched for a suitable programming language we could buy
| support for, and ended up using PASCAL - which was a P-code
| interpreter. The P-Code was portable across operating systems.
| So I "ported" an assembly-based system to Pascal, and was able
| to have equivalent runtime performance, because the DEC system
| had RAM-based overlays and the DG had disk-based overlays.
| Otherwise, performance of Pascal over ASM would have made it
| unfeasible.
|
| A few years later, C was commercially available. Oh I wish it
| was a choice that was available then. The rule of thumb was
| that C would run with 90% of the performance of assembly
| language. And that was before they made incredible strides in
| compiler technology. PL/1 would have been a disaster, assuming
| it could run at all on a 16-bit machine.
| thayne wrote:
| Sure c was better than anything else in the 70s. The question
| is why aren't we using something better 40 years later? Even
| if that is just a better version of C with a better libc,
| error handling, protection from overflows, etc.
| ModernMech wrote:
| It shocked me to realize this, but the 70s are now a half
| century ago.
| FabHK wrote:
| 1983 (Star Wars _Return of the Jedi_ , Michael Jackson
| _Thriller_ and _Billie Jean_ ) is closer to WWII than to
| now...
| agentultra wrote:
| You can if you want. Pick a board, a chip set, read the
| manual and get cracking.
|
| The Intel manual is only what, 2200 pages?
|
| I bet you could bootstrap an operating system, compiler,
| tool chain, and basic tools in a few years. And maybe in
| ten or twenty years you could have your new development
| environment up so you can start publishing software for all
| you new users.
|
| I think the reason it sticks around is because of network
| effects like platform exclusivity, ecosystem, etc.
| jacquesm wrote:
| From experience: it takes about two years so you are
| quite on the money with your estimate. The hard part is
| to gain traction.
| gumby wrote:
| > At the time, the choice was C or assembly language for
| embedded/operating systems. There was no other choice in the
| 1970's. In fact, it wasn't even an option for most of the
| 1970's.
|
| Unix was written in C because Thompson and Ritchie had been
| working on Multics, which was written in PL/1 in the 1960s.
| So the idea of an OS written in a high level language was
| hardly obscure and had nothing to do with C. It's hard to say
| that C was much of an option in the 1970s anyway as k&r
| wasn't even published until 1978.
|
| There was a lovely (and also annoying) Cambrian explosion of
| languages and OSs in the 70s and even into the mid 80s or
| later. Computer companies often wrote their own languages and
| OSs, which made porting difficult (but porting wasn't hugely
| common).
| devoutsalsa wrote:
| I've never played with Rust, but could Rust have been
| viable on early 70s hardware? Had Rust existed, could Unix
| have been originally written in Rust?
| jacquesm wrote:
| I will answer 'Yes' to this the moment we have a viable
| mainstream OS written in Rust. There are people working
| towards this so with some luck we will be able to see
| what the brave new world of a whole system running
| production software built in Rust looks like.
| AnimalMuppet wrote:
| > Unix was written in C because Thompson and Ritchie had
| been working on Multics, which was written in PL/1 in the
| 1960s. So the idea of an OS written in a high level
| language was hardly obscure and had nothing to do with C.
|
| OK, but at the time they started working on Unix, Multics
| had not yet been delivered. Nor was it clear that it would
| _ever_ be delivered. So the idea that an OS could be
| _successfully_ written in a high-level language was not yet
| proven.
| gumby wrote:
| I think you're trying to split hairs for some reason I
| can't figure out.
|
| In any case your assertion is not correct: Multics was
| operational around campus in 1969.
| mamcx wrote:
| > Personally, I'm tired of people bitching about C ... There
| was no other choice in the 1970's.
|
| This in fact prove the opposite. People not complain hard
| enough about how much terrible C/C++ are.
|
| 1970: That is more than 50 years of BEING WRONG.
| jacquesm wrote:
| Not really. For a long time C was a perfectly legitimate
| choice. The 'wrongs' of C have only really come to light
| with widespread usage of the Internet and the much higher
| focus on security. Back in the day people working with
| computers weren't necessarily doing so to make a quick
| buck, hose your system or to try to see if they could do
| damage. The bulk of the people working with computers was
| trying hard to produce something useful instead of to
| deconstruct that which was already there in a malicious
| way.
|
| The parasites came _long_ after C.
| stjohnswarts wrote:
| Also with minimal c++ you can fix almost all the issues with
| C and people in embedded who are new overlook it all the time
| for fancier things like rust/zig/whatever which would force a
| complete thought process change. Luckily I've been coding
| away with C++ as a better C for decades in embedded projects.
| It does require some knowledge of what's going on underneath
| classes, inheritance, basic templates, etc., but it's all
| very doable. I generally avoid RTTI and exceptions for
| example.
| TheOtherHobbes wrote:
| OK - but it's not the 1970s any more. Modern hardware is many
| orders of magnitude faster than it used to be. (If you check
| the numbers it's not just a linear jump in clock rate of a
| thousand or so, but a multiplier of another 10 or 100 because
| of pipelining, faster memory, larger caches, and bigger word
| sizes.)
|
| So why are we still using a language designed as a quick hack
| in the 70s and which is a dinosaur now?
|
| Beyond that - why are we still using the _ideas_ from that
| period without modernising them? Why are so many 1970s
| constraints and hacks baked into POSIX and OS features when
| modern issues - security, stability, consistency,
| reliability, multi-national localisation and support, and so
| on - should be taking precedence?
| MaxBarraclough wrote:
| You're right of course that hardware has improved
| immensely, but I'm not sure what your point was. There are
| plenty of domains where performance is still of great
| importance, and C still has excellent performance.
|
| I think the real point is that modern languages can
| significantly improve on the major issues with C,
| particularly its undefined behaviour and how that
| translates to real-world security issues, without
| significantly impacting performance. Rust (and in
| particular its Safe Rust subset) has been competing more
| with C++ than with C, but the point is still there.
|
| I admit though that I don't have hard numbers on what would
| be the performance cost of writing an OS (for example) in
| Rust rather than C.
| ogogmad wrote:
| Zig is apparently not much more complicated than C. It's
| got the same focus on low-level programming and manual
| memory management as C does. Doesn't support operator
| overloading though, and probably never will. :(
| MaxBarraclough wrote:
| I didn't mention Zig because as far as I know there's no
| _Safe Zig_ subset, nor are there plans to develop one.
| Zig itself is an unsafe language. [0][1]
|
| That's the nice thing about Safe Rust, it's a proper safe
| language akin to Java and JavaScript, while retaining
| high performance, plain old ahead-of-time compilation,
| and no garbage collector. Zig isn't playing the same
| game.
|
| [0] https://www.scattered-thoughts.net/writing/how-safe-
| is-zig/
|
| [1] https://news.ycombinator.com/item?id=26537693
| michaelcampbell wrote:
| > OK - but it's not the 1970s any more
|
| Sure, but that can be taken both ways. eg: "then stop
| griping about a language that was at the top of the heap in
| the 70's".
| lifthrasiir wrote:
| It should rather be "then stop _using_ a language that
| was at the top of the heap in the 70 's". The entire
| discussion around C happens because C is still actively
| being used.
| indymike wrote:
| > Personally, I'm tired of people bitching about C.
|
| The complaining gets old, but then again, memory leaks,
| overrruns and underruns and other C footguns get old, too. C
| has been and still is a great tool, but there is some level
| of... maybe we can do better 40 years later? You appreciate C
| more if you've had to implement anything reasonably large in
| assembly (which clearly you have).
|
| > In fact, it wasn't even an option for most of the 1970's.
|
| I started programming professionally in the mid 80s. There
| really wasn't much better than C. Pascal, compiled BASIC (it
| wasn't quite the VisualBASIC era yet)and ancient stuff like
| COBOL, PL/1 and FORTRAN were really the other real options.
| The old languages had a lot of limitations baked in. Pascal
| was better, but there were huge limitations imposed by Pascal
| arrays and Pascal's type system that rendered it very
| difficult to use for many entire classes of applications
| (anything where dynamic allocation of blocks of memory was
| needed, so for something like I/O... or video... or text
| editing (255 character lines much?) or whatever I happened to
| be working on. It wasn't impossible to do big projects with
| Pascal, but it was a lot more work.
| jacquesm wrote:
| > maybe we can do better 40 years later?
|
| But we do. There are plenty of programming languages
| besides C.
|
| Also, while we're at it, UNIX is now a good 50 years old
| and if anything it contributes as much to the problem of
| unsafe software as anything else out there, every driver
| has the potential to hose the entire system.
| yourapostasy wrote:
| _> PL /1 would have been a disaster, assuming it could run at
| all on a 16-bit machine._
|
| There was a PL/1 compiler of sorts that IBM flogged on
| MS-/PC-DOS in the early days of the IBM PC. IBM didn't write
| it and only distributed it IIRC, but I can't recall if the
| one I used was written by Digital Research or Language
| Processors, Inc. (LPI). It was riddled with compromises and
| indeed, slow as death compiling. That's saying something when
| I was used to fiddling with paper tape and audio cassette by
| then; floppy disks were considered lightning fast by
| comparison, and the compiler bogged down that experience. So.
| Many. Floppy. Swaps.
|
| It was sold under the value proposition that your mainframe
| programmers could prototype small bits of code on their PC's
| (even from home!!!), then when satisfied with the results
| they'd upload the polished source to the mainframe. I shudder
| to imagine what it took to make that USP a reality for real
| production code snippets.
| cagey wrote:
| Perhaps you're thinking of PL/M [0]? I had a brief
| encounter with it (as sold by Intel) targeting (of all
| amazing things) the 8051 microcontroller ISA.
|
| And yes: So. Many. Floppy. Swaps. The codegen was not
| horrendous, but IIRC its price kept it well out of reach of
| non-commercial users.
|
| [0] https://en.wikipedia.org/wiki/PL/M
| 41c2 wrote:
| agumonkey wrote:
| It seems the world is a pile of shitty shortcuts.
| judahmeek wrote:
| I don't know about the world, but our brains sure are! xD
|
| We rely on cognitive biases (shitty shortcuts) for as long as
| we can. In many cases, for longer than would be optimal.
| gameswithgo wrote:
| at the time the choice made sense for C, today it is a
| vanishingly rare case that you want a language that doesn't
| know how long its own arrays and strings are, or that doesn't
| know what might be null.
| dottedmag wrote:
| Seriously, the biggest gripe about C is the design of standard
| library?
|
| Not the pervasive undefined behaviour and compilers that become
| more aggressive every release about breaking previously-working
| code?
|
| Not the reams of code that assume sizes of integers and
| signedness of char?
|
| Not the wild build process that makes it awfully hard to actually
| build anything that has any dependencies whatsoever.
|
| strtol. Damn, what a nuisance!
| futharkshill wrote:
| the build process of C is one of its absolute benefits. Each
| unit compiles on its own, producing an object file. The fact
| that people have now started to make header-only libraries
| makes the story even better! Each function gets a name. No
| namespaces, classes, scopes, modules, w/e. You can even just
| declare a function as extern at compile time!
| Too wrote:
| Can't tell if this is sarcasm or not.
| dottedmag wrote:
| C compilation is not that bad, what makes it atrocious is the
| preprocessing step.
|
| Show me the large-ish (100K+ LOC) codebase with dependencies
| that can be cross-compiled, does not come with tons of cruft
| like autoconf or Meson, and does not require installing reams
| of software on the host as "libraries", and then we are
| talking.
|
| (edited: typo)
| futharkshill wrote:
| While a programming language and ecosystem includes some of
| the culture, bad code and project structure IMO should not
| be blamed on C. Modern C projects are a breeze
| dottedmag wrote:
| > Modern C projects are a breeze
|
| So, where's the link to a modern C project that is a
| breeze to work with? Requirements are in the parent
| comment.
| naasking wrote:
| The fact that people have to resort to header only libraries
| is a sign of how bad building portable C libraries really is.
| tyingq wrote:
| It feels valid to me. Redis might be a good example. They had
| to write their own string library (sds) to make a cache server.
| samatman wrote:
| It says (part 1) in the headline, even on HN!
|
| As a place to start discussing why a successor systems language
| is necessary, comparing string parsing across Rust, Zig, and C?
| Pretty good place to start, because the problems it introduces
| are _pervasive_ as the discussion continues.
|
| Magic error values that get globally mutated? Check. Pointers
| which are either null or exist so you can _do arithmetic to
| deduce a byte length_? Check. An almost aggressive distain for
| handling sum types of even the simplest sort? Check.
|
| Turns out Zig and Rust are whupping the ol' llama on undefined
| behavior and build processes as well, not to mention memory
| safety. If only this author had indicated that they might
| continue writing on the subject...
| dkersten wrote:
| The standard library is part of the language. Its also how the
| language is used, both because the language design encourages
| it and because people have a tendency to copy how the standard
| library does things, since you learn to do things how the
| libraries you use do them.
| hmry wrote:
| Where does the article say that those other things are not also
| big problems? In fact, it specifically says strtol is _one
| example_ of the things wrong with C. This comment seems
| needlessly dismissive.
| wudangmonk wrote:
| Why complain about a functions though? you can just write
| your own that does exactly what you want it to do. I don't
| see how this is a flaw in the language. You could at least
| mention things like the loss of size information when passing
| arrays between scopes, that is an annoyance that can be
| considered as a problem in the language.
| dottedmag wrote:
| Read the last section. None of the real problems of C are
| even mentioned.
| Accacin wrote:
| "None of the real problems of C are even mentioned."
|
| _In your opinion_. It may surprise you to know, others
| might have differing opinions.
|
| At least from how I read it, I completely agree with the
| post. C makes it hard for programmers to write safe code
| _in general_ and the author was pointing out one example of
| this behaviour and what causes it.
| shadowofneptune wrote:
| You do not consider it a sign of C's problems in the modern
| world that so many of its core functions (atoi, atol,
| atoll, atof, gets, strcat, strcpy, sprintf, etc.) are
| unsafe and yet still are out there in production and
| teaching?
| dottedmag wrote:
| Cumbersomeness of these functions is so trivial compared
| to the problems of C that are real that it's just not
| worth mentioning them.
|
| Even if these functions disappeared overnight and C
| acquired the standard library redone using the knowledge
| accumulated over years, it would not make a difference:
|
| - C is a disaster to write any amount of code in due to
| the definition of undefined behaviour in the
| specification.
|
| Not only compilers are free to do anything they want with
| code that exhibits undefined behaviour, they are unable
| even to detect undefined behaviour in the code, and hence
| the only way to write reliable code is to freeze your
| compiler, toolchain and target OSes: any minor change in
| any of components (including OS headers) may completely
| ruin your program.
|
| Ask Linux folks who were bitten by it many times, even
| though do not have to deal with garbage in vendors' OS
| headers.
|
| - C is a disaster due to its compilation model.
| Preprocessor defines wreak havoc on the source code, and
| it is a backbreaking job to maintain cross-compilability
| of any large-ish codebase. Forget about trying to cross-
| compile the whole software with dependencies: this is a
| full-time job by itself.
|
| - C does not specify sizes of short, int, long and
| signedness of char. This means any codebase that ever
| touches these types (and there are OS interfaces that are
| expressed in them!) is inherently non-portable: every new
| target means combing over the whole codebase and checking
| breakages in all arithmetic operations.
|
| There are other underspecified pieces in spec, but this
| one is just the most salient one.
|
| > in production and teaching
|
| If a teaching resource does not state "C spec has
| undefined behaviour, and you will have a very bad time if
| you don't know about it", then it's utter garbage.
| foxfluff wrote:
| > Cumbersomeness of these functions is so trivial
| compared to the problems of C that are real that it's
| just not worth mentioning them.
|
| I fully agree! C has many problems, and libc sucking is
| but one of them (and rather easily worked around). That
| said..
|
| > Ask Linux folks who were bitten by it many times
|
| A three-decade old project with thousands of developers
| and millions of lines of code (and heavy reliance on
| platform & implementation specifics in certain parts)
| will inevitably at some point along its life get bitten
| by the rough edges of whatever technology they settle on.
| Coverage of the few issues they've hit is wildly blown
| out of proportion, probably precisely because it's so
| rare that people find these things surprising when it
| bites them and thus it makes the news.
|
| If Rust gets well adopted in the kernel (as it might), I
| guarantee that in 30 years, they will have hit its rough
| edges.
|
| Now if you actually ask Linux folks, they will tell you
| to flip on -fwrapv and -fno-delete-null-pointer-checks
| and move on with your life, because whining about these
| old and solved issues is not productive use of anyone's
| time.
|
| > C is a disaster due to its compilation model.
| Preprocessor defines wreak havoc on the source code
|
| Did you ask the Linux folks? They make pretty good use of
| the preprocessor.
|
| > it is a backbreaking job to maintain cross-
| compilability of any large-ish codebase. Forget about
| trying to cross-compile the whole software with
| dependencies: this is a full-time job by itself
|
| Did you ask the Linux folk? It's hilarious that you
| mention Linux folk, given that it is one of the most
| frequently cross-compiled code bases on earth (along with
| much of the Linux userspace). Btw, I cross compile Linux
| and various applications regularly at work. In fact, I
| compile (and maintain) an entire distro with custom
| kernels for different devices & architectures. And that's
| not a full-time job. Most of my work is application
| development, with some driver development now and then.
|
| > - C does not specify sizes of short, int, long and
| signedness of char.
|
| But it does specify their minimum sizes, which is often
| all you need. Signedness of char? It sucks, and it is not
| a big deal. The signed and unsigned keywords exist, btw,
| if you need a specific sign. If that's too much typing
| for you, I can sell you typedef. You could ask the Linux
| folk for advice, they have a few typedefs seem very
| popular now.
|
| > This means any codebase that ever touches these types
| (and there are OS interfaces that are expressed in them!)
| is inherently non-portable: every new target means
| combing over the whole codebase and checking breakages in
| all arithmetic operations.
|
| That's not true at all. Minimum sizes are guaranteed, and
| there are times where using types with implementation
| defined size is exactly the thing you need because you're
| dealing with quantities that are inherently related to
| platform specific ranges. Using these types makes your
| code more portable, not less portable. I recommend you go
| ask the Linux folk, or take a look at their source code,
| which conveniently runs across quite a few platforms.
| seattle444 wrote:
| creativemonkeys wrote:
| One of C's design principles is to be fast at the cost of safety,
| just like an F1 formula car. It will let you make fast mistakes.
|
| You drove a Corolla in college, then got a job and drove a cool
| BMW for several years and now you think you're hot shit, so you
| hope in an F1 car and not only does it take forever to learn how
| to drive it, it has to be driven on a special track and the
| gearbox is different, what a nuisance!
|
| "If only we could add 4 doors, automatic transmission, snow
| tires, and a trunk to put our stuff in, people won't keep getting
| into accidents with this car", you say. Right, but then it
| becomes a BMW. If you want real speed, you need to first go slow
| and master the car because otherwise you'll crash and burn.
|
| C is messy because real world hardware is very messy. You can't
| push bytes through the hardware at its speed limit without
| getting your hands dirty, and we all come out into the real world
| wearing "class Dog extends Animal" white gloves.
|
| To use C effectively, you should not be coding in C in your mind.
| You should be thinking in assembly, but your fingers should be
| typing C code. It's not safe, but if you want to reach 230MPH and
| accelerate at 60MPH in 2.6 seconds, you better know exactly what
| you're doing when you hop behind the wheel of that car. It's not
| for the weak.
| pyjarrett wrote:
| > C is messy because real world hardware is very messy
|
| Ada was designed for embedded systems specifically and has
| guards over many of the pitfalls in C. Still, it provides easy
| access to in-depth low-level control when you need it
| (assembly, intrinsics, binding variables to specific memory
| locations, importing C, creating your own custom allocators).
| The difference is that you write intent, and then paint
| additional control on top of that. This makes Ada also suitable
| for higher level applications.
| umanwizard wrote:
| This would be more justifiable if C had support for vector
| instructions, which are crucial for high-performance code on
| modern CPUs.
| shadowofneptune wrote:
| You're right, we need to draw a distinction between the Real
| Programmers and the Quiche Eaters. A mere Java or Python user
| just isn't good enough, they can't write portable assembly like
| a Real Programmer can.
| creativemonkeys wrote:
| Absolutely not. Once a user reads "Head First Java" or
| customizes Django sites, they get their standard issue
| keyboard and they're ready to start writing interrupt
| handlers in C. If the code crashes, it must be the language.
| comex wrote:
| There is nothing about the design of strtol that makes it
| particularly fast. If anything, the extra checks and accesses
| to errno (which on modern systems is generally an implicit
| function call) that are required to use strtol correctly
| represent unnecessary overhead, though only a trivial amount of
| it. But mostly it's just an awkward API design.
| samatman wrote:
| I upvoted this because hey, no lies detected.
|
| The problem is that this particular design principle of C is
| ready for a comfortable retirement in a beach community.
|
| The machismo is probably why you're getting dragged a bit, but
| the bottom line is that being intimate with the hardware is
| orthogonal to pointlessly segfaulting. C does both, Zig is
| aiming for one of these things and I'll let you guess which.
| creativemonkeys wrote:
| A segfault lets the user know that the developer made a
| mistake, and where in the code it happened. Blaming C for
| segfaults is blaming the tool.
|
| C is a small language with a spec designed to adapt to new
| hardware while remaining fast. The spec is ambiguous in
| precisely the places where resolving the ambiguity would mean
| either limiting its portability or its speed. This increases
| the learning curve significantly and also requires diligence
| on behalf of the developer, so it's high effort to write.
|
| It's a perfectionist's language, because, if you can steer
| clear of the known pitfalls, you get a working piece of
| software that's maximally portable and fast, and fast is
| still what we want our tools to be.
|
| There is a place for Zig, and Nim and Rust in this world, but
| there is no world in which these tools make the same trade-
| offs as C and end up with a faster and more portable (across
| hardware) language.
|
| They can sacrifice speed to make it more difficult for the
| developer to make mistakes. They can sacrifice portability to
| make assumptions that resolve undefined behavior, which would
| also decrease the burden on the developer, but they will
| never get all three - correctness, portability and speed, so
| in that sense, they will never replace C, they can only hope
| to starve C of developers.
| samatman wrote:
| I work with power tools when I have to. A table saw is
| dangerous, and I won't refuse to use one on that basis. I
| wouldn't blame a table saw for cutting someone's thumb off.
|
| One of these days I'll have a big project space though, and
| I'll put a table saw in. That table saw will be one of the
| fancy ones which destroys blades instead of digits, when
| the two come into conflict.
|
| > _There is a place for Zig [...] but there is no world in
| which [this tool makes] the same trade-offs as C and end up
| with a faster and more portable (across hardware)
| language._
|
| This isn't the bar it needs to clear. It needs to be as
| fast and as portable. C can be the fastest possible
| language, and Zig could be exactly as fast (with, LLVM,
| say), and still be a language I would prefer because of
| comptime and some design choices which make it harder for
| me to lose a digit.
| tialaramex wrote:
| > That table saw will be one of the fancy ones which
| destroys blades instead of digits, when the two come into
| conflict.
|
| SawStop. You can expect suddenly a lot of tool
| manufacturers who would have assured you ten years ago
| that this technology is either dangerous or compromises
| the saw's usefulness, will over the next ten years offer
| substantially the same features as the first patents run
| out.
| umanwizard wrote:
| I'm not aware of any case in which unsafe Rust has any
| overheard over C. The advantage of Rust, then, is that you
| can restrict your use of `unsafe` to places where you
| actually care about things like the overhead of bounds
| checking.
| throwaway17_17 wrote:
| I think your breakdown of a language is a neat idea, the
| decomposing of implementations by there 'scores' in the
| three areas of correctness, portability, and speed. I think
| I'd like to replace speed with a performance score
| encompassing both speed and memory footprint though. I also
| agree that achieving high 'scores' in all three areas is a
| relative impossibility.
|
| For me, the best language is going to be the one that has a
| maximum in the performance area and is provably (at least
| to some reasonable measure) correct. I think portability
| between execution environments can be a loss for the types
| of things I enjoy programming.
| V_Terranova_Jr wrote:
| The F1 analogy is easy to use against this line of
| argumentation. Today's F1 cars are way faster than their
| predecessors. They are also safer, more automated, and in large
| part faster because they are easier to drive. The racing is
| more boring, and cars are uglier, but those are different
| topics.
|
| The idea that you can't maintain the runtime performance of C
| while innately supporting automated reasoning about
| invariants/safety just doesn't hold up. The idea is to move the
| whole Pareto front outward - that's what advancements in theory
| and technology do.
| continuational wrote:
| I like that analogy. You're saying that C should only be used
| in competitions, and not be allowed in the real world, right?
| ogogmad wrote:
| I think the brittleness of C's string handling functions is not
| a necessary consequence of anything you said. It's just
| sloppiness and inertia.
| adwn wrote:
| There's nothing fast about zero-terminated strings. In fact,
| many operations on them are much slower than sane alternatives,
| because they first have to scan the entire string to compute
| its length. You can't even create a temporary substring without
| either modifying or copying part of the original string. How
| lame is that? Zero-terminated strings are almost never the best
| solution, so why are they the language-supported default?
|
| > _You should be thinking in assembly, [...]_
|
| Well, then you shouldn't by typing in C, because Undefined
| Behavior coupled with modern C compilers will make sure that
| what you get is not what you thought. *cough* signed integer
| overflow * cough*
|
| > _You can 't push bytes through the hardware at its speed
| limit without getting your hands dirty_
|
| Rust proves you wrong (maybe some other languages, too, but I
| don't know them as well)
| creativemonkeys wrote:
| What you're missing is the difference between known issues
| and unknown issues. You're looking at a language that's been
| heavily used for 60 years and accumulated a long list of
| known issues and things not to do, that powers pretty much
| everything in computers, and you're comparing that with the
| new kid on the block with a vocal fanbase.
|
| You could invest your time into learning that finite list, or
| you could invest your time into learning a new language with
| a long list of _unknown_ issues yet to be discovered - but
| out of sight, out of mind, right?
|
| As far as runtime speed goes, assuming equal instructions
| being generated, if Rust spends even one CPU cycle checking
| array lengths, its generated code will be slower than C's, by
| definition. You can justify the trade-off ("it checks array
| lengths for me because I am human and I forget sometimes") or
| relax the restrictions ("it's not humanly noticeable"), but
| you can't claim it runs faster or even just as fast, because
| it's not.
|
| The only thing Rust proved to me is that there was a whole
| generation of developers who did't mind writing unreadable
| Perl code who had kids that are equally unaware of how
| unreadable Rust code is and it'll take a few decades for them
| to see that, assuming that Rust stays relevant for another
| decade.
| WalterBright wrote:
| The #1 problem with C is buffer overflows. The solution is pretty
| simple:
|
| https://www.digitalmars.com/articles/C-biggest-mistake.html
|
| and does not break existing code.
| rst wrote:
| The CHERI extensions for the ARM architecture allow for
| compilers that achieve this effect by making all pointers
| "fat", with bounds, and doing pervasive hardware bounds checks.
| They've been playing with FPGA versions and emulators for a
| while, but the first actual SoCs just got shipped:
| https://www.theregister.com/2022/01/21/arm_morello_testing/ --
| software for it includes tweaked versions of BSD and I think
| Linux which use bounds-checked pointers throughout, including
| in the kernel.
| WalterBright wrote:
| Making them all fat doesn't fix code that uses strlen().
| BenFrantzDale wrote:
| So built-in std::span?
| https://en.cppreference.com/w/cpp/container/span
| gjvc wrote:
| djb has
|
| https://cr.yp.to/lib/stralloc.html
|
| and
|
| https://cr.yp.to/lib/array.html
| thingsgoup wrote:
| Neat idea. What does implementing new syntax in one of the
| established C compilers involve? Is it the kind of thing that
| could be reasonably tackled in a small patch just to play with?
| WalterBright wrote:
| It wouldn't be hard. The semantics are straightforward and
| don't interfere with the way a C compiler already works.
| bhaak wrote:
| > C is probably the patriarch of the longest list of languages.
| Notable among these are C++, the D programming language, and
| most recently, Go. There are endless discussion threads on how
| to fix C, going back to the 80's.
|
| Why is Java missing in that list?
| laumars wrote:
| Maybe because Java is more of a direct descendant of C++ than
| pure C (though obviously C++ is an iteration of C).
|
| Or maybe they omitted it before there are literally hundreds
| of languages that were inspired from C and listing them all
| would have been boring for the reader.
| futharkshill wrote:
| Why would you think Java is a descendant of C++? There may
| be overlap in _some_ syntax, mainly from C. C++ is not, and
| was not, the only OOP language, and I have heard no such
| that that is should be descendant of C++.
| coldtea wrote:
| > _C++ is not, and was not, the only OOP language_
|
| No, but it was the only one that mattered at the time, as
| far as adoption was concerned, and regarding marketing
| Java as similar to existing programmers and their
| managers...
|
| That's also how it was hyped at the time and the kind of
| people it was sold too (I was -barely- there).
| pletnes wrote:
| I was once at a talk given by James Gosling. He said that
| Java-the-language was <<a trick to get C++ programmers to
| use the Java Virtual Machine>>. He deliberately made Java
| very similar to C++ but removed what he saw as the hard
| and risky parts (memory management, operator overloading,
| etc) that are typically not required for standard
| applications.
|
| Well, that was my interpretation of what he said, errors
| are my own etc. But this would make Java a direct
| descendant of C++, in my mind.
| bhaak wrote:
| I have never heard that quote from James before. Are you
| sure about the JVM? The JVM was quite controversial back
| then, Java first had to prove that you could make a
| performant virtual machine.
|
| But Guy Steele claimed "We were not out to win over the
| Lisp programmers; we were after the C++ programmers. We
| managed to drag a lot of them about halfway to Lisp."
| pletnes wrote:
| Well, it's many years ago and memory corruption is real.
| I got the impression that their goal was to get adoption
| of (what at some point became) the JVM, or the <<compile
| once run anywhere>> vision. They envisioned many
| languages to coexist on the JVM, which kinda happened but
| maybe not as much as they thought. So they designed a
| language to get started, Java, and made it familiar-
| looking to get people om board.
| mftb wrote:
| Because at the time it was pretty clear.
|
| 'At that stage, C and C++ "absolutely owned the
| universe"' - https://www.zdnet.com/article/programming-
| languages-java-fou...
|
| They took a lot of inspiration from C/C++'s syntax and
| seemed to be pretty concerned with improving memory
| management, security and developer velocity.
| futharkshill wrote:
| Another programming language being popular by no means
| mean that it is a derived language of any sort. Any
| development is of course retrospective, but it is sorta
| like saying all music is descendant from pop.
| laumars wrote:
| I understand the point you're trying to make but writing
| music is a creative process whereas marketing programming
| languages isn't.
|
| I was around at the time and C++ was trendy so Sun were
| marketing it as the future for C++ developers. It was
| definitely influenced by what was in vogue at the time
| even if it doesn't adopt all of the traits of C++.
|
| I remember this because I wasn't a fan of C++ back then
| as I'd come from the ALGOL family of languages so found
| C-style syntax a little alien (and tbh I still don't like
| C++ now even though I've since warmed to C's syntax) so
| it took me years before I warmed to Java.
| AnimalMuppet wrote:
| Define "derived".
|
| In particular, if Java kept (almost?) all the keywords,
| and the operators, and the statement terminators, and the
| block delimiters, and the same approach to object-
| oriented... how is it _not_ derived from C++?
| samatman wrote:
| I was around at the time.
|
| Java's object semantics are explicitly intended as a
| streamlining of C++, the keywords are the same for the
| most part, and it was sold as a C++ which runs anywhere
| with no memory leaks.
|
| Note that I mentioned the semantics: the object
| _semantics_ of Java and C++ are so similar as to have
| corrupted the entire concept of objects in their favor.
|
| This wasn't an accident, and it wasn't malice, it just
| feels like it sometimes.
| gpderetta wrote:
| I'm not a Java programmer but as far as I can tell java
| object semantics, far from being corrupted, do indeed
| come from simula via c++.
|
| Thanks to reflection and a featureful VM, Java does have
| a significant amount of dynamic behaviour that can be
| used to implement a lot of features of the smalltalk side
| of the OO family tree.
| pjmlp wrote:
| Java has a C++ syntax to sell it to the C++ devs at the
| time, however its major influence was Objective-C.
|
| https://cs.gmu.edu/~sean/stuff/java-objc.html
| protomyth wrote:
| Yet it didn't have any of the dynamic nature of
| Objective-C which made the attempt to replace Objective-C
| in OS X with Java a failure.
| pjmlp wrote:
| What?
|
| There was no attempt to replace Objective-C with Java on
| OS X.
|
| Apple was unsure if the strange look from Objective-C
| would ever appeal to the Object Pascal/C++ communities of
| Apple developers, thus they used Java wave as plan B, in
| case Objective-C was rejected by them.
|
| As this did not happen, there was no reason to keep plan
| B around.
| protomyth wrote:
| _There was no attempt to replace Objective-C with Java on
| OS X._
|
| I would say there was a heck of an attempt with the Java-
| Cocoa bridge that didn't do well because Java didn't have
| a lot of the dynamic nature of Objective-C. They
| certainly to my eyes as a developer tried to push Java.
| pjmlp wrote:
| As you wish, Java-Cocoa bridge could have never replaced
| Objective-C, when writing Objective-C was still part of
| the game to actually use it.
|
| Do you actually believe that Jobs liked Java, when Apple
| was created on top of Object Pascal and C++, and then he
| was responsible for bringing Brad Cox to NeXT?
| protomyth wrote:
| Yeah, given the crap they were sending in the monthly
| discs to developers at the time. They certainly seemed
| like we should of took Java seriously. I was rather
| annoyed given I had learned Objective-C on NeXTSTEP. I'm
| glad that someone realized it was not substitute, but
| they did push it. Jobs hated it later, but he changed his
| mind on things fairly often given just looking at iPod
| features.
| pjmlp wrote:
| How could Java be a substitute when Cocoa bridge only
| allowed for a subset of OS X frameworks to be called from
| it?
|
| Java was already available on System 7.
| coldtea wrote:
| Or so somebody said in an interview or thought they did,
| but otherwise Java is nothing like Objective C with
| respect to messages (and of course, wrt to syntax, but
| that's not that important).
| SeanLuke wrote:
| That's my posting that was linked to.
|
| Of course, Java doesn't have dispatch. And it's also true
| that Gosling's team originally considered, then rejected,
| C++ in favor of building Oak. But Oak borrowed an awful
| lot directly from Obj-C, and only later underwent a lot
| of syntactical surgery (turning into Java) in order to
| "look" like C++ specifically to attract C++ programmers,
| even though it didn't feel like C++ at _all_. This is
| pretty well documented.
| pjmlp wrote:
| If you want to do dynamic dispatch, there are ways to
| achieve it via reflection and dynamic proxies.
|
| Interfaces, dynamic code loading, JAr bundles,
| lightweight class type reflection, all trace back to
| Objective-C, or Smalltalk, if one wants to be pendantic.
|
| In case you missed it, even JEE started as an Objective-C
| framework for the Spring distributed OS, Distributed
| Objects Everywhere.
|
| https://en.m.wikipedia.org/wiki/Distributed_Objects_Every
| whe...
| thingsgoup wrote:
| I suspect it's because manual memory management in Java isn't
| built into the language. Is it even possible? I'm not a Java
| programmer and I don't know. My understanding has been that
| the runtime doesn't expose the memory model to you.
| kaba0 wrote:
| In a way it does. Java just likes to push most features to
| methods on special objects, instead of exposing them as
| native functionality (to avoid backwards incompatible
| changes).
|
| So it would look something like
| MemorySegment.allocateNatice(100, someScope). This new API
| has a runtime ownership model, so by default only a single
| thread can access this memory address, and it can be freed
| at will.
| andai wrote:
| I'm fairly new to C, am I understanding correctly that the new
| syntax is just sugar for a struct containing two values: a
| pointer to the start of the array, and its length? This can of
| course be done without the new syntax, and it seems exceedingly
| useful. Are such structs commonly used in C projects?
|
| What is the actual source of bugs in this regard? How does
| passing the length as a separate parameter lead to more bugs
| than having it bundled -- is the main source of error passing
| the wrong variable?
| WalterBright wrote:
| > How does passing the length as a separate parameter lead to
| more bugs than having it bundled
|
| It boils down to being inconvenient, unreliable, error prone,
| and difficult to audit. That's why it isn't used and C's #1
| problem remains buffer overflows. And so it goes for all the
| other solutions for C for this problem, except my proposal.
|
| My proposal is how D works, and it's been convenient,
| reliable, robust and auditable for 20 years. You can still
| use raw pointers in D, they are fully supported, but the use
| of the arrays make use of raw pointers rare.
| thingsgoup wrote:
| If the length is bundled with the pointer as syntax then the
| compiler and maybe even the runtime can provide checking on
| behalf of the programmer. Passing it by hand means it's the
| programmers responsibility to (remember to) validate
| coldtea wrote:
| > _This can of course be done without the new syntax, and it
| seems exceedingly useful. Are such structs commonly used in C
| projects?_
|
| There are string libraries that work that way in C.
|
| If you do it manually, without a lib, then you need to check
| that the length is valid yourself (after every operation), so
| it's not as useful as a language with first class support for
| it.
| naasking wrote:
| Presumably you can add an inlined array access function or
| macro in the header to do this for you.
| WalterBright wrote:
| If that worked, people would have done it, and C buffer
| overflows would be a thing of the past.
| [deleted]
| marginalia_nu wrote:
| > The #1 problem with C is buffer overflows.
|
| There is also the null hypothesis that buffer overflows are
| simply a category of bugs that is simply easy to identify, and
| thus, apparently prevalent.
| jodrellblank wrote:
| Would it be reasonable to assert that bugs which have such
| rare and uninteresting consequences that nobody has either
| noticed (not widely prevalent) or identified them (worth
| investigating because the consequences were severe), could be
| the number one problem?
| marginalia_nu wrote:
| The cold is one of the most common human infections, does
| that make it the most severe health problems facing
| humanity as well?
| ciphol wrote:
| I've never heard of anyone dying of the cold. I've heard
| of many major security breaches caused by buffer
| overflows.
| [deleted]
| prirun wrote:
| For strings, you need more than the allocated length: you need
| the _current_ length too. Otherwise you end up with:
|
| - O(n) algorithms that are constantly scanning strings looking
| for a zero byte
|
| - strings can't contain zero bytes
|
| - strings _have_ to contain one zero byte
|
| - putting a zero byte in the middle of a string chops it off
|
| - probably other nonsense I haven't thought of
|
| Please don't adopt another half-assed solution just because it
| fits more easily into C's existing set of crap. That's how we
| got fake strings in the first place.
| WalterBright wrote:
| This solution has been in D for 20 years. It works very well.
| It is fully assed.
|
| It is very, very rare to see a buffer overflow in D because
| the use of these arrays is so easy, convenient, and robust.
|
| Not only does it virtually eliminate buffer overflows (when
| used), it is _more_ efficient than 0 terminated strings. It
| does not need to scan the strings, nor does it need to load
| the string into memory to determine its length.
|
| I understand your concerns about mixing it up with 0
| terminated strings. They are real, but have not been a
| particular problem in practice. What happens is one simply
| moves away from using 0 terminated strings. A zero terminated
| string can be converted to a length one with:
| a = s[0 .. strlen(s)];
|
| Going the other way requires a memory allocation similar to
| what strdup() does.
| prirun wrote:
| I read your article before posting, and it says:
|
| -----
|
| void foo(char a[..])
|
| meaning an array is passed as a so-called "fat pointer",
| i.e. a pair consisting of a pointer to the start of the
| array, and a size_t of the array dimension.
|
| -----
|
| I didn't see a "current length" mentioned. Is it there? Can
| I have a string with an allocated length of 20 bytes and a
| current length of 10 bytes, without using a looking for a
| zero byte?
| WalterBright wrote:
| The capacity value is not part of it.
|
| This proposal is not about memory management any more
| than 0 termination is about memory management. It is just
| about finding the end of the array.
| p0cc wrote:
| I might be a persnickety nitpicker, but `wrritten` in the first
| sentence makes me not want to read the rest.
| nitrix wrote:
| You have complete freedom.
| junon wrote:
| Such comments are against the ethos of HN and its guidelines,
| which you can find at the bottom of this page.
| dcposch wrote:
| Author mentions four increasingly obscure C replacements (first
| I've heard of Odin) without mentioning that the creators of the
| original C and Unix went on to make Go.
|
| Go does not have manual memory management. Despite (actually
| because of) that captures the spirit and design goal of the
| original C beautifully. It's a minimalist systems programming
| language.
|
| One of the amazing things about Go is the standard library-- the
| thing he complains about with C. The Go standard library is
| incredibly readable. It's night and day from C/C++ where opening
| glibc/STL etc is assault on the senses.
| dkbrk wrote:
| https://fasterthanli.me/articles/i-want-off-mr-golangs-wild-...
| kazinator wrote:
| This isn't the usual way this is coded: char *one
| = "one"; char *end; errno = 0; // remember errno?
| long i = strtol(one, &end, 10); if (errno != 0) {
| perror("Error parsing integer from string: "); } else if (i
| == 0 && end == one) { fprintf(stderr, "Error: invalid
| input: %s\n", one); } else if (i == 0 && *end != '\0') {
| f__kMeGently(with_a_chainsaw); }
|
| It's actually like this: errno = 0;
| long i = strtol(input, &end, 10); if (end == input) {
| // no digits were found } else if (*end != 0 &&
| no_ignore_trailing_junk) { // unwanted trailing junk
| } else if ((i == LONG_MIN || i == LONG_MAX)) && errno != 0) {
| // overflow case } else { // good! }
|
| errno only needs to be checked in the LONG_MIN or LONG_MAX case.
| These cares are ambiguous: LONG_MIN and LONG_MAX are valid values
| of type long, and they are used for reporting an underflow or
| overflow. Therefore errno is reset to zero first. Otherwise what
| if errno contains a nonzero value, and LONG_MAX happens to be a
| valid, non-overflowing value out of the function?
|
| Anyway, you cannot get away from handling these cases no matter
| how you implement integer scanning; they are inherent to the
| problem.
|
| It's not strtol's fault that the string could be empty, or that
| it could have a valid number followed by junk.
|
| Overflows stem from the use of a fixed-width integer. But even if
| you use bignums, and parse them from a stream (e.g. network), you
| may need to set a cutoff: what if a malicious user feeds you an
| endless stream of digits?
|
| The bit with errno is a bit silly; given that the function's has
| enough parameters that it could have been dispensed with. We
| could write a function which is invoked exactly like strtoul, but
| which, in the overflow case, sets the *end pointer to NULL:
| // no assignment to errno before strtol int i =
| my_strtoul(input, &end, 10); if (end == 0) {
| // underflow or overflow, indicated by LONG_MIN or LONG_MAX value
| } else if (end == input) { // no digits were found
| } else if (*end != 0 && no_ignore_trailing_junk) { //
| unwanted trailing junk, but i is good } else { //
| no trailing junk, value in i }
|
| errno is a pig; under multiple threads, it has to access a thread
| local value. E.g #define errno
| (*__thread_specific_errno_location())
|
| The designer of strtoul didn't do this likely because of the
| overriding requirement that the _end_ pointer is advanced past
| whatever the function was able to recognize as a number, no
| matter what. This is lets the programmer write a tokenizer which
| can diagnose the overflow error, and then keep going with the
| next token.
| xigoi wrote:
| Sure, you can't get away from handling the cases, but as the
| article clearly demonstrates, there can be a much better
| interface for it.
| kazinator wrote:
| I'm not convinced. Can the Rust function which is shown (that
| function alone) tokenize a number out of a string such that
| the number is overflowing the target type? Yet indicate to
| the caller where that overflowing number ends, so that
| tokenization can continue with subsequent characters, if any?
|
| E.g. suppose we have a string with this kind of syntax:
| "12345 : 12345 , 12345"
|
| We can
|
| 1. use strtol to get the first integer and a pointer to just
| after it.
|
| 2. use ptr += strspn(ptr, " ") to skip spaces
|
| 3. check for the colon and if we find it, skip with ptr++
|
| 4. use strtol to get the second integer (possibly preceded by
| space).
|
| 5. similarly to the colon handling, do the comma
|
| 6. strtol again to get the integer.
|
| This is efficient: no splitting of the string into pieces
| requiring memory allocation, and extra list processing.
|
| We can code this robustly: it can recognize valid syntax even
| if some of the numbers overflow. So for this kind of input:
| "1234523442345345345234534545454545 : 12345 12345"
|
| the code could diagnose the overflow, _and_ the missing comma
| in one pass.
|
| If you don't care about the details, just "is this number in
| range, with no trailing junk, or else is it bad", then raw
| strtol isn't convenient. But takes only a little code to wrap
| it.
| xigoi wrote:
| How often do you actually need this?
| tempodox wrote:
| Whenever you do lexical analysis on syntax containing
| numbers.
|
| On today's hand-held supercomputers avoiding allocations
| and, generally, exercising memory-efficiency may not be a
| primary concern, but it very much was at the time when
| this stuff was built. And it's still relevant today on
| restricted systems, like microcontrollers, where C is
| still the primary language.
| lifthrasiir wrote:
| > Whenever you do lexical analysis on syntax containing
| numbers.
|
| If that was an intention C should have a full set of
| lexical analysis functions, but it doesn't (scanf doesn't
| count). strtol being able to distinguish two error cases
| and thus being marginally useful for lexical analysis is
| most likely accidental.
| xigoi wrote:
| In my experience, reading a single number is a much much
| much more common operation than doing lexical analysis.
| kaba0 wrote:
| Rust can return string slices, so a tuple of a return type
| with a potential number and a slice for the still
| unprocessed string would be an option, which is much safer
| than your proposed one and arguably more readable.
| iovrthoughtthis wrote:
| but this is not a appropriate place for that functionality.
| kazinator wrote:
| It is _entirely_ appropriate for a function which
| lexically analyzes a buffer in memory in order to match
| an integer to be able to tell you where that integer
| ends.
| foxfluff wrote:
| > Sure, you can't get away from handling the cases, but as
| the article clearly demonstrates, there can be a much better
| interface for it.
|
| It's a very apples to oranges comparison, to the point that
| it almost feels like a straw man. "Interface (that does X)
| sucks for doing Y; look at how easy the Rust interface for
| doing Y is!"
|
| Yes, there can be a much simpler interface for the case when
| you want to assert that a string is nothing but digits and
| must fully convert. That's not what strtol is for though.
|
| Now I think libc sucks (no surprise given its age;
| complaining about is beating a dead horse), and it sucks more
| if you don't take various GNU & BSD extensions with it, but
| I'm kinda getting tired of people complaining that "foo in C
| is hard" when their unstated requirement is that they can't
| use any libraries to help them do it. Like this fellow the
| other day: https://news.ycombinator.com/item?id=29990897
|
| If you look at programs written in "modern" languages, they
| almost invariably bring a plethora of libraries and
| dependencies with them anyway so why is C repeatedly judged
| on the merits of ancient libc interfaces that you _don 't
| have to use_?
| xigoi wrote:
| IMO, external libraries are for domain-specific tasks. If
| something is needed in pretty much every program, it should
| be a part of the language or the standard library.
|
| Also, it's _much_ easier to use external libraries in other
| languages. npm install, cargo install, nimble install,
| cabal install, gem install, ...
| foxfluff wrote:
| > If something is needed in pretty much every program, it
| should be a part of the language or the standard library.
|
| It sure would be convenient that way. That said, you
| don't need to convert strings in pretty much every
| program. There's a lot of C code out there that does very
| little with strings.
|
| Now do you dismiss an entire language if its standard
| library is lacking or doesn't exist? IMO that would be
| throwing out baby with the bathwater.
|
| > npm install, cargo install, nimble install, cabal
| install, gem install, ...
|
| Yes, I've witnessed the mountain of unaudited
| dependencies that somehow turn a 300 line program into
| something the size of my kernel.. should I dismiss all
| those languages because people do something I don't like
| with their libraries?
| xigoi wrote:
| I wonder how your programs do I/O if not with strings.
| Reading numbers from STDIN is the next thing after Hello
| World.
|
| As another comment pointed out, C has many flaws
| unrelated to its standard library. Also check out
| https://eev.ee/blog/2016/12/01/lets-stop-copying-c/.
|
| You know what's the main cause of dependency hell?
| Needing a library for every basic thing. Notice that
| mountains of dependencies are much less common in
| "batteries-included" languages.
| foxfluff wrote:
| > I wonder how your programs do I/O if not with strings.
|
| There's this one weird trick we call binary. Let me give
| you an example of how I did I/O yesterday:
| static void usb_tx(struct usb_ep *ep, const void *data,
| uint len) { if (len) memcpy(ep->buf, data,
| len); *ep->bufctl = BC_FULL | ep->datax <<
| BC_DATAX_S | BC_AVAIL | len; ep->datax ^= 1;
| }
|
| Usage example: struct kb_report r =
| {.m={.id=KB_ID_M, .x=-a[1], .y=-a[0]}};
| usb_tx(KB_IN, &r.m, sizeof r.m);
|
| stdin does not exist in this program.
|
| > As another comment pointed out, C has many flaws
| unrelated to its standard library.
|
| Yes it does, but this thread has already become a tangent
| of a tangent. Let's not turn it into a general diatribe
| against C, as opposed to a discussion about the library
| interface that TFA takes issue with.
|
| > You know what's the main cause of dependency hell?
| Needing a library for every basic thing. Notice that
| mountains of dependencies are much less common in
| "batteries-included" languages.
|
| In theory, yes. Like I said, libc sucks, and I would love
| to have a better standard (or de-facto standard) library.
| But anecdotally C programs are not very prone dependency
| bloat, perhaps precisely thanks to the fact that C
| doesn't have a de-facto package manager that allows you
| to just install a bunch of crap.
|
| Anecdotally, "batteries included" languages are still
| prone to dependency bloat if there's a package manager.
| This includes recent experience with Python (I can't
| remember the last time I had to lay my hands on a python
| project that didn't need a bunch of things to be
| installed with pip) and somewhat less recently with Perl
| (isn't cpan pretty much the grandfather of "oh there's a
| library for that"?).
|
| Hilariously, my recent experience has people using Python
| and depending on Python libraries which then depend on C
| and C++ libraries in order to implement the same things
| that I'm doing in plain C with no dependencies.
|
| But I'll conclude my participation in this subthread with
| this message because it's gone too far off the rails into
| a pointless language flame war.
| cb321 wrote:
| > isn't cpan pretty much the grandfather of "oh there's a
| library for that"?
|
| The TeX CTAN in 1992 [1] was clearly the inspiration for
| CPAN a year or three later [2] (in both name & thing).
| So, maybe CTAN is the great grandfather? :-) { My intent
| is only to inform, not be disputatious. I know you said
| "pretty much". }
|
| To be fair, C has an ecosystem. OS package
| managers/installers are a thing. There is surely a list
| of much >1 "core libs/programs" (terminfo/curses/some
| text editor/compilers/etc.) that would be in most "bare
| bones" OS installs upon which you could develop. One
| certainly depends upon OS kernels and device drivers.
| IMO, at least one mistake "language" package managers
| make is poor integration with OS package managers. Anyway
| you cut it, it is hard to write a program without
| depending upon a lot of code. Yes, some of that is more
| audited.
|
| As the "lump" gets giant, dark corners also proliferate.
| There was a recent article [3] and HN discussion [4]
| about trying to have the "optimal chunkiness/granularity"
| in various ecosystems. I agree that it is doubtful we
| will solve any of that in an HN sub-to-the-Nth thread. I
| think that article/discussion only scratched the surface.
|
| I will close by saying I think it's relatively
| uncontentious (but maybe not unanimous) that packaging
| has gone awry when a simple program requires a transitive
| closure of many hundreds of packages. FWIW, I also often
| write my own stuff rather than relying on 3rd parties and
| have done so in many languages. Nim [5] is a nice one for
| it. It's not perfect - what is? - but it sucks the least
| in my experience.
|
| [1] https://en.wikipedia.org/wiki/CTAN
|
| [2] https://en.wikipedia.org/wiki/CPAN
|
| [3] https://raku-
| advent.blog/2021/12/06/unix_philosophy_without_...
|
| [4] https://news.ycombinator.com/item?id=29520182
|
| [5] https://nim-lang.org/
| thfuran wrote:
| >Now do you dismiss an entire language if its standard
| library is lacking or doesn't exist?
|
| As anything much more than a toy, yes. If there's no
| standard library at all (or nearly so), the language
| ecosystem is quite likely to end up a complete mess of
| incompatible implementations of even the most basic
| functionality, which is a waste of everyone's time to
| deal with.
| lanstin wrote:
| I think my point remains valid, to do safe string stuff in
| C I have to think a lot harder about stuff to do with
| lengths that I don't have to think about in go. And I
| didn't want large dependencies because i was writing a .so
| to preload and intercept execve and open. And even after
| all these threads I don't know the name of a small string
| library to use in C, except TCL because i used it before.
| foxfluff wrote:
| Would you be open to sharing what you did with strings?
|
| My central argument in the response there is that writing
| buf[len] = '\0'; is almost always a sign that you either
| don't know libc functions, aren't willing to use them,
| are trying to outperform them (the performance of libc
| functions is a legitimate complaint for some use cases),
| or what you're dealing with is not a string but some
| arbitrary binary blobs that you're trying to make strings
| out of (in that case, you can't blame the string
| representation or string handling functions for not
| knowing what the extent of your binary is; yes, you'll
| have to first create a string, knowing the length).
|
| To put it more explicitly, if you always provide a valid
| buffer and size, snprintf() will always terminate your
| string. strlcat() and strlcpy() will always terminate
| your string. If you need formatted catenation, you can
| make a trivial wrapper around snprintf that takes a
| pointer to the end of your string and updates the "head";
| this can be called successively without ever having to
| compute a length outside the wrapper. asprintf() will
| allocate and terminate your string. Things that need the
| length of your string (strspn, strchr, etcetra) will
| figure it out since it is implied by the already-present
| nul byte. strtok & co (they have their issues) also work
| without requiring you to do any manual termination.
|
| What this means in practice is that you can have
| thousands of lines of string handling code that _never_
| manually terminates a string and only deals with lengths
| to the extent that your "business logic" needs to.
| Unless you're actually trying to use the string
| representation to your benefit by manually splicing it
| any which way, inserting nul bytes based on arcane
| computations.. in that case, it sounds like you got what
| you wanted. Yes, people actually do that sometimes: they
| figure out how easy it is to manipulate the string
| representation by hand and thus avoid library functions,
| and then they complain about doing it by hand.
|
| There are always exceptions of course, so I'm giving you
| benefit of the doubt. That's why I'm curious to see what
| you were doing. Having to point out library functions
| however is a regular thing as people seem to always start
| out by hand-rolling it for some reason.
|
| As for the question about string libraries.. well, I
| gotta point out that "small" wasn't a qualifier in the
| previous discussion. Popular libraries include sds,
| bstring, glib strings. Plan9port also has the extensible
| string library. There's icu for fancy unicode stuff but I
| have no experience with it and it probably isn't "small."
| There are plenty more if you look around, and I'll let
| you judge the size of the choices for yourself. I'm
| pretty sure one of these choices is always mentioned in
| these HN threads when someone asks for recommendations,
| including sds in the bchs thread.
| LegionMammal978 wrote:
| Regarding buf[len] = '\0', I've personally had to use it
| in many scenarios following strncpy, which doesn't add a
| null terminator if the maximum length is reached. Do you
| know of any simpler way of getting a prefix up to a
| certain length?
| foxfluff wrote:
| snprintf. If you want to stick to the (safest) pattern of
| only passing the buffer size for the second parameter,
| you'd do this: snprintf(buf, sizeof
| buf, "%.*s", prefix_length, source_str);
|
| Example: $ cat x.c #include
| <stdio.h> int main(void) { char
| buf[128], tinybuf[5]; const char *copythis =
| "hello there\n"; snprintf(buf, sizeof buf,
| "%.*s", 5, copythis); snprintf(tinybuf, sizeof
| tinybuf, "%.*s", 5, copythis); printf("buf:
| %s\n", buf); printf("tinybuf: %s\n", tinybuf);
| } $ cc -W -Wall -O3 x.c x.c: In
| function 'main': x.c:6:41: warning: 'snprintf'
| output truncated before the last format character
| [-Wformat-truncation=] 6 | snprintf(tinybuf,
| sizeof tinybuf, "%.*s", 5, copythis); |
| ^ x.c:6:2: note: 'snprintf' output 6 bytes into a
| destination of size 5 6 | snprintf(tinybuf,
| sizeof tinybuf, "%.*s", 5, copythis); |
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
| $ ./a.out buf: hello tinybuf: hell
| LegionMammal978 wrote:
| Thanks! I've never considered using snprintf in that way
| before; the default warnings are annoying, even though
| their intent is understandable.
| dfawcus wrote:
| Often one can ignore the errno case, as the input is (by
| then) semi-constrained, and its ambiguity resolution is not
| needed.
|
| e.g. so an idomatic pattern from some real code would be
| something like: static bool parse_thing
| (char *value, struct thing *th) { char *endp
| = NULL; unsigned long firstport = strtoul(value,
| &endp, 10); if (endp == value || *endp || firstport
| > 0xffff) { /* Do something with error, like
| log it */ return false; }
| th->firstport = firstport; return true; }
|
| But granted, for the general case I'd prefer to use some
| helper like either of: bool
| parse_decimal_uint32(struct string const *str, uint32_t
| *outval); bool parse_decimal_uint32(char const *str,
| unsigned slen, uint32_t *outval);
| [deleted]
| [deleted]
| EVa5I7bHFq9mnYK wrote:
| That case has existed for 40 years now, yet C still stands. Guess
| its the power of network effect.
| ErikCorry wrote:
| In practice it's _worse_ than that because you probably don't
| want a "long", you probably want a particular size like a 64 bit
| integer. So you have to add ifdefs to call either strtol or
| strtoll depending on the size of "long" and "long long".
|
| And if you are using base 16 then strtol will allow an optional
| "0x" prefix. So if you didn't want that you have to check for it
| manually.
|
| Strtol also accepts leading whitespace so if you didn't want that
| you have to test manually for it.
|
| Don't pass a zero base thinking it means base ten. This works
| almost all the time but misinterprets a leading zero to mean
| octal.
|
| Good luck!
| thesuperbigfrog wrote:
| >> you probably don't want a "long", you probably want a
| particular size like a 64 bit integer. So you have to add
| ifdefs to call either strtol or strtoll depending on the size
| of "long" and "long long".
|
| stdint.h (https://en.cppreference.com/w/c/types/integer)
| provides fixed-width integers in specific sizes. It became a
| standard in C99.
| ErikCorry wrote:
| Doesn't provide strto32 and strto64 though so you still need
| an ifdef.
| foxfluff wrote:
| Newsflash, the C language has conversions. You can assign a
| long or long long to your int32_t.
| ihattendorf wrote:
| Great, and what happens when you accidentally assign
| something greater than 2^31 to an int32_t when using
| strtol? You won't benefit from a range error if LONG_MAX
| is 2^63, and now you have to make sure to handle any
| implementation defined behavior.
| foxfluff wrote:
| That's true. If the range you're interested in is not the
| same as the range of long or long long, you'll have to
| check the range yourself before you go on and use the
| value. No ifdefs required. If you're not happy doing it
| yourself, I do recommend strtonum or any of the
| alternatives that allow you to explicitly specify the
| range you're interested in. I don't see the point in
| littering the standard library with functions having
| hard-coded range for every range you might be interested
| in.
| marcosdumay wrote:
| And get undefined behavior where the compiler can do
| anything it wants...
| cwzwarich wrote:
| Unlike other cases of signed overflow, you actually don't
| get UB on integer-to-integer conversions. You still have
| a bug in your code, though.
| remorses wrote:
| This is crazy
| ngcc_hk wrote:
| Welcome to the real world. At least it is understandable
| crazy. After all it is in a library call not part of the
| mental model of c.
|
| To compare, I can't say about the mental baggage you carry
| with javascript the core language. Still, it sort is of
| works. World moved on. Good luck.
| draw_down wrote:
| gumby wrote:
| > It exists because it became part of the POSIX standard way back
| when a pdp7 was an advanced computer...
|
| The PDP-7 was long obsolete by the time the POSIX effort started.
| By then the most common Unix host was a VAX (32 bits), though it,
| or Unix-alikes, ran on a variety of 16 and 32 bit machines, hence
| a desire for standardization.
___________________________________________________________________
(page generated 2022-01-22 23:02 UTC)