[Help]Virtual functions from 2 separate base classes

So I have 2 seperate base classes,
(note that I removed the variables and functions that do not relate to the topic)
Object.h

1
2
3
4
5
6
7
8
9
class Object{
	
public:
	Object();
	~Object();

	virtual bool update(sf::Time frameTime) = 0;

};


and

Drawable.h

1
2
3
4
5
6
7
8
#include <SFML/Graphics.hpp>

class Drawable{

public:
	virtual sf::Sprite* getImage() = 0;

};


Both of these are being used by

StaticObject.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include "Object.h"
#include "Drawable.h"

class StaticObject : public Object, public Drawable{

private:
	sf::Sprite sprite;

public:
	StaticObject();
	~StaticObject();

	bool update(sf::Time frameTime);
	sf::Sprite* getImage();

};


They are also both used in Player.h but Player.h looks pretty much identical to StaticObject with the extra stuff removed so I just didn't post it.

Anyway, down to my problem. When I try to access getImage by casting the current object in the queue (the queue data type is of void*) as a Drawable*, the update function is called instead.

Here is an example,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
bool update(Object* updatable){
        return updatable->update(frameTime); //frameTime is initialized elsewhere
}

sf::Sprite* getImage(Drawable* drawable){
	return drawable->getImage();
}

void draw(QueuePtr objectQueue){
	QueuePtr temp = objectQueue;
        while(temp != NULL){
                update((Object*)temp->object);
                temp = temp->next;
        }
        temp = objectQueue;
	while(temp != NULL){
		window.draw(*getImage((Drawable*)temp->object));
                temp = temp->next;
        }
}


The error I get is saying I am calling a function declared with one calling convention with a function pointer declared with a different calling convention and this makes perfect sense because for some reason, the function pointer is pointed at the virtual function Object::update but I can't figure out why and how to make it point at the virtual function Drawable::getImage.

Also, the virtual update function is called in a different place just before this and works correctly. Can someone that understands a little more about this help me understand what is going on?
Last edited on
You cannot store pointers to polymorphic types in void * because it can cause loss of runtime type information, and you should never use void * to begin with.

Just store Drawable * in your queue to begin with.

I don't understand why you would use a self-made C-style linked list when you're using C++ anyway. C++ should not be used as "C with Classes" - that will get you into big trouble.
Last edited on
I guess I should have clarified, the objects in the queue are also updated via the virtual function. So there is one loop that updates all the objects in the queue and another loop that draws the objects. I am not sure what type to use as the queue type since there are 2 base classes. I will add this to the original post.
Last edited on
Your code claims that the address of an Object is the address of a Drawable.
See Figure 3 in http://deepakgiri123.blogspot.fi/2011/05/c-up-casting-down-casting-in.html

> (the queue data type is of void*)

(Drawable*)temp is a static_cast.

You are:
a. implicitly converting a pointer to an object to pointer to void
b. and then converting that pointer to void to a pointer to a different type via a compile-time cast

Instead:
a. implicitly converti a pointer to an object to a pointer to void
b. first restore the type by converting that pointer to void to a pointer to the original type via a compile-time cast
c. convert the result of that cast to a pointer to another class in the same hierarchy

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
#include <iostream>
#include <memory>

struct Object { virtual ~Object() = default ; } ;
struct Drawable { virtual ~Drawable() = default ; } ;
struct StaticObject : Object, Drawable {} ;

#define SHOW(a) ( std::cout << #a << ": " << (a) << '\n' )

int main()
{
    StaticObject c ;
    StaticObject* ptr_static_object = std::addressof(c) ; SHOW(ptr_static_object) ;
    Object* ptr_object = ptr_static_object ; SHOW(ptr_object) ;
    Drawable* ptr_drawable = ptr_static_object ; SHOW(ptr_drawable) ;

    std::cout << "------------\n" ;
    void* pv = ptr_static_object ; SHOW(pv) ;
    SHOW( static_cast<StaticObject*>(pv) ) ; SHOW(ptr_static_object) ; // fine

    std::cout << "------------\n" ;
    // can't do this: implicitly convert StaticObject* to void*
    // and then cast it to a diiferent type via a static_cast and use the result
    SHOW( static_cast<Object*>(pv) ) ; SHOW(ptr_object) ;
    SHOW( static_cast<Drawable*>(pv) ) ; SHOW(ptr_drawable) ; // *** wrong

    std::cout << "------------\n" ;
    // do it this way:
    SHOW( static_cast<Drawable*>( static_cast<StaticObject*>(pv) ) ) ; // fine if the cast is correct; compile-time cast
    SHOW(ptr_drawable) ;
    Drawable* ptr_drawable2 = static_cast<StaticObject*>(pv) ; // implicit conversion from derived to base
    SHOW(ptr_drawable2) ;

    std::cout << "------------\n" ;
    void* pv2 = ptr_object ; SHOW(pv2) ;
    SHOW( dynamic_cast<Drawable*>( static_cast<Object*>(pv2) ) ) ; // fine
    SHOW(ptr_drawable) ;
    
}

