Difference between revisions of "C++ vs. C"

From Pin Eight
Jump to: navigation, search
(Exceptions: it looks like a noun was left out of this sentence)
(Exceptions: gsl::finally)
 
(6 intermediate revisions by the same user not shown)
Line 16: Line 16:
 
* templates instantiated several times, when compared to using the preprocessor to instantiate multiple copies
 
* templates instantiated several times, when compared to using the preprocessor to instantiate multiple copies
  
PROTIP: A <code>class</code> is a <code>struct</code> whose first member is <code>private</code>.
+
PROTIP: A <code>class</code> is a <code>struct</code> whose default access is <code>private</code>.
 
That's the only difference.
 
That's the only difference.
 +
If you specify access before any members, <code>class</code> and <code>struct</code> mean the same thing.
  
 
== Minuses ==
 
== Minuses ==
Line 28: Line 29:
 
Templates have a couple drawbacks:
 
Templates have a couple drawbacks:
 
* Type names in error message become far more difficult to interpret. Common implementations may expand a template type name fully in diagnostic messages even if the source code accesses the type through a <code>typedef</code>. For example, an error message involving the <code>std::string</code> type is likely to provoke this reaction: "[http://www.sgi.com/tech/stl/basic_string.html <code>basic_string</code>]? I'm using C++, not [[wikipedia:BASIC|BASIC]]! And what the fsck is [http://www.cplusplus.com/reference/string/char_traits/ <code>char_traits</code>]?" (True, implementations are not the language, but a language is only as good as its best [[wikipedia:free software|free]] implementation.)
 
