5

I have a const uint8_t* that I want to convert to a char* for an interface that expects a char*.

The easiest way to do this is with a C-style cast:

const uint8_t* aptr = &some_buffer;
char* bptr = (char*)aptr;

However, our internal style guide (which is based on the Google C++ Style Guide) bans C-style casts.

Another option is this monstrosity, which I find pretty unreadable:

char *cptr = reinterpret_cast<char*>(const_cast<uint8_t*>(aptr));

These other options I tried all failed to compile:

char* dptr = reinterpret_cast<char*>(aptr);
char* eptr = const_cast<char*>(aptr);
char* fptr = static_cast<char*>(aptr);

Is there any way I can perform this cast using a C++-style cast without nesting two separate cast operations?

Kerrick Staley
  • 1,583
  • 2
  • 20
  • 28
  • 1
    Unless you are certain that your `const uint8_t*` *always* points to a *non-const* buffer, this could lead to *undefined behavior*. – Galik Aug 01 '18 at 18:46
  • See https://ideone.com/4SMImR. When compiling this code, I get the error `prog.cpp:11:43: error: reinterpret_cast from type ‘const uint8_t* {aka const unsigned char*}’ to type ‘char*’ casts away qualifiers` – Kerrick Staley Aug 01 '18 at 18:46
  • @Galik That's only if the C-API in question actually tries to write to the buffer. If it only reads, then the behavior is well defined. – Xirema Aug 01 '18 at 18:46
  • @Galik It could also point to a `const` buffer, provided the use of the now-non-const pointer doesn't try to modify the buffer. For example a non-const correct API function which only reads from it. – François Andrieux Aug 01 '18 at 18:46
  • @Xirema, nope. The behavior is defined if original object was not created const. – SergeyA Aug 01 '18 at 18:47
  • 1
    Yes, exactly, this is an interface that takes a `char*` but doesn't actually modify its contents (the interface designer should have added a `const`, but I can't change it). – Kerrick Staley Aug 01 '18 at 18:47
  • "Another option is this monstrosity" - the fact that you need to use such a monstrosity should be a pretty big hint that you are *probably* doing something wrong (if having to use a cast wasn't a bit enough hint in the first place). At least, by using the C++ casts rather than the C cast you make it very clear (and visible) that something nasty is going on (and you are being *explicit* about *what* nastiness is happening). – Jesper Juhl Aug 01 '18 at 18:47
  • @FrançoisAndrieux and nope as well. Like I said above, const_cast being defined or undefined doesn't depend on how the casted object is used. It depends on the cv-qualification of original object. – SergeyA Aug 01 '18 at 18:48
  • Do you need this cast once, or several times? If once, then you have to live with this. If several times, you may want to create an inline function which does the cast. – geza Aug 01 '18 at 18:49
  • 3
    Then you could write a const correct wrapper function and send a patch to the authors :) – Galik Aug 01 '18 at 18:49
  • 2
    @SergeyA [cppreference](https://en.cppreference.com/w/cpp/language/const_cast) disagrees with you. Can you provide a source for your claim? It says *"const_cast makes it possible to form a reference or pointer to non-const type that is actually referring to a const object"*. – François Andrieux Aug 01 '18 at 18:49
  • Answering question as asked: if you are using C++ casts, you'd have to use two, unless `char` is defined as unsgined type/ – SergeyA Aug 01 '18 at 18:50
  • @FrançoisAndrieux, ok I was wrong. If the object is not modified through casted pointer, const_cast is defined. – SergeyA Aug 01 '18 at 18:56
  • 1
    The API in question is part of the STL :( I'm trying to do what's asked in this question, but also go from uint8_t to char: https://stackoverflow.com/questions/13059091/creating-an-input-stream-from-constant-memory – Kerrick Staley Aug 01 '18 at 19:06
  • 2
    @KerrickStaley Please show a [MCVE]. The standard library is fully `const` correct. That means if at any point you think you need `const_cast` to use a standard library feature, you are making a big mistake somewhere. – François Andrieux Aug 01 '18 at 19:07

4 Answers4

8

Is there any way I can perform this cast using a C++-style cast without nesting two separate cast operations?

Not portably, no. There is no single "the type is wrong and the const is also wrong" cast.

Another option is this monstrosity, which I find pretty unreadable:

char *cptr = reinterpret_cast<char*>(const_cast<uint8_t*>(ptr));

Do that.

Both C++ casts and your internal style guide are striving to make this look monstrous.

You can prevent the repetition of these casts by writing your own cast.

template< typename T >
char* awful_monster_cast( const T * ptr )
{
    return reinterpret_cast<char*>(const_cast<T*>(ptr));
}
Drew Dormann
  • 59,987
  • 13
  • 123
  • 180
5

Another option is this monstrosity, which I find pretty unreadable:

char *cptr = reinterpret_cast<char*>(const_cast<uint8_t*>(aptr));

You might find it unreadable, but this is the idiomatic way to express this conversion in C++.

Here's what's important:

  • You are using a C-style API that, for whatever reason, is not itself const-correct, but you want to preserve the const-correctness of your own code, as much as possible, necessitating a cast to remove the cv-qualifications of the type.
  • The C-style API uses signed data types, whereas your application uses unsigned data types.

In total, those are two conversions that your code needs to make—removing the const-ness, and converting to a signed type. This demands that you make two explicitly expressed conversions, if you want to obey these coding practices. You may not agree with this principle, but your company/coding practices certainly do.

Of course, I don't think anything is stopping you from writing something like this:

char * convert_to_c_data_buffer(uint8_t const* ptr) {
    return reinterpret_cast<char*>(const_cast<uint8_t*>(ptr));
}

char* dptr = convert_to_c_data_buffer(aptr);
Kerrick Staley
  • 1,583
  • 2
  • 20
  • 28
Xirema
  • 19,889
  • 4
  • 32
  • 68
4

Is there any way I can perform this cast using a C++-style cast without nesting two separate cast operations?

If you want it done in a single line like char* foo = some_cast(source) then no. The only cast that can remove const is const_cast so you need that plus an additional one to convert that now non const pointer into your source type. That leaves you with the monstrosity

char *cptr = reinterpret_cast<char*>(const_cast<uint8_t*>(aptr));

as the single line solution. This is a safety feature and makes it so it is painfully obvious you are removing constness.

NathanOliver
  • 171,901
  • 28
  • 288
  • 402
0

Is there any way I can perform this cast using a C++-style cast without nesting two separate cast operations?

Certainly. There is no need to nest those cast operations. Instead, you can use two separate expressions:

auto cuint = const_cast<uint8_t*>(aptr);
auto cptr  = reinterpret_cast<char*>(cuint);
eerorika
  • 232,697
  • 12
  • 197
  • 326