Linus Torvalds and C++

Started by
149 comments, last by Anon Mike 16 years, 4 months ago
Quote:Original post by Gage64
You seem to suggest that it's more likely that someone will do something "evil" in C++ than in C. Why? Because C++ offers more creative ways to achieve this?

It depends on the intentions of the person who wrote the code. If he wants to do something "evil", he will figure out a way to do it in any language. It's up to you to decide if you trust his code or not.


I edited my above post and added a section which I think answers your question. Editing posts while others are replying to it is bad. We need some post mutex or something :)

included here for comfort:

its not an issue of accidentally formatting the harddrive, its an issue of invisibly putting a slow copy constructor to a temporary variable somewhere in a performance critical code. Yes, you could find it easily being an experienced C++ programmer, but wouldn't it be easier to find if it was a function call with visible memcpy?
Advertisement
I saw your edit while writing my reply, but I thought my argument was still valid so I posted anyway.

Regarding hidden slow code - in my opinion, a profiler will point you to it quickly enough.
I did not mean to suspect someone else's code for evil. Not at all.
If you are interfacing with someone else's code hopefully you are just using some API (preferablly using abstract base classes interface [smile]) and you never ever have to look in its source code. What I meant is if you interface with someone and you encounter a problem (bug or performance problem for example) and the other person is away sick, then you have to search and explore his code yourself and then you are better off exploring simpler more visible code.

Of course, this simplicity comes at a heavy price of development costs and much harder maintainability which is a very bad trade off, but on the sole parameter of exploring unknown code- the less hidden code the better. And on that parameter alone C wins.

edit: I am late for my train, will continue at home in an hour or so.
Quote:
the less hidden code the better. And on that parameter alone C wins.

Tell me which is slower, I dare you:
a = b;//ormemcpy(&x,&y,z);


Go ahead, I'll wait.

The problem with your argument is that you're basing your attack on "C++" on a lack of context, but apparently allowing for that same context to be available in the case of "C." In the C++ case you're basically assuming that given the assignment (or in general any other use of an operator or function call, et cetera) you don't have any idea what the types are or what their values are, and so you can't tell if operator overloading is involved or what the cost of the operation will be. But without that information in C, you have the same problem - the size of the type would be an issue in C, had I used a sizeof and not a third variable, but the value of the third variable is still a "potential unknown." If you make you argument with a level playing field, it doesn't hold up well.

I can pass or copy data efficiently or inefficiently in C, just as easily as in C++. It just looks different. I find it very difficult to believe that somebody who knows both languages to a suitable degree of competency doesn't know what to look for in terms of performance pitfalls in either language. Most of it simply good software engineering; being aware of what that is and reflecting it in the idioms and syntax of your chosen medium.

Quote:
The big difference is that you know full well when you activate a function, but its harder knowing about operator overloading, hidden constructors, polymorphic methods and invisible variables.

Knowing that operator overloading exists in C++, and is something to watch out for, is part of knowing C++. Anybody who isn't aware of these issues, I would happily fire.

Now, overloading is prone to...abuse. Redefinition of operator+ to subtract your FixedPoint instances, for example. I think, however, that we can both agree this falls into the category of "stupid" and anybody who would seriously consider this for production code should, similarly, be fired. It's the same thing you said regarding functions: it's not a matter of a broken hard drive; nobody in practice would be that idiotic, at least not on purpose.

And besides, what does it matter? Okay, so a function call is virtual. And? Do you think that's a performance bottleneck? Did you profile it? The features in question are higher level language features design to increase productivity of the developer. Yes, they may from time to time have a performance impact. That does not make them wrong. Using them incorrectly makes them wrong, and any feature in any language can be abused in such a fashion. Prematurely worrying about expensive copies and all that other early, unguided optimization is frequently a productivity killer.

And let's not even get into all the tools available in modern development environments to detect and track most of these issues, to navigate and profile and sanity check code, et cetera.
Quote:Original post by Yann L
Now, take a look at some of Torvalds work.


Quote:
/* Copied here from <linux/kernel.h> - we're userspace. */#define container_of(ptr, type, member) ({                           const typeof( ((type *)0)->member ) *__mptr = (ptr);         (type *)( (char *)__mptr - offsetof(type,member) );})static void delayed_put_task_struct(struct rcu_head *rhp){        put_task_struct(container_of(rhp, struct task_struct, rcu));}


...

...

... I don't think I'm going to be able to get any more work done today.

