Live data from Hacker News

C++ Patterns: The Badge

awesomekling.github.io

41–50 of 160 posts

Re: C++ Patterns: The Badge

#41

C++ could solve this without badges, if it didn't have a very tiny, silly misfeature. The misfeature is this: a class A can only declare a specific member function of class B as a friend , if that B member function is public! Example: The idea here is that Device has a static member function called Device::registrationHelper . That specific function (not the entire Device class) is declared a friend to VFS, and so th…

Your solution is similar to mine and you might find it helpful:

https://news.ycombinator.com/item?id=20160957

Re: C++ Patterns: The Badge

#42
> These functions are called by all Device objects when they are constructed and destroyed respectively. They allow the VFS to keep track of the available device objects so they can be opened through files in the /dev directory.

> Now, nobody except Device should ever be calling VFS::register_device() or VFS::unregister_device(),

Why?

Either have VFS call the constructor because non-registered devices are banned and RAII is good, or don't create arbitrary restrictions to hamstring how the rest of the system manages Devices.

Re: C++ Patterns: The Badge

#44
I used to carefully design my C++ code with public / private / protected / friend (and the pointer-to-impl pattern for "compile time private") but a few years ago I started doing everything all-public ala python (struct instead of class by default) with occasionally an m_ prefix for "intended to be private", and I've never looked back. It's been great, made my programming life much easier.

This badge thing is clever, but seems like yet more accidental/unnecessary complexity on top of the already-probably-unnecessary private/protected/friend - as requirements change you now have this extra layer of access-control cruft to reason about, that you probably wouldn't even miss if it didn't exist.

Not to get too ranty, and no disrespect to Andreas, but the C++ culture seems to thrive in unnecessary / accidental complexity, deep template hacks and the like that add friction to refactoring, obfuscate underlying logic so you miss opportunities to simplify, and turn straightforward programming problems into brain-bending template puzzles.

OOP also; I've seen so many crazy class architectures that could just be a handful of plain-old-functions, complex virutal "type families" that could just be a TypeOfThingThisIs enum with some if statements in the respective functions, etc.

I don't know the Serenity codebase and use-case very well, and perhaps this kind of thing has a place in large libraries, but honestly even then I'd lean towards just making everything simple & public and use a prefix when needed, or describe appropriate usage in documentation. Seems to work in the python world (minus a few outlier codebases that go OOP-crazy).

Re: C++ Patterns: The Badge

#45
This is a typical example of a solution to an artificial, self-imposed problem created by C++'s assumptions.

As a programmer, I would feel frustrated to have to figure this out instead of spending time on something a user would actually care about.

Re: C++ Patterns: The Badge

#46
post #38

Personally I'd move the register_device() and unregister_device() functions outside of the VFS class entirely, perhaps to namespace scope. Alternatively, there are other ways to leverage friendship and access control in C++. Here's one option: template class DeviceManager; class Device { template friend class DeviceManager; int y; }; class VFS { friend struct DeviceManager ; int x; }; template struct DeviceManager {…

The thing is, there is no restriction on who calls DeviceManager::register_device. main() is able to do it.

This doesn't seem so different from:

   class A { private: friend void foo(A &, B &); int x; };
   class B { private: friend void foo(A &, B &); int y; };

   void foo(A &a, B &b)
   {
      a.x = 1;
      b.y = 2;
   }
   
   int main() {
      A a; B b;
      foo(a, b);
   }
We've just obfuscated it a bit by using a template class with one function in it, and declaring that to be the friend instead of just a non-member function.

Re: C++ Patterns: The Badge

#48
This is especially helpful in conjunction with private constructors and make_shared or make_unique. You cannot even friend make_shared because of namespace aliasing in standard libraries, but shared_from_this can make improperly allocated objects error at runtime. Private badges/tokens solve that problem by allowing public constructors requiring a parameter value of the badge/token type that has limited visibility, but can be constructed inside a factory and passed to make_shared.

Re: C++ Patterns: The Badge

#49
post #38

Personally I'd move the register_device() and unregister_device() functions outside of the VFS class entirely, perhaps to namespace scope. Alternatively, there are other ways to leverage friendship and access control in C++. Here's one option: template class DeviceManager; class Device { template friend class DeviceManager; int y; }; class VFS { friend struct DeviceManager ; int x; }; template struct DeviceManager {…

The thing is, there is no restriction on who calls DeviceManager ::register_device. main() is able to do it. This doesn't seem so different from: class A { private: friend void foo(A &, B &); int x; }; class B { private: friend void foo(A &, B &); int y; }; void foo(A &a, B &b) { a.x = 1; b.y = 2; } int main() { A a; B b; foo(a, b); } We've just obfuscated it a bit by using a template class with one function in it, a…

Yes, your version is what I mean by moving it to namespace scope, and it's probably what I'd do to start off with.

> there is no restriction on who calls DeviceManager::register_device

Well, you can make it private inside DeviceManager and give the class its own friends :-))

Another benefit of my solution is that adding things to DeviceManager doesn't require changing the VFS.h or Device.h headers at all. Anything that links these 2 classes is an entirely separate concern and very much in line with thinking in terms of models or concepts rather than types. What you want at the end of the day is an absolute bare minimum of member functions in any class.

To be honest, whatever the solution, the key is that having the Device class self-register to the VFS in its constructor (as in Andreas's blog post) is, imho, an anti-pattern and is what is really leading to this Badge mess.

Re: C++ Patterns: The Badge

#50
post #45

This is a typical example of a solution to an artificial, self-imposed problem created by C++'s assumptions. As a programmer, I would feel frustrated to have to figure this out instead of spending time on something a user would actually care about.

Which popular programming languages provide proper ACLs (or equivalent functionality) for class members?
Post reply on HN