http://rextester.com/SCVUJ34023
Thanks JLBorges, this helped me fix my probelm. One question though. Why is it that you need to cast the void* back to the original type before casting it as the new base? I changed the queue type to Object* and it still works. So now the original type is Object* and I am able to do a dynamic cast to get at the virtual functions from the Drawable class which is only related because they share a derived class? This is a little confusing. The link that keskiverto posted helps a little. It says that there are multiple vptrs when there is multiple inheretence but how does it figure out what vptr to use when the type changes via a cast? As I typed that last sentence I realised why it wasn't working before but I just don't understand how it works using a dynamic cast now.
Last edited on
> Why is it that you need to cast the void* back to the original type before casting it as the new base?
> I just don't understand how it works using a dynamic cast now.

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
53
54
55
56
57
58
59
60
61
62
63
64
65
#include <iostream>
#include <memory>

struct Object { virtual ~Object() = default ; } ;
struct Drawable { virtual ~Drawable() = default ; } ;
struct StaticObject : Object, Drawable {} ;

#define SHOW(a) ( std::cout << #a << ": " << (a) << '\n' )

template < typename DERIVED, typename BASE > std::ptrdiff_t offsetofbase() // *** caution: not portable
{
    // **** assumes that 0x1000000 - sizeof(derived) to 0x1000000 are addresses
    // **** on which pointer arithmetic is well-defined
    // **** assumes that sizeof(derived) is < 16 MB
    const uintptr_t address = 0x1000000 ;
    const DERIVED* pderived = reinterpret_cast<const DERIVED*>(address) ;
    const BASE* pbase = pderived ;
    return reinterpret_cast<const char*>(pbase) - reinterpret_cast<const char*>(pderived) ;
}

int main()
{
    // these offsets are not guaranteed by the IS; but they are typical for 64-bit pointers
    SHOW( ( offsetofbase< StaticObject, Object >() ) ) ; // 0
    SHOW( ( offsetofbase< StaticObject, Drawable >() ) ) ; // 8

    /*
                 Typical object representation of StaticObject

                                            --------------
                                            |            |
                                            |            |
                                            |  Drawable  |
                                            |            |
                                            |            |
              Drawable*  pdrawable -------->--------------<--------- void* pvd = pdrawable (Drawable* converted to void*)
                                            |            |
                                            |            |
                                            |  Object    |
                                            | (8 bytes)  |
                                            |            |
                                            |            |
                   Object* pobject -------->--------------<--------- void* pvo = pobject (Object* converted to void*)

    */

    std::cout << '\n' ;
    StaticObject so ;
    Object* pobject = std::addressof(so) ;
    SHOW(pobject) ;

    void* pvo = pobject ;
    SHOW(pvo) ;

    std::cout << '\n' ;
    Drawable* pdrawable = std::addressof(so) ;
    SHOW(pdrawable) ;
    void* pvd = pdrawable ;
    SHOW(pvd) ;

    std::cout << '\n' ;
    SHOW( (Drawable*)pvo ) ; // *** wrong *** C-style cast is interpreted as static_cast<Drawable*>(pvo)
    SHOW( (Drawable*)pobject ) ; // *** wrong *** C-style cast is interpreted as reinterpret_cast<Drawable*>(pobject)
    SHOW( ( dynamic_cast<Drawable*>(pobject) ) ) ; // fine; type-safe run-time dynamic cross-cast
}

http://coliru.stacked-crooked.com/a/f8f1eecd3236ad78

For more information, see: http://www.informit.com/guides/content.aspx?g=cplusplus&seqNum=179
Topic archived. No new replies allowed.