By relying on github for the latter while redirecting to another mirror for the former allows you to utilise github's reputational system to promote both your project AND the mirroring alternative.
https://news.ycombinator.com/item?id=40307098
> Inspired by datatype99, but consisting of one small standard-conforming C99 macro-only header that is fast to compile.
In the context of C99, isn't tagged union the more usual name instead of sum type?
Take one of the most basic sum types in Haskell's base library, Maybe:
data Maybe a = Nothing | Just a
This simple construction gives you the ability to write functions over optional values and avoid the issues with null pointers. This would be an amazing feature to have in C but it can't be achieved due to C's very limited type system.Sure it has trade offs like being empty by exception, but that's necessary if you don't want it to allocate.
Also, pattern matching would be nice instead of std::get with visitor, but that's more of a language issue than an std::variant issue
Wait, what does one have to do with the other?
It's probably a more relevant tool for a larger class of problems than many people imagine.
Anyone thinking of trying this I would advise against going to C directly. The footguns are dangerous enough as it is and are arguably even more dangerous if you're coming from a more modern language that if you came at C directly. You don't need to go this far back in time to have these things.
But I do strongly agree that anyone who has done nothing by dynamic scripting language development should pick up a modern static language and learn how to use it. I consider having at least one of each a basic tool in the well-rounded developer's toolkit. And if you're still running on 200x-era arguments about the advantages and disadvantages of static versus dynamic languages, as many people are, you're going to be pleasantly surprised by the modern static languages.
But you won't be pleasantly surprised by C. Most of the arguments developed for static vs. dynamic in that era were really about dynamic versus C. They all still apply; C qua C has not moved much since then.
(Maybe you could justify C as "closer to the metal" but in the modern era personally I'd recommend just going straight to assembly for orientation and a tour. You don't have to dive super deep into it to get the sense.)
I think some devs are fed up of Complexity and excessive abstractions.
C has very few language features, it's a very simple language -- while all modern languages have new (complex and taxing) features.
It's kinda like a Minimalist movement, can we do the same Modern Mumbo Jumbo but in a simple, minimalistic C way?
GC and RAII? Nah, just use Arenas/Pools. Or don't use heap at all.
C is an unsafe language but modern tools get better and better every year, with GCC doing really cool static analysis and finding buffer overflows at compile time.
Also, C runs anywhere and everywhere.
This will solve more than 90% of problems. I'll also add that replace C pointers with a "Fat Pointer" or Go-like slice struct and avoid the C stdlib. Then you'll have fixed 99% of issues.
Did C ever go away? The computing industry is built on C. It is the foundation underpinning everything.
> Are their good reasons to be using C in product development rather than more modern languages?
Even if you use a "modern" programming language you'll be building on C at some level. Linux, Postgres, Redis, CPython, etc. are all written in C. Most programming languages have a C FFI for a reason. It is worthwhile to familiarize yourself with C if only to have an understanding of what underpins everything. Besides, you never know, you might need to write a native Python module or Postgres plugin one day.
I'd be curious to see how new developers do with Rust out of the gate moving forward. It basically makes you generate a machine checkable proof that you're using advanced memory management techniques correctly, and those techniques were invented to make zero cost abstractions work in C++.
Anyway, for me, it's Rust for all new projects these days, but I still like the minimalism of C. Big companies say it takes about 6 months for strong engineers unfamiliar with all this stuff to reach productivity with Rust. I'd guess those same engineers could get to the point of writing sort-of-correct C in a week or so.
I would not reach to c libraries on many other platforms but recently I started using more c, just not from c..
Thats my observation at least. I also think C is just so frictionless, that even if it is dangerous people will use it anyway. Yes there are a thousand footguns like people have said, but in the end you can get something compiling and running pretty easily, which is enough I guess.
Things that you hope will be used in widely different contexts need to be created using tools that can easily be "glued" into those widely different contexts.
Right now, C remains the best glue language we have. If you want to write things that can be used from dozens of other languages (or even just 2 other languages), you are likely to write it in C.
In 5-10 years time? We'll see (no pun intended)
I mean, there's a reason why Karpathy is hacking on "llm.c" as part of his "clean up the world" project and not "llm.rs".
Is that a good thing or bad thing? I won't engage. But I think that's where the psychology is. For myself, I genuinely love C in a way that it's clear I never will Rust.
You post this as if everyone is supposed to know who Karpathy is or what any of that means...
Firstly, this is HN! If every commenter mentioning Karpathy has to prefix their comment with an intro about Karpathy and LLM, then HN will become a boring read.
Secondly, if you don't know who Karpathy is and what any of this means, it is not all that difficult to search for "Karpathy" and "LLM" in your favorite search engine.
It doesn't mean that it is simple to use correctly. An expert might be able to accurately judge the torque achieved by hand, but a beginner can easily under- or over-torque things. Etc...
Still have null pointers though. So an optional type would be useful too. Billion dollar mistake and all. Oh, and you have to check errno, might want a better way to handle errors so that you have to check them, or at least acknowledge that they exist. Probably need a linter to make those stick, you really do want violating a nullability constraint to fail at compile time.
And yeah, you have to avoid libc now, but it has so much useful stuff! So we'd want a library that offers all that useful stuff, but uses our fancy custom allocators, and optionals, and error checks, and slices.
While we're dreaming, wouldn't namespaces be nice? Like you have a function do_bar on a struct Foo type, so there's this foo_do_bar function (almost like a method), it'd be nice to be able to just say foo.do_bar, y'know?
Of course, at that point, you've almost got a whole different language! But it can compile and link with C, so yeah, best of both worlds.
What if I told you...
valueless_by_exception happens when the move assignment operator throws (which should never happen in good C++, but is not disallowed by the language). That means, the variant used to hold value X, we were moving value Y into it, but the move assignment operator failed. What is now the state of the variant? It's not X, since we started running the move constructor, which could have started destroying the old value before throwing. But it's not Y either, because the constructor didn't finish. So what's the value that's being held? The answer is that there is no valid value.
How could this be fixed using allocation? Well, if you assume that std::variant allocated, it could be implemented in such a way that the std::variant just held a pointer to a heap area which stored the actual contents of the value. When you move assign, you construct a new element on the heap and move into that.
If the move assignment completes successfully, you swap the old value pointer for the new one and destroy the old value. But if move assignment/construction fails, you just retain the old pointer: nothing has been destroyed, and the variant still holds a valid value. This is very similar to the "strong exception guarantee" for std::vector, which is violated if the move assignment operator for the value throws.
This is one of many reasons why the move constructor/assignment operator should always be noexcept: there's no reason why it should ever throw, and it violates a bunch of these kinds of guarantees.
I think, anyway. I would be happy to be corrected if I got this wrong.