5 ms·
Smart Pointers in (GNU) C
- GGerome 2y agoIt is time to reply to the issues that were posted since these years...
- cozzyd 2y agoit's great when the ratio of cmake to C is close to 3...
- sph 2y agoWhy do some people hate Make so much to use an abomination like CMake? It has terrible syntax, terrible semantics, and you need to run Make anyway. I do not get it. If it was noticeably better, I'd understand. Probably because it looks nice in the tutorial with apparently simple syntax, but then on real projects it devolves into a hundred lines of abstraction nonsense over environment variables and spawning programs.
- ReleaseCandidat 2y ago> Why do some people hate Make so much to use an abomination like CMake? Because Make doesn't work either. This holds for Make too: > Probably because it looks nice in the tutorial with apparently simple syntax, but then on real projects it devolves into a hundred lines of abstraction nonsense over environment variables and spawning programs. The real solution is of course using neither (and neither Automake). Pick one (that works for more than C, C++ and Fortran at best) build system and use that. I've choosen Buck 2 https://buck2.build/ https://buck2.build/ https://github.com/Release-Candidate/Cxx-Buck2-vcpkg-Examples https://github.com/Release-Candidate/Cxx-Buck2-vcpkg-Example... https://github.com/Release-Candidate/Cxx-Buck2-Conan-Examples https://github.com/Release-Candidate/Cxx-Buck2-Conan-Example...
- fn-mote 2y agoHow about the importane of your build system lasting for a decade or more? For all the hate about Make, it works just like it used to a decade ago.
- ReleaseCandidat 2y ago> Make, it works just like it used to a decade ago. So, not at all for anything not simple. It's not that we all got together one day and said "you know what, (GNU) Make does all what we need, let's build some new, more complex, alternatives". Make is fine, until it isn't, which is a point that's easy to reach with C, C++ and Fortran with modules (so, everything after Fortran 77). And nowadays it's _way_ more homogenous as back in the day when you had all the real Unices with their own compiler and own linker and assembler and way to build and use libraries, in 32 und 64 bit on the same system. Not to forget about the oddball using VMS.
- acuozzo 2y agoIs your deliverable source code or is it a binary? In my experience, Make (and build scripts) work just fine when your deliverable is a binary. You have complete control of the build environment, so hardcoding e.g., /opt/my/cross/compiler or relying on $PATH is a non-issue. However, if your deliverable is source code, then you can only make so many assumptions about the build environment. This is where GNU Autotools and CMake shine.
- pjmlp 2y agoMake isn't cross-platform, and depends on a ton of external executables. By all means stay with Make if UNIX is the only target, but then also take care to only use the POSIX subset.
- cozzyd 2y agoCMake does make configuration easier (via its tui), and for trivial shared libraries/ simple programs it can be convenient, but once you're outside its happy zone and have to run code generators and do text processing, it quickly becomes much more unwieldy than a makefile + sed
- acuozzo 2y agoMy deliverable is source code; my code targets several different platforms; and my clients expect to produce builds for all supported platforms from one build environment. Delivering with GNU Make alone would require me to either write tons of documentation for my clients to read in order to setup their build environment just right or write tons of shell script to accomplish a fraction of what GNU Autotools already does. I'll pre-emptively state that delivering a "use this to build" Docker image is not an option, unfortunately.
- fargle 2y ago"because it looks nice in the tutorial with apparently simple syntax, but then on real projects it devolves into a hundred lines of abstraction nonsense over environment variables and spawning programs" you just described "make" as well as cmake. let alone autotools (./configure, Makefile.am, etc.) "It has terrible syntax, terrible semantics, and you need to run Make anyway" agree. the core reason, overriding all of that, is that it fundamentally does a different job. Make is an engine that takes a high-level description of rules and dependencies, builds a graph of them, and then executes on it. but in order to work correctly, you need a full dependency graph. the problem is that when you have a large project with lots and lots of directories that build a bunch of inter-dependent artifacts, you don't necessarily like having one single big monolithic "Makefile". so of course you break it up. Every directory has a fragment that describes just what it builds, and so on. the old way of doing that was hierarchical makefiles and "recursive make" [1] which kinda works OK, but treats each leaf makefile and directory as an independent product. the core makefile only knows coarse dependency information about those directories as a single unit. this also has pretty severe performance and parallel build issues. the right way of doing it is some kind of multi-stage makefile system. the first stage doesn't build the product, it builds a concrete monolithic makefile containing the whole dependency graph from distributed information in each part of the project. the second stage builds the product. you can write the first stage in python, bash, gnu-make, etc. but make is really a difficult tool to write procedural type code in. the linux build system does this though, but it's certainly not simple. so that's all CMake is (wonky syntax and all) is doing. and it's just a bespoke system tuned specifically for doing C and C-like compilation that allows you to build a makefile (or Ninja or etc. etc.) backend. of course it has all kinds of other features as well, but to me the core difference is that make is low-level and cmake fills that role for a high-level system. [1] https://accu.org/journals/overload/14/71/miller_2004/ https://accu.org/journals/overload/14/71/miller_2004/
- loeg 2y ago(2015) or earlier
- variadix 2y agoWorth noting that the metadata structure can be done more efficiently with a flexible array member, but it requires alignment to alignof(max_align_t) to obey the ABI. The structure will consume the same amount of memory (16 bytes on x86-64) but you avoid a dereference and you have an extra pointer worth of space to use for whatever.
- naasking 2y ago> This sucks, but if we make the assumption that no destruction shall happen during an async context, it’s kind of fine (Oh boy, this will definitely make people jump out of their seat). On a side note, I have yet to see a proper solution to that, and I would like to avoid having double pointed types to solve the issue – if anyone has an idea, feel free to send me a message or send a pull request on the github repository. An async context marks pointers to destroy rather than destroy them immediately (defer destruction by unqueueing them), and a dedicated thread periodically wakes and processes the queue of marked objects. It must should be processed on another thread because the object is by definition no longer reachable from the original thread since it's marked for destruction.
- Spivak 2y agoI'm assuming the OP is doing this specifically to avoid implementing a GC so I'm not sure this approach would be satisfactory, but it would work.
- naasking 2y agoYou wouldn't need to spin it up it if you don't use "async contexts", and the GC only collects objects freed during those contexts so it can be very limited. You could eliminate it entirely if your program threads periodically call into a cleanup function manually to process this queue. The point of the thread is to make this automatic since that's ostensibliy the whole point of smart pointers.
- Dwedit 2y agoBasically a wrapper for "__attribute__(cleanup)" GCC took the best C++ feature (destructors) and added it into C. Not sure about compatibility with other C compilers.
- loeg 2y agoI'm fairly certain Clang also implements it.
- indigoabstract 2y agoApparently only Clang so far. https://clang.llvm.org/docs/AttributeReference.html#cleanup https://clang.llvm.org/docs/AttributeReference.html#cleanup
- ainar-g 2y agoThere is a proposal to get this using the "defer" keyword into either the next C revision or the one after that[1]. [1]: https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3199.htm https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3199.htm.
- tredre3 2y agoThat would be sublime! Defer is something I've been wanting in C for 20 years. It makes resource management clear, easy to reason about, and concise.
- Dwedit 2y agoThat looks a lot like what Zig does. One problem with the proposed defer there is that it has no way to interact with stack unwinding, which is part of the platform ABIs for handling exceptions. Maybe some alternative syntax, such as "defer_finally" could make the defer block act as both a regular defer block, and also explicitly add that block to the stack unwinding chain.
- deleted 2y ago[deleted]
- 2y ago
- sp1rit 2y agoGLib also provides macros that use autocleanup.[0] Using a bunch of macro magic, they allow you to write `g_autoptr(GPtrArray) arr = ...` to automatically unref arr when the scipe exits. One footgun that autocleanup has and C++ smart pointers don't is, that the cleanup function isn't called on reassignment, so reassigning on a autoptr causes the previous value to leak. [0]:https://docs.gtk.org/glib/auto-cleanup.html https://docs.gtk.org/glib/auto-cleanup.html
- tempodox 2y agoStrong vibes of Greenspun's tenth rule: Any sufficiently complicated C or Fortran program contains an ad hoc, informally-specified, bug-ridden, slow implementation of half of Common Lisp. If you want garbage collection, use a language that has it.
- jay-barronville 2y agoSmart pointers aren’t exactly the same thing as garbage collection…
- eska 2y agoI think the desire for smart pointers comes from having many allocations of various lifetimes. In my opinion smart pointers are a band-aid at too low a level to solve this problem. In C++ this seems particularly attractive because it is very common to consider freeing memory and uninitializing memory to be the same thing and handle both with RAII. C becomes much more bearable and even ergonomic when introducing multiple allocators, especially an arena. This reduces the amount of separate allocations and lifetimes dramatically. A big failure of the C standard library in my opinion is that it does not offer such allocators and the APIs don’t make use of them (which is why strings are terrible in C).
- indigoabstract 2y agoThe thing I like most about C is its simplicity, its minimalism and it being the interface of choice for other languages to talk to each other. You can completely understand the language since it's so small and doesn't change much and (if you avoid complicated pointer expressions) that also helps with reading the code. That said, I don't enjoy manual memory management, but I'd like a GC even less. Arena allocators are useful, but since reference counting is the preferred general solution in C++, could something similar also work in C without taking away that minimalism? I think it might, but that is just my opinion.
- andrekandre 2y ago> could something similar also work in C without taking away that minimalism? maybe automatic reference counting? [1] [1] https://en.wikipedia.org/wiki/Automatic_Reference_Counting https://en.wikipedia.org/wiki/Automatic_Reference_Counting
- indigoabstract 2y agoYeah, I remember about that. It works well for Objective-C. But since C doesn't have objects or messages, the best equivalent thing I can think of is for the C compiler to emit some type of signal on assignments and variables going out of scope. I don't claim to know what's the best solution for adding automatic memory management to C, but I'm pretty sure a good one exists. And it would need compiler support for it to work.