LWN: Comments on "Object-oriented design patterns in the kernel, part 1" http://lwn.net/Articles/444910/ This is a special feed containing comments posted to the individual LWN article titled "Object-oriented design patterns in the kernel, part 1". hourly 2 Object-oriented design patterns in the kernel, part 1 http://lwn.net/Articles/447925/rss 2011-06-16T13:46:37+00:00 gowen <blockquote><i>There's the matter of exception handling logic not being able to be placed where you might want it, too. With a simplistic C error handling facility I can store an error code and check it later when it matters or is most convenient.</i></blockquote> Wow. I am truly staggered ... just utterly lost for words. Exceptions/RAII automatically propogate non-recoverable errors to the correct level of abstraction, error return values have to be propogated by hand. <blockquote><i> With exceptions, I need to wrap each individual function call (and operator, etc.) in order to ensure that nothing slips past.</i></blockquote> Holy living Christ on a bicycle... Do you really think that's how to write code that uses exceptions for error handling? If you do, please stop writing about it, because you don't know what you're talking about. Object-oriented design patterns in the kernel, part 1 http://lwn.net/Articles/447853/rss 2011-06-16T09:12:14+00:00 elanthis <div class="FormattedComment"> There's big differences.<br> <p> A C-style error handler can fail without bringing down the rest of the system. An exception cannot. There's also different definitions of "fail" with different levels of consequences, while an exception is always a fatal error unless you explicitly check for it.<br> <p> There's the matter of exception handling logic not being able to be placed where you might want it, too. With a simplistic C error handling facility I can store an error code and check it later when it matters or is most convenient. With exceptions, I need to wrap each individual function call (and operator, etc.) in order to ensure that nothing slips past.<br> <p> Operator overloading, virtual functions, etc. are all massively useful features that help an unbelievable amount in writing good code. Exceptions just don't bring any meaningful benefit to large, complex programs that need to keep operating, don't bring any readability/maintainability benefit, and pretty much only appear to be beneficial when you look at tiny little trivial error handling examples compared to very poorly written C APIs. (Operator overloading is often panned, but anyone who's ever written a game in C with C-style math libraries can tell you that operator loading opponents are full of doody; and anyone who's ever tried a math library in Python/Lua/JavaScript/Ruby/Java/C#/etc. can tell you with five times the conviction that dynamic typing or static typing without local types that C++ critics are full of doody.)<br> </div> Object-oriented design patterns in the kernel, part 1 http://lwn.net/Articles/447844/rss 2011-06-16T08:11:31+00:00 gowen <pre>void somefunction() { do_something(); do_something_else(); important_cleanup_code(); }</pre> <p>Can do_something() fail for some reason? If so, why don't you check for it? If not, why do you think you need to worry about it throwing an exception? <p>Under what circumstances do you imagine that C++ do_something_cpp() will throw an exception but do_something_c() silently succeed? Ah, Google Go http://lwn.net/Articles/447756/rss 2011-06-16T00:15:02+00:00 cmccabe <div class="FormattedComment"> There have been a lot of dynamically typed languages in recent years that have implemented what's called "duck typing." Python and Ruby come to mind, but I'm sure there are others. What is different about Google Go is that it is statically typed. Common Lisp is dynamically typed.<br> </div> Object-oriented design patterns in the kernel, part 1 http://lwn.net/Articles/447234/rss 2011-06-13T00:20:49+00:00 cmccabe <div class="FormattedComment"> <font class="QuotedText">&gt; Plus C's lack of initialisation also hurt a lot, no?</font><br> <p> I remember a thread here on LWN a while back where people were complaining about a performance regression in the kernel caused by zeroing struct page. So it seems that at least in some corner cases, not initializing memory is a feature. In a perfect world, non-initialization probably should be something that the programmer has to ask for specifically, rather than the default. But C was designed in the 1970s-- give it a break already.<br> <p> When C++ was designed, in the mid-1980s, the decision was made to keep all the old uninitialized variable behavior and add some more. So if you create a C++ class with some primitives, and forget to initialize them in one of the constructors, they'll be uninitialized. This also applies to copy constructors. The rationale was that programmers shouldn't have to pay for what they didn't use.<br> <p> Luckily there is some relief in sight, in the shape of Coverity and similar static analysis tools. These can catch most uninitialized variables.<br> </div> Object-oriented design patterns in the kernel, part 1 http://lwn.net/Articles/447024/rss 2011-06-10T13:22:45+00:00 paulj <div class="FormattedComment"> You can use single-loops for ultra-light-weight exception handling in any C-syntax-like language:<br> <p> <a href="http://bit.ly/j69nwV">http://bit.ly/j69nwV</a><br> <p> Obviously the do_final_stuff pattern can be used within the do_exceptional_stuff pattern (inc via helper funcs).<br> </div> Object-oriented design patterns in the kernel, part 1 http://lwn.net/Articles/447013/rss 2011-06-10T13:08:04+00:00 jond <div class="FormattedComment"> <font class="QuotedText">&gt; This kind of code is BAD.</font><br> <font class="QuotedText">&gt; For this reason:</font><br> <font class="QuotedText">&gt; ====================</font><br> <font class="QuotedText">&gt; void somefunction() {</font><br> <font class="QuotedText">&gt; do_something();</font><br> <font class="QuotedText">&gt; if (do_something_else())</font><br> <font class="QuotedText">&gt; return; //Whoopsie!</font><br> &amp;#9986; &amp;#9986; &amp;#9986; &amp;#9986; &amp;#9986;<br> <p> That isn't the example code I provided, at all. You've injected a return which I did not have in my code. My code, as written, will (in C) guarantee the last line to be executed if the process has not been terminated. Exceptions in C++ prevent you from making the same assertion, which is why you need RAII and/or…<br> <p> <font class="QuotedText">&gt; If you use smart pointers it's even nicer:</font><br> <p> …stuff like smart pointers.<br> <p> There's a good overview of using smart pointers and other techniques to ensure deterministic resource management here:<br> <p> <a href="http://www.slideshare.net/eplawless/exception-safety-and-garbage-collection-and-some-other-stuff">http://www.slideshare.net/eplawless/exception-safety-and-...</a><br> </div> Object-oriented design patterns in the kernel, part 1 http://lwn.net/Articles/446872/rss 2011-06-09T16:56:45+00:00 jd <div class="FormattedComment"> Well, no. Implicit code is (IMHO) always a Bad Thing because when the specs or compiler change, the resultant binaries will change behaviour when given the same source.<br> <p> The source is a specification document, to all practical intents and purposes, which the compiler uses to generate a program. If the program can change in nature for the same specification, the specification is incomplete and insufficient.<br> <p> In other words, by depending on something external (in this case, the compiler) you cannot do reliable testing. There are too many external parameters that you can never reliably take account of. Good engineering practice is to always work to reduce or eliminate the unknowables. If the compiler is any good, it'll ignore initializing to the compiler's defaults since the instructions generated by the compiler's default will already be present. If the compiler isn't any good, you really shouldn't be trusting it to be doing the Right Thing anyway.<br> <p> If you explicitly initialize, you always know the state of a variable, no matter what new C or C++ standard is produced. This is guaranteed safe.<br> <p> Compiler-tolerant software is necessarily well-engineered software. Yes, it's more work, but if you wanted to avoid work, you'd not be using C or C++, you'd be using a fourth- or fifth-generation language instead. The only reason to use anything in the C family is to be able to balance development efficiency with code efficiency. Higher levels of language offer better development efficiency, lower levels (Fortran, assembly, etc) offer better code efficiency. Neither is useful on its own for something like an OS kernel, which is why nobody writes general-purpose kernels on the scale of Linux in either Erlang or IA64 assembly.<br> <p> (Kernels do exist in both those, and indeed in Occam, but because of tradeoffs are almost always much more special-purpose.)<br> </div> Object-oriented design patterns in the kernel, part 1 http://lwn.net/Articles/446870/rss 2011-06-09T16:29:50+00:00 jd <div class="FormattedComment"> If we're going to have a language flame-war anyway, I propose rewriting the kernel in Occam. :)<br> <p> </div> Object-oriented design patterns in the kernel, part 1 http://lwn.net/Articles/446824/rss 2011-06-09T14:21:58+00:00 renox <div class="FormattedComment"> <font class="QuotedText">&gt; Implicitly generated code hurts. Default constructors hurt. Default parameters hurt even more. You might disagree, but I've had a lot of time to come to this opinion.</font><br> <p> Sorry but C++ poor mix of features doesn't mean that those features are always bad..<br> Plus C's lack of initialisation also hurt a lot, no?<br> </div> When in doubt, add another layer of indirection http://lwn.net/Articles/446773/rss 2011-06-09T07:44:53+00:00 kevinm <div class="FormattedComment"> The savings are per inode, not per inode_ops.<br> </div> When in doubt, add another layer of indirection http://lwn.net/Articles/446772/rss 2011-06-09T07:43:54+00:00 kevinm <div class="FormattedComment"> This seems like a reasonable idea, although the hit to readability and simplicity is such that you'd only want to do it in places where it would make a real memory difference.<br> <p> Why not try it and see?<br> <p> (By the way, it'd be 48 bits that you'd save replacing a 64 bit pointer with a 16 bit index).<br> </div> Object-oriented design patterns in the kernel, part 1 http://lwn.net/Articles/446672/rss 2011-06-08T19:22:01+00:00 marcH <div class="FormattedComment"> Linking at start time makes Java very flexible indeed. It is also what makes every Java program insanely slow to start up.<br> <p> You cannot have your cake and eat it.<br> <p> </div> Object-oriented design patterns in the kernel, part 1 http://lwn.net/Articles/446505/rss 2011-06-07T18:54:39+00:00 cmccabe <div class="FormattedComment"> I stand corrected. I thought that binary was way too small... but ldd didn't tell me anything useful.<br> <p> It's been a long time since I built gcc. Even back when I did embedded stuff, we always seemed to use prebuilt toolchains.<br> </div> Object-oriented design patterns in the kernel, part 1 http://lwn.net/Articles/446399/rss 2011-06-07T07:46:05+00:00 jezuch <div class="FormattedComment"> <font class="QuotedText">&gt; And the LLVM library is 20MB when compiled, whereas /usr/bin/gcc is 262kb</font><br> <p> Are you sure you're not looking at just the GCC driver that invokes the actual compiler?<br> <p> jezuch@zx-spectrum-1:~$ du -shc /usr/lib/gcc/x86_64-linux-gnu/4.6/{cc1,cc1plus,lto1}<br> 11M /usr/lib/gcc/x86_64-linux-gnu/4.6/cc1<br> 12M /usr/lib/gcc/x86_64-linux-gnu/4.6/cc1plus<br> 11M /usr/lib/gcc/x86_64-linux-gnu/4.6/lto1<br> 33M razem<br> <p> </div> Object-oriented design patterns in the kernel, part 1 http://lwn.net/Articles/446385/rss 2011-06-07T06:24:06+00:00 elanthis <div class="FormattedComment"> Sure, there's cases where C++ bites you. There are cases where C does this, too, and there are many ways in which C++ helps you.<br> <p> I doubt I'll convince you to change your opinion with anything short of a small novel, though. :)<br> </div> Object-oriented design patterns in the kernel, part 1 http://lwn.net/Articles/446380/rss 2011-06-07T06:13:51+00:00 cmccabe <div class="FormattedComment"> <font class="QuotedText">&gt; First, I don't think you are quite in tune with game development, at least </font><br> <font class="QuotedText">&gt; not professional game development. Stability and even security are quite </font><br> <font class="QuotedText">&gt; important - nobody wants to be Fallout New Vegas</font><br> <p> Look, I'm not trying to trash game development or game developers. I know you guys have a difficult and challenging job.<br> <p> But let's be realistic. If I'm the project manager and I have a choice of releasing with a crashing bug or two or releasing a game that is dull or outdated, I am going to release with the bugs. If I don't, I'm going to get fired. Nobody wants a game that looks like it should have come out last year.<br> <p> On the other hand, the economics are different for some other kinds of software.<br> <p> <font class="QuotedText">&gt; C++ is very much a safer language. You bring up hidden code and</font><br> <font class="QuotedText">&gt; such as if that makes it unsafe, which is silly</font><br> <p> It's not silly at all. Every time I write class with any kind of non-trivial destructor, I have to remember to hide the copy constructor and assignment operator. If I fail to do this, a simple typo later in the program-- say typing use_foo(Foo f) instead of use_foo(Foo &amp;f)-- could bring the program to its knees.<br> <p> Or take conversion constructors or default parameters. Here's a true story of something that happened very recently. Someone on our project decided to change a function prototype from foo(int i) to foo(const MyObject &amp;o). What he didn't realize was that there was a conversion constructor from an int to a MyObject. So when he overlooked this little gem:<br> <font class="QuotedText">&gt; foo(0)</font><br> <p> The compiler happily generated this:<br> <font class="QuotedText">&gt; foo(MyObject(0))</font><br> <p> Needless to say, the results were not what he intended at all.<br> <p> Implicitly generated code hurts. Default constructors hurt. Default parameters hurt even more. You might disagree, but I've had a lot of time to come to this opinion.<br> <p> <font class="QuotedText">&gt; Simple fact is that there are kernels written in C++ which work very very </font><br> <font class="QuotedText">&gt; well.</font><br> <p> Well, as I already mentioned, there is XNU, which has no templates, no RTTI, and no exceptions, but is "technically C++". Apparently the Windows NT kernel also has some kind of C++ support-- again, I think exceptions are left out, as well as who knows what else.<br> <p> I would say a bigger C++ success story is the success of the LLVM project. They managed to write a very capable compiler in C++, which has been gaining a lot of momentum. One of my friends who works on the project is also one of the few people who can stump me with a C++ question. I guess it's hard to out language-lawyer the language implementers!<br> <p> But oh, they don't use exceptions :) And the LLVM library is 20MB when compiled, whereas /usr/bin/gcc is 262kb.<br> <p> C++ has been the inspiration for a lot of other languages like Java and Golang, and I respect it for that. It got a lot of things right, and it's still the language that I know the best. But can't we have some new ideas after all these years?<br> </div> Object-oriented design patterns in the kernel, part 1 http://lwn.net/Articles/446372/rss 2011-06-07T03:40:11+00:00 elanthis <div class="FormattedComment"> You're making a few claims I have to disagree strongly with, but in few words as ive got to walk out the door in a few. First, I don't think you are quite in tune with game development, at least not professional game development. Stability and even security are quite important - nobody wants to be Fallout New Vegas. Second, game engines are not rewritten that often. All of the popular engines are well over a decade old, and the majority of the code has not changed in architecture at all. Most crashes you see in games are actually video driver crashes, save a few particularly poorly engineered titles.<br> <p> C++ is very much a safer language. You bring up hidden code and such as if that makes it unsafe, which is silly. C++ doesnt just magically do things wrong behind your back anymore than C does. Yes, a destructor runs "automatically" but never ever unexpectedly. On the other hand, C requires programmers to cut-n-paste large swaths of boilerplate code, leading to a much higher bug count in general.<br> <p> Simple fact is that there are kernels written in C++ which work very very well.<br> </div> Object-oriented design patterns in the kernel, part 1 http://lwn.net/Articles/446303/rss 2011-06-06T19:17:02+00:00 cmccabe <div class="FormattedComment"> In my opinion, video games are a great example of a place where you *should* use C++. Kernels and systems-level software are a great example of a place where you should *never* use C++.<br> <p> Why do I say this? Well, C++ was designed to increase the speed of development and allow you to mix high-level and low-level code easily. It is very good at doing those things. At the same time, a clever programmer can get reasonable performance out of C++ by spending a little time optimizing.<br> <p> When writing a video game, you don't care that much about long-term maintainability or safety. An exciting game that crashes a few times a month will sell many more copies than a boring game which is rock-solid. Stuff gets rewritten from scratch every few years to take advantage of the latest and greatest hardware. If things are not written in the absolute most efficient way, who cares? You just keep tweaking things until the game runs all right on the target hardware.<br> <p> Kernels are the opposite. There are still pieces of code in the Linux kernel that go back to the early 1990s. Maintainability and stability are much more important than features or speed of development. It's important to use a simple style, in a simple programming language, that many different contributors can understand and use. Efficiency is key-- we don't have extra cycles to burn on things like std::string or std::map. In some structures, people are reluctant to enlarge the structure even by 4 bytes for performance reasons.<br> <p> A lot of people claim that C++ is somehow safer than C. But this is ridiculous. C++ has all of the different flavors of undefined behavior that C does, plus many, many more. C++ has a lot of "generated code"-- default copy constructors, default destructors, and so on, that will burn you if you're not careful. Again, it's about speed of development, *not* safety.<br> <p> Claiming that C++ is faster than C, or even as fast, is equally ridiculous. Even the most basic operations in C++, like iostreams, are much slower than the C equivalents like printf. Maybe some kind of theoretical perfect programmer could write equally fast, or even faster, code in C++ than C. But in the real world, programmers will take shortcuts if they exist. Let me draw an analogy-- if your wife bakes a cheesecake for you every night, you're going to get fat. Yes, in theory you could not eat the cheesecake, but it's right there.<br> <p> So we're left with the real C++ advantage-- speed of development. I want to be fair here. C++ is good at doing what it does. But let's not get confused here-- it's not the right choice for a lot of projects. Use the right tool for the job.<br> </div> Object-oriented design patterns in the kernel, part 1 http://lwn.net/Articles/446252/rss 2011-06-06T15:00:52+00:00 michaeljt <div class="FormattedComment"> <font class="QuotedText">&gt;&gt; In C you can get round that with the following (you don't have to like it if you don't want to!)</font><br> <font class="QuotedText">&gt;&gt; void somefunction_wrapper() {</font><br> <font class="QuotedText">&gt;&gt; somefunction_core();</font><br> <font class="QuotedText">&gt;&gt; important_cleanup_code();</font><br> <font class="QuotedText">&gt;&gt; }</font><br> &gt;<br> <font class="QuotedText">&gt; That's even worse. You'll have either put resources into global variables &gt; (yikes!) or pass around some kind of 'local frame' structure. Essentially &gt; reinventing exceptions.</font><br> <p> Not quite sure what you mean there.<br> <p> [...]<br> <p> <font class="QuotedText">&gt;&gt; And if you are going to think about it beforehand anyway you can also return a pointer to an error structure (which can potentially be statically or dynamically allocated) in C rather than just an integer error code. (That was just an example of course as there are lots of other ways to achieve the same thing.)</font><br> <font class="QuotedText">&gt; The problem is that it quickly becomes very cumbersome as each and every function has to pass around pointers to error structures.</font><br> <p> Not really - you just return a pointer where C traditionally returns an integer error code, and pass that on if a function you call returns a non-NULL error pointer.<br> </div> Object-oriented design patterns in the kernel, part 1 http://lwn.net/Articles/446251/rss 2011-06-06T14:47:16+00:00 Cyberax <div class="FormattedComment"> <font class="QuotedText">&gt;In C you can get round that with the following (you don't have to like it if you don't want to!)</font><br> <font class="QuotedText">&gt;void somefunction_wrapper() {</font><br> <font class="QuotedText">&gt;somefunction_core();</font><br> <font class="QuotedText">&gt;important_cleanup_code();</font><br> <font class="QuotedText">&gt;}</font><br> <p> That's even worse. You'll have either put resources into global variables (yikes!) or pass around some kind of 'local frame' structure. Essentially reinventing exceptions.<br> <p> <font class="QuotedText">&gt;The devil's advocate in me says: my (admittedly limited) experience is that you can't do that even in C++ without thinking very carefully about error handling before you implement it - including memory management issues, particularly if you may end up handling out of memory conditions using that mechanism. </font><br> OOM conditions are not really worth it to handle in userspace applications.<br> <p> In kernel level handling OOM would be mandatory, but not so much different from the current situation. Kernel would just set aside a small pool of RAM and use it during OOM allocations.<br> <p> <font class="QuotedText">&gt;And if you are going to think about it beforehand anyway you can also return a pointer to an error structure (which can potentially be statically or dynamically allocated) in C rather than just an integer error code. (That was just an example of course as there are lots of other ways to achieve the same thing.)</font><br> The problem is that it quickly becomes very cumbersome as each and every function has to pass around pointers to error structures.<br> </div> Object-oriented design patterns in the kernel, part 1 http://lwn.net/Articles/446232/rss 2011-06-06T08:58:02+00:00 michaeljt <div class="FormattedComment"> <font class="QuotedText">&gt; This kind of code is BAD.</font><br> <font class="QuotedText">&gt; </font><br> <font class="QuotedText">&gt; For this reason:</font><br> <font class="QuotedText">&gt; ====================</font><br> <font class="QuotedText">&gt; void somefunction() {</font><br> <font class="QuotedText">&gt; do_something();</font><br> <font class="QuotedText">&gt; if (do_something_else())</font><br> <font class="QuotedText">&gt; return; //Whoopsie!</font><br> <font class="QuotedText">&gt; important_cleanup_code();</font><br> <font class="QuotedText">&gt; }</font><br> <font class="QuotedText">&gt; ====================</font><br> <p> In C you can get round that with the following (you don't have to like it if you don't want to!)<br> <p> void somefunction_wrapper() {<br> somefunction_core();<br> important_cleanup_code();<br> }<br> <p> void somefunction_core() {<br> do_something();<br> if (do_something_else())<br> return; //No whoopses.<br> do_something_really_else();&gt; return; //Whoopsie!<br> }<br> <p> <font class="QuotedText">&gt; //And pray that your numeric error codes do not collide.</font><br> <font class="QuotedText">&gt; res=file_open_file(name, FOR_READ, result_place);</font><br> <font class="QuotedText">&gt; //What if we want to pass a verbose message</font><br> <font class="QuotedText">&gt; //like 'file &lt;NAME&gt; is locked by another process'?</font><br> <p> The devil's advocate in me says: my (admittedly limited) experience is that you can't do that even in C++ without thinking very carefully about error handling before you implement it - including memory management issues, particularly if you may end up handling out of memory conditions using that mechanism. And if you are going to think about it beforehand anyway you can also return a pointer to an error structure (which can potentially be statically or dynamically allocated) in C rather than just an integer error code. (That was just an example of course as there are lots of other ways to achieve the same thing.)<br> </div> Object-oriented design patterns in the kernel, part 1 http://lwn.net/Articles/446224/rss 2011-06-06T03:56:43+00:00 jmm82 <div class="FormattedComment"> It is also used in many kernel subsystems when the base struct will have a pointer to a private struct where the driver can hold variables Specific to that driver. The base struct is like a super in this case and if all you have is a pointer to the private struct you can use container_of() to get the base struct.<br> <p> Also, container_of is used with the kref code when deleting the struct which has embedded a kref for refence counting.<br> </div> Object-oriented design patterns in the kernel, part 1 http://lwn.net/Articles/446216/rss 2011-06-05T19:32:13+00:00 elanthis <div class="FormattedComment"> C++ has problems. The thing is, no other language is better. Sure, C is clearer about memory allocation. But it lacks critical things like templates. Which also have problems, but without them it's literally impossible to write a sane container library, generic algorithm library, or even a simple sort that doesn't end up making logN function calls. Dynamic languages or languages like C#/Java almost manage this, but either screw up safety or just totally screw up performance and memory usage in their generics implementation.<br> <p> And then there's user-defined value types. Trying to do basic graphics work in any language other than C/C++ is a nightmare, because your vectors are referece types. In games, I use vec3's and vec4's more often than I use int's and float's. Now imagine if every number in your language of choice was a reference type. That's what doing games (or just about any other interesting mathematical application) in every single popular language besides C/C++ is like. C's failing here is the lack of operator overloading for user-defined types, which just makes doing simple expressions with vec's and such a total pain. Even languages supposedly focused towards games or systems programming, like C#, utterly fail when it comes to user defined types.<br> <p> And then in the C/C++ realm, C++ actually solves the very memory management problems we started talking about through it's ability to let users define their own handle types. Raw pointers in C++ present a lot of problems that also exist in C, but C lacks the ability to solve those problems at a language/API level like you can in C++.<br> <p> So yeah, C++ fails in many ways, and fails hard. Every other language just fails even harder (for certain classes of application, that is). D comes closest to being a viable replacement, but it's not better enough to justify breaking compatibility with all the excellent C++ libraries out there.<br> </div> Ah, Google Go http://lwn.net/Articles/446204/rss 2011-06-05T11:23:06+00:00 khim <p>Well, the Google Go structural subtyping is crippled and limited version of <a href="http://en.wikipedia.org/wiki/Common_Lisp_Object_System">ages old</a> idiom.</p> <p>Of course it does not mean Google Go is bad language: it's version is simpler and less flexible - but as consequence it's less dangerous too. But still... it's funny how people are becoming excited when fashionable language presets something from languge veteran they forever disliked for some reason.</p> Object-oriented design patterns in the kernel, part 1 http://lwn.net/Articles/446167/rss 2011-06-04T10:11:11+00:00 liljencrantz <div class="FormattedComment"> I always use the name «this» for the object pointer when doing OOP in C. From what I've seen, this is a common convention.<br> </div> Object-oriented design patterns in the kernel, part 1 http://lwn.net/Articles/446163/rss 2011-06-04T07:36:24+00:00 chad.netzer <div class="FormattedComment"> Whereas the existing kmalloc/kfree idiom is quite straightforward. So where is the gain? If the most basic memory allocation features of the language have to be banned from use, it's hardly a sales point for adopting the language. You use it *in spite* of these deficiencies, just as Linux does with C.<br> </div> Object-oriented design patterns in the kernel, part 1 http://lwn.net/Articles/446162/rss 2011-06-04T07:19:16+00:00 elanthis <div class="FormattedComment"> I feel your argument is a little weak.<br> <p> Video games are almost certainly "just another piece of software" and we deal with the exact same memory management types of issues. Many game engines outright ban the use of new and delete because of the problems they cause, requiring instead that specialized factories and memory managers be used, especially on console titles and mobile games. It's every last bit as complex of a set of interfaces as the kernel, if not more so. And they're still just software, and they still mostly all use C++. :)<br> </div> Object-oriented design patterns in the kernel, part 1 http://lwn.net/Articles/446160/rss 2011-06-04T07:14:01+00:00 chad.netzer > There is nothing magical about the kernel. It's software.<p> This got me thinking about something else. There are about 5000 uses of kmalloc() in the linux kernel, but kmalloc() takes a flag argument. So, to replace these uses with idiomatic C++ would require widespread use of placement new().<p> However, using placement new requires care when deleting objects. So, rather than just using delete, you have to both manually call the class destructor, and then operator delete() with the right arguments to invoke the proper delete function (I suppose the kernel C++ stdlib could kludge the delete operator so that it works for placement new; now you are writing non-conformant C++)<p><p> ie. <pre> struct T *p = kmalloc(sizeof(struct T), GFP_KERNEL | GFP_DMA); some_init_function(p); kfree(p); </pre> becomes something like: <pre> T *p = new (GFP_KERNEL | GFP_DMA) T; p->~T(); operator delete(p, GFP_KERNEL | GFP_DMA); // Or whatever it takes to match the call signature </pre> This unorthodox usage is necessary because the kernel is not just like other software. Not when it comes to things like memory allocation. And what about a std::vector of struct *T? How do you allocate those objects with the right memory flags?<p> Perhaps there is an elegant solution to this, most likely by eschewing bare pointers in the kernel and always using some templated smart pointer class. A fairly radical change for kernel development. What are your thoughts on it? Haiku http://lwn.net/Articles/446159/rss 2011-06-04T05:52:08+00:00 elanthis <div class="FormattedComment"> It saddens me that it's become the norm to expect a "novice" who's had a full four years worth of a $100,000 education to have his first experience with real-world languages be a StackOverflow posting.<br> </div> Haiku http://lwn.net/Articles/446148/rss 2011-06-04T01:11:55+00:00 cmccabe <div class="FormattedComment"> As a side note, I found this highly amusing:<br> <a href="http://stackoverflow.com/questions/214321/what-c-features-do-you-avoid">http://stackoverflow.com/questions/214321/what-c-features...</a><br> <p> Literally every comment has a completely different view of which C++ features are "evil." I don't think you can find even two distinct answers that agree. I can only imagine what a novice programmer, fresh out of school, would think after reading this :)<br> </div> Haiku http://lwn.net/Articles/446145/rss 2011-06-04T01:07:40+00:00 cmccabe <div class="FormattedComment"> You're mixing apples and oranges. gets() isn't a language feature, it's a library function.<br> <p> <font class="QuotedText">&gt; The point is that the tools to use shouldn't be artificially restricted. </font><br> <font class="QuotedText">&gt; If someone wants to use protected inheritance, let them as long as they </font><br> <font class="QuotedText">&gt; can show why it's necessary or beneficial.</font><br> <p> You didn't answer my question. When was the last time you used those features?<br> <p> You say that programmers shouldn't be "artificially restricted" from doing things that are "necessary and beneficial", but those are weasel words. The reality is, you'll just define necessary and beneficial as whatever you've been doing. So if you've been throwing exceptions as pointers, it's obviously "necessary and beneficial" for the new code to do the same. If you haven't been using throw specs, obviously the new code shouldn't have them. But you're not using a subset of the language, oh no.<br> <p> As a side note, what's with the gets() obsession in these programming language debates. I don't think I was even alive the last time someone used gets() in a real program.<br> </div> Object-oriented design patterns in the kernel, part 1 http://lwn.net/Articles/446141/rss 2011-06-04T00:50:47+00:00 elanthis <div class="FormattedComment"> <font class="QuotedText">&gt; How would, for example, making "mixin" a special kind of class look in the place of MI?</font><br> <p> The closest you get to what most people would consider a mixin in C++ would be the CRTP, but it's a little gimped because you can't properly specify that the "mixin" is implementing methods in an interface that the class inherits from.<br> <p> e.g., if you have class Foo that implements interface IDoStuff, a mixin SimpleDoStuff might include members and methods that implement some or all of the methods of IDoStuff. There's no direct way to do that in C++, though, and so you end up with either grotesque inheritance trees or diamond inheritance.<br> <p> The only way to handle common partial implementations of interfaces in C++ is to create intermediate classes, which then results in you having a ton of intermediate classes, and you start wanting to mix different sets of then, and then you end up with either (a) an uber base object replacing the interface, making all your object bloated and over-complex, (b) a metric crapload of intermediary classes with tons of code duplication, or (c) diamond hierarchies and virtual bases and all the problems those impose.<br> <p> <font class="QuotedText">&gt; isa inheritance is usually good design. hasa inheritance is usually bad design. For the latter, composition is almost always the way to go. If you can find examples where "isa" is the wrong thing to do, I am very interested. Myself, I tend to prefer generic code and composition to inheritance but there are obvious cases where inheritance is useful and necessary.</font><br> <p> We perhaps are using slightly different definitions of "is-a", as I wasn't including interface derivation in that statement. Composition is the opposite of what I consider is-a. Yay for ambiguity in computer terminology. :)<br> <p> (On a related note, composition is super important. Not understanding composition is the other thing that leads to massive inheritance trees and horribly screwed up architectures. Composition isn't necessarily trivial in C++ due to a lack of language/syntax support, but it's still possible to do and do well, unlike mixins which are borderline imposssible.)<br> </div> Object-oriented design patterns in the kernel, part 1 http://lwn.net/Articles/446142/rss 2011-06-04T00:21:51+00:00 chad.netzer <div class="FormattedComment"> <font class="QuotedText">&gt; This is how I look at things. Given a tool, use it.</font><br> <p> I agree. Projects that adopt a tool like C++, should be prepared to adopt all of it. Those that don't want to adopt all of it, should seriously consider adopting none of it. The ones that want to adopt a subset of C++ (ie. "We'll just use it as a 'better' C, or 'C with classes'") should be prepared for the burden of that, and are probably best suited to a fairly small team of developer's (ie. not Linux sized). I think Linus was right to adopt none of it.<br> <p> <font class="QuotedText">&gt; The two statements are entirely consistent.</font><br> <p> The statements themselves are not inconsistent with each other, but I feel the argument you are making with them is not, imo. But let's leave it. I think it is impractical for a large active codebase with wide collaboration to transition from C only, to fully incorporating and embracing modern C++, without a rewrite essentially from scratch. I'm not sure of any such examples in the open source world (though gcc was pointed out earlier as a related example), but perhaps I'm wrong.<br> <p> <font class="QuotedText">&gt; Perhaps I misunderstood the intent of your statement:</font><br> <p> I think you were just restating my intent in your own words, but using the quotes made it appear that I said those words. I'd say 20+ years of C++ inheritance experience has shown it to be flawed: it's difficult for many to understand all the ramifications and options, carelessness leads to the fragile base class syndrome despite it's encapsulation features, people have devised the ugly "pimpl idiom" to deal with other compiler and encapsulation issues, it has multiple inheritance :), and so on and so on. I think C++ has been a useful experiment, and hopefully something better will now come to prevalence that has learned from it's flaws (disregarding Java et al; I mean a language w/ pointers).<br> </div> Object-oriented design patterns in the kernel, part 1 http://lwn.net/Articles/446138/rss 2011-06-03T23:43:08+00:00 daglwn <div class="FormattedComment"> <font class="QuotedText">&gt; But knowing how to evaluate a design and implementation requires asking </font><br> <font class="QuotedText">&gt; the kind of concrete questions that you claimed were not the right ones.</font><br> <p> Your overarching question was, "what parts of C++?" All of your other questions were about dealing with consequences of using a subset, save the one about overloading and that one is equivalent to, "how do we make sure we don't introduce bugs?"<br> <p> This is how I look at things. Given a tool, use it. Everyone on the project should evaluate how that tool is being used. If we start with a tool, reduce its functionality by saying we can't do this or that with it, it doesn't eliminate the need to evaluate how we're using it, but it does eliminate the some possibilities of using it better when we find certain cases of poor usage.<br> <p> <font class="QuotedText">&gt; You started this whole thread by claiming "One does not have to use </font><br> <font class="QuotedText">&gt; every feature of a language to get good productivity out of the </font><br> <font class="QuotedText">&gt; language.", which implied a conservative approach to feature adoption. </font><br> <font class="QuotedText">&gt; Now you are claiming that if Linux allowed for C++ development, that all </font><br> <font class="QuotedText">&gt; features should be available for use without restriction. Change of </font><br> <font class="QuotedText">&gt; heart?</font><br> <p> Not at all. The two statements are entirely consistent. All the tools should be available and we should make sure we use them correctly. Of course a project in transition isn't going to use every language feature immediately, simply because not all of the code will transition immediately. That does not mean that this or that language feature should be off-limits when transitioning any particular piece of code.<br> <p> <font class="QuotedText">&gt; You phrase that as though I haven't already been making concrete points.</font><br> <p> Forums are a poor way to convey expression. Believe me, no disrespect was intended. I was genuinely happy to see examples as I have been asking for them in various places for a long time.<br> <p> <font class="QuotedText">&gt; Those quoted proclamations you are apparently attributing to me are also </font><br> <font class="QuotedText">&gt; incorrect, btw.</font><br> <p> Perhaps I misunderstood the intent of your statement:<br> <p> Btw, the inheritance model that you tout as a feature (for "simple <br> uses") is arguably one of C++'s *worst* additions to the C language<br> <p> If so, I apologize. It reads to me like a condemnation of the entire C++ inheritance model.<br> <p> </div> Object-oriented design patterns in the kernel, part 1 http://lwn.net/Articles/446137/rss 2011-06-03T23:32:49+00:00 daglwn <div class="FormattedComment"> I think it's actually a strength that C++ doesn't differentiate among types of classes. It allows more flexibility and reuse. I agree with you that there should be a good case made before using MI.<br> <p> How would, for example, making "mixin" a special kind of class look in the place of MI?<br> <p> I'm not sure about "intermediate classes very often" part, but you certainly are correct about the diamond hierarchy. A diamond with state in the virtual base is usually poor design and causes countless headaches in constructors. A diamond without state in the virtual base (i.e. it's a pure interface) is sometimes useful.<br> <p> isa inheritance is usually good design. hasa inheritance is usually bad design. For the latter, composition is almost always the way to go. If you can find examples where "isa" is the wrong thing to do, I am very interested. Myself, I tend to prefer generic code and composition to inheritance but there are obvious cases where inheritance is useful and necessary.<br> <p> </div> Object-oriented design patterns in the kernel, part 1 http://lwn.net/Articles/446130/rss 2011-06-03T22:56:45+00:00 chad.netzer <div class="FormattedComment"> <font class="QuotedText">&gt; Does this code or proposed design exhibit sound engineering given the project goals?</font><br> <p> That is only the right question to ask in that it must always be asked, and is contentless. Clearly a good designer will start by saying, "What is a good, sound design for this task?" But knowing how to evaluate a design and implementation requires asking the kind of concrete questions that you claimed were not the right ones.<br> <p> <p> <font class="QuotedText">&gt; Even in a project transitioning from one implementation to another, there's no reason to artificial limit what people are allowed to do, especially _a_priori_.</font><br> <p> You started this whole thread by claiming "One does not have to use every feature of a language to get good productivity out of the language.", which implied a conservative approach to feature adoption. Now you are claiming that if Linux allowed for C++ development, that all features should be available for use without restriction. Change of heart?<br> <p> <p> <font class="QuotedText">&gt; Ah, good, some concrete points.</font><br> <p> You phrase that as though I haven't already been making concrete points. I disagree, and I think I've been more concrete in my points than you have. I find Linus's arguments about using C++ for kernel coding to be largely correct (for the Linux kernel), and your defense of your assertion that he is "dead wrong" to be unconvincing.<br> <p> <p> <font class="QuotedText">&gt; But to take that to an extreme and proclaim the C++ inheritance model "broken" or "wrong"</font><br> <p> Those quoted proclamations you are apparently attributing to me are also incorrect, btw.<br> </div> Object-oriented design patterns in the kernel, part 1 http://lwn.net/Articles/446132/rss 2011-06-03T22:26:55+00:00 elanthis <div class="FormattedComment"> <font class="QuotedText">&gt; Multiple inheritance is very useful. If a language with inheritance doesn't have it, it has a major missing feature.</font><br> <p> Sort of. Multiple inheritance in the C++ sense is useful simply because C++ doesn't differentiate between base classes, interfaces, and mixins. The need for multiple inheritance with multiple base classes is pretty small, and at least every example I've personally seen has been an example of bad interface design rather than a showcase for the need for MI (granted, my experience is not all-encompassing).<br> <p> <font class="QuotedText">&gt; Virtual base classes are trickier. They are painful, I admit, but necessary for some uses of MI. Many uses of MI don't need them at all. C++'s possible mistake was to place the decision point at the intermediate class level rather than at the "final" class level.</font><br> <p> I will argue that if you start having intermediate classes very often at all, or a diamond tree, you're using inheritance wrong. Which most object-oriented developers do, unfortunately. A large part of this I fear is that it's simply taught wrong in schools. The classical examples of inheritance are things like Mammal-&gt;Dog, or Shape-&gt;Circle, or Vehicle-&gt;Car. These are attempts at modelling real-world "is-a" relationships inside of algorithmic data structures, which is really really wrong. It's a very long discussion to explain it in the necessary detail, and I believe Herb Sutter already did some fantastic talks on the topic (I'm having trouble Googling them though; maybe it wasn't Sutter that did them?).<br> </div> Object-oriented design patterns in the kernel, part 1 http://lwn.net/Articles/446117/rss 2011-06-03T21:21:37+00:00 daglwn <div class="FormattedComment"> <font class="QuotedText">&gt; Enlighten us as to the right questions.</font><br> <p> Does this code or proposed design exhibit sound engineering given the project goals?<br> <p> <font class="QuotedText">&gt; As I said, unless you plan to rewrite Linux from scratch in C++, any </font><br> <font class="QuotedText">&gt; proposal on the table is about using a restricted set of features in some </font><br> <font class="QuotedText">&gt; parts of the kernel.</font><br> <p> Why? There is nothing magical about the kernel. It's software. Even in a project transitioning from one implementation to another, there's no reason to artificial limit what people are allowed to do, especially _a_priori_.<br> <p> <font class="QuotedText">&gt; What other language after C++ uses multiple inheritance, has virtual </font><br> <font class="QuotedText">&gt; base classes, uses "protect" or equivalent, allows slicing, etc.?</font><br> <p> Ah, good, some concrete points.<br> <p> Multiple inheritance is very useful. If a language with inheritance doesn't have it, it has a major missing feature.<br> <p> Virtual base classes are trickier. They are painful, I admit, but necessary for some uses of MI. Many uses of MI don't need them at all. C++'s possible mistake was to place the decision point at the intermediate class level rather than at the "final" class level. But it's a convenience/efficiency tradeoff and I'm not totally sure the committee got it wrong..<br> <p> By "protect" I assume you mean "protected." Protected methods certainly are useful. Protected inheritance, not so much but I'm sure someone has a use case for it.<br> <p> Slicing is also a big pitfall. But I think it's a natural consequence of call-by-value semantics. One could argue that C++ should have made call-by-reference default but that would have broken C compatibility, a major strength of the language.<br> <p> <font class="QuotedText">&gt; I'm happy to be enlightened if there is a language out there that said </font><br> <font class="QuotedText">&gt; "C++ inheritance is spot on, let's copy it".</font><br> <p> I don't think anyone has said that. I have not said that. To do so would be to say that the language is perfect, an impossibility in an imperfect world.<br> <p> There are valid criticisms of C++. But to take that to an extreme and proclaim the C++ inheritance model "broken" or "wrong" is, well, wrong.<br> <p> </div> Haiku http://lwn.net/Articles/446116/rss 2011-06-03T21:09:18+00:00 daglwn <div class="FormattedComment"> This makes no sense. If this is your criteria, everyone programs in a subset of their favorite language. When's the last time you used gets()?<br> <p> The point is that the tools to use shouldn't be artificially restricted. If someone wants to use protected inheritance, let them as long as they can show why it's necessary or beneficial.<br> <p> </div>