* Type names in error message become far more difficult to interpret. Common implementations may expand a template type name fully in diagnostic messages even if the source code accesses the type through a <code>typedef</code>. For example, an error message involving the <code>std::string</code> type is likely to provoke this reaction: "[http://www.sgi.com/tech/stl/basic_string.html <code>basic_string</code>]? I'm using C++, not [[wikipedia:BASIC|BASIC]]! And what the fsck is [http://www.cplusplus.com/reference/string/char_traits/ <code>char_traits</code>]?" (True, implementations are not the language, but a language is only as good as its best [[wikipedia:free software|free]] implementation.)
* Programmers can lose track of for how many different type combinations they have instantiated a template, causing code size to balloon. There is a common extension called <code>extern template</code> allowing for [http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2006/n1987.htm explicit instantiation], but it's not in C++98, and [http://wiki.apache.org/stdcxx/C%2B%2B0xCompilerSupport not all compilers support it].
+
* Programmers can lose track of for how many different type combinations they have instantiated a template, causing code size to balloon. There is a new feature in C++11 called <code>extern template</code> allowing for [http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2006/n1987.htm explicit instantiation], but [http://wiki.apache.org/stdcxx/C%2B%2B0xCompilerSupport not all compilers support it yet].
  
 
=== Exceptions ===
 
=== Exceptions ===
Line 34: Line 35:
 
* Some platforms require different stack frame formats for code that uses exceptions and code that does not use exceptions. Unless you completely disable exception support using compiler flags, the exception support may still eat up a few percent of CPU time, roughly 8 to 10 percent in GCC.[http://slashdot.org/comments.pl?sid=3050131&cid=40997471]
 
* Some platforms require different stack frame formats for code that uses exceptions and code that does not use exceptions. Unless you completely disable exception support using compiler flags, the exception support may still eat up a few percent of CPU time, roughly 8 to 10 percent in GCC.[http://slashdot.org/comments.pl?sid=3050131&cid=40997471]
 
* Even if you have determined that a particular compiler imposes little to no runtime speed penalty for exceptions, the size of the required library support might cause a problem on embedded or handheld devices with less than about a megabyte of RAM. I'll grant that this has become somewhat less important in the 2010s as the market has transitioned from handheld devices with 384 KiB of RAM, such as a Game Boy Advance, to smartphones with a thousand times that much memory.
 
* Even if you have determined that a particular compiler imposes little to no runtime speed penalty for exceptions, the size of the required library support might cause a problem on embedded or handheld devices with less than about a megabyte of RAM. I'll grant that this has become somewhat less important in the 2010s as the market has transitioned from handheld devices with 384 KiB of RAM, such as a Game Boy Advance, to smartphones with a thousand times that much memory.
* A practical counterpart to the <code>finally</code> keyword of Java and Python was not added until C++11. True, there isn't as much need for <code>finally</code> in C++ as in languages that rely on a garbage collector, given the [[wikipedia:Resource Acquisition Is Initialization|idiom of allocating resources in constructors]] that C++'s deterministic destruction allows. But a method often still needs to restore the object's fields to a consistent state before eating or rethrowing the exception. Once compilers implement C++11, however, the programmer can build a scope-guard with <code>std::shared_ptr</code> and lambda expressions.
+
* A practical counterpart to the <code>finally</code> keyword of Java and Python was not added until C++11. True, there isn't as much need for <code>finally</code> in C++ as in languages that rely on a garbage collector, given the [[wikipedia:Resource Acquisition Is Initialization|idiom of allocating resources in constructors]] that C++'s deterministic destruction allows. But a method often still needs to restore the object's fields to a consistent state before eating or rethrowing the exception. A C++11 programmer can build a scope-guard with <code>std::shared_ptr</code> (or <code>gsl::finally</code><ref>Rainer Grimm and Bartłomiej Filipek. "[https://www.modernescpp.com/index.php/c-core-guidelines-when-you-can-t-throw-an-exception C++ Core Guidelines: finally in C++]". 2018-08-13. Accessed 2019-07-15.</ref>) and lambda expressions.
 
* If a function throws exceptions, all callers have to be aware of all exception types that it may throw, and all callers have to be written in C++ using exception style so that they can catch the exceptions. And unlike Java with its [[wikipedia:Exception handling#Checked exceptions|checked exceptions]], C++ compilers offer no built-in automated tests to ensure that a method either handles or rethrows exceptions that its callees throw. This is one reason why Google is not a fan of C++ exceptions.<ref> Andrew Binstock. "[http://www.drdobbs.com/architecture-and-design/the-scourge-of-error-handling/240143878 The Scourge of Error Handling]". ''Dr. Dobb's'', 2012-12-05. Accessed 2012-12-08.</ref>
 
* If a function throws exceptions, all callers have to be aware of all exception types that it may throw, and all callers have to be written in C++ using exception style so that they can catch the exceptions. And unlike Java with its [[wikipedia:Exception handling#Checked exceptions|checked exceptions]], C++ compilers offer no built-in automated tests to ensure that a method either handles or rethrows exceptions that its callees throw. This is one reason why Google is not a fan of C++ exceptions.<ref> Andrew Binstock. "[http://www.drdobbs.com/architecture-and-design/the-scourge-of-error-handling/240143878 The Scourge of Error Handling]". ''Dr. Dobb's'', 2012-12-05. Accessed 2012-12-08.</ref>
  
Line 44: Line 45:
 
=== iostream ===
 
=== iostream ===
 
The <code><nowiki><iostream></nowiki></code> library is another divisive issue.
 
The <code><nowiki><iostream></nowiki></code> library is another divisive issue.
It was envisioned as a type-safe alternative to <code><nowiki><cstdio></nowiki></code>, but implementations are [http://yosefk.com/c++fqa/io.html hairy], bloated, and [http://www.vterrain.org/Implementation/effective.html inefficient].
+
It was envisioned as a type-safe alternative to <code><nowiki><cstdio></nowiki></code>, but implementations are [http://yosefk.com/c++fqa/io.html hairy], [http://slashdot.org/comments.pl?sid=5102311&cid=46887333 convoluted], bloated, and [http://www.vterrain.org/Implementation/effective.html inefficient].
 
This goes double if you have to use a statically linked implementation of the C++ standard library, either because the operating system provides no C++ standard library (e.g. handheld video game systems) or because your compiler's C++ ABI differs from that of the operating system publisher's own development tools (e.g. MinGW).
 
This goes double if you have to use a statically linked implementation of the C++ standard library, either because the operating system provides no C++ standard library (e.g. handheld video game systems) or because your compiler's C++ ABI differs from that of the operating system publisher's own development tools (e.g. MinGW).
 
[[wikipedia:Hello World program|Hello World programs]] with <code>-Os</code> and statically linked libstdc++ in one version of MinGW resulted in 5,632 bytes for <code><nowiki><cstdio></nowiki></code> but 266,240 bytes for <code><nowiki><iostream></nowiki></code>.
 
[[wikipedia:Hello World program|Hello World programs]] with <code>-Os</code> and statically linked libstdc++ in one version of MinGW resulted in 5,632 bytes for <code><nowiki><cstdio></nowiki></code> but 266,240 bytes for <code><nowiki><iostream></nowiki></code>.
Line 56: Line 57:
 
In fact, one C++ advocate claims that comparing memory usage of C++ to C by targeting a sub-megabyte platform with GNU libstdc++ sets up a huge [[wikipedia:Straw man|straw man]].[http://slashdot.org/comments.pl?sid=2952181&cid=40521511]
 
In fact, one C++ advocate claims that comparing memory usage of C++ to C by targeting a sub-megabyte platform with GNU libstdc++ sets up a huge [[wikipedia:Straw man|straw man]].[http://slashdot.org/comments.pl?sid=2952181&cid=40521511]
  
Yet some C++ fanboys claim that anything using good old <code><nowiki><cstring></nowiki></code> and <code><nowiki><cstdio></nowiki></code> instead of new-fangled <code><nowiki><string></nowiki></code> and <code><nowiki><iostream></nowiki></code> isn't in the spirit of C++, [[wikipedia:no true Scotsman|whatever that means]]. They cling to item 2 in the second edition of Scott Meyers' ''Effective C++'', which promotes <code><nowiki><iostream></nowiki></code> over <code><nowiki><cstdio></nowiki></code>, and ignore item 23 of his sequel ("consider alternative libraries"). It appears that Meyers eventually recognized that <code><nowiki><iostream></nowiki></code> is imperfect and removed item 2 from the third edition.
+
Yet some C++ fanboys claim that anything using good old <code><nowiki><cstring></nowiki></code> and <code><nowiki><cstdio></nowiki></code> instead of new-fangled <code><nowiki><string></nowiki></code> and <code><nowiki><iostream></nowiki></code> isn't in the spirit of C++, [[wikipedia:no true Scotsman|whatever that means]]. They cling to item 2 in the second edition of Scott Meyers' ''Effective C++'', which promotes <code><nowiki><iostream></nowiki></code> over <code><nowiki><cstdio></nowiki></code>, and ignore item 23 of his sequel ("consider alternative libraries").
 +
Meyers did remove item 2 from the third edition, evidently recognizing that <code><nowiki><iostream></nowiki></code> is far from perfect.
 
Someone who knows a few people on the standards committee thinks [http://slashdot.org/comments.pl?sid=3050131&cid=40998911 Boost.Format is idiomatic and <code><nowiki><iostream></nowiki></code> is just idiotic].
 
Someone who knows a few people on the standards committee thinks [http://slashdot.org/comments.pl?sid=3050131&cid=40998911 Boost.Format is idiomatic and <code><nowiki><iostream></nowiki></code> is just idiotic].
  
 
"As an embedded programmer, I shun C++." -- Arlet, [http://slashdot.org/comments.pl?sid=2503778&cid=37910334 2011-11-01]
 
"As an embedded programmer, I shun C++." -- Arlet, [http://slashdot.org/comments.pl?sid=2503778&cid=37910334 2011-11-01]
 +
 +
=== Designated initializers ===
 +
 +
Some who take the time to truly understand C find that C is often the right language for the job.
 +
C compiles to much smaller WebAssembly than idiomatic C++ does.
 +
The designated initializer syntax introduced in C99 is an unfortunate omission from C++<ref>Andre Weissflog. "[https://floooh.github.io/2018/06/02/one-year-of-c.html One year of C]". ''The Brain Dump'', 2018-06-02. Accessed 2018-06-03.</ref> prior to the forthcoming C++2a standard.
 +
The eighteen different types of object initialization in C++ are "bonkers".<ref>Mike Lui. "[https://mikelui.io/2019/01/03/seriously-bonkers.html Initialization in C++ is Seriously Bonkers]". ''No TV and No Beer'', 2019-01-03. Accessed 2019-01-05.</ref>
  
 
== References ==
 
== References ==

Latest revision as of 23:06, 15 July 2019

This is a mini-rant, a short essay refuting a common misconception among users of an Internet forum. If you think this essay is FUD, feel free to explain why on the essay's talk page.

The C++ programming language brings improvements over its predecessor C, but fans of other languages point out that C++ is still not perfect. Over a decade after the 1998 standardization of the C++ programming language, the C++ vs. C debate continues.

Pluses

Compared to C, C++ has a bunch of new language features with little or no runtime overhead because they are translated to code equally as efficient as the equivalent C code:

  • namespaces
  • function overloading
  • type-safe new(std::nothrow)
  • STL, the part of the C++ standard library with containers and algorithms
  • non-virtual methods
  • references, needed by operator overloading

A few features are as efficient as C yet still rawther deceptive and easy to misuse because the "simple" syntax hides how much code is actually being generated:

  • operator overloading, when compared to ordinary function-call syntax
  • virtual (polymorphic) methods, when compared to C function pointer tables, especially the pure virtual method's undefined behavior that some compilers may implement as an exception
  • templates instantiated several times, when compared to using the preprocessor to instantiate multiple copies

PROTIP: A class is a struct whose default access is private. That's the only difference. If you specify access before any members, class and struct mean the same thing.

Minuses

C++ also has some features requiring possibly expensive runtime library support:

  • throw
  • the default version of new without std::nothrow, which throws std::bad_alloc
  • <iostream>

Templates

Templates have a couple drawbacks:

  • Type names in error message become far more difficult to interpret. Common implementations may expand a template type name fully in diagnostic messages even if the source code accesses the type through a typedef. For example, an error message involving the std::string type is likely to provoke this reaction: "basic_string? I'm using C++, not BASIC! And what the fsck is char_traits?" (True, implementations are not the language, but a language is only as good as its best free implementation.)
  • Programmers can lose track of for how many different type combinations they have instantiated a template, causing code size to balloon. There is a new feature in C++11 called extern template allowing for explicit instantiation, but not all compilers support it yet.

Exceptions

Exceptions (throw) also have a few drawbacks:

  • Some platforms require different stack frame formats for code that uses exceptions and code that does not use exceptions. Unless you completely disable exception support using compiler flags, the exception support may still eat up a few percent of CPU time, roughly 8 to 10 percent in GCC.[1]
  • Even if you have determined that a particular compiler imposes little to no runtime speed penalty for exceptions, the size of the required library support might cause a problem on embedded or handheld devices with less than about a megabyte of RAM. I'll grant that this has become somewhat less important in the 2010s as the market has transitioned from handheld devices with 384 KiB of RAM, such as a Game Boy Advance, to smartphones with a thousand times that much memory.
  • A practical counterpart to the finally keyword of Java and Python was not added until C++11. True, there isn't as much need for finally in C++ as in languages that rely on a garbage collector, given the idiom of allocating resources in constructors that C++'s deterministic destruction allows. But a method often still needs to restore the object's fields to a consistent state before eating or rethrowing the exception. A C++11 programmer can build a scope-guard with std::shared_ptr (or gsl::finally[1]) and lambda expressions.
  • If a function throws exceptions, all callers have to be aware of all exception types that it may throw, and all callers have to be written in C++ using exception style so that they can catch the exceptions. And unlike Java with its checked exceptions, C++ compilers offer no built-in automated tests to ensure that a method either handles or rethrows exceptions that its callees throw. This is one reason why Google is not a fan of C++ exceptions.[2]

On platforms without virtual memory, a program must be aware of possible out-of-memory conditions. The STL allows passing an allocator to all containers, but most implementations appear to exhibit undefined behavior when allocate() returns 0 like new(std::nothrow) does. I've been told one STL implementation can be built with nothrow in mind: STLPort.[2][3] EA Games created an out-of-memory-aware allocator.

iostream

The <iostream> library is another divisive issue. It was envisioned as a type-safe alternative to <cstdio>, but implementations are hairy, convoluted, bloated, and inefficient. This goes double if you have to use a statically linked implementation of the C++ standard library, either because the operating system provides no C++ standard library (e.g. handheld video game systems) or because your compiler's C++ ABI differs from that of the operating system publisher's own development tools (e.g. MinGW). Hello World programs with -Os and statically linked libstdc++ in one version of MinGW resulted in 5,632 bytes for <cstdio> but 266,240 bytes for <iostream>. A devkitARM project targeting Game Boy Advance had similar results: 5,156 bytes for C-style I/O and 253,652 bytes for <iostream>. (This is expected; Thumb and x86 have comparable code density.) Even removing some unreachable code with -Wl,-gc-sections couldn't get the GBA project below 180,032 bytes. (For comparison, the GBA's main RAM is 262,144 bytes.) These tests are with GNU libstdc++, which initializes date, time, and money aspects of a locale for each stream even if the program never shifts a date, time, or money object into the stream. This is because it was conceived before templates and instead uses virtual inheritance and "facets", which C++ implementations can't optimize well.[4] Some third-party C++ standard library implementations such as uClibc++ are designed for space efficiency and leave out features such as locale support that aren't as useful in small-memory systems. In fact, one C++ advocate claims that comparing memory usage of C++ to C by targeting a sub-megabyte platform with GNU libstdc++ sets up a huge straw man.[5]

Yet some C++ fanboys claim that anything using good old <cstring> and <cstdio> instead of new-fangled <string> and <iostream> isn't in the spirit of C++, whatever that means. They cling to item 2 in the second edition of Scott Meyers' Effective C++, which promotes <iostream> over <cstdio>, and ignore item 23 of his sequel ("consider alternative libraries"). Meyers did remove item 2 from the third edition, evidently recognizing that <iostream> is far from perfect. Someone who knows a few people on the standards committee thinks Boost.Format is idiomatic and <iostream> is just idiotic.

"As an embedded programmer, I shun C++." -- Arlet, 2011-11-01

Designated initializers

Some who take the time to truly understand C find that C is often the right language for the job. C compiles to much smaller WebAssembly than idiomatic C++ does. The designated initializer syntax introduced in C99 is an unfortunate omission from C++[3] prior to the forthcoming C++2a standard. The eighteen different types of object initialization in C++ are "bonkers".[4]

References

  1. Rainer Grimm and Bartłomiej Filipek. "C++ Core Guidelines: finally in C++". 2018-08-13. Accessed 2019-07-15.
  2. Andrew Binstock. "The Scourge of Error Handling". Dr. Dobb's, 2012-12-05. Accessed 2012-12-08.
  3. Andre Weissflog. "One year of C". The Brain Dump, 2018-06-02. Accessed 2018-06-03.
  4. Mike Lui. "Initialization in C++ is Seriously Bonkers". No TV and No Beer, 2019-01-03. Accessed 2019-01-05.

External links