I'd consider myself the former: I use C because it's the only viable option for what I'm doing - but most of the time it's nothing to do with C itself, but the various extensions and builtins of GCC. It's the inline asm, the control of registers, placement of code and data, control of inlining, etc, which are missing from all of the C "replacements". The replacements assume you are building a user application on top of an existing unix-like OS or VM, but they're missing the parts necessary to write the OS or VM itself. They don't provide simple ways to use hardware-specific features.
Obviously there is C++ which can leverage most of this too, but C++ traps you into an ABI which is difficult to use from any language which is not C++.
I also consider myself the former. I often use C not so much to stay close to the hardware, but to be closer to the operating system APIs. Of course, there are often API bindings for other languages, but they're often very incomplete, buggy, non-idiomatic, non-ergonomic, or lacking performance. And IME you only find this out after a few months. But from the get-go they're always worse documented and less flexible. I tend to mix C with other languages, like Rust or Python. This is mostly for audio and graphics stuff.
With that said: Swift replaced C (and Obj-C) 100% in the Apple world for me. Because the API bindings are as good.
"register" is still supported by GCC for combined use with asm. eg, `register uintptr_t x asm ("edx");`
Does D support anything like `__attribute__((section("t1")))`, so we can have the linker decide how to locate some compiled code, or computed gotos?
Can we use D's inline assembler to clobber registers?
I've had trouble even with clang and extended asm. For example, LLVM does not support the "g" constraint.
I understand that there aren't many use-cases for these specific features, but I have an unusual direct-threaded VM which relies on some of them, and the only real alternative I see is to write plain assembly.
I've been using D for OS development and have found it very good for controlling low-level details. GDC is the GCC frontend for D, and has most/all of the same features for controlling this stuff as GCC. For example, you can use `@register("edx") ulong x;` to specify that a variable should be in a particular register, `@section("t1")` on functions to place them in certain sections, and the inline assembler is the same as with GCC. Note: the @register feature is new with GDC 13. See here for the docs: https://gcc.gnu.org/onlinedocs/gdc/.
And of course LDC supports all the LLVM custom attributes, plus GCC-compatible inline assembler (not sure about the "g" constraint though) and LLVM-style inline assembler.
Thanks for the information. Appears that it does support the section attribute from GCC too. I'll have to explore this a bit and see if I can use it, since it seems to have all or most the features I use.
I did learn D some years ago when the standard library situation was not great, but might be worth looking at again.
Depending on how much language support you want, you may want to compile without the D runtime (in which case you only have access to the C standard library, and various features are disabled, such as classes/interfaces, garbage collection, exceptions, and most of the D standard library). You can disable the D runtime in GDC with -fno-druntime and in LDC with -betterC. With those flags, the basic hello world program looks like this:
The register support you describe is an extension, not really part of C. Gcc has lots and lots of C extensions, but they don't make the language simple.
D's inline assembler does register management for you, i.e. it tracks register usage through the instructions, so you don't have to say which registers are read or written to.
D does not support the "section" extension. I understand that certain gcc C extensions are needed for special purposes. Gcc may be the most suitable language if you need those extensions.
What I would do is use gcc for the parts of the code that need those extensions, and D for the rest (D code can directly interact with C code).
Thanks. My root comment mentioned that it was not really the features of C that I depended on, but the GCC specific features.
I'm interested in using D now as sibling comment mentions that I can use some of these features in gcd, but I'm not yet sure how much benefit I'd get over using C by using a subset of the D features which are compatible with the constraints of my VM.
The (experimental) VM I'm working on embeds type information into pointers. I place some functions at fixed virtual addresses and use the type information from the pointer to materialize these addresses at runtime, without having to dereference any pointers until I actually call the function. Essentially, if you have a pointer you will know the type of value it points to from the pointer itself.
This method places some tight constraints on how memory can be allocated, but I don't think it will be too much of a limitation for most applications intended to run on it. I have 12-bits in a 48-pointer which provide type information, which leaves a maximum 36-bits of virtual address space per type (or 35 bits if you discount the most significant bit which refers to kernel space).
I'm currently using the section attribute to implement it, but I'm aware there are other methods to achieve this. I could do it at runtime by `mmap`ing the virtual memory and then loading in the machine code at the addresses I need. This method might be more flexible in the long run and would free me up from using GCC specific attributes.
> Obviously there is C++ which can leverage most of this too, but C++ traps you into an ABI which is difficult to use from any language which is not C++.
To be fair to C++, all the other languages in this space are as bad at providing ABIs in their own languages. All (almost?) mostly provide ways to declare C ABIs, which C++ also supports.
C++ has rough C++ ABIs, but mostly because it bothers to try in the first place. It's not like other languages really "solved" ABI better than C++ has.
Even C ABIs are a little rough since there's so much preprocessor use to factor in and/or avoid.
C's ABI is that much simpler because it doesn't really place any constraints on the memory layout of your types. With C++ (and others), the values passed around may also have vtables linked to them, so your language must then also provide an in-memory representation of objects which is compatible with the C++ representation in order to do FFI method calls. That places quite a constraint on your language because to do it efficiently you basically want a copy of the C++ object model. So you're developing a kind of C++ sibling language, and C++ is quite complex, and now any other languages which want to interoperate with yours has to do the same.
This is why it's pretty standard to just have a C FFI and provide a C wrapper for C++ libraries to use them with other languages.
Of course the C ABI places constraints on the memory layout of the types. For example, alignment. Also in what register "small" types are passed when passing them to functions. It actually depends if the struct has float members or not.
C itself is quite complex already.
C++ adds more features.
But for example, the Rust ABI uses the C++ ABI, because C is too simple and doesn't support unwinding (for panics) or 128bit integers
The use of trademark enforcement to coerce conferences into certain policies (which might contradict local law) is a deal breaker. It also demonstrates a willingness of the foundation to use wield power for purposes completely unrelated to the language.
There is no such things about forcing policies for conferences in the current trademark policy. (You're getting confused with a proposal that is being re-worked)
And even if there was it's hardly a deal breaker to use the language.
The proposed policy, which is clearly political in nature, has resulted in our firm decision not to adopt the crab language for our projects. Although Rust™ offers valuable features, introducing politics into the equation was both unnecessary and has caused a rift that may prove difficult to mend.
so then, add a declarator or two to solve the problem, don't just throw the baby out with the bathwater. The population of C programmers who understand how C works is the population of people who can understand how hardware works. The reverse has never been true, hardware designers have never understood software, and academic software experts have spent too much time babysitting college freshman who can't code and spend all their time dreaming about languages that freshmen could learn well enough to TA the course. But that's probably not a language good enough for systems programmers to adopt.
> C is not aligned with modern hardware and optimizing compilers are severely hampered by things like undeclared pointer aliasing.
Which language is?
I mean, if you're going to parrot this line, surely you have an example of a language that is more closely aligned to hardware than C, right?
I see this line repeated in every HN thread about C. It's not a new sentiment, but it is mostly wrong because the implication is that there exists some other popular language that actually aligns with the hardware better than C does.
A language can not be exactly aligned to every possible hardware variation at the same time of course. So C does dome abstracting from the hardware level but keeps the mental model of memory locations which can be pointed to by pointers.
> A language can not be exactly aligned to every possible hardware variation at the same time of course. So C does dome abstracting from the hardware level but keeps the mental model of memory locations which can be pointed to by pointers.
Sure, and I agree with you, but anyone complaining that the language which models hardware more closely than any other language, "doesn't model the hardware" should be prepared for the followup question of "Well, which other language in common use is closer to the hardware?"
It's really tiring reading the same old assertions in every thread about C; usually backed up by the same two or three sources that everyone has already read.
> The System V ABI is closer to being a standard than some official standards are.
That brings back some unpleasant memories. What happens when a popular compiler decides that whatever they choose is the ABI, and the published spec be damned? https://gcc.gnu.org/bugzilla/show_bug.cgi?id=38496
Obviously there is C++ which can leverage most of this too, but C++ traps you into an ABI which is difficult to use from any language which is not C++.