News for hackers. Stuff that matters.
Re:Linus vs C++, again by mojuba on Thursday June 10, 2010 @04:54PM (#1421637)

   write(fd, buf, size);
Can you list all the caveats, possible side effects, reasons of failure of this simple C function call? Hint: remember, fd can be a file, as well as a NFS-mounted file, pipe, network or local socket, FIFO, device, etc. Hint2: I doubt anyone can give a comprehensive description of the possible consequences of this call.

Bottom line being, C can be incredibly hard to understand, or C++ can be clean, succinct and straightforward. Personally, I'm for the second, although I know probably 99.9% of all existing C++ code is just awful.

Re:Linus vs C++, again by jerf on Thursday June 10, 2010 @07:32PM (#1422160)
In addition to the other already-good answers, I want to add the following answer, which I believe is even closer to the "real" answer Linus had in mind.

Of course you can not look at the function and immediately know all consequences. The real point is that there is a procedure you can follow which will let you determine the answer.

1. Locate the "write" function. There will only be one, because C has no namespaces.

2. Read the "write" function.

3. Repeat recursively as needed (including for macros).

For C++, the equivalent would be something like fd->write(buf), and the procedure is:

1. Determine the type of 'fd'.

2. Determine what subtypes you could have there and which you might actually have in hand. Or is the class not virtually inherited in which case it doesn't matter?

3. Determine what the write method does. In order to do so, have intimate knowledge of all operator overloading any value used in the write method may have.

4. Figure out what "buf" is and whether it magically overloads other operators.

write(fd, buf, size) resolves to one function with three arguments that themselves can't be that magical, and usually one basic approach to the question of memory management. fd->write(buf) involves classes, inheritance, potentially overloads, interfaces that 'buf' may correspond to and the potential need to follow a chain of some number of functions just to see whether that was constructed automatically into another type, endless permutations of how memory may be handled, and so on and so forth. The assembler that the C code will generate will basically push three arguments and call a function; the assembler the C++ generates is effectively unbounded in complexity. This assembler complexity directly corresponds to complexity that must be understood in order to understand the line of code.

In general I'd prefer the C++, but when writing a kernel where every twitchy detail counts for everything and the slightest bit of "wrong" could be a rootable security bug, I see the counterarguments.

Re:Linus vs C++, again by Xurinos on Friday June 11, 2010 @12:39AM (#1422822)
This comment made things a bit clearer for me. This seems to also be an argument in favor of a Lisp-1 over a Lisp-2 (same symbol found in multiple packages) as well as an argument against use of generic methods (have to understand the types to find the right implementation). Something to think about.
Re:Linus vs C++, again by jpr on Friday June 11, 2010 @01:12AM (#1422879)
Lisp-1 vs Lisp-2 is about whether functions and other variables are in the same namespace or not. Packages are orthogonal to this.

Also, generic functions in Common Lisp don't suffer from nearly the same amount of complexity as can be found in C++: there is always only one signature (lambda-list) for any function name, whether generic or not, there are no implicit conversions, no memory-allocation details, value/pointer/reference distinction, constness, virtualness, overloading of assignment operators etc.

Re:Linus vs C++, again by Xurinos on Friday June 11, 2010 @02:11PM (#1424165)
By your wording, aren't CL packages considered to be just an implementation of a namespace? Is there another way to declare symbol namespaces in CL (outside of rolling our own organisms)?

I was under the impression that the function signature was not the issue raised... but the fact that the same function name could lead to completely different behaviors depending on a context. In the case of generic functions, the specific function implementation is usually (ignoring some of the other matching features) determined by the input types. The input types define the context.

All that said, I realize now that this might be less of a problem in CL if you just group your defmethods in the same area. If the problem is grepping for the definition, it is easy enough to rearrange things so that the related functions are in a close-enough place. In C++, the classes try to own their methods -- with the exception of warily-regarded "friend" methods.

Re:Linus vs C++, again by jpr on Saturday June 12, 2010 @02:05AM (#1425638)
Finding a method definition is quite easy in CL: just use the function 'find-method':

http://www.cs.cmu.edu/Groups/AI/html/hyperspec/HyperSpec/Bod...

Though grep would probably do just fine too, since method definitions are defined with 'defmethod' instead of 'defun', so you can see at a glance whether something is generic or not.

Re:Linus vs C++, again by jpr on Saturday June 12, 2010 @02:11AM (#1425644)
About packages and namespaces:

Package is essentially a collection that maps symbols to values. In CL packages have (at least) two namespaces: one for functions, other for any kind of variables. The reason for this is that when you write the form

    (fun #'a b)
you and the compiler can be sure that 'fun' and 'a' are both meant to be functions. There are both advantages and disadvantages to this.
Re:Linus vs C++, again by strlen on Friday June 11, 2010 @01:42AM (#1422932)
Ugh, in C under UNIX this could mean two things:

* If some moron created a "write()" macro, it means expand that macro (preferably it should also mean email the director of HR to suggest the author of the macro consider having the company pay for their MBA, to make sure they aren't allowed to touch code again)

* Otherwise, if a header has a definition for write use that. Otherwise, emit instructions to call put "fd, buf, size" on the stack and call write.

In C++ it could mean:

* There's a class "write" which has a constructor that takes three arguments

* There's a function in the global namespace called "write" that takes three arguments

* There's a macro called write

* There's method called write in the local class. That method could be invoked virtually or non-virtually. It could be inherited from a parent class.

* write could be an instance of an class that has "operator ()" defined

I probably left out some more. In fact, there's likely a firm somewhere that thinks "what could write(a, b, c) mean in C++" is a wonderful interview question (perhaps they could ask it to all those losers who don't know what "explicit" keyword does but still have the gall to think they're competent enough to work for them!)

In a UNIX system, with right headers included, what write does is specified by the POSIX API. POSIX is one of the best defined and cleanest APIs. It has existed before the world of IDEs and plug-and-play libraries. I can write non-blocking C network code for a UNIX OS with vi, from muscle memory after reading through man pages and working through Richard Stevens books.

Writing Java NIO code requires: an IDE to prevent RSI, traversing Javadocs to understand the non-intuitive APIs and searching mailing lists through Google to e.g., find out that Java NIO selector doesn't let me use edge triggered epoll because it would involve "tight coupling" (read: it might be difficult for somebody writing code on an AS/400 to use it).

JDK7 NIO2 potentially changes this (I can implement internals of a selector myself). I'm also sure I'll be able to target JDK7 with a Perl6 compiler... that I'll use to implement the firmware for my flying car, in which I'll travel pick up Hans Reiser when he's paroled from prison.

Note: in this case, it has nothing to do with the language. java.util.concurrent API is very well defined because it was written by great programmers (Doug Lea and Joshua Bloich) who are apt at API design. Unfortunately, Java doesn't "force you" to create a clean API like C does: there are no design patterns available, no IDEs, no built-in tools for literate programming in C; you either build a clean API, or no one will use it. While I'm in no way of Joshua Bloch's caliber, I can relate to him when he says that he stayed with imperative C until finding Java.

While Java doesn't force you to build a clean API, C++ almost makes it impossible to build a clean API: witness boost::spirit ("generic programming", C++'s idiom for extending the language), compare with lex/yacc (external DSLs) and parser combinators (internal DSLs) in Haskell or Scala.

I want to like C++. I have programmed it for a living before and will almost certainly do so again: there's a certain combination that requires low-level code with no memory management and Object Orientation; my chosen specialty (distributed systems) often requires that combination (fortunately, not always: Erlang and JVM languages have been used to build some incredibly impressive systems).

Perhaps Go and D could come along and step up to that challenge, but I am skeptical: Modula-3, despite influencing other languages hasn't been able to step up to that plate. I feel C++ 0x, Intel collections for C++ and some parts of boost, STL and tr1 e.g., tr1::unordered_map, boost::scoped_ptr are very cool and useful. Boost Graph library is simply awesome and has no equivalent.

It seems, though, as if C++ was built by warring hordes: one horde that wanted generic programming and thought OO was pointless, another horde that wanted OO but thought generic programming was pointless and yet another that hated both. Neither won nor lost, each side said "mission accomplished" and developers were treated as "collateral damage". I could care less about which one of those to use: I am productive doing OO programming in Perl, Python, Scala and Java. I am also productive doing generic programming with CLOS in Common Lisp or with type classes (or their equivalents) in statically typed languages. Likewise, I am perfectly productive writing imperative code in C. I just want clean, easy to program to APIs with understandable error messages (either at run time or compile time, I am not picky about dynamic vs. static typing -- they're tools, means to an end) and C++ doesn't allow for that.

Re:Linus vs C++, again by tptacek on Thursday June 10, 2010 @05:04PM (#1421677)
That's userland code. In the kernel, write(2) very quickly finds the open file based on the fd and passes the call off to extremely explicit code that handles the write().

Stripping off ambiguity is almost the write syscall handler's only job.

It's worth mentioning here that not only is the convenience vs. explicitness tradeoff different in application code than in bare-metal systems programming code, but that that specific example of convenient app-level API is also an infamous Unix failure. In reality, app code cannot safely assume that an fd is just an abstract bucket you can read, write, close, and seek in.

Re:Linus vs C++, again by ramidarigaz on Thursday June 10, 2010 @06:50PM (#1422042)
I've been meaning to ask this for a very long time: What does the (2) mean? I see (n) in lots of descriptions of calls, but I've never found out what it means.
Re:Linus vs C++, again by eru on Thursday June 10, 2010 @07:03PM (#1422085)
It refers to sections of the man pages. They are organized like this:

1. General Commands

2. System Calls

3. Subroutines

4. Special Files

5. File Formats

6. Games

7. Macros and Conventions

8. Maintenence Commands

Re:Linus vs C++, again by ramidarigaz on Friday June 11, 2010 @01:40PM (#1424065)
Exactly what I wanted to know. Thanks.
Re:Linus vs C++, again by cycojesus on Saturday June 12, 2010 @04:00AM (#1425728)
and it's used like this: man 2 write
Re:Linus vs C++, again by tptacek on Thursday June 10, 2010 @07:25PM (#1422138)
You got a lot of good answers here, but just so you know: you really only have to remember that open(2) is a syscall and fopen(3) is a library call. That's the important distinction.
Re:Linus vs C++, again by jbarham on Thursday June 10, 2010 @07:04PM (#1422091)
The Unix manual is divided into sections (http://en.wikipedia.org/wiki/Man_page#Manual_sections). Section 2 is for system calls. Specifying the section number allows you to disambiguate which section of the manual you want to look in if a man page exists in multiple sections (e.g., "man 1 write" vs. "man 2 write").
Re:Linus vs C++, again by stonemetal on Thursday June 10, 2010 @07:06PM (#1422096)
It is where it lives in man. man write returns write(1) an application that sends a message to another user. man 2 write returns write(2) the system call mentioned above(or at least it does on Ubuntu).
Re:Linus vs C++, again by gxti on Thursday June 10, 2010 @07:04PM (#1422090)
It's the "man" section. According to "man man", 2 is the section for system calls. Often you'll have manpages for the same name under different sections, for example sync(1) is a command while sync(2) a system call (which, unsurprisingly, the sync command uses).
Re:Linus vs C++, again by bretthoerner on Thursday June 10, 2010 @07:05PM (#1422093)
It's the man page section number. I believe this dates back to when the man(uals) were actually printed out. You'd go to section 2 to find X to disambiguate from a different Y.

http://www.december.com/unix/ref/mansec.html

Re:Linus vs C++, again by eru on Thursday June 10, 2010 @07:18PM (#1422125)
And the disambiguation is still useful.
Re:Linus vs C++, again by CUViper on Thursday June 10, 2010 @06:35PM (#1421998)
That same write call can exist in C++ too, with exactly the same consequences. Everything ugly in C can be done in C++, but C++ lets you be so much uglier.

  int i = 42;
  foo->bar(i);
In C, we can tell that foo is either a "struct S* foo" or "union U* foo" which has a member "X (*bar)(Y)". Type X is unknown from this context, but Y is some type compatible with int. We will call the function which bar points to with a single value 42. The value of i will be unchanged.

In C++, it could be the same. Or foo could be not a pointer at all, but some object with "operator->". bar() might take its first parameter by reference and end up changing i. bar() might have additional parameters with default values. bar might not even be a function, but some object with "operator()". etc. etc.

Most of the code I write is C++, so I'm not against it -- but I definitely understand the point that C requires less context.

Re:Linus vs C++, again by ANH on Thursday June 10, 2010 @10:24PM (#1422536)
This is one reason why idioms and patterns are important. I don't think there are many reasonable C++ coders out there who would override operator-> in such a way as to introduce such ambiguity.
Re:Linus vs C++, again by gchpaco on Friday June 11, 2010 @01:45AM (#1422941)
Unfortunately that particular override comes up a lot when doing "smart pointers". Smart pointers are crazy difficult to write and fail in all kinds of subtle ways.
Re:Linus vs C++, again by ramchip on Friday June 11, 2010 @02:28AM (#1423029)
I've never had any problem with Qt or Boost smart pointers. They may be delicate to write, especially in multithreaded use cases, but once it's written it works rather well.
Re:Linus vs C++, again by forgottenpaswrd on Thursday June 10, 2010 @06:06PM (#1421917)
Hard to understand?

Really? write a block of data of size on bytes "bytes" on the file-pipe-network... pointed by the file descriptor "fd"??

It is extremely simple to understand, it makes a very simple thing, always right. I doubt you can make it simpler.

I have been using this all my life for NFS-mounted file, pipe, network or local socket... Never had any problems, and I have done very complex things.

Of course, in c++ you will use the very same function under a different calling, you can make it part of a class, whatever, but is going to use the same backend internally(because the OS only uses one), only that you can add a lot of abstraction(complexity) from c++ code.

Of course C++ could be clean, but the question is: what will happen if we make it into the kernel?. Linux thinks it won't work. I agree with him.

Re:Linus vs C++, again by JoeAltmaier on Friday June 11, 2010 @09:08AM (#1423710)
I've actually done this. Wrote an Infiniband kernel driver, in C++.

Overcoming the obvious difficulties, including "new" being a variable name in a library, there were no technical difficulties. (Had no one in history EVER written in C++ on linux before?)

The argument FOR C++ in the linux kernel is the same argument for using C++ anywhere else - object abstractions are useful. The project took far less time. The bugs were fewer. The code was of course more readable - because of the tremendous wealth of context that C++ provides thru strong typing.

C people argue its easier to use simple constructs, since they are instantly understandable. What is NOT understandable is WHY the code is putting an int into an array. Those types have no obvious meaning beyond the line of code they are in.

In an object-typed language, you CAN learn the scenery and rapidly become familiar with the object set. The argument above (write) is largely silly - right-click and GoToDefinition works in almost any IDE - yes there may be more than one possibility, the IDE will display them all. So navigation thru code is vastly simpler than grepping for names.

Anyway, in 18 months we had an Infiniband layer integrated into linux, not too hard. Had to invent fundamental kernel/user page primitives that were missing. Interesting to note: Windows had all the driver support we needed, didn't have to invent anything.

Re:Linus vs C++, again by tptacek on Thursday June 10, 2010 @07:27PM (#1422143)
He's just pointing out that the first argument to write() is the exact same kind of abstraction as the "foo" in "foo->perform()".
Re:Linus vs C++, again by wvenable on Thursday June 10, 2010 @07:35PM (#1422169)
C++ is one of the few languages where in "foo->perform()" the -> and () can be something completely different than what you expect. There's almost nothing in that simple statement you can be completely sure of.
Re:Linus vs C++, again by NickPollard on Friday June 11, 2010 @05:25AM (#1423305)
They can be, but they shouldn't be. Operator overloading is useful when you want to do something that is semantically similar but implementationally different.

Even mathematics has different processes for the same operators on different types. If you see 'a * b' you should be able to assume that it's multiplying two variables, but if those two are real numbers it's different than if they're two matrices of real numbers, for example.

Implementing a Matrix class that overrides * to imlement matrix multiplication seems perfectly fine for me - It's just the same as implementing a Multiply(a, b) method. Just as someone who writes a Multiply method could in theory make it do anything, it is assumed by reading the word 'Multiply' that that is what it does. Just as someone who makes a Multiply() function that does something other than multiply is an idiot, so is someone who overrides operator * to do something other than multiply.

Re:Linus vs C++, again by wvenable on Friday June 11, 2010 @10:19PM (#1425421)
The STL overrides the bit shifting operators for output and the deference operator for accessing the current item in iterators.
Re:Linus vs C++, again by tptacek on Thursday June 10, 2010 @07:38PM (#1422179)
That's a true and valid argument but beware that any C++ advocate could knock it down by saying "then the kernel code standard should reject things like that".
Re:Linus vs C++, again by varjag on Friday June 11, 2010 @07:04AM (#1423453)
And when you reject things like that you end up with C anyway.
Re:Linus vs C++, again by sanderjd on Friday June 11, 2010 @07:33PM (#1425133)
Meh, this is a straw-man. Conventionally rejecting overridden -> and () operators is a very long reach from ending up back at C. Disallowing operator overriding in general (as opposed to specific operators), namespaces, and classes would be much closer, and it still wouldn't be all that close to C (reinterpret_cast, templates...) There is a goodly sized amount of gray area between the two. Or did you mean your "things like that" to be much broader than my interpretation?
Re:Linus vs C++, again by albertzeyer on Thursday June 10, 2010 @08:47PM (#1422325)
Similar thing with C macros.

(Note that my point here is that for sane/good code, you normally can be sure that it does what it looks like -- for C++ just the same way as for C.)

Re:Linus vs C++, again by russell_h on Thursday June 10, 2010 @11:26PM (#1422668)
I have to disagree with you with regards to write(). The write(2) man page is deceptively simple, check out open(2), specifically the NOTES section. I count 12 occurrences of the string "NFS".

My favorite part of the whole man page is the Linus quote:

    "The  thing  that  has always disturbed me about O_DIRECT is that the whole interface is
    just stupid, and was probably designed by a deranged monkey on  some  serious  mind-con-
    trolling substances." -- Linus
But thats not a language failing. A language that actually prevents you from writing complicated interfaces is probably entirely unsuitable for... almost anything.
Re:Linus vs C++, again by mojuba on Thursday June 10, 2010 @07:33PM (#1422162)
Just a few questions as an example:

* If you write to an NFS-mounted file and write() returns an error, does it mean the block wasn't written to the remote disk?

* If you write to a TCP socket and write() returns OK, does it mean data was received by your network peer?

* UNIX pipes: how many times is your block being copied before it reaches your peer's buffer supplied to read()?

And don't get me started on signals.

Re:Linus vs C++, again by perlpimp on Thursday June 10, 2010 @11:32PM (#1422675)
When we were taught an OO course, we had smalltalk first, and then we moved to C++. Professor enumerating all rules, ifs and buts was like an operator of a german machine gun that was mowing down wave after wave of good natured allied programmers. Or so it felt and classmates have conferred on my feelings.

C++ has too many rules and exceptions, while giving some sort of control to a genius - in hands of a good natured well meaning coder, it all goes to hell. That is so not in the beginning but in the long run.

All misgivings noted, compilers gone to future and back. They help alot.

C has few rules, and whats more if you use UNIX / ISO convention there are very few ways you can go wrong. C++ is an ugly duckling of era of 586 type machines, when you have tried to do something in binary , efficiently and with a style and yet still was found wanting.

Explicit is always best, because you always refactor. Say what you mean, write what it does. This way its easier to read, and easier to coax it to do something else. In the end good coders achieve things by correcting few things here and there. If you have some sort magic that developer can't trust to be exactly what they expect it to be - they can't code with honestly and without fear. And fear as we know is a mind killer. So it is a catch 22.

I am totally 100% with Linus on that one.

Re:Linus vs C++, again by albertzeyer on Thursday June 10, 2010 @08:45PM (#1422319)
I can show you a lot more awful looking C code than awful looking C++ code.

Edit: Well, seems you don't believe it.

Take a look here: http://www.google.com/codesearch?hl=en&lr=&q=lang:c%...

And here: http://www.google.com/codesearch?hl=en&lr=&q=lang:c+...

Just browse a bit through both, check a few different projects. Check also some of the big projects. (Apache, GCC, glibc, Linux, LLVM, clang, WebKit, Chromium, etc.)

Re:Linus vs C++, again by grogers on Friday June 11, 2010 @01:50PM (#1424100)
Its all just header comments...