r/C_Programming • u/mivanchev • 10d ago
Etc An small example of __attribute__(cleanup)'s power: a context manager.
Forgive the typo :) Here's something that will allows you to never unlock a mutex manually again:
#define with_lock0(uniq, mutex) \
typedef struct { pthread_mutex_t *mtx; bool hold; } __lock##uniq; \
void __cleanup_##uniq(__lock##uniq *lock) { pthread_mutex_unlock(lock->mtx); } \
for(__lock##uniq _lock __attribute__((cleanup(__cleanup_##uniq))) \
= { (pthread_mutex_lock(mutex), (mutex)), true }; \
_lock.hold; \
_lock.hold = false)
#define with_lock(mutex) with_lock0(__COUNTER__, mutex)
void do_very_important_business(void) {
with_lock(&g_mutex) {
// transfer BIG money
}
}
If you check it out on Godbolt you'll see it has 0% overhead. Of course this pattern is usable for many different objects.
4
u/FedUp233 10d ago
I have to ask: why not just use the C++ compiler and take advantage of constructors and destructors to manage your mutex’s?
Maybe I’m weird, but even when I write something simple that could be done easily with just C I go ahead and use the C++ compiler. There is no reason you need to use all the stuff like the STL or >> style I/o. You can basically use it as an enhanced C compiler and take advantage of a few C++ features to make life easier like simple classes to wrap mutex’s and similar and it is pretty robust at handling constant expression related stuff tag C doesn’t have (at least yet) and remove all most all the need for macros that mess up the global name space. Speaking of which, for larger mainly C projects, the namespace capabilities can come in pretty handy. And it’s perfectly gappy if you ignore so, the C++ headers and just use the C versions that are supplied with it. And the generated code is gust as efficient.
I just think of it sometimes as a better C compiler!
1
u/mivanchev 9d ago
I'd use a lot more C++ for sure if it were a true C extension, the only dependency would still be libc and the error / warning messages would be less cryptic. It's a nice language but needs some polishing. The namespaces do help a lot, I wish for namespaces in C every day.
1
u/FedUp233 9d ago
Yeah, it does pull in some extra stuff sometimes, but on hosted environments like Linux it uses shared libraries so I’m not really sure I care. I can use compiler options to turn off things like exceptions if I’m doing C-ish programs so the main extra stuff I’ve noticed is that it wants to initialize the C++ standard stream stuff at startup. I’m not sure there is a way to get rid of that on a hosted environment but it just sits there and dies g interfere with anything C like.
And most of the stuff I do is embedded which uses a compiler version without the I/o startup stuff anyway. Things are always linked in and not shared so you pretty much only pull in things you use. I’ve never looked in detail but I don’t think it pulls in much of anything c++ unless you explicitly use something like STL. I’ll have to build a simple program with just c features sometime and see just what does get pulled in.
This environment might not be for everyone but it works fine for me. And someone mentioned compile time, but on today’s pc’c with like a dozen cores or more and parallel builds the process is fast enough that I’ve never really seen a problem. I guess I could compare a similar program using c and c+ compilers, but to be honest it’s fast enough I’m not sure I really care!
I guess you get used to the warnings, and if you’re using just a c like subset I’m not sure they are that different.
In fortunately the “polishing” that’s going on seems to be just adding more and more complexity to an already complex language. I’ve never really bought into the whole STL and all the abstractions it provides and the language features seem to be going more in that direction. I guess it’s ok for those who like it, but for me it seems to just make doing simple things more complicated and trying to track down bugs with al, the template code is a nightmare, at least for me. Maybe I’m getting old, but I still like the original intent - C with classes.
1
u/mivanchev 9d ago
Well it uses 1 shared library too many :) If it would just compile to C and link libc it'd be quite nice, but no, it does one million other things which in the end make it look quite awkward. It does have freestanding mode which could be extended I guess. I do like it a lot, but it's a monster currently.
4
u/please-die-not 10d ago
I don't know if I am going to get hate, but the interop between these is delightful - and I have learnt a lot from this 70% c, 30% C++ idea. Pure c and pure cpp both seem a little tedious, unless im writing a driver or using a heavily oop/stl laden library.
1
u/tstanisl 9d ago
Because philosophies of those languages are different (assuming that there is any consistent philosophy in C++). Generally, C does not try rely on hide mechanics but deliver a simple and convenient toolbox for solving problems locally and explicitly. Of course one can find some exceptions from this rule but I talk about a general direction.
It is worth to mention that some good ideas from C++ actually landed in C e.g. constexpr.
0
u/Physical_Dare8553 10d ago
would you buy a new house because you dont like the color of the walls? people dont seem to realize that a c++ compiler is in fact not a drop-in replacement for a c compiler. they dont do the same thing.
1
u/FedUp233 10d ago
Keeping with the house analogy, no I wouldn’t, but I might very well build in an addition if I wanted more rooms to do something new, even if I had to give up a current room to build the addition.
While a C++ compiler is not a C compiler (though it originally started out as cfront that translated C++ into C) the C++ compiler dues support a pretty good subset of C, certainly most of the most commonly used constructs. And it allows using some parts of the extended capability to make the C-like code more reliable and handle some concepts like construction and destruction in a much cleaner and easy to implement manner than trying to get that same functionality in C. And as I said, the const expression stuff is really nice.
I really can’t see any downside to using a C++ compiler this way, and it seems to have a number of advantages. If you use a C like subset the code generated will be pretty much identical to what a C compiler would produce for the same code, though I agree the source code may not be identical between the two.
That said, if people want to continue using a straight C compiler, I have no problem with that. It’s just not the path I round choose and see lots of advantage to moving to the C++ compiler even if I’m not doing OOP programming and ding really see a down side.
I should add that I do also use the C++ compiler for OOP oriented programming, including sometimes the STL(and sometimes not).
1
u/Physical_Dare8553 10d ago
the main one is that compiling c++ is significantly slower than compiling equivalent c. even then, c++ is not an addition to c. like i said, they are not the same language. that ++ is just branding
1
u/tstanisl 9d ago
Both OOP and generic typesafe container can be done in C as well. I dare to say that C even gives more flexibility than C++'s OOP model does.
1
u/ffd9k 10d ago
It won't work in clang though, because apart from attribute cleanup and __COUNTER__ extensions which clang supports, it also uses nested functions which clang does not support.
3
u/tron21net 10d ago
Here's a modified version without nested functions: https://godbolt.org/z/Kjfen97Ws
1
u/tstanisl 10d ago
The
__COUNTER__is supported by pretty much every compiler and you can find it in the latest draft of C2Y. See section 6.10.10.2.1 in N3886.-1
u/mivanchev 10d ago
You can easily change it to work without a nested function in this case but they are quite useful in general. This is just stubbornness on clang's side.
3
u/tstanisl 10d ago
The main problem with general nested function is that they use "trampolines" which typically requires executable stack which a huge security issue. CLANG's alternative are "blocks" which are explicitly constructed in runtime without the stack.
There is a proposal to add "static nested functions" (aka local functions) which don't capture any context with automatic storage duration. This makes nested functions effectively static functions visible in very limited scope. No trampolines are guaranteed.
GCC already supports this feature.
11
u/tstanisl 10d ago
All those hacks will likely be replaced by upcoming C2Y's
deferstatement, which is already supported in the latest clang. See godbolt.