I hope Microsoft beats-up Linux because Microsoft is not commies.
I have no problem with C++. I didn't do all of the language in my compiler. Some of it is a wash, some of it very nice, some things foolish.
In industry, you might want to keep it simple, so you can hire a replacement easier. In rare circumstances, you want it easier to make a compiler.
I like to do global find-and-replace string operations across all my files. If a function is named FileRead() and not File::Read(), then I can do replaces or searches.
I am not passionate either way on this topic. I'm not fond of the STL, though.
I don't like the "const" type system. Too many namespaces isn't always better. Is it really always better to alphabetize soup cans in the cupboard when hashing them is okay?
----
to remove it from Ephraim's head unto Manasseh's head.
48:18 And Joseph said unto his father, Not so, my father: for this is
the firstborn; put thy right hand upon his head.
48:19 And his father refused, and said, I know it, my son, I know it:
he also shall become a people, and he also shall be great: but truly
his younger brother shall be greater than he, and his seed shall
become a multitude of nations.
48:20 And he blessed them that day, saying, In thee shall Israel
bless, saying, God make thee as Ephraim and as Manasseh: and he set
Ephraim before Manasseh.
48:21 And Israel said unto Joseph, Behold, I die: but God shall be
with you, and bring you again unto the land of your fathers.
That's what I thought too, until I found that there exists C++ ABI on linux. I tried to link together two small files, both using iostreams, one compiled with Intel C++, the other with g++, and it worked.
Though I'm in doubt [haven't tested it yet] whether it would have worked had I tried to use something std::vector across functions compiled with different compilers.
It is less of an issue in linux where there is a healthy ecosystem of libraries where you have access to source code and can reasonably expect to be able to compile everything with the same compiler. (Obviously there are times when this is not practical, but for most cases, it is ok).
Under Windows, the whole situation is a mess.
For current interesting platforms, the problem is often solved by either having no C++ compiler, or just one (e.g. the Qualcomm QDSP, which I think only recently gained a C compiler lacks a C++ compiler, and I don't imagine this changing soon).
Everyone loves to bash the lack of a C++ ABI. Now please name another language with native implementations with a standard ABI across different compilers.
C implementations have a standard ABI, because they usually are the operating system ABI.
No, because language standards only specify the language itself not the implementations.
So each vendor comes up with its own library format and tricks how to make the language usable in dynamic libraries. And as far as I know, they seldom talk among themselves about common ABIs.
C ends up being used as universal ABI, because in the end all implementations that generate native code need to interface with the operating system, which usually is developed in C.
But this was not always like this. Before C won its place as system programming language outside the UNIX world, there were other systems programming languages in use. On those systems there was no C ABI as such.
The Pascal calling convention ABI exists, because on the early 80s a few operating systems were developed in extended version of Pascal, like the first MacOS, for example.
For example, Lisp, Scheme, Ada, Modula-3, Delphi, D, Go, Oberon don't have compiler implementations that produce object files compatible across vendors.
Usually you are only able to link to libraries compiled with the same compiler. The same way as it happens to C++.
It's also much easier to specify a C abi. You don't have to worry about name mangling, exception propagation, inheritance relationships, templates etc.
Actually this is mentioned by "Rhythmic Fistman", but the post only got 6 points.
From a purely practical standpoint, the lack of an ABI is the #1 reason I don't bother with C++ anymore because chances are pretty good that I'll want to interface the lower-level code with something like Python or Go, both of which integrate with C code much more easily than C++.
This is exactly the problem with C++ today. C++ "wants to be on top". Unlike C, it makes for a very uncooperative lower layer for other languages to build on. Unlike C, it's difficult to build up component libraries in C++ that can be easily used by the rest of your code. C++ makes more sense if you're writing your entire application in C++ but that's just way too much hassle for most applications.
The fact that C++ is almost a superset of C is commonly used as an argument for C++, and the accepted answer in the post is a common response - C++ is not quite a superset of C, you have to cast malloc, you lose out on C99 features, etc. But I think it misses the point: if you could really get the advantages of C++ by using its features in a few necessary places in your C code, I think it would be worth it to make your code slightly uglier to accommodate it.
You can't.
Generic containers are probably the C++ feature C is most sorely lacking. But as soon as you start using map<> in your code, you have to deal with slower compilation in all the files that use it (if a map is declared in a header file, e.g. as a struct member, that could be quite a lot), repetitive code generation the linker might or might not optimize, verbose syntax (std::map<struct foo *>::iterator, yay), and the requirement (more or less) to use new instead of malloc for any struct that contains a C++ object. And if you want your map's performance to not suck, you'll need to depend on either a nonstandard extension or C++11 to get hash_map/unordered_map.
Then there's writing templates - even in the context of C++, template code tends to be hideously ugly (template<typename T> everywhere, the aforementioned issues with code generation and sticking everything in header files, pages of confusing error messages when you mess up), and is hard to debug with a debugger. Trying to use them with C can only lead to pain - especially since they work best when you have actual objects to specialize on. Templates do allow certain C code to be written more nicely, such as binary formats where there are multiple versions of the same struct, but even then there are limitations to the magic - template-ness tends to spread to callers.
gcc apparently wanted to switch to C++ partly in order to use destructors: now you have spooky action at a distance, where the caller of your method is a closing brace, thanks to a declaration a page away somewhere up in the function.
Do you want to use a few classes to add some structure to your code? It will certainly start to seem a bit forced to declare struct foo and functions like foo_alloc and foo_get_bar once you're in C++, when declaring a class expresses the concept more elegantly. And then you're stuck with C++'s context sensitive nature (anyone who understands the code will know that it->getName() is a reference to Foo::getName, and fooCount used here is a member variable, so it's fine, right?) and poor separation of interface and implementation (it really doesn't make sense that you have to declare private fields in your header file, and it doesn't make compilation any better), and if you're not careful your code will start to feature a vector of objects where an array of ints is appropriate, or yet another method whose only purpose is to pass on slightly different arguments to a method of a member variable.
C++ programmers accept these issues in exchange for a high-level coding style (less security bugs!) and an easier time writing clean code than C programmers (although bad C++ code is a whole lot worse than bad C code). You will gain hardly any of that by sprinkling C++ into C. Solving these problems in C sometimes leads to more work and more verbose code, but the result is also often a better solution. If you absolutely must have code generation, use a macro, they're not as bad as C++ programmers make them out to be - or, at least, they're ugly enough that most programmers know to use them very sparingly. (I'm rather a fan of <sys/queue.h>, for example, although you do have to understand how it works to make sense of code that uses it.)
> where the caller of your method is a closing brace
Although I agree with the sentiment of your comment destructors and RAII in general are arguably the best C++ features.
If I were forced to use C++ again I would restrict my code to a lightweight subset of C++, especially I would not use the C++ Standard library (STL, iostreams, string) and of course not use BOOST (or any template heavy library) and C++0x. But I would definitely include 'full-fledged' exception handling from the start.
The big problem with RAII and destructors is that you have no way of returning an error. You're not supposed to throw an exception and you have no way to return an error code so you can't really do much in a destructor if you want to write 100% correct code. Manually managing error codes is tedious but it's also more explicit and flexible.
> The big problem with RAII and destructors is that you have no way of returning an error.
That's actually a feature. Destructors are for cleanup. If code is supposed to report an error (by throwing an exception) it should not be put into a destructor.
- If the file has been opened for reading only you can put fclose into the destructor and safely ignore the return value.
- If the file has been opened for writing you need to call fclose() before the destructor to be able to react accordingly. Throwing an exception in the destructor would not be helpful in this case anyway because your scope is gone.
BTW, writing a file safely is much harder than what many internet sources and textbooks make it appear.
But similar constructions in other languages (Python's with- statement, various Lisp versions, using in C#) can handle this cleanly. C++ doesn't give you "finally" and since you can't do non-trivial cleanup in destructors RAII is really a lot less useful than it seems at first blush.
In C++ you can write the cleanup code once in the destructor,
"similar constructions in other languages" must be written every time you use it. You can and should do "non-trivial cleanup in destructors", e.g. DB rollback, but you should do only cleanup in destructors.
C++ is not a superset of C, therefore using C as C++ needs porting. It's fine if you consciously restrict yourself to C++-compatible subset of C (GCC warning -Wc++-compat can help you), but if you don't it can take a significant effort.
For a realistic case of compiling large C codebase not written in C++-compatible subset of C as C++, take a look at GCC. I think it took something like 6 months to compile GCC as C++ regression-free.
I work on aviation software. We have to use C because C++ generates too much code behind the scenes for you. If you don't create a constructor or destructor, there is an implicit one created. Templates are instantiated once for each type, but we still have to prove that (and test each instantiation). We'd also have to certify a new compiler, since our current one only supports C.
My point is basically that in domain-specific cases, C can be a good choice because of the lack of sugar and simplicity. I doubt this applies to most cases though.
You have a large body of code that is using C just fine, thanks, and moving it to C++ (while probably "trivial" for some meaning of trivial involving a few weeks/months/years of refactoring) would expose you to every C++ yahoo's whim.
"I wanna use maaaaaap! Why can't I use map? Map map mapmaapmap."
"Because this stuff runs in an interrupt handler, and is shared with thread-world code through a very carefully designed API involving a couple layers of synchronization. Many Bothans died to make this work well, and fast."
"No map?"
"No map. Go read about NUMA."
"What about streams?"
[buries head in hands]
tl;dr; It's an idiot shield. Kids, get off the lawn :-)
The question is pretty weird in the first place. If I like a particular language, why would I automatically like some arbitrary superset of that language?
But there are many practical reasons as well. One that hasn't been discussed much is exceptions. In order to use C++ libraries in C code you'd have to "RAIIfy" your entire codebase. That means wrapping all dynamic memory allocations in their own objects and replacing malloc with new. What's left of C after doing that?
Switching off exceptions on the compiler level isn't a viable solution either because it leads to undefined behavior.
39 comments
[ 4.0 ms ] story [ 79.2 ms ] threadconservative==industry
liberal==academia
conservative==C
liberal== lisp
I hope Microsoft beats-up Linux because Microsoft is not commies.
I have no problem with C++. I didn't do all of the language in my compiler. Some of it is a wash, some of it very nice, some things foolish.
In industry, you might want to keep it simple, so you can hire a replacement easier. In rare circumstances, you want it easier to make a compiler.
I like to do global find-and-replace string operations across all my files. If a function is named FileRead() and not File::Read(), then I can do replaces or searches.
I am not passionate either way on this topic. I'm not fond of the STL, though.
I don't like the "const" type system. Too many namespaces isn't always better. Is it really always better to alphabetize soup cans in the cupboard when hashing them is okay?
----
to remove it from Ephraim's head unto Manasseh's head.
48:18 And Joseph said unto his father, Not so, my father: for this is the firstborn; put thy right hand upon his head.
48:19 And his father refused, and said, I know it, my son, I know it: he also shall become a people, and he also shall be great: but truly his younger brother shall be greater than he, and his seed shall become a multitude of nations.
48:20 And he blessed them that day, saying, In thee shall Israel bless, saying, God make thee as Ephraim and as Manasseh: and he set Ephraim before Manasseh.
48:21 And Israel said unto Joseph, Behold, I die: but God shall be with you, and bring you again unto the land of your fathers.
Though I'm in doubt [haven't tested it yet] whether it would have worked had I tried to use something std::vector across functions compiled with different compilers.
Under Windows, the whole situation is a mess.
For current interesting platforms, the problem is often solved by either having no C++ compiler, or just one (e.g. the Qualcomm QDSP, which I think only recently gained a C compiler lacks a C++ compiler, and I don't imagine this changing soon).
C implementations have a standard ABI, because they usually are the operating system ABI.
So each vendor comes up with its own library format and tricks how to make the language usable in dynamic libraries. And as far as I know, they seldom talk among themselves about common ABIs.
C ends up being used as universal ABI, because in the end all implementations that generate native code need to interface with the operating system, which usually is developed in C.
But this was not always like this. Before C won its place as system programming language outside the UNIX world, there were other systems programming languages in use. On those systems there was no C ABI as such.
The Pascal calling convention ABI exists, because on the early 80s a few operating systems were developed in extended version of Pascal, like the first MacOS, for example.
Usually you are only able to link to libraries compiled with the same compiler. The same way as it happens to C++.
Also, to answer your question: Java, C#/Mono.
I forgot about that, as I was thinking about compilation directly to native code, as I wrote my comment.
From a purely practical standpoint, the lack of an ABI is the #1 reason I don't bother with C++ anymore because chances are pretty good that I'll want to interface the lower-level code with something like Python or Go, both of which integrate with C code much more easily than C++.
Except when you see Duff's device for the first time and realize that C you thought you knew what's the actual C :)
http://catb.org/jargon/html/D/Duffs-device.html
On Duff's device. My thought, the first time I saw it, was "brilliant!"
You can't.
Generic containers are probably the C++ feature C is most sorely lacking. But as soon as you start using map<> in your code, you have to deal with slower compilation in all the files that use it (if a map is declared in a header file, e.g. as a struct member, that could be quite a lot), repetitive code generation the linker might or might not optimize, verbose syntax (std::map<struct foo *>::iterator, yay), and the requirement (more or less) to use new instead of malloc for any struct that contains a C++ object. And if you want your map's performance to not suck, you'll need to depend on either a nonstandard extension or C++11 to get hash_map/unordered_map.
Then there's writing templates - even in the context of C++, template code tends to be hideously ugly (template<typename T> everywhere, the aforementioned issues with code generation and sticking everything in header files, pages of confusing error messages when you mess up), and is hard to debug with a debugger. Trying to use them with C can only lead to pain - especially since they work best when you have actual objects to specialize on. Templates do allow certain C code to be written more nicely, such as binary formats where there are multiple versions of the same struct, but even then there are limitations to the magic - template-ness tends to spread to callers.
gcc apparently wanted to switch to C++ partly in order to use destructors: now you have spooky action at a distance, where the caller of your method is a closing brace, thanks to a declaration a page away somewhere up in the function.
Do you want to use a few classes to add some structure to your code? It will certainly start to seem a bit forced to declare struct foo and functions like foo_alloc and foo_get_bar once you're in C++, when declaring a class expresses the concept more elegantly. And then you're stuck with C++'s context sensitive nature (anyone who understands the code will know that it->getName() is a reference to Foo::getName, and fooCount used here is a member variable, so it's fine, right?) and poor separation of interface and implementation (it really doesn't make sense that you have to declare private fields in your header file, and it doesn't make compilation any better), and if you're not careful your code will start to feature a vector of objects where an array of ints is appropriate, or yet another method whose only purpose is to pass on slightly different arguments to a method of a member variable.
C++ programmers accept these issues in exchange for a high-level coding style (less security bugs!) and an easier time writing clean code than C programmers (although bad C++ code is a whole lot worse than bad C code). You will gain hardly any of that by sprinkling C++ into C. Solving these problems in C sometimes leads to more work and more verbose code, but the result is also often a better solution. If you absolutely must have code generation, use a macro, they're not as bad as C++ programmers make them out to be - or, at least, they're ugly enough that most programmers know to use them very sparingly. (I'm rather a fan of <sys/queue.h>, for example, although you do have to understand how it works to make sense of code that uses it.)
Although I agree with the sentiment of your comment destructors and RAII in general are arguably the best C++ features.
If I were forced to use C++ again I would restrict my code to a lightweight subset of C++, especially I would not use the C++ Standard library (STL, iostreams, string) and of course not use BOOST (or any template heavy library) and C++0x. But I would definitely include 'full-fledged' exception handling from the start.
http://bstring.sourceforge.net/
Personally, I'd like to see a simple C string library using modified UTF-8 (to be compatible with ASCIIZ) which is grapheme aware.
I'm tempted to take a shot at writing one myself, but haven't gotten around to it yet. If anyone is interested, see http://unicode.org/reports/tr29/#Grapheme_Cluster_Boundaries for the algorithm and http://www.unicode.org/Public/UNIDATA/auxiliary/GraphemeBrea... for the property data...
That's actually a feature. Destructors are for cleanup. If code is supposed to report an error (by throwing an exception) it should not be put into a destructor.
- If the file has been opened for reading only you can put fclose into the destructor and safely ignore the return value.
- If the file has been opened for writing you need to call fclose() before the destructor to be able to react accordingly. Throwing an exception in the destructor would not be helpful in this case anyway because your scope is gone.
BTW, writing a file safely is much harder than what many internet sources and textbooks make it appear.
For a realistic case of compiling large C codebase not written in C++-compatible subset of C as C++, take a look at GCC. I think it took something like 6 months to compile GCC as C++ regression-free.
http://gcc.gnu.org/wiki/gcc-in-cxx
My point is basically that in domain-specific cases, C can be a good choice because of the lack of sugar and simplicity. I doubt this applies to most cases though.
"I wanna use maaaaaap! Why can't I use map? Map map mapmaapmap."
"Because this stuff runs in an interrupt handler, and is shared with thread-world code through a very carefully designed API involving a couple layers of synchronization. Many Bothans died to make this work well, and fast."
"No map?"
"No map. Go read about NUMA."
"What about streams?"
[buries head in hands]
tl;dr; It's an idiot shield. Kids, get off the lawn :-)
But there are many practical reasons as well. One that hasn't been discussed much is exceptions. In order to use C++ libraries in C code you'd have to "RAIIfy" your entire codebase. That means wrapping all dynamic memory allocations in their own objects and replacing malloc with new. What's left of C after doing that?
Switching off exceptions on the compiler level isn't a viable solution either because it leads to undefined behavior.