Need some technical help an object oriented wrapper I am creating for bindbc.sfml

thebluepandabear therealbluepandabear at protonmail.com
Tue Jan 24 03:42:34 UTC 2023


Hello everyone 👋, hope everyone is having a good day.

Hopefully I am allowed to ask technical questions here, if not 
please tell me and I will remove this.

I am trying to create object oriented wrappers around 
`bindbc.sfml`, this is because I don't like the C-style syntax of 
CSFML.

The C-style syntax is not right -- in my opinion -- for an object 
oriented language. Dealing with pointers all the time is also 
unsafe.

This is not to say that CSFML isn't good -- it's great, and I've 
made some apps using `bindbc-sfml`. I just want to extend it to 
my liking with object oriented wrappers that can more closely 
match the C++ SFML syntax.

For the wrappers, I created a `Shape` class. This `Shape` class 
is seen in the original C++ SFML implementation:

```D
class Shape : Transformable, Drawable {
     void setTexture(sfTexture* texture, bool resetRect) {
         ptr.sfShape_setTexture(texture, resetRect);
     }

     void setTextureRect(IntRect rect) {
         ptr.sfShape_setTextureRect(rect.to_sfIntRect());
     }

     void setFillColor(Color color) {
         ptr.sfShape_setFillColor(color.to_sfColor());
     }

     void setOutlineColor(Color color) {
         ptr.sfShape_setOutlineColor(color.to_sfColor());
     }

     void setOutlineThickness(float thickness) {
         ptr.sfShape_setOutlineThickness(thickness);
     }

     const(sfTexture)* getTexture() {
         return ptr.sfShape_getTexture();
     }

     IntRect getTextureRect() {
         return ptr.sfShape_getTextureRect().toIntRect();
     }

     Color getFillColor() {
         return ptr.sfShape_getFillColor().toColor();
     }

     Color getOutlineColor() {
         return ptr.sfShape_getOutlineColor().toColor();
     }

     float getOutlineThickness() {
         return ptr.sfShape_getOutlineThickness();
     }

     size_t getPointCount() nothrow {
         return ptr.sfShape_getPointCount();
     }

     Vector2f getPoint(size_t index) nothrow {
         return ptr.sfShape_getPoint(index).toVector2f_noThrow();
     }

     FloatRect getLocalBounds() {
         return ptr.sfShape_getLocalBounds().toFloatRect();
     }

     FloatRect getGlobalBounds() {
         return ptr.sfShape_getGlobalBounds().toFloatRect();
     }

     private sfShape* ptr;
}
```

The `sfShape` pointer isn't currently initialized, I'll get to 
that issue soon.

As you can see, `Shape` extends the `Transformable` class and the 
`Drawable` interface. This again roughly matches what's seen in 
SFML. SFML.NET also did a similar wrapper for their CSFML C# 
bindings. What's great about SFML.NET is that you don't even know 
that you're using CSFML, this is because it feels just like C++ 
SFML.

Now, I will create a `RectangleShape` which will be a subclass of 
the `Shape` class:

(Btw I took a lot of inspiration from SFML.NET when it comes to 
these wrappers.)

```D
class RectangleShape : Shape {
     this(Vector2f size) {
         _size = size;
         setSize(_size);
     }

     Vector2f getSize() {
         return _size;
     }

     void setSize(Vector2f size) {
         _size = size;
     }

     override {
         size_t getPointCount() {
             return 4;
         }

         Vector2f getPoint(size_t index) {
             final switch (index) {
                 case 0:
                     return Vector2f(0, 0);
                 case 1:
                     return Vector2f(_size.x, 0);
                 case 2:
                     return Vector2f(_size.x, _size.y);
                 case 3:
                     return Vector2f(0, _size.y);
             }
         }
     }

     private Vector2f _size;
}
```

As you can see, the `Rectangle` class only overrides the 
`getPointCount` and `getPoint` methods.

**These are the methods that the superclass - `Shape` - will use 
to construct the shape object for it to actually be drawable.**

Now, let us add the following code to the `Shape` class so that 
we can construct a `Shape` via these two methods, which we assume 
that the child provides us a good implementation for:

```D
class Shape : Transformable, Drawable {
     this() {
         ptr = sfShape_create(&getPointCount, &getPoint, 
cast(void*)this);
     }

     extern(C) private static ulong getPointCount(void* data) 
nothrow {
         return (cast(Shape)data).getPointCount();
     }

     extern(C) private static sfVector2f getPoint(size_t index, 
void* data) nothrow {
         return 
(cast(Shape)data).getPoint(index).to_sfVector2f_noThrow();
     }
```

I hear you asking, what's going on here?

We are providing two callbacks to the `getPointCount` and 
`getPoint` methods via function pointers, and we're passing in 
the current object to the `data` `void*` pointer. It's kind of 
hard to understand, but if you read through it carefully you 
should get a rough idea of what's going on.

Now, when we create a new instance of `Rectangle`, I will assume 
that the constructor will be called, the `sf_shape` ptr will be 
initialized correctly (as it will be utilizing the crucial 
`getPoint` and `getPointCount` methods) and everything will be OK.

This is the following test code I had:

```D
void main() {
	loadSFML();

	RectangleShape rectangleShape = new RectangleShape(Vector2f(50, 
50));
	rectangleShape.setPosition(Vector2f(50, 50));
	rectangleShape.setFillColor(Color.Blue);

	RenderWindow renderWindow = new RenderWindow(sfVideoMode(500, 
500), "Tests", sfWindowStyle.sfDefaultStyle, null);
	sfEvent event;

	while (renderWindow.isOpen()) {
		while (renderWindow.pollEvent(&event)) {
			if (event.type == sfEventType.sfEvtClosed) {
				renderWindow.close();
			}
		}

		renderWindow.clear(Color.Yellow);
		renderWindow.ptr.sfRenderWindow_drawShape(rectangleShape.ptr, 
null);
		renderWindow.display();
	}
}
```

I would read through this line by line to get a good idea of 
what's going on.

Really, for demonstration purposes, we're using the 
`renderWindow`'s `ptr` variable for drawing. When I can get this 
to work I will create wrapper functions so that it's nicer to 
use, but for now it's not important.

What I'd expect to pop up on screen is a 50x50 rectangle, filled 
with a blue color, at the position 50x50 on the screen.

Upon running the application, I don't see anything -- it's just a 
yellow screen.

I am very confused why this is the case, it seems like I've done 
everything fine, but I've obviously made a mistake somewhere in 
my implementation. I don't know specifically if it's an issue on 
my end, or a bug in `bindbc-sfml`, but this issue has infuriated 
me for days, because I am not getting what I expected to show up 
on screen :(

Any help with this would be greatly appreciated,

Regards,
thebluepandabear


More information about the Digitalmars-d-learn mailing list