Hacker News new | past | comments | ask | show | jobs | submit login

I like it but is access restriction really a problem? In my years of C++ programming it always seemed to be a theoretical problem more than something that leads to crashes.

This solves a chronic architectural maintenance problem in complex C++ code bases, which I've also run into countless times.

Any visible interface, public or private (via friend), will eventually be used by other programmers for other than its intended use case simply because it is there and accessible. For the maintainer of an interface with a single intended purpose, what should be a clean, tidy modification within the scope of its intended use ends up breaking all kinds of unrelated code that the maintainer wasn't even aware of that is using the interface in ways the designer didn't anticipate. It is a common form of architectural rot. Reducing the incidence of this in large code bases is usually enforced by soft means, like sternly worded comments, vigorous policing, etc but that only goes so far. Ideally, an access policy would be in the code itself and enforced by the compiler.

The public/private labels don't address this. If you make things public, the world has the ability to do unseemly things with your interface. If you friend a bunch of classes so they can access private methods, those classes have the ability to do unseemly things with your private implementation details. Both cases embrittle the architecture. What you really want is fine-grained ACLs to class members to document both intent and dependencies.

This method of restricting usage of methods to specific classes at compile time is clever and provides good documentation of design intent, I haven't seen that pattern before. I also would guess that the compiler elides the Badge argument since it is never used.

There's a name for this phenomena: Hyrum's Law.

> With a sufficient number of users of an API, it does not matter what you promise in the contract: all observable behaviors of your system will be depended on by somebody.


I never knew it had a "real name". I always referenced back to the comic: https://xkcd.com/1172/

Essentially, a bugfix that prevents CPU overheating breaks someone's workflow involving a temperature sensor (jokingly).

The best guard against unintended uses that hamstring the maintenance of the library is the good old opaque handle:

   typedef struct foo_struct *foo;

   foo foo_make();
   void foo_destroy(foo);
   void foo_frobnicate(foo, int how);
The client executables have no idea how big a foo object is, or what alignment requirements are, let alone what its members might be. The client executables don't control the allocation, construction, clean-up, freeing, or anything else.

The programmers who use this cannot just edit a header file to gain access to something; the don't have a header file which tells them anything. They could reverse engineer the data structure, but to thwart that, you could scramble the order of the structure members with each new release of the component, so their hack wouldn't be backward or forward compatible.

That doesn't solve the problem the OP solves, which is to expose a method only to a specified set of other classes.

It solves the problem by decoupling the public interface and necessary implementation details (object size), the register/unregister functions can be moved to an internal only header file, maybe even some file scoped functions. AFAIK this is not possible with a class declaration.

I think you could also solve this specific problem with an internal header file in c++ too (by moving register/deregister to it's own class) but this solution solves other problems like binary compatibility.

> I also would guess that the compiler elides the Badge argument since it is never used.

It's also zero width, so no temporaries, etc.

Problem is, nothing elides the presence of these obtrusive badge parameters in the source code.

A few bytes of stack is the least of my concerns on some file system device registration function that is called five times when the system boots.

If we imagine a software organization "going to town" with this badge approach so that there are badges all over the code base, it's not hard to imagine how it would be a nuisance.

Depending on how you approach it (eg if you don’t instantiate it as {} but instead something like access::device, which is a type alias for Badge<Device>) then I find being explicit about access rights is quite helpful sometimes. In this case, I probably would avoid it too in favour of private headers, but then, I prefer freestanding namespaces functions over objects when possible anyway (too much functional programming maybe), so I’d partition my API that way instead.

This pattern is defensive by nature so people who use your library in the future will be less prone to making mistakes. Also, it shows intent to protect that asset clearly which generally leads to easier maintainability. If someone sees this guys code, they will definitely ask questions about why he is doing it, and if they are competent, they can draw the same conclusions that his blog leads you to without having to read the blog.

I'd guess it's more architectural concern than runtime. Making sure only code that is supposed to uses a semi-private interface so people don't build dependencies that aren't wanted.

The C++ access restrictions exist solely to teach and enforce proper API usage by other developers.

They're supposed to trigger a reconsideration of the API when a new developer runs into a restriction, but more often in my experience the new developer will just add a new public interface.

And you can add yourself a public interface even if all you have is a compiled library, and header files.

Flipping a "private:" to "public:" has no effect on the binary compatibility.

> Flipping a "private:" to "public:" has no effect on the binary compatibility.

That's not the case on Windows. The access qualifier is part of the mangled name. https://en.wikiversity.org/wiki/Visual_C%2B%2B_name_mangling...

It's still an honor system. There's a way to generate code that will call the "private" or "protected" data/function even if it violates what's specified in the headers.

That shouldn't prevent the subversion of data members or inline functions, or the ability to add your own member functions to the class.

Because Badge is a template, this will produce a linker error (duplicate symbols).

Of course you can get around if you really, really want to. But I think this subthread is starting to split hairs.

It can change the "POD for the purpose of layout" status of a class on Itanium. It can change layout.

Guidelines | FAQ | Support | API | Security | Lists | Bookmarklet | Legal | Apply to YC | Contact