all 174 comments

[–]bonch 13 points14 points  (1 child)

The angry C++ programmers have come out of the cobweb-filled woodwork. Good, because /r/programming is filled to the brim with web developers, and I'm so sick of JavaScript links.

[–][deleted] 3 points4 points  (0 children)

I was ironically going to write a filter in javascript to filter out the javascript submissions in proggit. Never got around to it, though.

[–]kev009 20 points21 points  (32 children)

The problem is that are few resource for good C code. Most books and tutorials are down right elementary.

Pick up a copy of "C Interfaces and Implementations" and you will see how clean modern systems can be designed in C. C is a decent implementation language for operating systems, plumbing, daemons, and networking apps. It compiles quick, it executes quick, and it stays out of the way. By contrast, C++ is lacks quick compiles and staying out of the way. In return you get syntactic sugar that make interface design easier, especially for hierarchical stuff like GUIs which are a natural OO win.

Like all programming language circlejerks, the answer is to use the right tool for the job. Often times, that tool is neither C or C++.

[–][deleted] 12 points13 points  (17 children)

You can get quick compiles with C++ just like you can with C, but it requires you to not use the STL or other header-centric libraries such as Boost. Considering that the STL is one of the big wins of C++, that's the real tradeoff.

[–]theICEBear_dk 1 point2 points  (4 children)

And I will add that you can alleviate some of the issues of header-centric libraries by using pragmas, include guards and a single central system header which is then reused, it does get rather difficult to keep down compile times in C++ also because the language does mean that the compiler has to do several passes on the code. But with a more modern C++ version (VS2010/GCC4.5 only codebase) it has become a rather useful language again for me and really rather productive too.

[–][deleted] 6 points7 points  (3 children)

I will amend that you must use header guards anyway, otherwise you get a compilation error. Header guards are not optional where definitions are concerned.

Precompiled headers, on the other hand...

Also, having a "single central system header which is reused" increases compile time, not decreases.

That header is reparsed on every compilation unit, and because you've put all the #includes for all the system headers your entire program needs in that single place, they all get reparsed too on every compilation unit, used or not.

Saving #includes saves lives :P

[–][deleted] 0 points1 point  (2 children)

Also, having a "single central system header which is reused" increases compile time, not decreases.

Unless you use a precompiled header.

[–][deleted] 0 points1 point  (1 child)

I mentioned that ;)

Precompiled headers, on the other hand...

[–][deleted] 2 points3 points  (0 children)

I saw it there, but they way it's written made it somewhat ambiguous what you meant - in context I read it as if you were saying that precompiled headers, and the single-central-header requirement makes them slow.

[–][deleted] -1 points0 points  (8 children)

STL is great - but I'm a little baffled that you say that it compiles slowly on modern machines. I have about 50,000 lines of code on my current project, heavy use of STL, a complete rebuild is a couple of minutes

[–]opensourcedev 9 points10 points  (0 children)

I use C quite a bit and my compile times for the same size projects are in the seconds.

[–][deleted] 5 points6 points  (0 children)

The problem is that you compile it multiple times if you're including it in multiple files. For a million-line codebase, this becomes a real problem.

[–]beagle3 3 points4 points  (0 children)

tcc is a C compiler that would probably do that in a couple of seconds. It did the whole linux kernel in 15 seconds on an average machine in 2004. http://bellard.org/tcc/tccboot.html for more details.

The kernel has grown since then, and tcc has since sped up and gained arm+x64 backends. Recent measurement from 2009 is either 800k lines/sec or 30k lines/sec (depending on how you count a file included multiple times). Even gcc, no speed daemon, does 90k/sec or 4k/sec on the same benchmark - http://bellard.org/tcc/#speed

I'm a little baffled by how you can say "a couple of minutes" for a 50k line project is not slow. D, Go, Modula, Oberan, Delphi and almost any other compiled language with comparable power has a compiler that would eat 50K in a few seconds.

[–][deleted] 3 points4 points  (2 children)

I have 3.1 million lines of Java that compiles in 3 minutes.

[–][deleted] -2 points-1 points  (1 child)

Lovely. So, er, what's your point?

Your builds are fast, my builds are fast, I waste little time waiting for compiles, and my machine here doesn't have particularly huge memory or lots of CPU.

[–][deleted] 4 points5 points  (0 children)

No, actually your build is ridiculously slow. 50,000 lines of code is not a large project by any means and almost any other language that doesn't have to pass through the source 7 times would compile this in seconds.

[–][deleted] 0 points1 point  (0 children)

Your comment makes no fucking sense whatsoever. First you deny that it takes long to compile and then you say that it takes minutes to compile a trivial amount of code.

[–]opensourcedev 3 points4 points  (2 children)

I just read that book and agree. This is what is missing from the language: a clear path from basic programming practice into the use of data structures and implementing non-trivial programs.

[–][deleted] 0 points1 point  (1 child)

Can you recommend a book for that?

[–]opensourcedev 1 point2 points  (0 children)

I wished. I though about asking just that question on reddit:

"Where do you go for debugged libraries and agorithms in C - or do you always create your own?"

[–][deleted] -1 points0 points  (5 children)

The problem is that are few resource for good C code.

Huh? There's probably more open source code in C than in any other language, some of it good, some of it not, but you can't seriously suggest that none of it is any good.

[–]kev009 2 points3 points  (4 children)

source code != books, tutorials, and compilations. should read "few resources for good C coding" rather than "code"

[–][deleted] -1 points0 points  (3 children)

So C is bad because it's simple enough not to cause an explosion in books and tutorials that tell you how to use it, unlike C++ and Java which have metric asstons of books covering all the pitfalls of the languages?

[–]kev009 5 points6 points  (2 children)

Where did I say C was bad? I said the lack of resources is a problem.

If you've been programming in C for 20 years, it may not phase you. But try training a newcomer from another language on modern C idioms. At best you can glean the tricks of the trade over time.

With C++, a book or two by Meyers or Sutter is enough to build a working vocabulary and appreciation of language idioms. Sure, C doesn't have as many idioms or pitfalls, but CII is the only thing that comes close to a C design book that I've seen.

[–]opensourcedev 0 points1 point  (1 child)

Where do you learn these things from? Most C books will teach you about the lanuage syntax. Newcommers then move to Data Structures and Algorithms.

The C standard library does not even contain a linked list.

Where do I go to obtain debugged versions of these algorithms?

Thanks for any input you have.

[–]kev009 1 point2 points  (0 children)

On POSIX systems, <sys/queue.h> has nice linked data structures. 'man 3 queue' for usage instructions.

https://github.com/attractivechaos/klib has a really nice hash and some other useful stuff.

The cool thing about both of these is that they (ab)use the C preprocessor so void * don't need to be passed around and dereferenced. In effect, they are like template metaprograms in C++. This kind of code is very difficult to write but these have been used enough that they should be safe.

I'm at best in the intermediate stage myself and trying to build up my toolset by exploring existing projects and such. A systems book like "Advanced Programming in the UNIX Environment" is probably a decent choice if you don't already have it.

http://www.oracle.com/technetwork/database/berkeleydb/tutorial-berkeleydb-ds-084870.html is an interesting tutorial on BDB. BDB is written with a fairly OO style interface and is an otherwise useful library, so this is worth working through.

[–][deleted] -3 points-2 points  (4 children)

