5

Unlike other examples of this error message i already have a pointer to A and want to retrieve the actual child class.

This kind of arrangement is part of some C++ wrapped C code there A is some POD C structure (whatswhy no dynamic cast) and test is some callback in C that calls C++ functionality and to retrieve the correct object the cast should be used. But to prevent C++ user code messing the C-Baseclass i would like to have the inheritance protected.

MSVC does not complain about this but g++ does!? Which one is correct from the standards point of view and why?

#include <iostream>

using namespace std;

// plain C structure
struct A{
    int i;
};

// some C++ Wrapper class
struct B: protected A{
  A* get() { return this; }
  void print(){cout << i << endl;}
};



extern "C" {
  // C callback that gives it this pointer
  void test(A* io_self){
     auto b2 = static_cast<B*>(io_self);
     b2->print();
  }
}    

int main()
{
   B b;
   test(b.get());
   return 0;
}

gives:

$g++ -std=c++11 -o main *.cpp
main.cpp: In function ‘void test(A*)’:
main.cpp:21:43: error: ‘A’ is an inaccessible base of ‘B’
          auto b2 = static_cast<B*>(io_self);
                                           ^
vlad_tepesch
  • 6,681
  • 1
  • 38
  • 80
  • 2
    If you want to successfully cast from one type to another outside of those class' scopes, you should use `public` inheritence, instead of `protected` one – Fureeish Dec 20 '17 at 15:19
  • 1
    It is a valid question though it is not clear how existance of method `get()` prevents user code to mess with base. – Slava Dec 20 '17 at 15:25
  • Do you pass `test()` by name? Otherwise I think there is no need for extern "C" and it can be a static method of `B` – Slava Dec 20 '17 at 15:48
  • @Slava the `extern "C"` also forces the compiler to use C calling conventions and disable name mangeling for the function. if this would not be the case, the call of the function would screw up the stack since the C-Code would have a different idea about how to transfer parameter to the called function. – vlad_tepesch Jan 02 '18 at 10:21
  • That's why I asked if you pass `test()` by name. If not then name mangling does not matter. As for calling convention that's compiler specific, on gcc Inever had any problems passing C++ functions to C routines, like pthread. – Slava Jan 02 '18 at 13:48

2 Answers2

4

If you use protected inheritance, only derived types can be aware of that inheritance. As far as test is concerned, there is no relation between A and B. Since a static_cast can't cast pointers between unrelated types, you would need to reinterpret_cast instead. Or you may be able to provide B with a static method to preform this conversion, since B is aware of the inheritance. For example :

// some C++ Wrapper class
struct B : protected A {
    A* get() { return this; }
    void print() { cout << i << endl; }
    static B* cast_to_b(A* io_self) { return static_cast<B*>(io_self); }
};


extern "C" {
// C callback that gives it this pointer
void test(A* io_self) {
    auto b2 = B::cast_to_b(io_self);
    b2->print();
}
}

Be sure that the object referred to by A* io_self is actually a B and not just a A or it's undefined behavior.

Are you sure protected is the right inheritance for you here? It seems like private inheritance might be clearer as there doesn't seem to be any intention of inheriting from B. You may also want to consider forgetting about inheritance and simply giving B a A member.

François Andrieux
  • 28,148
  • 6
  • 56
  • 87
  • I doubt you can use `dynamic_cast` with POD – Slava Dec 20 '17 at 15:26
  • @Slava Yep, I missed that. Thank you for comment. – François Andrieux Dec 20 '17 at 15:28
  • using an A-Member disables me to retreive the B-Pointer in the callback. – vlad_tepesch Dec 20 '17 at 15:30
  • Though having those 2 methods just a convoluted way of making that inheritance public – Slava Dec 20 '17 at 15:31
  • `reinterpret_cast` is also not wrong because it breaks the addresses because `B` also inherits from another class. – vlad_tepesch Dec 20 '17 at 15:36
  • @Slava no, as stated, the `test` function is some callback, that gets called from C-Code and it retrieves the C++-Object and calls the appropriate member-function. From the rest of the C++ Code this C-Wrapping should be hidden and direct access to the C-Base class should be prohibited. – vlad_tepesch Dec 20 '17 at 15:39
  • @vlad_tepesch I did not mean `test()`, I mean with existance of `get()` and `cast_to_b()` it is pretty much the same as having public inheritance. Btw why not to make `test` a friend and eliminate those pesky functions? – Slava Dec 20 '17 at 15:45
4

From c++11 N3337 draft (a bit old but it's the one I have lying around) 5.2.9/11 (static_cast):

A prvalue of type “pointer to cv1 B,” where B is a class type, can be converted to a prvalue of type “pointer to cv2 D,” where D is a class derived (Clause 10) from B, if a valid standard conversion from “pointer to D” to “pointer to B” exists (4.10), cv2 is the same cv-qualification as, or greater cv-qualification than, cv1, and B is neither a virtual base class of D nor a base class of a virtual base class of D.

In this case, since you use protected inheritance there is no valid standard conversion from B to A so your static_cast is illegal (g++ is correct to diagnose it).

In this case since you're providing a c++ wrapper around a C API I think the simplest approach is to just stick with public inheritance and have a small amount of trust that your users won't abuse the C API directly if they've already consciously chosen to use your C++ API

Mark B
  • 95,107
  • 10
  • 109
  • 188