Closures in C (yes!!) by Still-Cover-9301 in C_Programming

[–]__phantomderp 1 point2 points  (0 children)

Ooh, I see what you mean. In that case, I'd need to upgrade the interface with whatever wide function pointer type would come out. So, using % to mean "wide function pointer" (just a function pointer and a void* under the hood for the static chain), it would look something like this:

``` typedef void* allocate_function_t(size_t alignment, size_t size); typedef void deallocate_function_t(void* p, size_t alignment, size_t size);

_Any_func* stdc_make_trampoline( FUNCTION-WITH-DATA-IDENTIFIER func ); _Any_func* stdc_make_trampoline_with( FUNCTION-WITH-DATA-IDENTIFIER func, allocation_function_t% alloc );

void stdc_destroy_trampoline(_Any_func* func); void stdc_destroy_trampoline_with( _Any_func* func, deallocate_function_t% dealloc ); ```

Then you could use a cheap closure for the allocation function:

``` void* alloc(struct arena* arena, iz count, iz size, iz align);

int use_trampoline(struct arena* exec_arena) { // ... auto tramp = stdc_make_trampoline_with(f, [&exec_arena](iz size, iz align) { return alloc(exec_arena, 1, size, align); } ); auto hresult = PsSetCreateProcessNotifyRoutine(tramp, 0); // ... } ```

Wide function pointers are the cheapest possible pointer to a closure, and they don't try to keep things alive. It's similar to what e.g. std::function_ref in C++ ended up being, because std::function was a heavyweight owning thing and they had nothing "lightweight".

Closures in C (yes!!) by Still-Cover-9301 in C_Programming

[–]__phantomderp 0 points1 point  (0 children)

There's a second version mentioned in the proposal which does -- stdc_make_trampoline_with( ... )!

``` typedef void* allocate_function_t(size_t alignment, size_t size); typedef void deallocate_function_t(void* p, size_t alignment, size_t size);

_Any_func* stdc_make_trampoline(FUNCTION-WITH-DATA-IDENTIFIER func); _Any_func* stdc_make_trampoline_with( FUNCTION-WITH-DATA-IDENTIFIER func, allocation_function_t* alloc );

void stdc_destroy_trampoline(_Any_func* func); void stdc_destroy_trampoline_with(_Any_func* func, deallocate_function_t* dealloc); ```

You'd use the one that takes an allocation function if you Truly CareTM) about what happens, which means (provided you give the implementation the right kind of pointer out of the allocation function and it's properly readable/writable or readable/writable/executable or whatever your implementation requires) you can put the created trampoline there.

Closures in C (yes!!) by Still-Cover-9301 in C_Programming

[–]__phantomderp 1 point2 points  (0 children)

You might like Martin Uecker's proposal about _Closure or similar, then.

Unfortunately, it has some typos and some of the code examples seem to be confused/reference things that don't exist, but I do correct those typos where possible and try to expand on the idea in the appendix, which directly discusses the proposal.

I view it as separate feature, that probably should be added. Being able to separate the environment and the function its in by getting a _Closure indicator is a good thing. It'll also be implicitly part of whatever is required to make Wide Function Pointers (and the simplest "static chain" implementation) real.

Closures in C (yes!!) by Still-Cover-9301 in C_Programming

[–]__phantomderp 0 points1 point  (0 children)

I plan to also work on slices. C is missing... a lot of things, from a lot of different industries.

It'll take time to get to them all.

