viega
while (i-- != 0) { ... }
It's more clear than the most idiomatic way, which is to drop the explicit comparison: while (i--) { ... }
All of it is better than the clunky old for-loop syntax.Meaning, if you have a struct and you want to bit-pack your booleans, you can declare each one as, say, uint8_t some_bool : 1;
You may then do `x.some_bool = true;` etc.
It's a small nicety to avoid bitwise operators, anyway.
As for stack bounds, address of a local is easy enough to make work well, but if you care about scanning thread stacks, I'm not aware of a good way to do that with compiler intrinsics only. If you're willing to assume pthreads, it's doable, but even that requires platform-specific code. The only viable portable approaches for dealing w/ thread stacks I'm aware of aren't awesome:
1. Assume your control of thread creation isn't circumventable, and add your own sentinel at the other end of the stack. 2. Probe the stack for the first access violation.
No option is particularly satisfying there; wonder if I missed some other intrinsic added along the way to solve that problem.
The epoch isn’t CAS’d; it is FAA. The epoch is then used to determine if there is contention due to the tail meeting the head, or due to a wrap-around due to slow writes.
There’s also a back-off scheme to ease contention for a full queue.
Though, I did originally have a variant that adds a fairly straightforward ‘help’ mechanism that makes the algorithm wait-free and reduces the computational complexity.
However, the extra overhead didn’t seem worth it, so I took it out pretty quickly. Iirc, the only place where the ring in the queue wouldn’t out-perform it are on tiny queues with a huge write imbalance.
If you go run the tests in the repo associated w the article, you probably will see that a ring with only 16 entries will tend to start being non-performant at about a 4:1 writer to reader ratio. But iirc that effect goes away before 128 slots in the ring.
There, the ring still fits in a page, and even with a big imbalance I can’t remember seeing less than 1m ops per second on my Mac laptop.
Real world observational data beats worst case analysis, and I’ve never seen an issue for scenarios I consider reasonable.
But, if my unrealistic is realistic to you, let me know and I can break out the wait free version for you.
However, I definitely did not see the paper you've sited, but just spent a few minutes with the paper you cited. Section 5.2 seems to cover their version. It's far from clear what their algorithm is, but they are talking about page eviction; it doesn't seem like they're using even a single fixed array for the ring, but I'm not 100% sure because it's pretty light on any detail. a
The article does mention wanting to coordinate across multiple identical processes running on the same node in a wide variety of environments as the motivator.
So maybe it should be a feature, not a bug :)
Fun-reliable side-channels for cross-container communication
Same thing would be for making it much easier to statically lay out fixed caches implemented via hashing, instead of inserting into them at start-up, etc.
Some of the stuff you're doing in the library I've also been doing recently, and has been working well, like using the string of the type name to allow for run-time checking of, what I would called "mixed" data (your variadic type). I've also done the same basic thing as your option type in a way that's closer to your sum type than the maybe type.
But I'd had enough problems trying to get something like your vector actually working that I'd given up, but I think now I'll build something at some point over the holidays. I think as I'm coming up to speed on the history of changes to _Generic that's partially due to the attempts before I had a C23 compiler, but even then, your code there is impressive-- both clear and clever.
I also have enough stuff passed via pointer that the option type for me needed to handle pointers differently-- I basically just have run-time code that does the null check at run time when set. At that point, there doesn't really need to be an 'is_set' type flag.
I've noticed many people are still building on systems where the compiler is a bit older, and defaults to C11, even if it has support for C17, so perhaps that's the problem?
But, if it weren't on the iceberg page, it'd make sense. The semantics of `do { ... } while(0)` are in the standard, and the preprocessor has nothing to do with those semantics.
You are, of course, right, that the construct is used all over the place in macros for good reason, though these days every compiler I care about has the same (I believe not in the standard) syntax for statement expressions, which will work in even more contexts.
They are both only a couple lines, but it deals with things like the fact that you've got one more argument than you should have commas, and the use of the # and ## operators in these things.
https://c.godbolt.org/z/6zqx1dsn3
I've fully annotated it, so it might seem like more than it is. About half the macro code is from the original article (the chunk at the top). And I do implement both transforms for you.
Each one I think is only 6 lines of code by itself, despite the rediculous amount of exposition in the comments.
If you have any questions about it, let me know.
But while CPP is pretty finicky and not very modern, getting such things working seamlessly with C build systems can be vastly worse (though better than the days where the GNU tools were ubiquitous).
I tend to find meson easy to use compared to all the others, and do this kind of thing, but it's still difficult and brittle.
Ergonomically, I have tended to start using _Generic for static type checking where possible, and that pushes me more toward to avoiding arrays in types for this kind of thing.
And I was definitely looking around for this kind of history when I was searching around when writing. Perhaps my google skills have decayed... or google... or both!
Thanks very much.
I do try to avoid this kind of thing unless necessary, so I don't have experience as to where the different compilers will fall down on different corner cases. I'd find it very interesting though, so please do share if you kept any record or have any memory!
All code can have bugs, error out and die.
There are lots of good reasons to run code at compile time, most commonly to generate code, especially tedious and error-prone code. If the language doesn't have good built-in facilities to do that, then people will write separate programs as part of the build, which adds system complexity, which is, in my experience, worse for C than for most other languages.
If a language can remove that build complexity, and the semantics are clear enough to the average programmer (For example, Nim's macro system which originally were highly appealing (and easy) to me as a compiler guy, until I saw how other people find even simple examples completely opaque-- worse than C macros.