Programmers Stack Exchange is a question and answer site for professional programmers interested in conceptual questions about software development. Join them; it only takes a minute:

Sign up
Here's how it works:
  1. Anybody can ask a question
  2. Anybody can answer
  3. The best answers are voted up and rise to the top

As I've been developing my position on how software should be developed at the company I work for, I've come to a certain conclusion that I'm not entirely sure of.

It seems to me that if you are programming in C++, you should not use C style anything if it can be helped and you don't absolutely need the performance improvement. This way people are kept from doing things like pointer arithmetic or creating resources with new without any RAII, etc. If this idea was enforced, seeing a char* would possibly be a thing of the past.

I'm wondering if this is a conclusion others have made? Or am I being too puritanical about this?

share|improve this question
3  
6  
There's a performance improvement from using "C-style"? Are you sure? – user16764 Nov 7 '12 at 18:19
2  
If user16764's comment is not clear enough, well written templates are usually faster than C-style "generic" code with void*s, because the templates can be better inlined by the compiler. – Jan Hudec Dec 3 '12 at 9:22
up vote 11 down vote accepted

It is basically the way standard C++ is intended and encouraged (by the committee and the community) to be used:

  • use C++ language idioms (mostly based on RAII, like smart pointers)
  • don't use C language idioms until you can't avoid it (which still happen regularly, when interfacing with C interfaces)

This is what we have been calling "modern C++" for almost 10 years now. But most C++ developers start only now to realize it makes code looks like there is no much need for raw pointers, writing new/delete, and other error-prone constructs.

Now these constructs (C or not) are still there both for retrocompatibilty and for allowing you to write libraries to, again, free the other developers from having to play with them. Also, C should be used to interface with C libraries, for low level constructs that require C-style code. Any other case, you can avoid using C idioms when you have C++ available.

For clarification: using C style doesnt improve performance (assuming you understand C++ constructs and RAII). In fact, a lot of algorithms written in C++ are faster than the same in C, just because C++ give more info to the compiler to make him optimize in calling context (I'm thinking about template algorithms/types for example).

So performance is not a valid reason to use C idioms when you write C++.

share|improve this answer
1  
"Now these constructs (C or not) are still there both for retrocompatibilty and for allowing you to write libraries to, again, free the other developers from having to play with them.": Would it be possible to remove them for good through separate compilation, i.e. define a subset of C++ that does not use old constructs? – Giorgio Nov 7 '12 at 18:01
    
No, first because C++ versions are highly retro-compatible (you can take C++98 code, if you didn't use auto or some obscure features, you will compile with a C++11 compiler), second because maintaining C interfacing is essential to continue to use C++ everywhere and with any other language's library. C is still the interface language between languages. – Klaim Nov 7 '12 at 18:14
    
Yes but one could interface with C or with C++98 using separate compilation. Using the same method one can even interface Haskell with C. Regarding retro-compatibility, maybe one can use compiler switches to give a warning or error message when using a "forbidden" feature. My question is rather technical, i.e. whether it would be possible to eliminate dangerous stuff like new and delete and still have a working language, or whether there are other, higher-level features of the language that depend on the lower-level features in order to work. – Giorgio Nov 7 '12 at 18:23
3  
You can't remove them without losing the ability to implement from scratch. The language would only be able to "utilize" features like RAII rather than "implement" them. It would become a high-level language rather than an systems implementation language. How do you think RAII and smart pointers were implemented? – mike30 Nov 7 '12 at 21:06
    
"It would become a high-level language rather than an systems implementation language.": I am a bit confused, because some claim that C++ is a high-level language. But yes, as long as it retains low-level constructs inherited from C I guess you are right that it is rather a low-level, system implementation language. – Giorgio Dec 3 '12 at 9:43

C++ is a language that provides a lot of features that you may chose to use or not. Your decisions should be based on

a) Frameworks you use

b) The domain of your application

c) Your current code base

Frameworks

Some frameworks lend themselves to specific styles. For example, if you are using Qt/KDE/Wt you will usually use their memory model and will not have your own smart pointers. You're unlikely to use exceptions as much. Qt's MOC also has certain implications for templates that limits how much you can do with them.

At the same time, code that is heavily based on boost tends to be heavy in what others have described as 'Modern C++'.

Then you have Google-stylized C++ which is essentially C with classes. http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml

Application Domain

If you are coding software where performance is critical (financial, embedded,etc), a lot of the 'niceties' of C++ might go out the window in favour of limiting memory footprints or CPU cycles. There are situations where C is the 'right' language to use for a small part of the program, but not for the whole thing; C++ allows you to do that so that you can have your pointer arithmetic when doing crazy matrix math but have high level abstractions when combining the modules that use it.

Current Code Base