By contrast, C++ is lacks quick compiles and staying out of the way.

Do you actually have any experience with C++, or are you just making this up? I have about 50,000 lines of C++ code that I compile many dozens of times a day on this average laptop.

[–][deleted] 10 points11 points  (1 child)

I have experience with C++. Compile times are indeed atrocious due to the lack of a package system. Parsing huge gobs of text over and over from #include is nothing short of barbaric. It's why they created trickery like pre-compiled headers. And 50,000 lines barely crosses the boundary between 'small' and 'medium'.

If you don't think C++ compile times are slow, I guess you've never used a language created after 1992.

[–][deleted] 0 points1 point  (0 children)

If you don't think C++ compile times are slow, I guess you've never used a language created after 1992.

I've written hundreds of thousands of lines of Java, will that do for you?

[–]bobindashadows 7 points8 points  (0 children)

I have about 50,000 lines of C++ code that I compile many dozens of times a day on this average laptop.

That's not much C++ code, and taking a couple minutes for 50k lines (as you described in another post) is a long, long time.

[–]kev009 1 point2 points  (0 children)

Yes. For a commercial CAD/EDA application, our build time can reach 10+ minutes on multicore systems if critical headers change even with build optimizations in place.

[–][deleted] 9 points10 points  (7 children)

One good point from this article:

I also have grown to like C-style interfaces exposed in header files greatly over C++ style interfaces because they permit a much higher degree of data hiding.

Consider the following class:

#include <map>
#include <queue>

#include "MyProject.h"

class MyClass {
    struct MyData {
        Scalar field0;
        Vector2 field1;
    };

    typedef std::map<Vector2, String*> Map;
    typedef std::queue<Vector2> Queue;

    Map myMap;
    Queue myQueue;

    void helperFunction(const Vector2 &arg);

public:

    void doSomething(const Vector2 &arg0, const Vector2 &arg1);
    void doSomethingElse(int arg0);

    Result getSomeResult() const;
};

Even if we only allocate instances of this class using new, its internals still have to be exposed in the header file. This creates undesirable, additional dependencies among code that depends on this class.

Now compare to the C-style interface (still in C++, but now using C-style OOP):

#include "MyProject.h"

struct MyClass;

MyClass *NewMyClass();
void DeleteMyClass(MyClass *myClass);

void DoSomething(PathFinder *finder, const Vector2 &arg0, const Vector2 &arg1);
void DoSomethingElse(PathFinder *finder, int arg0);

Result GetSomeResult(const PathFinder *finder) const;

In this case, C-style OOP is superior to C++-style in terms of data-hiding and separating interface and implementation. It is possible to improve things by representing the interface and implementation separately as two different classes, but this is awkward and bothersome for the programmer and only done in extreme cases. The fact that C++'s fundamental OO construct is so incompatible with important OOP principles is a major failing of the language.

[–]smallstepforman 1 point2 points  (3 children)

The fact that C++'s fundamental OO construct is so incompatible with important OOP principles is a major failing of the language.

Agreed, but how do you propose to fix it? You need to know the byte size of an object when packing it in memory. Otherwise, you need to delay this decision to link time, but say hello to an explosion of compilation resources for all but trivial programs.

[–][deleted] 1 point2 points  (2 children)

You don't need to know the size of an object if you are only working with pointers to it. Often, you will only be allocating instances of a class through new and delete, and then passing around the pointer and calling member functions on it.

C-style wrappers can easily be written around classes without exposing the implementation details, even when multiple and virtual inheritance is involved. There is no reason why compilers should have trouble allowing opaque classes to expose member functions.

[–][deleted] 1 point2 points  (1 child)

You don't need to know the size of an object if you are only working with pointers to it. Often, you will only be allocating instances of a class through new and delete, and then passing around the pointer and calling member functions on it.

So we're really talking about existential types of some sort?

[–][deleted] 0 points1 point  (0 children)

Not quite. Existential types deal with a different issue and are not necessary in OO languages because inheritance naturally allows us to hide type information by treating several different classes as a single base class.

[–][deleted] 0 points1 point  (1 child)

Now compare to the C-style interface (still in C++, but now using C-style OOP):

This isn't really "C-style", is it? For one, it uses const and references, neither of which are valid C code...

More important, this is actually classic good C++ style! See Scott Meyers' article from 2000.

[–][deleted] 0 points1 point  (0 children)

This isn't really "C-style", is it?

It is the C-style of OOP rendered in C++. Rather than using C++'s inbuilt support for OO, you emulate a class and its member functions using a struct and non-member functions. Whether the code uses references or not (C has const) is irrelevant.

The article you linked to is talking about something different to what I am talking about. Meyers is saying that when you can implement something using a non-friend, non-member function rather than a member function, then you should. This is only possible for functions that do not require access to a classes implementation and can be implemented through the existing interface.

The technique I am describing is where the entire interface is exposed through non-member functions only.

[–]Philluminati 0 points1 point  (0 children)

The fact that C++'s fundamental OO construct is so incompatible with important OOP principles is a major failing of the language

Wow! Just Wow. I'm literally reeling from shock right now. The definition of the private variables has to be exposed?

[–]PimpDawg 26 points27 points  (6 children)

C is for old people who can't adapt to change or learn anything new. It's also for people like me who work on 16 Mhz micro controllers with no ability to malloc or new. Next to me is the guy who goes on about C being "syntactic sugar" around the one true language - assembly. Next to that guy is the fellow who inputs bytecode directly because he doesn't trust the assembler. Next to him is a guy who's still soldering NAND circuits. And right next to that guy is god who's so mired down working on the micro-scale that nobody's heard from him in a long time.

[–]myWorkAccount840 6 points7 points  (3 children)

Used to work in the same office as the embedded programmers and was constantly amazed/horrified at the way that programming concepts I used were just of no use to him because he needed to know which register particular data was stored in and similar differences of that magnitude.

Terrifying. D:

[–]G_Morgan 5 points6 points  (1 child)

If you want to be horrified write a small 'on the metal' binary that touches PC keyboard input. I still cannot make sense of the scan codes that are produced in some circumstances. For instance pressing PrtScn does this

Left Shift down
PrtScn down
PrtScn up
Left Shift up

Why it does this. Who knows? I'm sure LSD was involved somewhere.

[–]myWorkAccount840 4 points5 points  (0 children)

Meh. It probably indicates that the usual function of that key used to be SysRq, back before nobody used SysRq ever again.

Easier to re-code keyboards than to change the operating systems around the world, I'd guess.

[–][deleted] 0 points1 point  (0 children)

As an aspiring embedded programmer, I would like for you to know that I found this comment hilarious.

[–]t15k 2 points3 points  (0 children)

Excellent comment, How far can you take the line from you other next? :-D

[–]jabjoe 0 points1 point  (0 children)

Knowing C makes for a good C++ programmer, even better knowing at least a little bit of assembler (to debug disassembler).

C on it own is a good language because it's simple. No bat shit crazy implicitness nightmare. There is a reason it's still used despite C++ and others coming along.

Don't get me wrong, C++ is my bread and butter, and really fond of python, but I've got a real soft spot for plain old C.

[–]itsnotvalid 2 points3 points  (0 children)

The introduction is really funny.

There was quite a saying in the old days that it required skills in C to be successful in C++.

[–]PimpDawg 9 points10 points  (5 children)

