5 ms·
I wouldn't call this equivalent to the private keyword: * It does not work on a field, but on the whole struct. Either all fields are public or all are private
by gjulianm 3y ago
I wouldn't call this equivalent to the private keyword:
* It does not work on a field, but on the whole struct. Either all fields are public or all are private. The latter case forces you to write getters/setters for properties you want users to access, and that in C can be even more cumbersome as you need to write the definition in the .h and the implementation in the .c.
* It breaks, without an explicit and specific and error message, several actions. As it's mentioned in the article, malloc isn't possible, but neither are copies by value, sizeof() breaks... And those will break with an "incomplete type" error message, not a "this type is intentionally made private", which can add confusion in some situations.
* Completely incompatible with inlining code. Considering a lot of people still use C precisely for its performance, I think this can be a drawback in a lot of usecases.
I honestly think that hiding struct declarations should be done sparingly, and preferably limiting it to cases where it's actually necessary (for example, a library that doesn't expose internal struct fields so the same executable works with different versions of the dynamic library; or proprietary libraries that want to expose as little as possible). In the end it's still easy to bypass, and the distinction between header and code files already provide an indication of which functions you should use and which ones you shouldn't.
- Joker_vD 3y ago> It does not work on a field, but on the whole struct. struct PrivateFieldsOfMyShinyClass; struct MyShinyClass { int somePublicData; double morePublicData; struct PrivateFieldsOfMyShinyClass *p; }; > malloc isn't possible, but neither are copies by value, sizeof() breaks Those are implementation details which are deliberately being hidden. > Completely incompatible with inlining code. They are as incompatible with code inlining as public/private modifiers in C++ are. That is, LTO is your best friend here. Also, have you ever tried to maintain binary compatibility with several versions of a third-party C++ library that keeps adding/removing private fields to/from its classes?
- jstimpfle 3y agoHiding members in this way is only possible with pointer indirection, which isn't satisfying. However, having only a boolean private/public access state isn't generally satisfying either. It often leads to violation of the principle of separation of concerns when all the functions (methods) acting on certain "private" fields need to live in the same class. In simple classes, like std::vector, it's possible to get away with private. But in many cases that are more complex than that, it seems to me that the best approach is still to expose the data and to be just very clear about the exact purpose of each member.
- gjulianm 3y agoThe code sample still isn't equivalent. Now you have yet another pointer, which implies another allocation, another source of possible memory leaks and mistakes, and a separate memory space that will hurt the cache. > Those are implementation details which are deliberately being hidden. I know they're being hidden deliberately, but in C++ "private" doesn't break malloc (new), copies by value or sizeof. Or stack allocation, to add to the list. > They are as incompatible with code inlining as public/private modifiers in C++ are. That is, LTO is your best friend here. public/private aren't incompatible with inlining in C++. That is, you can call class functions that access private members and the compiler can inline those functions. Also, LTO is not always enabled by default, and doesn't always inline the things you want it to inline. > Also, have you ever tried to maintain binary compatibility with several versions of a third-party C++ library that keeps adding/removing private fields to/from its classes? I mentioned binary compatibility as one of the reasons one might want to do this. However, if you have a third party that doesn't care about API compatibility I doubt struct fields are the only thing they're going to change constantly.
- skribanto 3y agoMaybe have your public fields defined as a second struct, and then you can cast the pointer to your struct to the concrete struct that has all the public fields. This has the restriction that all public fields must be at the start, and you must make sure to maintain the same order between the two structs. At this point though, I think I honestly would prefer setters/getters. struct MyClassPublic { int x; int y; ... } /* using it */ MyClass *myclass = myclass_create(); ((MyClassPublic *)myclass)->x = 5;
- dfawcus 3y agostruct public_stuff { ... } struct private_stuff { struct public_stuff public; ... } struct public_stuff *make_public(/* ... */) { struct private *prv = malloc(sizeof *prv); /* ... */ return &prv->public; } Then when passed in to functions, cast the passed in struct pointer to the private one. The public struct doesn't even have to be at the start if one make appropriate use of offsetof and ensuring valid alignment. Nothing new under the sun...
- falcrist 3y agoI guess I'm reading the article a different way, because what I'm getting is that the author is suggesting the C analogue of a class is an entire header/source "module". That would make more sense, since you can use the header to craft an interface in which some components are public and some are private.
- ricardo81 3y agoThat's how I read it. Clearly you can abstract away the parts of data that the API should not see.
- gjulianm 3y agoYes, that's usually how it goes, each header/source is like a "class". And usually, what you have is a "main" struct that represents the "class" itself. In this case that main struct can't have both private and public fields.
- falcrist 3y ago> And usually, what you have is a "main" struct that represents the "class" itself. Yes that tracks with the work I've done. It could even be so simple that only one value needs to be exposed through a getter (along with a couple "methods"). An ADC in an embedded system could operate like that.
- lelanthran 3y ago> Yes, that's usually how it goes, each header/source is like a "class". And usually, what you have is a "main" struct that represents the "class" itself. In this case that main struct can't have both private and public fields. You can, in a defined way (i.e., no invoking of UB). I just didn't put that in.
- gjulianm 3y agoOut of curiosity, how would you do it? The ways I've seen require using another "public" struct and either casting public to private or using a nested pointer, each with their set of problems. In either case, it's still a bit hacky and still each struct is all-or-nothing public or private.
- lelanthran 3y ago> I wouldn't call this equivalent to the private keyword: I didn't mean to imply it is, I lead with: >> All too often someone, somewhere, on some forum … will lament the lack of encapsulation and isolation in the C programming language. This happens with such regularity that I now feel compelled to address the myth once and forever. It's only about the myth that C doesn't have any level of encapsulation or isolation.
- brabel 3y agoNearly every rebuttal on the internet starts with misinterpretation of what's being said. Good reading skills are extremely rare, it seems. To be clear: you tried to say "C offers encapsulation/isolation". People read "This solution is equivalent to 'private'", an almost completely unrelated statement, and then respond to that. That could typically be classified as a "Straw man fallacy"[0], but I believe people who do this in many cases simply do not have the necessary reading skills to understand what proposition has been made, and therefore honestly believe themselves to be reasoning correctly (i.e. without fallacy). Reading comprehension used to be a topic at school when I was a child. I suppose that's no longer the case?? [0] https://en.wikipedia.org/wiki/Straw_man https://en.wikipedia.org/wiki/Straw_man
- jasode 3y ago>To be clear: you tried to say "C offers encapsulation/isolation". People read "This solution is equivalent to 'private'", an almost completely unrelated statement, and then respond to that. >That could typically be classified as a "Straw man fallacy"[0], but I believe people who do this in many cases simply do not have the necessary reading skills Fyi... the author's article that this thread is about has in bold heading: "Myth: C has no equivalent to “private”" So, a reasonable interpretation of the text following that headline is how to use C Language constructs to dispel that myth. Doesn't seem like "straw man" applies here.
- gjulianm 3y agoAs the other commenter said, and I want to reiterate, what led me to start with the talk about the private keyword is the big bold header that says "Myth: C has no equivalent to “private”" and the first code snippet that shows how you can have "private" fields in C++ and the following ones that show an "equivalent" implementation in C. So I think it's reasonable to infer that the author is talking about encapsulation and "the private keyword" as somewhat interchangeable (and I don't disagree, for this discussion they're practically the same). Not only that, but the points I made are about the implementation shown in the article, which is independent of whether the talk is about "private equivalency" or "lack of encapsulation": the gist of it is "yes, you 'encapsulate' things but not in the same way and it comes with disadvantages that aren't really there in other languages". With all that said, I don't think all that condescending talk about the lack of reading comprehension or skills, without actually going into the arguments themselves, is really necessary or positive.
- throwawayiddqd2 3y agoWith plan9-extensions gcc flag: struct Thing { int i; }; In an implementation c file: struct _Thing { struct Thing; int b; }; struct Thing *new_thing() { struct _Thing *thing = malloc.. thing->i = 0; thing->b = 1; return thing;
- dmitrygr 3y ago>* Completely incompatible with inlining code You were right 10 years ago. Today we have LTO
- gjulianm 3y agoLTO isn’t always going to inline things you want it to inline, that’s the main issue I have with it.