In the real world, if you are in a shop with 10 C coders who don't want or like C++ you will have a much better time sticking to something like the Google style guide than something that is more 'Modern'. If all your libraries are written in C and you are only gluing them together in C++ then char * might make more sense than going to and from std::string all the time.

Conclusion

C++ is not a langauge that is ideal in any academic sense, but it is a pragmatic compromise that worked out very well. So when using it, you should also have a very pragmatic approach to the problem and not get too bogged down on 'The Right Way Of Doing Things'.

To answer your question: you may or may not be too puritanical, but that depends on the specifics.

share|improve this answer
    
Google's C++ code makes heavy use of templates and permits or encourages other C++ techniques such as smart pointers, (parts of) Boost, etc. Saying that Google-stylized C++ is merely C with classes might be an overstatement. – Josh Kelley Nov 9 '12 at 14:22
    
@JoshKelley I agree with you, it might be too strongly worded in that sense. But I am trying to identify a differnt direction of coding style which is never really clear-cut, and they will always overlap. – MrFox Nov 9 '12 at 16:41
    
from the google style guide: "Use only approved libraries and language extensions from C++11 (formerly known as C++0x). Currently, none are approved." Uhmmmm... Time to upgrade the style guide ... – Emilio Garavaglia Nov 9 '12 at 20:55

There is one case where you definitely should write in C style, and this is hardware-related programming. The main reason why the "raw C" is preserved in the C++ language is not just backwards compatibility, but the "raw C" also enables C++ to be used in bare bone hardware applications: when you are accessing hardware directly, when there is no OS present, or when you are writing the OS itself.

There is also the performance argument. Idealists will tell you that you shouldn't use raw arrays, you should only use std::vector, and it should be equally fast as a plain array - if not then the compiler is bad. This is utopia, we have yet to see it. So far, STL and templates remain slower than "bare bone" code. However, the more C++ programmers that use templates, STL abstraction etc, the more pressure there will be on the compilers to produce faster code for it.

If the C features where removed from C++, then it wouldn't be as much of a general-purpose language as it is today. It wouldn't be used as much in embedded systems or performance-critical applications, it would become yet another desktop-only language like Java or C#.

share|improve this answer

If this idea was enforced, seeing a char* would possibly be a thing of the past.

I tend to blend C and C++ together, making me that kind of funny "C/C++" developer. I think at least some of the reasons I list below will be legit hopefully. Often I'm using C interfaces with C++ implementations, or C++ interfaces with C-like implementations, though most of the time still C++ interfaces with C++ implementations.

There's several reasons I reach for C interfaces or C implementations (in descending order of importance):

  1. API/ABI (interface)
  2. Exception-Safety (implementation: huh?)
  3. Lack of Type-Safety (implementation: huh?)
  4. Performance (implementation: rare)
  5. Build Times (implementation)
  6. Aesthetics (implementation)

API/ABI

A lot of the codebases I've worked on have always been rather large and designed for third parties to extend, with plugin developers actually basing their livelihood on writing plugins for our products. As a result, the central, abstract interfaces are actually software development kits for both internal and third party developers to use when writing plugins to extend the product.

The third party plugin writers use a wide range of compilers and standard library implementations.

With this requirement, using C++ interfaces at the core API level is often out due to the ABI difficulties associated.

If we started passing std::vector through our central APIs, we end up facing ABI incompatibilities from one vendor implementation to another, and so we actually rolled our own vector-like container, but it's fully standard-compliant down to fill ctors, range ctors, range erase, etc. with performance tests to make sure it matches up with std::vector.

If we use vtables and virtual functions to model our abstract interfaces, we end up requiring plugin writers to use the same compilers we use, and will also break ABI the moment we introduce a single virtual function. As a result, the interfaces I design at the central core API level are C interfaces, mostly tables of function pointers, but with a C++ wrapper SDK which is statically-linked to plugins which wrap these into classes that conform to RAII and so forth. The wrappers are free to also use standard containers since they're internally linked to the plugin.

If we used exception-handling across module boundaries, we also face the same dilemma, so I use C error codes at the module boundary level (but often translated to/from exceptions). The statically-linked C++ wrapper SDK takes these error codes and translates them back into exceptions, but safely throwing from inside the plugin to the plugin itself.

Exception-Safety Part 1

This is a weird one, but since throwing an exception during stack unwind leads to std::terminate behavior, killing the process, it's extremely dangerous if any kind of release of resources can encounter an external error. Another is recursively throwing from a catch handler when doing so would end up aborting an outer transaction which shouldn't be aborted.

In these cases, occasionally we're tempted to allocate a resource for some reason that could possibly fail (rare but sometimes necessary). For example, I might need to allocate memory in an obscure case during the release of a resource from a general destructor or the roll back of a side effect from within a scope guard's destructor.