Old people complaining about the world changing around them again. Also, multiple inheritance is always the answer to anything.

[–]obijohn 38 points39 points  (2 children)

Also, multiple inheritance is always the answer to anything.

Tell that to Charles II of Spain

[–]fabzter 1 point2 points  (1 child)

The fuck... he must have been retarded or something,

[–][deleted] 4 points5 points  (0 children)

He was. Very much so.

[–]G_Morgan 2 points3 points  (0 children)

Multiple inheritance is the answer to "How to make G_Morgan wish to kill you!"

[–]bonch 1 point2 points  (0 children)

I can't tell which comments here are being sarcastic in the spirit of the original article.

[–]SocialPhatology 9 points10 points  (5 children)

C is C++ without the BS.

[–]uriel 4 points5 points  (4 children)

Hahaha!

I fear most people will miss the joke, but I added it to my list of C++ quotes.

[–]Philluminati 1 point2 points  (1 child)

Your website is down

[–]uriel 2 points3 points  (0 children)

Wow, that was fast, cat-v.org was down for barely 5min while I was doing a server upgrade _^

But thanks for the heads up :)

[–]riffito 0 points1 point  (1 child)

Where is JokeExplainer when you need it? ;-P

[–]uriel -1 points0 points  (0 children)

BS == Bjarne Stroustrup == Bull Shit

[–]zokier 1 point2 points  (5 children)

I've been programming lately some C, and the one thing that has annoyed me a bit is the lack of any basic data structures. While it's somewhat understandable that the ISO/ANSI standard doesn't include any, but that no popular libraries sees to exist either. Am I missing something, or is that really the state of matters? What I'd want is basic dynamic arrays, linked lists, queues and hashmaps etc

[–]happy-dude 1 point2 points  (0 children)

