Casting pointers the hard way

Consider the following class hierarchy:
1
2
3
4
5
6
7
class S{ /*some data*/ };

class A : public virtual S{ /*some data*/ };

class B : public virtual S{ /*some data*/ };

class C : public A, public B{};
Now consider this code:
1
2
3
C *c = ::operator new (sizeof(C));
A *a = f(c, kC, kA);
B *b = f(c, kC, kB);
where kA, kB, and kC are arbitrary constants that designate the types A, B, and C respectively.
Is it possible to implement f() in a portable way such that, once the object pointed to by c is constructed, a and b point to valid instances of A and B respectively?

Just as extra background, I need this because I'm working on a serializer generator and I need to be able to handle this case:
1
2
3
4
class Q{
    std::shared_ptr<A> a;
    std::shared_ptr<B> b;
};
where both a and b point to the same instance of C, and both shared pointers must be initialized independently of whether the target has been constructed.
Last edited on
Is it possible to implement f() in a portable way such that, once the object pointed to by c is constructed, a and b point to valid instances of A and B respectively?


Maybe I didn't understand but, A and B should receive an event notification about creation of C object?
both A and B maintain an internal pointer to C (which is NULL), but updated to C object
once C instance is created.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class S{ /*some data*/ };

class C; // forward decl.

class A : public virtual S
{
  C* p_to_CA;
  void on_c_creation_handlerA(C* instance_ptr);
 /*some data*/ 
};
class B : public virtual S
{
  C* p_to_CB;
  void on_c_creation_handlerB(C* instance_ptr);
 /*some data*/ 
};
class C : public A, public B
{
  private:
  void notify_creation(C* pThis); // or it can be part of constructor.
};


now both a and b are valid regardless of existence of c, and p_to_C is initialized trough event to the same object of C.

If that's what you mean?
Last edited on
No, that's not possible.
1. a and b don't yet point to anything valid, so there's nothing to pass as this to on_c_creation_handlerX().
2. The number of functions that must be written grows quadratically with the depth of the inheritance tree. Consider if the linear distance between S and C is 10.

What I mean is that f(c, kC, kA) must do the equivalent of static_cast<A *>(c), but without actually using static_cast(), and the reason for this is related to my point #2 above. The source and destination types are only known at run time, so if the inheritance tree is 10 levels deep I would have to generate a function with ~50 branches to decide between ~50 casts.
1
2
3
4
5
void *f(void *src, int src_type, int dst_type){
    if (src_type == kC && dst_type == kA)
        return static_cast<A>((C *)src);
    //etc.
}


But if instead of that f() can be implemented like
1
2
3
void *f(void *src, int src_type, int dst_type){
    return (byte_t *)src + offsets[src_type][dst_type];
}
then that's much easier.
Last edited on
not sure if it will work, but can you do it with pointer references, such that f returns a reference pointer to something that once updated, is correct?
I can't change any of the types involved, thus it's not possible to add levels of indirection.
Here's an implementation I came up with. It works on MSVC, but I don't know how portable it is:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
std::map<std::pair<int, int>, int> cast_map;

template <typename T>
struct get_type_id{};

template <>
struct get_type_id<S>{
    static const int value = 1;
};

template <>
struct get_type_id<A>{
    static const int value = 2;
};

template <>
struct get_type_id<B>{
    static const int value = 3;
};

template <>
struct get_type_id<C>{
    static const int value = 4;
};

template <typename src, typename dst>
void set_cast(){
    static_assert(std::is_base_of<dst, src>::value, "src must be a subclass of dst");
    const auto k = std::numeric_limits<intptr_t>::max() / 4;
    
    auto key = std::make_pair(get_type_id<src>::value, get_type_id<dst>::value);
    cast_map[key] = (int)((intptr_t)static_cast<dst *>((src *)k) - k);
}

void initialize_cast_map(){
    set_cast<S, S>();
    set_cast<A, S>();
    set_cast<A, A>();
    set_cast<B, S>();
    set_cast<B, B>();
    set_cast<C, S>();
    set_cast<C, A>();
    set_cast<C, B>();
    set_cast<C, C>();
}

void *dynamic_static_cast(void *src, int src_type, int dst_type){
    auto it = cast_map.find(std::make_pair(src_type, dst_type));
    if (it == cast_map.end())
        throw std::bad_cast();
    return (char *)src + it->second;
}
Last edited on
In the end I figured out this doesn't work. If there's a virtual inheritance on the inheritance path from C to S, casting statically from C * to S * may require dereferencing the pointer, so a) it can't be done as a simple addition, and b) it can't be done before the object has been constructed.
Topic archived. No new replies allowed.