8 ms·
As someone who has a file with similar hacks, I will say this: I am not a C++ fan, but if you find yourself writing C code where you simulate methods via struct
by throwaway7894 1y ago
As someone who has a file with similar hacks, I will say this: I am not a C++ fan, but if you find yourself writing C code where you simulate methods via structs with function pointers often, just use C++ as a basic "C with classes" at that point. You want methods anyway, you have to go through a pointer dereference to call the function, it's just not worth the code weirdness. If you have the grit to use structs with function pointers everywhere, you have the grit to stick to the simpler subset of C++.
- uecker 1y agoWhy? I do not find the syntactic sugar C++ adds very helpful and it misses other C features.
- ryao 1y agoPerhaps he wants to jump through hoops to avoid function pointers, even when doing that in C++ for optional functions like is done in the VFS requires gymnastics: https://godbolt.org/z/4GWdvsz6z https://godbolt.org/z/4GWdvsz6z That is the closest I can get it to implementing an optional function via a C++ class member function instead of a function pointer. It is not only insane, but also masochistic in comparison to how it would be done via function pointers: https://godbolt.org/z/qG3v5zcYc https://godbolt.org/z/qG3v5zcYc
- unclad5968 1y agoI'm torn. The step from C to any c++ is big. Now if you want anybody to be able to use your code they need to be using c++ or you have to provide a C api anyway. On the other hand, manually implementing vtables is annoying. Ive been sticking to pure C and haven't been bothered enough to go back to any c++ yet (about 6 months on my current project). I mostly only miss templated containers so far.
- ryao 1y agoIt is more annoying to want to implement an optional function in a class and then have no simple way to check if that optional function is implemented in the object without, having to edit code that guards the call sites every time you add a derived class that implements it, or having to implement your own way of querying the object to know if it is supported.
- unclad5968 1y agoI've never come across a situation where I wanted to do this. What would be a use case for optional class functions?
- ryao 1y agoWell, it would not be a class in that case, but a use case would be anything that is modular where you want to support a new function, but do not require all of your implementors to use it. The best example of this is the VFS. Here is documentation for the Linux VFS: https://www.kernel.org/doc/html/latest/filesystems/vfs.html https://www.kernel.org/doc/html/latest/filesystems/vfs.html The vast majority of the function pointers in those structures are optional (even if not explicitly stated). To give a few common sense examples: * If your filesystem does not support extended attributes, you would not implement .listxattr and instead set it to NULL. * There are multiple ways of implementing read and write in file_operations. You have the basic read and write operations, and more efficient variants. You don’t need to implement the more efficient variants if you don’t want to implement them. * The .bmap call is used to find out how the filesystem stores a file on a block device, which used to be used by the syslinux (and might still be). This obviously is incompatible with NFS (or any multidisk filesystem like ZFS) so it absolutely must be optional. Then there are other options, like not supporting mmap, or not supporting creation/removal of subdirectories. That sounds absurd, but some FUSE filesystems, particularly those exporting a program’s statistics, don’t bother with either of those since they are not needed. I do not believe Linux sysfs allows users to make directories either. I could continue, but this gives a few examples of why you might want to have optional functionality in a class-like interface. By the way, I mentioned setting things you do not implement to NULL. This is done simply by not specifying them when using the structure initializer syntax. The compiler will zero unspecified members.
- pjmlp 1y agoNothing prevents implement something like that in C++, it is still C++ code. If you want to make something fancy, templates, if constexpr requires func-to-call, call func.
- codr7 1y agoNope, not from my experience. Because in C++ the features are just there right around the corner, they will seep into the code base. And I don't want even classes, there's too much junk in there that I don't need.
- ryao 1y agoThis is very bad advice for a few reasons: 1. It is not possible to add optional member functions (which would be pure virtual functions) to a C++ class base class and then check at runtime if they are unimplemented in the object (at least not without implementing some way to query the object, which is slow). If you say to handle this by having typeid checks at runtime, look at the VFS and then notice that you cannot implement this typeid check in advance, since you cannot add a typeid check for a derived class that did not even exist when you compiled your code. Thus, you still need to use structs of function pointers in C++. Maybe you can use C++ classes for some cases where structs of function pointers are used, but you would giving up the ability to implement optional functions in a sane way. 2. It ignores all of the things in C that are absent from C++. In particular, C++ refuses to support C’s variably modified types and variable length arrays, which are useful language features. 3. It ignores all of the things in C++ that you likely do not want, such as exceptions and RTTI. The requirement to typecast whenever you assign a void pointer to any other pointer is also ridiculous.
- humanrebar 1y agoYou can do everything you describe in C++. Even the language features are available (or diableable) as compilation flags.
- ryao 1y agoNot everything. See the features that C++ refuses to support. Beyond that, there is no compiler flag to stop requiring explicit casts of void pointers before assigning them. Furthermore, less is more. You get faster build times with C because it does not support all of the features C++ has. Just because you can do it in C++ does not mean you should. I used C++ for one of my first projects for a startup in health care and I really wish I had not. C++ made development a hellish experience as I spent most of it on fighting the compiler to be able to use every C++ language feature I could imagine and not enough on actual issues. It easily doubled development time since I spent most of it on things that only existed because C++ had overcomplicated everything (e.g. reference versus pointer, public versus private, shoehorning OOP into places it did not belong, operator overloading, templates, etcetera). This was during my initial attempt at graduate studies and after ruining a semester because of it (this had been intended to be a part time thing), I parted ways with the company. The C++ daemon went on to be the heart of the company, despite the lingering bugs. I ended up fixing the remaining issues as a consultant years later, but eventually, I realized that everything would have been better had I not used C++ in the first place. There are times when I fantasize about rewriting it in C. One of these days, I might actually do that for the company for free if only to put an end to a mistake of my youth. Unfortunately, now that I have fixed the daemon, it has the advantage of being a mature, reliable codebase, so it is difficult to justify a rewrite. That said, despite my complaints about the effect C++ had on development, I did a number of things right when architecting that daemon. The lingering bugs turned out to be trivial and it has scaled with the company for 13 years with no end in sight. When it finally is replaced, the reason will likely be that it did not support HA, rather than some inability to scale. My younger self had refrained from pursuing HA since it seemed infeasible to do within the spare time I had during a single semester.
- jstimpfle 1y agoThere's a big issue with C++ classes -- or rather methods. You need to list the entire class definition to expose public methods. That includes private members and private methods. This breaks encapsulation, exposing implementation details and their dependencies in the public header. This in turn leads to looong compile times and needless recompilations.
- pjmlp 1y agoHence modules, and yes I know there are still some issues to sort out.