How is it even legal to have a block of statements inside the parameters to a function call? (If the - sign before 'offsetof' were a +, it would appear that it all simply is intended to do 'rhp->rcu' - as it is, it's some bizarre memory hack that I would have a hard time accepting as defined behavour without a quite long argument.)
Quote:What I meant is if you interface with someone and you encounter a problem (bug or performance problem for example) and the other person is away sick, then you have to search and explore his code yourself and then you are better off exploring simpler more visible code.

Of course, this simplicity comes at a heavy price of development costs and much harder maintainability


So C code is harder to maintain but easier to find bugs in? One of the main activities done during maintenance is reading code, so I'd say those two arguments are at odds with each other.

Also, what you consider simple highly depends on your understanding of the language and how much experience you have with it (I liked this reply by SiCrane in connection with this).
Quote:Original post by Zahlman
How is it even legal to have a block of statements inside the parameters to a function call? (If the - sign before 'offsetof' were a +, it would appear that it all simply is intended to do 'rhp->rcu' - as it is, it's some bizarre memory hack that I would have a hard time accepting as defined behavour without a quite long argument.)

The block inside the function call is a gcc extension, not standard C. This particular statement is used to find the address of a structure given an address of one of the member variables.
struct A {  int B;  int C;};

For example finding the A * for the A that contains a given C.
Quote:Original post by eedok
If you think there's unnecessary goto's in some of his code, refactor it to get rid of the goto, test it, then submit it as a patch.


Here is the very first example of 'goto' in the sample Yann L submitted:

 135 void release_task(struct task_struct * p) 136 { 137         struct task_struct *leader; 138         int zap_leader; 139 repeat: 140         atomic_dec(&p->user->processes); 141         write_lock_irq(&tasklist_lock); 142         ptrace_unlink(p); 143         BUG_ON(!list_empty(&p->ptrace_list) || !list_empty(&p->ptrace_children)); 144         __exit_signal(p); 145  146         /* 147          * If we are the last non-leader member of the thread 148          * group, and the leader is zombie, then notify the 149          * group leader's parent process. (if it wants notification.) 150          */ 151         zap_leader = 0; 152         leader = p->group_leader; 153         if (leader != p && thread_group_empty(leader) && leader->exit_state == EXIT_ZOMBIE) { 154                 BUG_ON(leader->exit_signal == -1); 155                 do_notify_parent(leader, leader->exit_signal); 156                 /* 157                  * If we were the last child thread and the leader has 158                  * exited already, and the leader's parent ignores SIGCHLD, 159                  * then we are the one who should release the leader. 160                  * 161                  * do_notify_parent() will have marked it self-reaping in 162                  * that case. 163                  */ 164                 zap_leader = (leader->exit_signal == -1); 165         } 166  167         sched_exit(p); 168         write_unlock_irq(&tasklist_lock); 169         proc_flush_task(p); 170         release_thread(p); 171         call_rcu(&p->rcu, delayed_put_task_struct); 172  173         p = leader; 174         if (unlikely(zap_leader)) 175                 goto repeat; 176 }


I could understand, *maybe*, that he wants to avoid the overhead of the compiler possibly not optimizing tail recursion, and has over years of "experience" cultivated this pattern such that he bangs it out automatically.

But seriously, folks - what we have here is a reinvention of the do-while loop. (Not to mention, if optimization is a concern, why doesn't he worry that 'list_empty' might not get inlined? ZOMG, a whole function call overhead to check if 'head->next == head'?)

By the way, BUG_ON is an assert-like macro defined in a few different places, slightly differently. It makes use of 'unlikely', which in turn is another macro (despite the lowercase) that wraps a compiler intrinsic intended for optimization. In code that's meant, basically, to assert, and which should disappear from release builds anyway.
Quote:Original post by SiCrane
Quote:Original post by Yann L
Quote:Original post by eedok
If something was completely useless they would have removed it from the language,

Legacy code support. If it wasn't for this, then goto would have been removed a long time ago. They just didn't want to break backwards compatibility.


Not really. In C, and other languages where SESE is a valid technique, goto can actually be used to make things like error handling and resource cleanup more readable than equivalent non-goto code. It helps if you remember that C and C++ are different languages, and techniques that are valid and indeed idiomatic in one can be reprehensible in the other.


But Linus doesn't use SESE - consistently, anyway, and in the cited code, anyway. He seems to jump through hoops to make it work half the time, and say "ah, fuck it" for the rest.
Quote:Original post by Zahlman
But Linus doesn't use SESE - consistently, anyway, and in the cited code, anyway. He seems to jump through hoops to make it work half the time, and say "ah, fuck it" for the rest.

Yes, well, I'm defending it's continued existence in C, not it's usage by any particular individual. Much in the same way that I support operator overloading in C++, but I think some of the things programmers do with it are quite sick.

This topic is closed to new replies.

Advertisement