4

I have read, both on this site and elsewhere, that the recommended programming style for C++ is to avoid using C-style casts, and prefer the C++-style static_cast, dynamic_cast, reinterpret_cast, and const_cast. The reasons for this recommendation generally boil down to (1) the C++ casts are safer because they are more specific and limited in meaning, and (2) they are intentionally verbose and ugly because you should not be using them often in good C++ code. However, this reasoning doesn't make as much sense when it comes to calling C functions from my C++ code.

In my C++ projects I am often forced to call OS-level libraries that only have a C interface, such as the POSIX sockets library for network operations. These C functions often expect the caller to make several pointer casts, and if I stick to only C++-style casts my code becomes clunky and verbose. For example, my networking code is littered with function calls like this:

std::size_t message_size;
ssize_t bytes_read = recv(fd, reinterpret_cast<char*>(&message_size), sizeof(message_size), MSG_WAITALL);

I'm not gaining any safety from using C++ casts (since reinterpret_cast doesn't really add any more compile-time checks than a C-style cast), and the ugliness of the code isn't alerting me to a problem - there's no way to use socket functions without casting.

In light of the interface mismatch between C and C++, would it be reasonable to use C-style casts when calling C functions that require casting? Or are C-style casts always bad practice, and I should just accept the extra code bloat when I need to call C functions?

2
  • For interop with C code I often just use C-style casts... not ideal practice, but whatever -- I'd rather spend my time focusing on writing tests for correctness than trying to find ways to do bulletproof type casts when communicating with a C lib. One argument maybe I could offer in favor of using C-style casts in this context is that it enriches the meaning of C++ casts elsewhere in the codebase.. Communicating with C APIs can require a boatload of casting, and if you sprinkle your code with static_casts here and reinterpret_casts there, you end up polluting...
    – user204677
    Dec 13, 2017 at 22:14
  • ... your searches for cases where you're performing casts in C++ code that isn't interacting with a C library, where the casts should be few and far between. There are very few reasons for C++ code calling C++ code to have to cast, so it might help to reserve the C++ casts for those cases and make them stick out instead of obscured by a boatload of casts required for C++ code to work with C code. So that might be one argument, but TBH I just find it ugly in this context and I've never found the use of a static_cast or reinterpret_cast to ever prevent problems, e.g., over a C-style cast.
    – user204677
    Dec 13, 2017 at 22:19

2 Answers 2

8

It's easy to prevent the verbosity of calling C functions from cluttering your code: wrap them. In the case of recv, for example, you can simply do this:

template <typename T>
ssize_t recv(int fd, T& data) {
  return ::recv(fd, reinterpret_cast<char*>(&data), sizeof(data), MSG_WAITALL);
}

You can improve on this by e.g. static_asserting that T is trivially copyable or has some other desired properties.

Now if you use this function instead of the C recv all over the code, you have reduced clutter and improved safety (you can't accidentally mismatch the data type and its size).

At the same time, you're still using C++-style casts, which prevents you from accidentally casting away constness.

4

The standard explains how the C style cast works (N4296):

5.4/4: The conversions performed by

  • a const_cast,
  • a static_cast,
  • a static_cast followed by a const_cast,
  • a reinterpret_cast , or
  • a reinterpret_cast followed by a const_cast,

can be performed using the cast notation of explicit type conversion. (...)

Without entering into the details, the rest of the paragraph also explains that the C cast relaxes some constraints that would apply to static_cast in presence of pointers to derived classes. This relaxed attitude is one of the reasons why it is advised not to use it in C++ code.

In your case, using (char*)&message_size would result in reinterpret_cast<char*>(&message_size) to be generated by the compiler. So you wouldn't lose anything by using reinterpret_cast directly.

Bjarne Stroustrup gives a couple of other reasons, why the C++ casting should be preferred in his book "The design and evolution of C++":

To sum up, old-style cast:

  1. Are a problem for understanding: they provide a single notation for several weakly related operations
  2. Are error-prone: almost every type combination [i.e. source type and target type] has a legal interpretation
  3. Are hard to spot in code and hard to find with simple tools
  4. Complicate the C and C++ grammars

The new cast operators represent a classification of the old's cast functionality.

Of course, in your special case, point 1, 2 and 4 are not a real issue. Nevertheless using the new style would bring you the benefit of point 3, and would get you used to the greater precision that is commonly used in C++ casting.

On the other side, IMHO, it would not be shocking to still use the C syntax if you restrict its use to the sole highlight in the code the interoperability with the C libraries.

1
  • I feel like saying point 1,2 and 4 do not matter in his case is a bit short-sighted. Looking at a correctly written snipped in isolation and state that the two ways are equivalent to the compiler is not an argument. If he had expected to be able to convert a primitive with static cast the compiler error would still have hinted him there was something is wrong in his reasoning. The compiler would still catch const - correctness issues. The reinterpret_cast still gives more information to the reader. Those points still stand. Apart from that, your answer still makes perfect sense though.
    – Teimpz
    Jan 3, 2018 at 23:18

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

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