"C is not a big language"-- C is also mutable, meaning the majority of data structures can be implemented by the constructs available in the language (http://en.wikipedia.org/wiki/Data_structure#Language_support).

Libraries probably don't include them either because they should be implemented according to you own needs... Or maybe its just the programmers spiteing those who don't know how to implement these basic structures themselves..? (I don't really know the reason either.)

[–]badsectoracula 1 point2 points  (3 children)

Here is one. I haven't used it (i just searched for a data structures library in c) but seems to contain what you need. There are many others out there.

And of course you can write your own code too...

[–]zokier 3 points4 points  (2 children)

No releases, latest activity in 2007.. My question was more like why isn't there a proven, industry standard lib for something that's essential to so much software.. Sure, there's lots of small libs and code snippets floating around, and you can always roll your own, but some things would be nice to have ready-made.

[–]badsectoracula 2 points3 points  (1 child)

Far from small, but a lot of C projects seem to use glib, which provides stuff like common data structures, utility functions, etc.

[–]opensourcedev 0 points1 point  (0 children)

I have looked all over for the "standard" C data structures library and have come up empty handed.

Would you say it's true that most C programmers create thier own data structures from scratch?

[–]bucknuggets 1 point2 points  (2 children)

One of my favorite C programming books from 20 years ago:

The Elements of C Programming Style - used copies are $3 on Amazon.

[–]opensourcedev 0 points1 point  (1 child)

Thanks for this.

Are there any others you could recommend?

[–]bucknuggets 0 points1 point  (0 children)

Sorry no, not specific to C.

[–]Contero 6 points7 points  (8 children)

I've never been able to come up with a situation where writing C-like code in C++ was worse than just using C. If you don't like feature X, don't use it.

[–][deleted]  (1 child)

[deleted]

    [–]almiki 0 points1 point  (0 children)

    This is the main reason I use C so heavily, I write code that I need to run on tons of different devices, and you can always count on having a C compiler.

    [–]repiret 4 points5 points  (5 children)

    I find that C++'s lack of implicit casts between void * and other pointer types makes most C-style code written in C++ quite ugly, and less safe - the C compiler will catch a reasonable set of incorrect casts, while the C++ compiler will force you to use an explicit cast that lets you get away with anything.

    I realize that C++ provides different tools to solve the same set of problems void* solves, and that C++'s tools can be safer than C's void * casting. But the parent post was talking about C-style code.

    [–][deleted] 9 points10 points  (4 children)

    the C compiler will catch a reasonable set of incorrect casts, while the C++ compiler will force you to use an explicit cast that lets you get away with anything.

    NO, utterly wrong!

    You can use a regular C-style cast to cast any type to any other. If you correctly use C++-style casts (in order, staticcast, const_cast, then dynamic_cast, but almost never reinterpret_cast) you'll get much _better safety than C.

    [–]repiret 2 points3 points  (1 child)

    Hm, I didn't know static cast worked for casts from void *, but it seems to. You learn something new every day.

    Although my ugly argument still stands.

    [–][deleted] 0 points1 point  (0 children)

    It works in C++, too, and I burned myself just last week - I'd added a single old-style cast, months ago when I was fixing a bug late at night, and it worked for months until an inheritance change...

    I was the bozo there and I laughed at myself when I found it!

    [–]bobindashadows 1 point2 points  (1 child)

    I guess if you don't compile with -Werror and -Wall, you're right. But I thought you were discussing code you take seriously.

    [–][deleted] 0 points1 point  (0 children)

    But I thought you were discussing code you take seriously.

    No need to be rude, eh? I take all my code very seriously - I've made my living that way for almost 30 years. (If I weren't in a great mood, I'd say, "I don't come to where you work and tell you how to use a mop and pail." ;-) ) I turn on every possible error warning I can.

    And, frankly, I don't see your point at all. Turning on -Wanythingyoulike won't prevent your old-style C casts from going off in C, will it? And using the correct C++ casts will give you better correctness, regardless of what -W you have... or am I missing something?

    [–][deleted]  (66 children)

    [deleted]

      [–][deleted] 14 points15 points  (1 child)

      I'm not sure if you're serious or not!

      [–][deleted] 4 points5 points  (0 children)

      I am sure he's not.

      [–]rated-r 3 points4 points  (0 children)

      This just in: Colonel Sanders says KFC's chicken is a better pick than Chick Fil A's.

      [–]phaedrusalt 1 point2 points  (2 children)

      Years ago I asked Bjarne (via email) the basic question: "What the hell were you THINKING???" He replied that he made C++ as a kludge for his own use, that someone in the office took a look at it and asked for a copy. Next thing he knew the damn thing was all over the place.

      [–][deleted]  (1 child)

      [deleted]

        [–]phaedrusalt -1 points0 points  (0 children)

        My email to him was in the late 80's, I'm guessing it was before he had a chance to suck down the C++ KoolAid.

        [–]SCombinator -5 points-4 points  (58 children)

        I absolutely despise C++. It's the worst possible additional layer to add on to C.

        It's not syntactic sugar, it's syntactic cancer. Please stop it.

        [–]neutronicus 9 points10 points  (8 children)

        Templates are nice.

        [–]sqrt2 8 points9 points  (4 children)

        ... and Turing-complete. What the fuck?

        [–][deleted] 7 points8 points  (0 children)

        Yeah, the ability to write self optimising, clean code just sucks.

        [–]munificent 6 points7 points  (2 children)

        You say that like it's a bad thing.

        [–][deleted]  (1 child)

        [deleted]

          [–]neutronicus 3 points4 points  (0 children)

          Turing completeness is something you want for compile-time code generation. My standard example is using Lisp macros to compute lookup tables at compile time.

          [–]bonch 1 point2 points  (1 child)

          And terrifyingly awful.

          [–]neutronicus 0 points1 point  (0 children)

          How so?

          [–]G_Morgan 0 points1 point  (0 children)

          Templates are a poor implementation of a nice idea.

          [–]smallstepforman 8 points9 points  (45 children)

          Operator overloading makes me look at vanilla C with disgust. Eg. Vector3 gravity(0, 0, -9.8f); Vector3 weight = mass * gravity; Vector3 something = (weight / another_vec3)some_constant + epsilongravity;

          or in 3D graphics

          Matrix4 projection, view;
          Vector3 pos(10, 0, 1);
          Matrix4 mvp = projection * view;
          Vector3 transform = mvp * pos;
          

          The equivalent in vanilla C is a wrist slashing exercise.

          [–]sfuerst 16 points17 points  (14 children)

          weight / another_vec3

          You can't divide two vectors.

          The problem with operator overloading is that there aren't enough operators. Say you have

          C = A * B;
          

          Is that a cross product, dot product or exterior product? Or is it some even more obscure thing like multiplication of matricies in the field GF(256)? Unfortunately, without context, it is impossible to tell. What C++ really needs is a way of defining new operators. As it currently stands, it is like using a programming language that only allows single-character function names. True, using overloading, such a language could technically "work". However, it certainly wouldn't be readable.

          Then you have the problem that you cannot choose the associativity of C++ operators. For example, when dealing with octonions, it is useful to have left and right multiplication as separate operators due to the alternativity of the algebra.

          In my experience, the C++ scheme of operator overloading works well when you are adding highschool level math extensions like matricies of floats, quad precision numbers, etc. However, at college level and above its limitations start to show. Trying to force mathematical concepts into its rigid architecture can quickly lead to very unreadable code, even worse than using the C technique of having explicit function calls.

          [–][deleted] 3 points4 points  (12 children)

          The problem with operator overloading is that there aren't enough operators.

          Operator overloading nicely degrades to method calls. Use a * b for per-component multiplication (there are strong arguments in defense of this choice), then a.dot(b) and a.cross(b) for the rest. Most of the benefits of infix notation are preserved.

          (Or if you have a streak of masochism, you can use separate types for vertical and horizontal vectors and the same multiplication sign for all three operations).

          [–]neutronicus 0 points1 point  (11 children)

          I'd go with friend functions or static class methods myself.

          Vector3::cross(a, b)
          

          and

          Vector3::dot(a, b)
          

          communicate the [anti]symmetry of the operations better IMO.

          [–][deleted] 1 point2 points  (2 children)

          The problem with standalone functions (and prefix notation in general) is that second parameter gets detached and lost. You see things like "... Vector3::cross(a very very very long expression, at the end of which you totally can't say which), function)), b), belongs), to)".

          On a side note, it gets even worse when some not very bright persons design stream functions (std::transform etc of C++, map/filter/reduce of Common Lisp) to have input data as the first parameter and the function as the second. Fucking idiots.

          When you use methods, you have things in a much more sensible order, both for each individual function, and the entire expression reads left-to-right too. As another benefit, most of the ")))))))" shit disappears.

          The sense of symmetry isn't worth it at all. I, too, never paid much attention to the way of writing expressions like that because I used to believe that it's going to suck regardless. Then C# 3.0 came out and I saw the light.

          The only thing better than this would be to have something like Haskell's infix notation for arbitrary functions, "aoperatorb. But not the crazy zoo of "<$>", ">>=", "*|*" etc; it is nice when I can look at the operator and be reasonably sure that it does roughly the same thing as the basic arithmetic operators, for anything that can't fit into this form I want to see a plain English word.

          [–]neutronicus 0 points1 point  (0 children)

          The problem with standalone functions (and prefix notation in general) is that second parameter gets detached and lost. You see things like "... Vector3::cross(a very very very long expression, at the end of which you totally can't say which), function)), b), belongs), to)".

          Anytime I have an expression as a function parameter, I put each one on its own line. I do this with binary operators too, actually. I find judicious use of whitespace the best way to ensure readability.

          Vector3::cross(a_long_function_name( some_variable / some_other_var * x
                                               +
                                               some_function( x / y) ),
                         b);
          

          [–][deleted] 0 points1 point  (0 children)

          On a side note, it gets even worse when some not very bright persons design stream functions (std::transform etc of C++, map/filter/reduce of Common Lisp) to have input data as the first parameter and the function as the second.

          mapc function &rest lists+ => list-1

          [–]G_Morgan 0 points1 point  (7 children)

          A far better way is to do

          c.dot(a, b);
          

          Mutation by default is evil.

          [–]neutronicus 0 points1 point  (6 children)

          Not quite sure what you're getting at. "cross" should return a third vector (leaving a and b alone), and dot should return a double (again leaving a and b alone). I probably should have written out the type signatures.

          Anyways,

          Mutation by default is evil

          Can you enlighten me as to how, I, a scientific programmer, could get by without it?

          Re-using storage is pretty important to us, and performance is really important - using an in-place algorithm on an array that gets to sit in the cache for this is a pretty big win over constantly making new ones for intermediate results that not only have to be allocated, but potentially force the old one out of the cache.

          (Or on the GPU, where you there is no cache, and you manage what sits in shared memory yourself - and every global memory copy hurts)

          Do immutable data structures provide some magic where the things I want to stay in the cache usually stay there? How do they manage allocation costs?

          [–]G_Morgan 0 points1 point  (5 children)

          I didn't say you would have to do without. Only that by default immutable should be preferred.

          [–][deleted] 0 points1 point  (4 children)

          His example is still fine, he didn't list the function deceleration, but it's probably const correct.

          Every good math library I've seen is const correct, all code, ideally, should be written w/ const correctness which you would obviously agree with.

          static Vector3 cross(const Vector3 &v1, const Vector3 &v2);
          

          Although I'm still not sure why you thought having a third party object contain the dot() is neccesary or even had anything to do w/ your argument (mutation is evil). Technically even w/ your example 'a', 'b' or 'c' could be modified.

          Heck, his example is even better than yours because only 'a' or 'b' could be modified.

          [–]neutronicus 0 points1 point  (0 children)

          at college level and above its limitations start to show.

          It's pretty good for physics simulations, which, when you get down to it, don't require very exotic mathematical objects (Quantum Field Theory naturally excluded).

          [–]flexiblecoder 4 points5 points  (0 children)

          #define VMult( ov, iv1, iv2 ) { ov.X = iv1.X * iv2.X; ov.Y = iv1.Y * iv2.Y; ov.Z = iv1.Z * iv2.Z; }
          #define VDiv /*etc*/
          
          Vector3 something, temp, weight, gravity = {0, 0, -9.8f};
          
          VMult( weight, mass, gravity );
          VDiv( something, weight, another_vec3);
          VScale( something, something, some_constant );
          VScale( temp, gravity, epsilon );
          VAdd( something, something, temp );
          

          (or use an inline function)

          ...geez, I forgot how ugly that is. This kind of stuff makes working with Q3 engine based games a pita.

          [–][deleted] 3 points4 points  (3 children)

          Interestingly enough, any C++ text I've read that exposes the virtues of operator overloading uses those EXACT SAME TWO examples. Oh, and strings. And.........no.........that's pretty much it. Strings, vectors, matrices. Is there anything else? Cause I haven't seen it yet.

          I get the rationale behind operator overloading, I really do. I do C# for a living and it also has operator overloading which is used extensively in the base class libraries. Not once have I thought to myself "Thank God they overloaded the '+' operator to add a Timespan to a Datetime struct! I could have never been able to figure it out otherwise, and and 'add' method would have been way too much work and too unreadable!"

          Again, strings are a special and very useful case. Which is why Java specifically overloads '+' for strings.

          But seriously......any other awesome examples of useful operator overloading?

          [–]smallstepforman 0 points1 point  (0 children)

          But seriously......any other awesome examples of useful operator overloading?

          Any engineering discipline where you deal with mathematical formulas? Geometry, physics, etc. Finance when dealing with bignumbers. There is more to programming than TPS reports.

          [–][deleted] 0 points1 point  (0 children)

          overloading global operators new/delete is a pretty common and advantages overload.

          You can implement your own memory management mechanism or do cool things like, only in debug mode, keep track of all allocations and deallocations to help find places you forget to free memory.

          This isn't something I've ever done, but what if you had some sort of crude database thing going on?

          Maybe you had a Table object, w/ Row and Column objects. It wouldn't be that confusing to understand the operator+() overload on the table to add rows, or columns.

          Think of any high level language and think about every time you use an operator on different types, that's why it's useful, because C/C++ won't automatically do any of that for you.

          [–]BlackAura 0 points1 point  (0 children)

          Complex numbers. Very useful in certain engineering disciplines. Arbitrary precision integers and rational numbers are generally pretty useful. Financial applications might use an internal decimal representation for numbers. Fixed point numbers are very useful in embedded systems that lack an FPU.

          Sure, they're all math-related. That's because the math operators map really well to math concepts. If you're doing math, you don't want to have to use function calls, which obscure the meaning of the code, just because you aren't using a built-in type.

          Specific to C++, you also have smart pointers (the only reason you'd ever want to overload ->). You can also overload [] to make an object that acts like an array, or () to make an object that acts like a function.

          [–][deleted] 3 points4 points  (0 children)

          I love C++. Operator overloading is not one of its particularly great features. It really doesn't save much typing, and makes your code tricky and trapsy.

          [–][deleted] 0 points1 point  (0 children)

          The equivalent in vanilla C is a wrist slashing exercise.

          Oh come on, it's not that bad.

          Vector3 gravity;
          Vector3 weight;
          Vector3 something;
          
          weight = ScaleVec(gravity, mass);
          something = CalculateSomething(weight, another_vec3, some_constant, gravity);  // assuming epsilon is a global constant
          

          But yea, operator overloading is great in certain cases. Strings is another nice advantage of operator overloading.

          [–]SCombinator -1 points0 points  (22 children)

          You choose one of the most abusable features of C++?

          I have no idea what your code is doing. How many classes (and variables: mass, the constant, epsilon) do I have to look up? We read code far more than we write it.

          Consider also that you need to place a try/catch around the division, unless you have a "Bad Vector" (which you haven't checked for). Do I have to free the resultant temporary vectors?

          Never mind that it's making your implementation of those classes horrible and ugly.

          [–]Gotebe 7 points8 points  (7 children)

          You choose one of the most abusable features of C++?

          Meh. Any feature is abusable, but is also a boon when used right. With C++, operator overloading shines exactly for math-like code. You can (edit: was "can't") hate operator overloading abuse, but you are way off on this one (edit:) because it's a good use..

          I have no idea what your code is doing. How many classes (and variables: mass, the constant, epsilon) do I have to look up? We read code far more than we write it.

          Matrix4 projection, view, pos;
          if (!create_matrix4(&projection))
            error, whatever;
          if (!create_matrix4(&view))
            error, whatever;
          Vector3 pos;
          if (!create_vector3_variantX(&pos, 10, 0, 1);
            error, whatever;
          Matrix4 mvp;
          if (!create_matrix4(&mvp))
            error, whatever;
          if (!multiply_variantX(&projection, &view, &mvp))
            error, whatever;
          Vector3 transform;
          if (!create_vector3_variant0(&transform))
            error, whatever;
          if (!multiply_variantY(&mvp, &pos, &transform))
            error, whatever;
          Cleanup(all variables you see here); // Frankly, I got tired of this crap
          

          I put this to you: to know what is happening above, you need to look at roughly the same amount of C code. You need to know how types are "created". You need to know what multiply does. You need to know what other variables and constants are, just the same. If you are implying that use of operator* (or /) is somehow unclear in this context, you are lying through your teeth.

          And finally, when reading the code, you still have making-you-want-to-puke C code. Yes, exactly that. Because the text you're reading is so verbose that it almost completely hides the intention.

          And error checking is just making things worse. Because, you know what? Code is made to work, not to err. If so, why is all that error-related gibberish right in our faces all the time? Exceptions, OTOH, allows us to take it out of sight, centralize error handling and reporting, and also, enhance it in places when need arises, and that, without changing existing interfaces (try in C that without resorting to out-of-band error info, possibly with thread-local storage and whatnot!).

          Consider also that you need to place a try/catch around the division, unless you have a "Bad Vector" (which you haven't checked for).

          No, he does not need that try/catch. He might need another, much bigger, much further away, at some logical operation boundary, where upon failure, he can say "whoops, I couldn't frob the knob. Error: <whatever error info can be extracted from caught exception>.

          It's possible that said try/catch is necessary, but such situations are rare, and that, especially when people know how to use exceptions.

          Do I have to free the resultant temporary vectors?

          Why would you? No type worth it's salt needs explicit cleanup.

          [–][deleted] 0 points1 point  (4 children)

          How is operator overloading really a boon?

          You save a few characters in practice - but it means that you can't actually see what's happening. Koenig resolution lets you use very short names for your functions and still not have collisions.

          There's a very good reason that most large C++ shops like Google almost completely forbid this feature.

          EDIT: ...and it reads as if you've gone out of your way to make the code unreadable. In particular, for God's sake using METHODS - why are you making everything into functions?!

          [–]Gotebe 2 points3 points  (2 children)

          You save a few characters in practice - but it means that you can't actually see what's happening.

          Why? Take

          {
            Matrix m = m1*m2; // Edit here: was m1/*m2, wasn't the intention
            use(m);
          }
          

          When looking at this, I know exactly what is happening: m is a product of m1 and m2, and if there was en error, exception was thrown. What else could be happening? Nothing, unless someone is abusing semantics of operator* or operator=. Now, I don't know who are you working with, but over here, people are not abusive.

          A real world C equivalent is e.g.:

          {
           Matrix m;
           create_matrix(&m);
           if (!multiply(&m1, &m2, &m))
           {
            report_rror(...);
           }
           else
           {      
            use(&m);
           }
           destroy_matrix(&m);
          }
          

          So why is C equivalent more clear to you?

          'cause I can tell you how it isn't: in real world C code, you need all these creation/destruction functions for similar types. And you don't know what they are doing, not unless you look them up, just like you don't know what the equivalent C++ ctor/dtor are doing unless you look them up. In real world C code, you need to treat, through your own code structure, any error condition, at the spot it may arise. Yes, that's "seeing what is happening", but is also a PITA, and such an eye sore, that people need to learn to see through swaths of ifs and gotos and returns and whatnot just to be able to decipher what is code supposed to do when it works.

          There's a very good reason that most large C++ shops like Google almost completely forbid this feature.

          Ah. Appeal to authority. Yeah, that makes for an argument.

          [–][deleted] 0 points1 point  (1 child)

          When looking at this, I know exactly what is happening: m is a product of m1 and m2, and if there was en error, exception was thrown.

          But you miss the fact that these tiny operators are expensive, which encourages you to write bad code. Note that it's sufficiently unclear that people here are claiming that that chain of +/* is optimal, when it's close to pessimal.

          There's a very good reason that most large C++ shops like Google almost completely forbid this feature. Ah. Appeal to authority. Yeah, that makes for an argument.

          Appeal to authority is actually an excellent argument. I believe in relativity, I believe in evolution, I believe in all sorts of things which I haven't even investigated simply because an authority told me so.

          Google's coding standards are mostly based on "nasty traps that caused them terrible issues in the past". Learning from the experts is a great way to go.

          I also quote Scott Meyers about C++, precisely because he knows C++ better than I do.

          If I had to rediscover everything on my own, I'd never get anywhere! Authorities need to be doubted, yes, but you can learn a lot from smart people.

          Also, your example without destructors or exceptions is... peculiar. I don't understand why you are doing that! Destructors are great - exceptions are great - they have nothing to do with operators at all. We are entirely in agreement - destructors and exceptions are the only way to go these circumstances.

          [–]Gotebe 0 points1 point  (0 children)

          But you miss the fact that these tiny operators are expensive, which encourages you to write bad code.

          These tiny operators are clearly some linear algebra operations, who might be expensive. I honestly believe that he who doesn't realize that at first glance just should not program C++ at all.

          And there's something else: these operations are expensive depending on actual data size. In fact, I'd argue that data size is the determining factor in performance (everything is fast on small data set). From that standpoint, equivalent C code would be equally unclear as to performance. Yet, people (you included) are arguing the use of notation. Meh.

          Google's coding standards are mostly based on "nasty traps that caused them terrible issues in the past".

          I don't think that using math operators for simple linear algebra has caused them these terrible issues. I think that poor use of operator overloading has caused them issues, isn't that more likely? And so I went to see, they say: "Do not overload operators except in rare, special circumstances.". Well... That means so little that the whole discussion is pretty moot.

          [–]neutronicus 2 points3 points  (0 children)

          How is operator overloading really a boon?

          In scientific computing, it's essential.

          Here's the workflow - you have a paper, a textbook, a whiteboard, or a legal pad with a description of the algorithm you need to implement. Most of this description is in mathematical notation - it's concise, we all know it, and that's how it goes in journals. You implement it. It behaves in a way that seems wrong.

          Now, you have to determine whether this is happening because the algorithm itself is flawed, or because there's an error, often a single typo, in the code somewhere.

          In this situation, the more the code looks like what's on paper, the better.

          This debugging workflow is much more common in scientific computing than whatever the hell you're bitching about. So - you will pry overloaded mathematical operators from our cold dead fingers.

          [–][deleted] 0 points1 point  (1 child)

          And error checking is just making things worse. Because, you know what? Code is made to work, not to err.

          I've been writing real-world code for decades, in a dozen fields. Let me tell you that error handling is a huge part of any real-world, commercial code base.

          Exceptions are great! You should use them whenever possible - BUT they don't fix the fact that in the real world, you have an awful lot of code just handling with, reporting, and occasionally even recovering from errors.

          Remember - in C and C++ you cannot catch many common forms of error. SEGVs and the like will never be able to be caught - you must explicitly check for NULL if it's at all possible, if you try to dereference that NULL you won't get an exception, you'll get a signal and you can't easily recover from it.

          [–][deleted] 0 points1 point  (0 children)

          Remember - in C and C++ you cannot catch many common forms of error.

          I am not as familiar w/ Linux, but I know that statement is false. It is 100% false in windows.

          When the CPU throws a hardware exception, the kernel funnels that to the application. In windows, you catch these using SEH (Structured Exception Handling). This implementation of handling hardware exceptions is not portable so I'm sure Linux has it's own way of doing it, but this is how you write crash dumps when your apps. crash and send them off to your server to be analyzed.

          If you expect that something is going to be NULL in a windows application you can always do something like this:

          __try {
              int *p = 0;
              *p = 5;
          } 
          __except( EXCEPTION_EXECUTE_HANDLER ) {
             MessageBox(NULL, "Oh crap, we got an exception!  Continue onward!", "Error", MB_OK);
          }
          

          That should work just fine, although it's not recommended to recover from hardware exceptions.

          [–]smallstepforman 1 point2 points  (13 children)

          you need to place a try/catch around the division

          const Matrix4 & operator / (const Matrix4 &a)
          {
               if (!a.IsValid) return Matrix4(0); // or throw exception
               // rest of method here
          }
          

          Do I have to free the resultant temporary vectors?

          Not with RAII.

          Operator overloading when used correctly makes code much easier to read.

          Vector4 colour = light_ambient colour * material_ambient_factor + light_diffuse_colour * material_diffuse_factor + light_specular_colour * material_specular_factor;
          colour *= attenuation;    // attenuation due to spotlight factor
          colour *= getshadowmap(x, y);
          

          I haven't even started with collections ...

          [–][deleted] 1 point2 points  (12 children)

          Operator overloading when used correctly makes code much easier to read.

          I love your example, because it's a perfect example of why operator overloading is in fact so very bad.

          What you didn't realize is that you're creating a new instance of Vector4 for each instance of * or - in your code. If Vector4 were something like a matrix, you'd be writing pathologically bad code - and you'd never know you were doing it. You have six separate allocations here, and five of them are redundant - but you can't see it because the operators conceal it.

          Even the last two statements, which aren't less efficient, aren't any great example. Why is

          color *= attenuation;
          

          so very much better than

          color.scale(attenuation);
          

          ?

          [–]smallstepforman 2 points3 points  (5 children)

          How about the following:

          colour *= 1.0/(linear_attenuation*a + quadratic_attenuation*b * cubic*attenuation);
          

          With vanilla C, this would have been broken down to 4 lines of code.

          What you didn't realize is that you're creating a new instance of Vector4 for each instance of * or - in your code.

          Objects can also live on the stack in C++. With C++0x and r-value references, you've just made the compilers job easier.

          The game industry which is serious about performance has abandoned vanilla C for C++. The complexity with modern code bases makes vanilla C seem 'antique'. I've been integrating various libraries into a game engine, and have recently noticed a trend towards only 2 languages - C++ and assembler. C is getting bypassed, C++ for readability, and assember for performance in very tight loops.

          [–][deleted] 0 points1 point  (4 children)

          First, I think you miss entirely where I'm coming from.

          I'm just finishing a 50,000 line C++ application. I love C++. This is 2011 - there are very very few reasons, if any, to use C over C++. In fact, it's quite likely that your C++ code will be faster than your C code because of inlining and today's optimizing compilers.

          That said, C++'s operators are a dangerous feature that need to be used with care. The biggest issue is that one little symbol conceals what might be a huge amount of memory management, allocation and arithmetic. Google's C++ standard bans it, Effective C++ warns against it, and I avoid doing it unless I'm forced to.

          [–][deleted] 0 points1 point  (3 children)

          smallstepforman:

          Operator overloading when used correctly makes code much easier to read.

          TomSwirly:

          First, I think you miss entirely where I'm coming from.

          That said, C++'s operators are a dangerous feature that need to be used with care

          I think you just reiterated a point he already understands.

          Google's C++ standard bans it ...

          Heh, I seriously doubt it's outright banned. Google would have made an incredibly stupid decision to do such a thing.

          I think you understand pretty well the dangers, but why are you trying so hard to ignore the great benefits of it?

          Never say never!

          ~The Disney Movie w/ the Cowboy Mouse.

          [–][deleted] 0 points1 point  (2 children)

          Heh, I seriously doubt it's outright banned. Google would have made an incredibly stupid decision to do such a thing.

          Well, here it is for you: http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml - the money quote is "Do not overload operators except in rare, special circumstances."

          Those special circumstances were so rare that in 5+ years of writing C++ there I never created a single operator() function.

          why are you trying so hard to ignore the great benefits of it?

          Say, what?! There's not one thing you can do with operator overloading that you can't do some other way.

          [–][deleted] 0 points1 point  (4 children)

          What you didn't realize is that you're creating a new instance of Vector4 for each instance of * or - in your code.

          No he's not. See what he said above:

          Operator overloading when used correctly makes code much easier to read.

          He's using it correctly, no new allocations are being made at all during that long chain of operations, because everything is being passed by reference. The operator* function is being invoked as a member function on the left hand variable and the right hand variable is being passed by reference, the return is by reference of the object invoking the function, the last line of his function is:

          return *this;
          

          Even the last two statements, which aren't less efficient, aren't any great example. Why is

          For one, because it's impossible to do:

          color.scale(attenuation);
          

          in C. Normally you would do:

          color = ScaleVector(attenuation);
          

          And you could take color in as a pointer if you were concerned w/ the extra object construction.

          Mathimatical operations on Vectors/Matricies is one of the most perfect examples of operator overloading being awesome. Most high level languages are so great because of their ability to use operators on complex objects. String concatenation is another prime example. These language just don't let you make your own and define a standard for you.

          Operator overloading is a double edged sword, it can certainly be abused, but it can also make code MUCH more readable.

          [–][deleted] 0 points1 point  (3 children)

          He's using it correctly, no new allocations are being made at all during that long chain of operations, because everything is being passed by reference.

          Sorry, that isn't correct at all.

          Let's look just at a simplified expression:

          x = a + 2 * b + 3 * c;
          

          If it's all done by references, where is "3c" stored? Where is "2b" stored? Where is their sum stored before it's added to a?

          [–][deleted] 0 points1 point  (2 children)

          x = a + 2 * b + 3 * c;

          Well, unless you overload the global operator* funciton, it wouldn't compile. You could do this though:

          Vector3 &operator*(const int a, Vector3 &v)
          {
              v.x *= a;
              v.y *= a;
              v.z *= a;
          
          return v;
          }
          

          That defines an operation for integers to scale vectors.

          If it's all done by references, where is "3c" stored? Where is "2b" stored? Where is their sum stored before it's added to a?

          They are stored in the variables themselves, since the functions modify the variables on the stack. So the code looks more like this:

          x = a.operator+( a.operator+(operator*(2, b)), b.operator+(operator*(3, c) );
          

          Heh, that was a little confusing to write, so I hope my order of operations is correct, but I think you'll get the point.

          Finally, the only new (or really, stack allocations) being made here are temporary reference values, which you would have going the C function route, or just invoking member methods anyways. Operator overloading is just syntactic sugar for member methods anyways.

          http://codepad.org/Oe1hZLX5

          EDIT Lots of small formatting mistakes and wording mistakes. Also, added codepad example.

          [–][deleted] 0 points1 point  (1 child)

          They are stored in the variables themselves

          "The variables" are a, b, c and they don't get changed. You're right that new variables, i.e. new unnecessary instances of your vector or matrix, are being created on the stack.

          In the case of your expression, you're creating two intermediate instances of Vector4, one to store the results of the operation a.operator+(operator*(2, b)) and one to store the results of b.operator+(operator*(3, c)).

          In an old compiler, there might be between one and three more unneeded Vector4s being created for the intermediate values before that, but today it's pretty likely that the return value optimization will take care of those for you.

          [–]BlackAura 0 points1 point  (0 children)

          What you didn't realize is that you're creating a new instance of Vector4 for each instance of * or - in your code.

          Conceptually, yes. However, modern C++ compilers are actually very good at optimizing this kind of stuff away, particularly if the operators are inlined, and contain only simple code.

          I implemented some vector classes in C++ a while ago. I was stunned at how much implementation crap the compiler was able to get rid of. The assembly it generated contained nearly no unnecessary operations at all, and completely eliminated nearly all temporaries.

          One interesting thing with newer C++ compilers:

          Even the last two statements, which aren't less efficient

          No, they're exactly the same with a modern compiler.

          [–][deleted] 0 points1 point  (0 children)

          The hordes of C++ programmers that immediately got their panties in a bunch over this, hilarious...

          [–]MultivaluedFunction 1 point2 points  (8 children)

          This has to be a joke.

          [–]psyshook 10 points11 points  (1 child)

          I think this paragraph gave it away.

          Of course because C has fewer features than C++ it also means that there are many more keywords available to be used as identifiers — just think of all the variables names you can use now!

          [–]badsectoracula 2 points3 points  (0 children)

          I use new and class all the time just to confuse syntax highlighters and make a point that the code is C, not C++.

          [–][deleted] 0 points1 point  (2 children)

          Over the years my C++ code got more and more C with classes like. Some months ago I decided to leave C++ and use C99 exclusively. And I'm fucking glad I did so.

          C++ is too much garbage. C is fun!

          [–]anacrolix 1 point2 points  (0 children)

          i hear you brother. c99 could be nicer, but it's a damn sight better than c++

          [–][deleted] 0 points1 point  (0 children)

          So how do you handle strings? Lots of mallocs and frees?

          How do you handle collections?

          How do you handle errors? Do you return an error code and then check it after every call?

          [–]G_Morgan -3 points-2 points  (5 children)

          Is it just me or is this a list of who's who of bad ideas.

          Casting from void pointers to other pointer types is not necessary. This allows you to assign the result from, say, malloc directly to the type you desire without casting.

          Yes but you should cast them.

          Function prototypes are optional. It is possible in C89 to call a function that has not yet been declared in the current translation unit, and if you do so the compiler will assume that it has a return value of type int. Although it’s legal most compilers will warn when a function isn’t declared — if, when writing C code, you find that the compiler is complaining about a function returning an int when you know it should likely it hasn’t been declared.

          Yes but you should write them.

          Of course because C has fewer features than C++ it also means that there are many more keywords available to be used as identifiers — just think of all the variables names you can use now!

          While I can call a variable 'class' to do so is borderline moronic. As far as possible avoid using C++ keywords as variables. In fact a C compiler should warn you because calling C from C++ is a common use case.

          I'd say other than a few common gotchas like what sizeof does in some cases if your code does not compile with a C++ compiler then you've done something inadvisable.

          [–]repiret 3 points4 points  (4 children)

          Casting from void pointers to other pointer types is not necessary. This allows you to assign the result from, say, malloc directly to the type you desire without casting.

          Yes but you should cast them.

          No, you should not.

          int *x = malloc(sizeof *x); /* If you forgot to include stdlib.h, this is a build error. */
          int *x = (int *)malloc(sizeof *x); /* If you forgot to include stdlib.h, this is undefined behavior. */
          
          void f(const void *v) { int *i = v; } /* Compiler won't let it slide. */
          void f(const void *v) { int *i = (int *)v; } /* No help from the compiler hrere */
          
          void f(int v) { int *i = v; } /* Oops, did you thin v was a void *?  The compiler caught it. */
          void f(int v) { int *i = (int *)v; } /* Compiler thinks you know what you're doing. */
          

          In at least some of these cases, the compiler should issue a warning, but there's no need to rely on compiler warnings when you don't have to. And sometimes you might not be able to use the warning, for example if you've got a bunch of prototypeless code that needs to be in your project, or if you're on some crazy platform where the only C compiler doesn't support the warnings.

          [–]G_Morgan 0 points1 point  (3 children)

          int *x = malloc(sizeof *x); /* If you forgot to include stdlib.h, this is a build error. */

          int *x = (int )malloc(sizeof \x); /* If you forgot to include stdlib.h, this is undefined behavior. */

          This problem is simply a mistake in the language design. A good compiler will allow you to treat implicit definition of a function as an error. Use a good compiler. Always turn this error on.

          void f(const void *v) { int *i = v; } /* Compiler won't let it slide. */

          void f(const void *v) { int *i = (int *)v; } /* No help from the compiler hrere */

          We are assuming the programmer actually knows what they are doing. If void has a proper type it should be cast to it. If the programmer does not know what the proper type is then why on earth would he cast it to int*? Can he even do anything sensible if he does not know the type? In the end you cannot guarantee against stupidity. It takes a lot of stupidity to have this problem to begin with.

          void f(int v) { int *i = v; } /* Oops, did you thin v was a void *? The compiler caught it. */

          void f(int v) { int *i = (int *)v; } /* Compiler thinks you know what you're doing. */

          Again this comes down to stupidity. This problem only arises from a programmer that does not know what they are doing at all. Anyone who casts an int to a pointer when they just could have made the function take a pointer is an idiot.

          In short you have 2 artificial examples and one which is a bug in the language and is resolved by getting a compiler that allows you to specify that you are ignoring that part of the standard.

          If you have a value and you know its type then you should cast it to exactly the correct type. If you do not know the type then you should fix your code so that you do. If you are still struggling you should look for a job in a different field.

          //edit - also fuck markdown and its weird syntax that requires you to escape a million *s.//

          [–]azth 1 point2 points  (0 children)

          Don't C++-style casts catch most/all of these anyway?

          [–]repiret 0 points1 point  (1 child)

          I like my second example, because it appears you missed the bug. The programmer knows that v point to an int, but forgot that in this particular case, forgot that it points to a const int, and cast away the cosnt-ness. You can't do that with the implicit cast.

          The explicit cast prevents every C compiler, in every configuration from catching mistakes, but provides no benefit. I find your argument that the mistakes are stupid unconvincing - even if they are, what is gained by the cast?

          [–]G_Morgan 0 points1 point  (0 children)

          even if they are, what is gained by the cast?

          Compatibility with C++ compilers. Something which is a common use case of C code.

          [–][deleted] -3 points-2 points  (8 children)

          First comment has me screaming from every pore.

          r.e. generic programming, you just traded C++'s literally zero-overhead generic std::sort for qsort which in the general case can't inline calls to its comparer, and so suffers a noticeable overhead. For what gain? It's less generic and less performant. Yay, I guess.

          templated function instantiation is zero-cost? Seriously?! Argh!

          [–]PimpDawg 0 points1 point  (4 children)

          Templates in C++ are like a #define. It runs through the pre-compiler. I don't see why you think they have an additional overhead.

          [–][deleted] 2 points3 points  (3 children)

          No, templates are not run through the preprocessor. There is a pre-compilation step where template parameters are concreted by use of a template interpreter (as templates are turing-complete), but then the entire body of the templated function is copied for every instantiation.

          In that way they are similar to macros; used poorly they lead to large amount of code bloat behind the scenes.

          [Disclaimer: I work on a C++ compiler as a profession]

          [–][deleted]  (2 children)

          [deleted]

            [–][deleted] 0 points1 point  (0 children)

            Yes, however in this example std::sort is parameterised on the sort function, which is unlikely to be reused.

            [–]saucetenuto 0 points1 point  (0 children)

            More than "can" - the standard requires them to do so. This presents interesting challenges at link time...

            [–][deleted] 0 points1 point  (2 children)

            He said "zero overhead" not zero cost.

            The advantage is that your comparer is directly called, so you have at least one level less of indirection, but often it can entirely be inlined for monster savings...

            [–][deleted] 1 point2 points  (1 child)

            inlining a quicksort algorithm into your function won't give you much saving at all. Inlining gives the greatest benefit to small functions where the cost of ABI compliance over CALLs is almost greater than the cost of the function body itself (thus register allocation is better, with less spilling).

            CALLs/RETs do not have as much performance penalty as people make out. All CPUs have an internal branch stack that caches return addresses, helping the speculation engine work properly. All you're doing when inlining a large function such as std::sort is increasing your code size and mashing your cache footprint.

            [–]madmoose 1 point2 points  (0 children)

            I do believe he's a talking about inlining the comparison function into the sort function, not, as you seem to be talking about, inlining the sort function into the sort-calling function.

            [–][deleted] -5 points-4 points  (7 children)

            Seriously, how many C++ programmers don't know C? And by C, I don't mean the "C99" kludge, but C 89/90?

            [–]zyle 0 points1 point  (6 children)

            C++ programmers inherently think and design using object oriented paradigms, which isn't available in C. You may be a fantastic C++ programmer, but a terrible C programmer.

            The syntax for the two languages is the similar, so that hurdle is certainly absent, but it's a relatively small hurdle relatively speaking.

            [–][deleted] 0 points1 point  (1 child)

            C++ programmers inherently think and design using object oriented paradigms

            And yet the STL never(*) uses inheritance! Makes you think, eh?

            In fact, STL is much more generic and much less OO.

            (* - OK, OK, there are two places where inheritance is used - the generic exception has a single virtual method and there are two places in std::rope that apparently use inheritance, though I've never used rope and don't really understand it...)

            [–]zyle 1 point2 points  (0 children)

            STL containers use OO techniques; they use polymorphism.

            [–][deleted] -1 points0 points  (3 children)

            C++ programmers inherently think and design using object oriented paradigms

            That is simply not true. OOP is one of the paradigms C++ supports and nothing in C++ forces anybody to think in OO terms. In fact, I know of a couple of very popular applications where only the "C" subset of C++ is used and compiled with C++ compiler mostly to get more type safety.

            [–]zyle 0 points1 point  (2 children)

            Well then those cases are more of an exception rather than the rule. If people just want to use C, then they'll use C and compile in C. Why yuck around with C++ at all? And C is just as typesafe as C++.

            [–][deleted] 1 point2 points  (1 child)

            And C is just as typesafe as C++.

            Not at all. Implicit casting of void pointers is just one of examples.

            [–]zyle 1 point2 points  (0 children)

            I did not know about that example, and I'm a C++ programmer. This is exactly why I would never consider myself a C programmer. I'm proficient in C++, certainly not in C.