In those cases, sometimes a failure is not critical and can kind of be ignored (not ideal but perfect exception-safety is hard!). For example, failing to allocate memory might allow us to skip some work that's a nicety, but isn't absolutely crucial (like logging a message). In these cases, I find it easier to just use C-style error codes for failures to avoid the possibility of a recursive throw for functions which are often triggered in release/roll back/catch contexts.

Exception-Safety Part 2

When interacting with ABI, it's not safe to throw exceptions to the caller from within a callee inside a different module, especially when caller uses a different compiler/build settings from callee.

As a result, we have to swallow up exceptions before they leak past API boundaries. Because of this, sometimes if I have a relatively simple implementation for a C API, I'll just go ahead and use C all the way which can't possibly throw so that I don't have to worry about the possibility of throwing and sprinkle try/catch blocks at every entry point into the API.

Lack of Type-Safety

Okay, this is a weird one, but when we're doing low-level communication with the hardware or treating memory as just raw bits and bytes, type safety can get in the way with code that inevitably has to have a whole bunch of really ugly explicit casts. An example is a fixed allocator provided through the central C API. In that case, I often just do the implementation in C instead of C++ since memory allocators don't have much use for type safety -- they're working at the bits and bytes level.

Performance (Careful!)

This is very tied to the lack of type safety above. It isn't necessarily that C style is faster or anything like that. It's just that when it comes to doing things that are really critical with respect to how they're paying attention with the interaction of the CPU cache and so forth, it's hard to avoid things like raw pointers here and there.

Take a stride interface design like this:

void process_points(int num_points, 
                    float* x, int stride,
                    float* y, int stride,
                    float* z, int stride);

It's really hard to wrap that into a very safe design. The stride, after all, has to use a stride across these floats passed in that may not even provide a contiguous index access (they might just be skipping over bytes).

This unfortunately violates type safety, but these designs are often essential for those interfaces that handle a very heavy load with no better than linear-complexity requirements (have to touch everything), while still enabling potential optimizations like SoA SIMD access when each individual field has a stride that matches sizeof(float).

I've tried to make these types of interfaces semi-safe before by using all kinds of type-safe wrappers, but I didn't like the inflation of build times and also just came to like the aesthetic (albeit lacking type safety) of plain old data passed through in bulk with custom strides (OpenGL API style).

This is only for very select areas of the codebase, like particle systems, and they also happen to be the designs I often write tests for the most, since they're so dangerous.

Build Times

This might be slightly superstitious, but C just seems to build faster even if we're including the same number of headers and so forth and still judiciously use pimpls and such in C++ when appropriate. It might just be due to my tendencies when I'm writing more C-like code, but as a result, I tend to use C when I'm implementing something that's not very complex behind a C API to try to keep the build times down.

I really hate build times so I favor using Lua as much as possible for a large amount of code for anything that isn't performance-critical (though LuaJIT even lets me do some things that are slightly performance-critical), then C++ for a middle amount of code, then C for a small amount of code for these reasons cited.

Aesthetics

This is probably the most biased section of them all, but occasionally I just like the lightweight, minimalist C aesthetic. I love C++ for its type-safety, ability to design rich interfaces and data types, genericity, etc. But there's a C aesthetic I also admire which is just minimalist -- dangerous but minimalist.

So sometimes I'm just in the "mood" for C. I'll choose to kind of arbitrarily implement one of the C API interfaces with a C implementation for some of the simpler areas just because I'm in the mood for it. It's a horrible reason, I know, but I wanted to include it because I think both languages are beautiful and can be blended together -- one doesn't have to supersede the other.

Conclusion

So anyway, these are the main reasons I use C or C-like coding from time to time even while primarily using C++. I don't know if they're great reasons or not. I try not to be that kind of dogmatic superstitious programmer, but I've found it beneficial to reach for C here and there, and essential for when it comes to ABI when targeting multiple compilers and possibly even multiple languages with a dylib API.

share|improve this answer

The new c++ has some advantages, this is much clearer than two 'c' style loops in i,j:

int array[5][10]; int i;
for_each(array, array+5,
  for_loop(var(i)=0, var(i)<10, ++var(i),
           _1[var(i)] += 1));
share|improve this answer
5  
I'd be very curious to see you write out the two nested loops side by side with above code segment so we can all compare and see which one is clearer :) – DXM Nov 7 '12 at 18:33
4  
this is much clearer than two 'c' style loops Is it? I suppose it depends on who is reading it. – Caleb Nov 7 '12 at 18:34
    
@DXM I was going to but I couldn't work out what the hell the C++ example was calculating! – Martin Beckett Nov 7 '12 at 18:41
2  
@Caleb - sorry, I'm British and an engineer = assume everything is irony – Martin Beckett Nov 12 '12 at 1:37
    
@Martin Beckett: Got the irony now. +1! – Giorgio Dec 3 '12 at 10:31

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.