2

When working with a C API that uses C-style inheritance, (taking advantage of the standard layout of C-structs), such as GLib, we usually use C-style casts to downcast:

struct base_object
{
    int x;
    int y;
    int z;
};

struct derived_object
{
    base_object base;
    int foo;
    int bar;
};

void func(base_object* b)
{
    derived_object* d = (derived_object*) b; /* Downcast */
}


But if we're writing new C++ code that uses a C-API like this, should we continue to use C-style casts, or should we prefer C++ casts? If the latter, what type of C++ casts should we use to emulate C downcasting?

At first, I thought reinterpret_cast would be suitable:

derived_object* d = reinterpret_cast<derived_object*>(b);

However, I'm always wary of reinterpret_cast because the C++ standard guarantees very little about what will happen. It may be safer to use static_cast to void*:

derived_object* d = static_cast<derived_object*>(static_cast<void*>(b))

Of course, this is really cumbersome, making me think it's better to just use C-style casts in this case.

So what is the best practice here?

Community
  • 1
  • 1
Channel72
  • 24,139
  • 32
  • 108
  • 180
  • Are you sure that by the time when you downcast `base_object*` to `derived_object*` this pointer points to valid object of type `derived_object` ? – LihO Mar 03 '13 at 16:20
  • downcasting itself is a risk in a general sense is it not? if this was a c++ class(struct) dynamic as well as static cast would have been ok. dynamic cast atleast throws exception that you can check.. – Koushik Shetty Mar 03 '13 at 16:22
  • 2
    Is `func` a callback? If it is, are you sure it always gets called with the derived type? If it is not callback, why not change parameter to derived type and not do casting? – hyde Mar 03 '13 at 16:54

4 Answers4

2

new C++ code that uses a C-API like this

Don't write new C++ code in a C style, it doesn't make use of the C++ language features, and it also forces the user of your wrapper to use this same "C" style. Instead, create a proper C++ class that wraps the C API interface details and hides them behind a C++ class.

should we continue to use C-style casts

No

or should we prefer C++ casts

Yes, but only when you have to.

Use C++ inheritance and virtual accessor functions (probably). Please show how you plan to use the derived object in func, this may provide a better answer for you.

If func expects to use the methods of the derived object, then it should receive a derived object. If it expects to use the methods of a base_object, but the methods are somehow changed because the pointer is to a derived_object, then virtual functions are the C++ way to do this.

Also, you want to pass a reference to func, not a pointer.

dynamic_cast, requires certain conditions to be met:

http://www.cplusplus.com/doc/tutorial/typecasting/

If you are just converting struct ptrs to struct ptrs and you know what you want, then static_cast, or reinterpret_cast may be the best?

However, if you truly are interested in writing C++ code, then the casts should be your last and final resort, since there are better patterns. The two common situations I would consider casting are:

  • You are interfacing with some event passing mechanism that passes a generic base class to an event handler.

  • You have a container of objects. The container requires it to contain homogenous types (i.e every element contains the same "thing"), but you want to store different types in the container.

Josh Petitt
  • 9,371
  • 12
  • 56
  • 104
  • 1
    "Don't do this" isn't an option if you're writing C++ code that needs to wrap API calls to a C-API. – Channel72 Mar 03 '13 at 16:27
  • @Channel72, you haven't shown the C API portion, can you please show that? – Josh Petitt Mar 03 '13 at 16:29
  • @Channel72, also please be specific on the C API functions versus the C++ functions you are adding. You haven't shown any of the C++ wrapper yet, that I can see. – Josh Petitt Mar 03 '13 at 16:31
  • @OliCharlesworth, agreed, also for clarification, I was saying "don't write new C++ code that works like this", not "don't write new C++ code that interfaces with a C API". I've updated my answer to reflect this. – Josh Petitt Mar 03 '13 at 17:22
2

However, I'm always wary of reinterpret_cast because the C++ standard guarantees very little about what will happen.

C++-style casts are no less safe than C-style casts, because C-style cast is defined in terms of C++-style casts.

5.4.4 The conversions performed by

— a const_cast (5.2.11),

— a static_cast (5.2.9),

— a static_cast followed by a const_cast,

— a reinterpret_cast (5.2.10), or

— a reinterpret_cast followed by a const_cast,

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

[...]

If a conversion can be interpreted in more than one of the ways listed above, the interpretation that appears first in the list is used, even if a cast resulting from that interpretation is ill-formed.

The sad answer is that you can't avoid casts in code like you written, because the compiler knows very little about relations between classes. Some way or another, you may want to refactor it (casts or classes or the code that uses them).

The bottom line is:

If you can, use proper inheritance.

If you can't, use reinterpret_cast.

milleniumbug
  • 15,379
  • 3
  • 47
  • 71
  • Doesn't `dynamic_cast` provides a measure of safety that `reinterpret_cast` lacks? Am I completely misunderstanding this? (It wouldn't be the first time.) – Joshua Green Mar 03 '13 at 17:02
  • 2
    @JoshuaGreen It does, but only if you use proper C++ inheritance. OP doesn't (just look at the code) – milleniumbug Mar 03 '13 at 17:03
2

If you look at the specification for C-style casts in the C++ spec you'll find that cast notation is defined in terms of the other type conversion operators (dynamic_cast, static_cast, reinterpret_cast, const_cast), and in this case reinterpret_cast is used.

Additionally, reinterpret_cast gives more guarantees than is indicated by the answer you link to. The one you care about is:

§ 9.2/20: A pointer to a standard-layout struct object, suitably converted using a reinterpret_cast, points to its initial member (or if that member is a bit-field, then to the unit in which it resides) and vice versa.

If you want to use a cast notation I think using the C++ type conversion operators explicitly is best. However rather than littering casts all over the code you should probably write a function for each conversion (implemented using reinterpret_cast) and then use that.

derived_object *downcast_to_derived(base_object *b) {
    return reinterpret_cast<derived_object*>(b);
}
bames53
  • 86,085
  • 15
  • 179
  • 244
  • This is the best answer really. I get the feeling that a lot of people are misunderstanding the question here and/or don't have much experience working with C-style object oriented APIs. – Channel72 Mar 03 '13 at 17:45
-1

I think dynamic_cast is exactly what you want.

Joshua Green
  • 1,517
  • 1
  • 10
  • 14