| ▲ | A C++ Mixin System(jennyjams.net) |
| 74 points by luu 6 days ago | 40 comments |
| |
|
| ▲ | Svetlitski 3 days ago | parent | next [-] |
| > I think the big asterick to all of this design is that my ideal framework would not look like standard C++ but like a slightly weirder Rust stdlib An interesting option in this space is rpp [1], which bills itself as a “Minimal Rust-inspired C++20 STL replacement” [1]: https://github.com/TheNumbat/rpp |
| |
|
| ▲ | kazinator 3 days ago | parent | prev | next [-] |
| GNU C++ once had a system called Signatures, could support mixing in. It was removed. Many years ago now I think. A signature resembles a class declaration in that it specifies member functions. A signature is never instantiated. Rather any class which has those member functions conforms to the signature, and a signature-typed pointer can point to instances of that class. Thus signatures bring about quack-like-a-duck polymorphism not requiring inheritance. |
| |
| ▲ | humanrebar 3 days ago | parent [-] | | You can implement that in C++ code, no language extensions required, by using type erasure and a pointer semantic type with at least one constructor templated on the type of the object being pointed to. | | |
| ▲ | kazinator 3 days ago | parent [-] | | A signature pointer type knows nothing about the types it is legally pointing to; moreover, those types know nothing about the signature. signature communicator {
size_t send(const unsigned char *buf, size_t size);
size_t recv(const unsigned char *buf, size_t size);
};
class foo {
size_t send(const unsigned char *buf, size_t size);
size_t recv(const unsigned char *buf, size_t size);
};
foo f;
communicator *c = &f; // valid: foo conforms to communicator signature
(Not sure if these member functions need to be virtual? I would have to dig up the Signatures docs.)This feature was removed because the implementation was becoming hard to maintain, or some reason like that. You can see it's pretty crazy from a C++ point of view, because c->recv(...) can be called through this pointer and it has to work for absolutely any object whose class conforms to the signature. And classes don't know they are conforming to any signature; nothing is declared in them for that. C++ polymorphism normally depends on declaration relationships through which implementation details like vtable positions can be inferred. | | |
| ▲ | saurik 3 days ago | parent [-] | | But yet it is still true that you can implement this idea in normal C++ the way humanrebar described. (This is similar to std::function, which is a pointer to an object that happens to have an operator() that conforms to listed signature; but you can generalize that to any other set of things you want to be able to abstract into the pointer.) |
|
|
|
|
| ▲ | cisters 3 days ago | parent | prev | next [-] |
| In a language with ad-hoc polymorphism like C++, mixins seems entirely unnecessary. You can just declare by convention that a freestanding clone(T x) -> T function should exist for it to be 'cloneable'. |
| |
| ▲ | cgh 3 days ago | parent | next [-] | | This “mixin” concept uses the CRTP pattern, as mentioned in the post: https://en.wikipedia.org/wiki/Curiously_recurring_template_p... It actually does have specific applications. That Wikipedia article shows a good example of polymorphic method chaining. In a former life, I worked with Microsoft’s Active Template Library, which is entirely based on this pattern. | |
| ▲ | Rucadi 3 days ago | parent | prev [-] | | And just use concepts and call it a day. |
|
|
| ▲ | mightyham 3 days ago | parent | prev | next [-] |
| While I've never really found much practical use for mixins, it is fairly easy to create a runtime system for them in Java. Any interface can become a mixin simply by storing state in a static global hashmap with `this` as the key to the map. Specifically for the map, I would use `Collections.synchronizedMap(new WeakHashMap<>())` so that the map is thread-safe and allows mixin instances to be garbage collected. |
|
| ▲ | Matheus28 3 days ago | parent | prev | next [-] |
| std::optional<T&>
Can't have optional references in C++. Use either std::reference_wrapper, or just a pointer |
| |
| ▲ | cocoto 3 days ago | parent [-] | | It is specified previously in the text that support for references would be nice for optionals. | | |
| ▲ | tialaramex 3 days ago | parent [-] | | And unless I'm mistaken C++ 26 gets std::optional<T&> with the preferred representation (ie it's the same size as T& like with Rust's Option<&T> and &T pairing) and the ergonomics are no worse than you'd expect for C++ |
|
|
|
| ▲ | heresie-dabord 3 days ago | parent | prev | next [-] |
| It seems messy and even the author of TFA is unconvinced. How does a mixin compare to role or interface in languages that do not have multiple inheritance? |
|
| ▲ | cherryteastain 3 days ago | parent | prev | next [-] |
| I don't really see the point when C++ already lets you write void foo(auto& t) { t.bar(); } which can be called with any class that has the .bar() method anyway. |
| |
| ▲ | Maxatar 3 days ago | parent | next [-] | | Because it can also be called with any class that doesn't have the bar method, and good luck deciphering the compiler error for it when it's nested 3 levels deep into some far corner of the STL. | | |
| ▲ | lbhdc 3 days ago | parent [-] | | That is what concepts fix. It lets you enforce at build time that `t` does have member `.bar()`. | | |
| |
| ▲ | Toritori12 2 days ago | parent | prev [-] | | man I love learning good stuff by casually learning HN. I code on cpp as a hobby and kinda new you could use "auto" on templates by didn't know about this. | | |
| ▲ | asyx 2 days ago | parent [-] | | Check out concepts. That allows you to enforce the existence of the function. Also works with static functions and such. |
|
|
|
| ▲ | tempodox 3 days ago | parent | prev | next [-] |
| That `String` leaks memory, it doesn't have a destructor. |
| |
|
| ▲ | bnastic 3 days ago | parent | prev | next [-] |
| A lot of this stuff has been investigated in Mr Alexandrescu's ironically named book Modern C++. Typelists (before variadic templates) recursive templates and componenet-like assembling of classes, etc.
I imagine there is a modern-modern-c++ version of Loki library somewhere on github. |
|
| ▲ | Asooka 3 days ago | parent | prev | next [-] |
| Code with types on the right like this makes me very sad static
auto create(const char* data) -> Result<String>
Types are a lot more ergonomic on the left - the return type of a function and the type of a variable are very important for understanding and skimming code. When the return type is on the right, it is physically very far from the name of the object and I have to scan the entire line with my eyes to get the same amount of information I would get by just looking at the left column and scrolling down if it were on the left. I am pretty sure in another 20 years types on the right will be regarded as one of the ergonomic fails of current language design. At least, if have to do this, put the type right under the object name, like so: static auto
create(const char* data)
-> Result<String>
Future readers will thank you. |
| |
| ▲ | 3836293648 3 days ago | parent | next [-] | | Types are only nicer on the left when it isn't also annotated with all the other static constexpr [[nodiscard]] nonsense. And left types are actually greppable seperately from variable declarations. Having both left and right types is stupid, but as a whole right types are easier to deal with | | |
| ▲ | winocm 3 days ago | parent [-] | | I learned of a cute side effect when one puts the function name on its own line, like above. In BSD of yore and modern contemporaries, one could often perform `grep '^function'` and end up finding the source file quite easily. I think it also makes using ctags(1) a bit easier too, but not entirely sure on that bit. | | |
| ▲ | Iwan-Zotow 3 days ago | parent [-] | | you could grep `grep '^auto function'` with the same efforts | | |
| ▲ | 3836293648 2 days ago | parent [-] | | No, that clashes with variable declarations. Sure, you could have a naming scheme that doesn't have that issue, but that doesn't help with library code. |
|
|
| |
| ▲ | PittleyDunkin 3 days ago | parent | prev | next [-] | | I cannot disagree more strongly. Putting the name of the function (or method) in the middle of the declaration drastically lowers readability. C-style type declarations were always the most painful part of reading C. | |
| ▲ | binary132 3 days ago | parent | prev | next [-] | | FWIW I completely agree, I think -> rt is a very silly idiom in declarations. It’s fine and useful for lambdas. | |
| ▲ | bigstrat2003 3 days ago | parent | prev [-] | | No they won't. Your example is way more unpleasant to read. |
|
|
| ▲ | surajrmal 3 days ago | parent | prev | next [-] |
| We've used this pattern for years. It definitely delivers in terms of being lower overhead. I will say that compiler errors can be nonsense though. |
|
| ▲ | binary132 3 days ago | parent | prev | next [-] |
| I am curious about this idea, and maybe it’s a “me problem”, but I’m having a very hard time following the article. There’s a lot going on here. |
|
| ▲ | DidYaWipe 3 days ago | parent | prev | next [-] |
| "Mixin?" What is that supposed to mean? |
|
| ▲ | DidYaWipe 3 days ago | parent | prev | next [-] |
| "Mixin?" What's it mixin'? |
| |
|
| ▲ | bfrog 3 days ago | parent | prev [-] |
| C++ is somehow aesthetically dis pleasing, thus mixin idea doesn’t change the needle for me. |