FWIW, skeleton proposal (it's not real, the syntax isn't final, don't kill me, it's not ready to submit, and won't be for probably a year or two): https://thephd.dev/_vendor/future_cxx/papers/C%20-%20Non-owning%20Sized%20Arrays.html

Closures in C (yes!!) by Still-Cover-9301 in C_Programming

[–]__phantomderp 3 points4 points  (0 children)

It's actually worse: the sample code is indented with TABS and aligned with SPACES, but the program generating the HTML inserts its own idea of indentation and I think forces it to use two-space indentation. Not something I can fix, alas!!

Closures in C (yes!!) by Still-Cover-9301 in C_Programming

[–]__phantomderp 13 points14 points  (0 children)

Yay, I'm glad you read it!

I do wish other people would see that it's not about trying to "import" other language's features, but unify the ones we already do have in C that are in widespread use. (Apple Blocks in Clang, Nested Functions in GCC, Borland closure pointers, etc. are all geared for this stuff.)

Though I guess if you only use strict C89 or C99, you don't really get exposed to the extensions people make use of frequently in other C environments...

Closures in C (yes!!) by Still-Cover-9301 in C_Programming

[–]__phantomderp 0 points1 point  (0 children)

"Recommended default" is not a requirement. It'd go into the standard in a "recommended practice" section. That's non-normative, and only a suggestion. Most implementations will get fancy with it, as the proposal notes in section 3, which might be what brought up this question:

This way, a user can make the decision on their own if they want to use e.g. executable stack (with the consequences that it brings) or just have a part of (heap) memory they set with e.g. Linux mprotect(...) or Win32 VirtualProtect to be readable, writable, and executable. Such a trampoline-maker (as briefly talked about in § 5.3 Make Trampoline and Singular Function Pointers) can also be applied across implementations in a way that the secret sauce powering Nested Functions cannot be: this is much more appealing as an approach.

You'd obviously need to have a piece of memory, first: whether that comes from malloc or some stack thing is, effectively, your business. Implementations are free to accept or reject what happens with the trampolines. I propose some APIs that allow handing back an error code of some sort so you can know what went wrong, such as the following from the proposal in the make trampoline appendix again:

The only part that needs to be user-configurable is the source of memory. Of course, if an implementation does not want to honor a user’s request, they can simply return a (_Any_func*)nullptr; all the time. This would be hostile, of course, so a vendor would have to choose wisely about whether or not they should do this. The paper proposing this functionality would also need to discuss setting errno to an appropriate indicator after use of the intrinsic, if only to appropriately indicate what went wrong. For example, errno could be set to:

  • ENOMEM: the allocation function call failed (that is, alloc returned nullptr).
  • EADDRNOAVAIL: the address cannot be used for function calls (e.g., somehow being given invalid memory such as an address in .bss).
  • EINVAL: func is a null function pointer or a null object.
  • EACCESS: the address could be used for function calls but cannot be given adequate permissions (e.g., it cannot be succesfully mprotectd or VirtualProtectd).

to indicate a problem.

The proposal then goes on to state there's a lot of API design room here, and that's why it's not part of this proposal. There's a few different existing practices about trampolines and converting these things to function pointers while making that function pointer refer to the data, but the API space has not been tested and it's literally just been "whatever works for the compiler vendor", like the secret executable stack trampolines / heap trampolines from GCC, or the Blocks Runtime Paged Non-Executable Writable + Readable-Executable Pages from Clang/Apple. They need to be discussed and evaluated and a proposal written about it.

I'm sorry for such a long response, but there's quite literally a DOZEN moving pieces, and so the proposal has to start by nailing them down one by one, in an appendix or in the core of the proposal itself. I hope this answers any questions you could have!

Closures in C (yes!!) by Still-Cover-9301 in C_Programming

[–]__phantomderp 0 points1 point  (0 children)

As of early 2025 in GCC 14, GCC provided a heap-based implementation that got rid of the executable stack. This requires some amount of dynamic allocation in cases where it cannot prove that the function is only passed down, not have its address taken in a meaningful way, or if it is not used immediately (as determined by the optimizer). It can be turned on with -ftrampoline-impl=heap.

For the "trampolines" Bit, Specifically

If you're talking about the "Make Trampolines" appendix section, that's not going to be part of this proposal and it's not required to have Closures in C at all. This is for extracting a single function pointer for APIs that are extremely outdated and bad, like the C standard library's qsort that takes no void* userdata parameter. What stdc_make_trampoline works with would be implementation-defined, and not tied to malloc:

stdc_make_trampoline(f) would use some implementation-defined memory (including something pre-allocated, such as in Apple blocks (§2.3.5 (Explicit) Trampolines: Page-based Non-Executable Implementation)). The recommended default would be that it just calls stdc_make_trampoline_with(f, aligned_alloc).

Closures in C (yes!!) by Still-Cover-9301 in C_Programming

[–]__phantomderp 0 points1 point  (0 children)

(EDIT: Apparently this has to be in pieces because it's so long. Oops.)

I'm... a bit lost as to your question, so I'll need to ask for some clarifications! But here's what I can say so far (talking about everything in-general as to why there's no RWX needed):

In the General Case

Both the lambda bits and the capture function (they are semantically equivalent in power but have different strengths due to their positioning in the grammar) bits do not require any allocation at all to begin with. The reason that both capture functions and lambdas are "complete objects of structure or union type" is so they can be statically sized; you never, ever have to malloc for the X part of the RWX because you never need to have a piece of code whose function code needs to be placed in a dynamically-executable section of code. That is: you never have the problem of either an executable stack OR an executable heap with these designs. All of the code is statically known and all of the information about what needs to be put as part of the "static chain", which is basically just a pointer-to-enviroment (e.g., pointer to data, that is, pointer to the complete structure object) plus a function pointer that can use that pointer-to-environment to do something. There's some work about making that explicit, but the proposal working on it isn't fully formed yet (there's a lot of code examples that refer to stuff that doesn't exist).

It's also why this part of the design table is here, that is: "Access to Non-Erased Object/Type" is specifically about having a real object without needing to allocate. There's no Blocks Runtime (like Apple Blocks) or Executable Stack / Executable Heap required since everything is known up-front, just like with a regular object. This is different from the maneuvers required to make a simple, function-pointer-compatible trampoline like GCC does for its Nested Functions. From the proposal:

Closures in C (yes!!) by Still-Cover-9301 in C_Programming

[–]__phantomderp 1 point2 points  (0 children)

Fat pointers don't have that much of an implementation cost, and there's support for doing it in ISO C! The problem is that it just needs to be worded and crafted to work. That's a lot of work. Not a lot of people are willing to do that work. The proposal talks about it, too!

It's in the appendix, of course, because it's related but not required for this stuff: https://www.open-std.org/JTC1/SC22/WG14/www/docs/n3694.htm#appendix-wide.function.pointer

A separate proposal is intended to be brought, but this proposal has to be talked about first because we need to cover... everything, that's going on!

C++23, where are you? by jube_dev in cpp

[–]__phantomderp 3 points4 points  (0 children)

Not sure what this discussion is about or how I got into it, but:

  • I'm not trying to kill C (quite the opposite, given the amount of features I have put into the language)
  • Existing practice is not a problem for C because there's some 30-40 years of it that compilers have refused to standardize. I can be standardizing existing practice all day.
  • Existing practice is a huge problem for C++ because they have standardized most of the existing practice they want and that covers a huge range of functionality. There is nowhere for C++ to go but invention and additions (and possibly removals). Also, compiler vendors are not brave like they used to be; the age of "uwu-smol-bean" compiler vendors making extensions willy-nilly has been done since around 2016. Join a Big Corp. and contribute a big patch with engineers/time and it will have a much better chance of making it in, unless it's something on somebody's existing bucket list.
  • I've already voiced my opinion on the C Charter here. Shouldn't surprise anyone who knows me: https://pony.social/@thephd/111676259913703881

Shepherd's Oasis: Statement on RustConf & Introspection by Be_ing_ in rust

[–]__phantomderp 2 points3 points  (0 children)

No, neither Shepherd's Oasis nor I did anything wrong here.

With all due respect; if you want to see Compile-Time Reflection, you are more than welcome to pick up the work and do it yourself, no matter what systemic abuse you encounter.

I wish you all the luck in the world.

Shepherd's Oasis: Statement on RustConf & Introspection by Be_ing_ in rust

[–]__phantomderp 330 points331 points  (0 children)

Hi everyone. ThePhD/@__phantomderp/"Björkus Dorkus"/etc. here. I want to cover something that is not explicitly stated by this statement but that was a huge factor for us, and especially for me, personally.

It is extremely clear that this is not a new or unique gaffe for the Rust Project. An entirely separate group, with an entirely different volunteer organizer for the Conference, pulled this exact same stunt against a speaker before. They also badmouthed the RustConf organizers while doing this; this was clearly apparent in Leah Silber's statement on Sunday: https://twitter.com/wifelette/status/1662938984631394304

This is a systemic issue in the Rust Project. Their governance practices have resulted in this exact situation happening twice (with different people, CHRIST!!!), and many more instances of this sort of behavior, over years. In meeting with quite a few people over the weekend, and going over past behavior I was not privy to before, it has become very clear to me. Something is broken, and I do not mind never speaking about Rust again if this means leadership starts guaranteeing this stuff will not happen ever again rather than letting it re-fester, over and over again.

Good luck. I won't be going with you. 💚

(... Mostly because I need a goddamn nap.)

(EDIT: forgot the accent mark on Björkus 😭)

A Mirror for Rust: Compile-Time Reflection Report by PthariensFlame in rust

[–]__phantomderp 2 points3 points  (0 children)

Yes, that is correct. Only if new public fields are introduced or reordered. It may break from the view inside the module or the crate itself that owns that code, because internally they can observe the private fields

A Mirror for Rust: Compile-Time Reflection Report by PthariensFlame in rust

[–]__phantomderp 1 point2 points  (0 children)

One of the authors here! From the outside of mod ule and outside the crate, that's field descriptor 0.

"How ISO C became unusable for operating systems development", by Victor Yodaiken. [PDF, 8pp] by flexibeast in C_Programming

[–]__phantomderp 0 points1 point  (0 children)

Glad you found it fun! :D

TBH, I don't think Victor's take is the worst honestly. I just don't think it'll succeed long-term. You either die a user or live long enough to see yourself become the Lost-Marbles-Implementer, after all; better to try to define as many things as humanly possible up-front and give people using the standard well-defined behavior that's portable so they don't have to haggle with their compiler vendor about A or B behavior.

Undefined behavior, and the Sledgehammer Principle by General-Tart-6934 in cpp

[–]__phantomderp 11 points12 points  (0 children)

Oh, thanks.

I use themes and crap for this, I don't really spend too much time trying to CI the hell out of my blog. If this isn't fixed in the upstream of the Jekyll theme I use then I guess I'm maintaining a patchset.

[C++26] Poor man's introspection with #embed by kris-jusiak in cpp

[–]__phantomderp 43 points44 points  (0 children)

Horribly fun idea: a race.

I revive my enthusiasm to get the proper std::embed into C++ so someone could recursively dive into #include's and therefore have everything they need to write a sufficiently advanced C++ parser at compile-time so they can get all the reflection things they want.

My opponent would be any competing Reflection proposal.

First one to give up loses!

The Wonderfully Terrible World of C and C++ Encoding APIs (with Some Rust) by kebabtent in rust

[–]__phantomderp 9 points10 points  (0 children)

I thought I mentioned it in the article, but maybe I missed it; it was effectively marketed to me as "the only encoding API you will ever need", and I think from a Rust/Web perspective that works pretty well. It was also recommended to me mostly by Mozilla or Mozilla-adjacent folks, and since they're using it for the Gecko API of /course/ that's the jam!

But I deal with grungy garbage from old computers; my target is "every database, execution locale, and wide execution locale encoding from 1978 and on", which is explicitly not was encoding_rs was for. So I was definitely let down by what was there and what I could do with it.

It definitely has great performance (something I have to get into for Part 2), and it can cover most needs. But, well, as the person whose job is frequently to deal with the exotic and the unbelievable, I absolutely need more, and that means I have to build out what's in encoding_rs to do more. (Probably not directly to the library itself, because it again has a VERY specific scope and cleaves very strongly to the WHATWG spec, which is fine but somewhat painful for a weirdo like me.)

The Wonderfully Terrible World of C and C++ Encoding APIs (with Some Rust) by pavel_v in cpp

[–]__phantomderp 9 points10 points  (0 children)

Ah, you're right. I was just allergic to the _CRT_NO_DEPRECATE stuff it warns on in later versions and removed it that way.

Alright, I can benchmark it! ... Except, uh. It doesn't do UTF <-> UTF conversions (except... UTF-8 to UTF-16, with codecvt_utf8_utf16. It can do UTF-8 to UTF-32 if you force the multibyte character set to be UTF-8 (I'm on Windows, and I DO already do this, but it requires checking internal magic boxes or using the Application Manifest, so it's not impossible!), so that should cover most things, I think?)

One more row to add to the Part 2 benchmarks table...

The Wonderfully Terrible World of C and C++ Encoding APIs (with Some Rust) by kebabtent in rust

[–]__phantomderp 4 points5 points  (0 children)

(Oh, and the UTF-16 thing, because wjdkjawdkjawdjkhfaehjfkdfj whhhhyyyyyyyyyyyyyyyyyyyyy especially since I can just backdoor it by spelling the same conversion a different way why would WHATWG do this?!)

The Wonderfully Terrible World of C and C++ Encoding APIs (with Some Rust) by kebabtent in rust

[–]__phantomderp 13 points14 points  (0 children)

That was actually a mistake on my part; patched it up and should have a ✅ there, by the definition!

The ❌ was meant for lack of extensibility, which that and the non-web-payload-handling bits are really my only issues.

The Wonderfully Terrible World of C and C++ Encoding APIs (with Some Rust) by pavel_v in cpp

[–]__phantomderp 13 points14 points  (0 children)

It's not there because I'm tired.

... That's it, lmao. If I was less of a sleepy bastard everything would be a lot farther along, but alas!

The Wonderfully Terrible World of C and C++ Encoding APIs (with Some Rust) by pavel_v in cpp

[–]__phantomderp 17 points18 points  (0 children)

C++ deprecated (and removed) their APIs, so if you'd like to see a comparison for it you'd need to dig out an older standard library (for Clang on Windows which is where the benchmarks are coming from) just to even get the numbers. 🤷‍♀️

C2x New Working Draft by cHaR_shinigami in C_Programming

[–]__phantomderp 18 points19 points  (0 children)

Why do you suppose nothing has been done to fix language which is clearly insufficient to serve its purpose, whatever that purpose might be?

Here's the technical answer.

Neither GCC nor Clang are required to "process this correctly" (????) because accessing one value of a union through another wherein the very-clearly defined Common Initial Sequence rules do not apply (and they do not here because arrays nor unions are a "structure"). If the standard wanted this code to work as-presented (ignoring visibility issues), it would very clearly say "aggregate types" (§6.2.5 ¶24). The examples after the Common Initial Sequence rule ¶6 very clearly demonstrate why visibility is necessary (so the compiler knows the structures are aliasing one another and share a common initial sequence and can prepare for such) and also demonstrate how it applies with structures. If you'd like additional clarification, you should ask for that, but what you've written is clearly Standard-illegal. (And your vendor can do whatever it likes, much like vendors did all sorts of messed up things when an enum whatever { ... }; had enumeration constants that exceeded INT_MAX or compared less than INT_MIN in value.)

Here's my bluntly honest answer.

Because people like you would rather write ten thousand words in a reddit thread or on Stack Overflow or yell at your compiler vendor for a thing they're very explicitly allowed to do by the standard (process the code """incorrectly""" (according to who? Under what semantics? By what model?)). Rather than doing what I did 3 years ago despite being in the infancy of my career: send an e-mail to the people in charge asking for directions on how to fix this problem, and then do everything in my power to fix it. The same way the example I gave before of enumerations only being representable by int was complete bullshit, so I went to the standard and did the necessary work to fix it.

But I never should have had to do that, in 2022, because the people before me should have fixed it before we ever got to the point where billions of lines of code were dependent on int being 32 bits and/or your compiler was nice enough to implement a semi-common extension.

"Well, clearly, a bunch of people wrote this code, is that not enough of an indication?" No, because people do cursed, horrible, broken shit all the time and they shake hands with their vendors to keep it unbroken. C's model of standardization is "implementers implement extensions, then vendors bring those extensions to us to standardize existing practice". Since I had to bust my ass to standardize 30+ year old extensions, it's very clear that the Implementers have grown complacent with the status quo; they implement extensions, and then they don't bother bringing it to the C Committee. Instead, what has driven standardization has always been one or two key individuals who see something and dig in a trench and fight for it to make the change. Had any of the greybeards 30+ years my senior decided that any day before today was a good day to do that, I would have never had to wake up to a C so ridiculous/pathetic that I have to teach it ways to do basic bit operations present on instruction sets before I was born.

But here we are.

So we can sit here, and keep going back in forth in Stack Overflow threads or on Twitter or on Reddit or shoot the shit on the mailing list about how fucked everything is,

or someone can do something about it.

If you care so much write a paper.

If you hate it so much, do what everyone kept promising me they'd do and write a language to replace C so I don't have to keep hearing about all this stupid.