Welcome, Guest. Please login or register. Did you miss your activation email?

Author Topic: Problems with void pointer and sf::Transformable [Solved]  (Read 1852 times)

0 Members and 1 Guest are viewing this topic.

MrSnack

  • Newbie
  • *
  • Posts: 3
    • View Profile
Problems with void pointer and sf::Transformable [Solved]
« on: May 29, 2017, 08:27:18 pm »
Hello,

I'm programming an ECS in C++ and wanted to integrate SFML into it.

I save my components invoid pointer and save the corrisponding class information with it.
When I cast the void pointer backt to sf::Transformable, my calls will be mostly ignored(?) or simpy have no impact on the object.

I have created an example outside my ECS to illustrate the issue:
Code: [Select]
#include <SFML/Graphics.hpp>

int main() {
{
sf::CircleShape* cs = new sf::CircleShape(50);
cs->setFillColor(sf::Color::Green);

void* t = cs;
void* d = cs;


sf::RenderWindow window(sf::VideoMode(640, 640), "SFML Test");

// game loop
while (window.isOpen()) {
// events
sf::Event event;
while (window.pollEvent(event))
{
switch (event.type)
{
case sf::Event::Closed:
window.close();
break;

default:
break;
}
}
static_cast<sf::Transformable*>(t)->move(0.1, 0.1);

window.clear();
window.draw(*static_cast<sf::Drawable*>(d));
window.display();

}
delete cs;
}
return 0;
}

When this code is executed, the circle will only move once.

When I replace the void* t = cs; with sf::Transformable* t = cs;, everything works just fine and as expected (The circle moves offscreen.).
I'm curious if I'm missing something specific inside SFML, because the same approach works with other classes and examples.
So why won't the circle move in each iteration?

Normally I would ask this in a Cpp only forum, but I can't replicate the problem in other libraries or construct a similar example.

I'm using Windows 10 and Visual Studio 2015.
« Last Edit: May 29, 2017, 09:10:25 pm by MrSnack »

eXpl0it3r

  • SFML Team
  • Hero Member
  • *****
  • Posts: 11034
    • View Profile
    • development blog
    • Email
Re: Problems with void pointer and sf::Transformable
« Reply #1 on: May 29, 2017, 08:38:03 pm »
While CircleShape is a Transformable it's not the original class and as such, I don't think you can just use a static_cast. I might be wrong on this, but I think you can only (more or less safely) cast from void* to object* if it's the original object, otherwise you have to use dynamic_cast.

Generally, you should not be using raw nor void pointers and manual memory management. Instead rely on safe alternatives like unique_ptr and rather think about building your ECS with templates and template specialization, etc.
Official FAQ: https://www.sfml-dev.org/faq.php
Official Discord Server: https://discord.gg/nr4X7Fh
——————————————————————
Dev Blog: https://duerrenberger.dev/blog/

MrSnack

  • Newbie
  • *
  • Posts: 3
    • View Profile
Re: Problems with void pointer and sf::Transformable
« Reply #2 on: May 29, 2017, 08:49:22 pm »
Thanks for the answer.
Sadly dynamic_cast only works for polymorphic types.

I wanted to prodcue a minimal example, without the memory overhead.
Also I would love to use unique_pointers, but the multi-inheritance of sf::Sprite makes this really hard.
I'm still using shared_ptr and templates.

I use the void pointer for storage only and I hide them behind an interface.
I do this, because I don't want to introduce an empty clas for the component.


I'm not sure if I understood you correctly, but the right function should be called:
Code: [Select]
#include <iostream>

class Base {
public:
virtual void foo() {
std::cout << "Foo base" << std::endl;
}
};

class Derived : Base {
public:
void foo() {
std::cout << "Foo derived" << std::endl;
}
};


int main(){


auto ptr = new Derived();
void* vp = ptr;
static_cast<Base*>(vp)->foo();

delete ptr;

std::getchar(); // Windows user

return 0;
}

Will output: "Foo derived"

Laurent

  • Administrator
  • Hero Member
  • *****
  • Posts: 32498
    • View Profile
    • SFML's website
    • Email
Re: Problems with void pointer and sf::Transformable
« Reply #3 on: May 29, 2017, 08:52:21 pm »
sf::CircleShape inherits sf::Shape, which itself inherits sf::Drawable and sf::Transformable. So, a sf::CircleShape* variable will probably point to a location in memory where you have some sf::Drawable data followed by some sf::Transformable data, followed by some sf::Shape data and finally the sf::CircleShape members. By casting it to void* and then to sf::Transformable*, you're basically trying to interpret the sf::Drawable data as sf::Transformable.

If there was not the sf::Drawable base class in first position, what you do would probably work (some libraries such as Qt rely on this kind of tricks...), but I wouldn't do that anyway.

Note that this is just a pragmatic view on what's probably happening, but depending on what the C++ standard says, the compiler might have some freedom to organize data differently in memory.

Conclusion: don't try to trick the compiler, try to keep type safety as much as possible, and don't rely on non-standard/undocumented behaviour :)
« Last Edit: May 29, 2017, 08:55:33 pm by Laurent »
Laurent Gomila - SFML developer

eXpl0it3r

  • SFML Team
  • Hero Member
  • *****
  • Posts: 11034
    • View Profile
    • development blog
    • Email
Re: Problems with void pointer and sf::Transformable
« Reply #4 on: May 29, 2017, 09:02:22 pm »
The basic example works but generally static casting void to base is not safe, see these SO answers: https://stackoverflow.com/questions/19614322/static-cast-from-derived-to-void-to-base

As Laurent mentioned, in SFML's case, CircleShape is derived multiple times. A CircleShape is a Shape which is a Transformable and a Drawable, so you'll most likely end up with a different class layout, so you can't just static cast.

I personally advise you to use your own types and just use SFML classes as member variables. That way you have full control over the ECS types.
Additionally, if you have to use void* your design is most likely flawed and I'm sure there are better alternatives.
« Last Edit: May 29, 2017, 09:04:11 pm by eXpl0it3r »
Official FAQ: https://www.sfml-dev.org/faq.php
Official Discord Server: https://discord.gg/nr4X7Fh
——————————————————————
Dev Blog: https://duerrenberger.dev/blog/

MrSnack

  • Newbie
  • *
  • Posts: 3
    • View Profile
Re: Problems with void pointer and sf::Transformable
« Reply #5 on: May 29, 2017, 09:04:11 pm »
Not only a pracmatic, but the correct view.
Better Google usage would have eased my search: https://stackoverflow.com/questions/2379427/multiple-inheritance-unexpected-result-after-cast-from-void-to-2nd-base-class

Thanks and sorry for my bad research.
I guess I have to scrap the void pointer and come up with a better (typesafe) storage.
Most likely the member variable approach.

Always knew, I couldn't trust thoose darn void pointers.
Seemed too easy...

Sidenote: I really like the multi inheritance idea (no really I think it's rather clever), but also curse it in many situations.

Laurent

  • Administrator
  • Hero Member
  • *****
  • Posts: 32498
    • View Profile
    • SFML's website
    • Email
Re: Problems with void pointer and sf::Transformable [Solved]
« Reply #6 on: May 30, 2017, 07:41:04 am »
Note that you can easily work around your problem by actually ensuring that your void* points to the sf::Transformable part of your CircleShape:

void* t = static_cast<sf::Transformable*>(cs);

This can work if you manage to ensure type safety by other means, but... yes there are probably simpler / better solutions :)
Laurent Gomila - SFML developer

 

anything