How to represent a c struct in hxcpp externs?

345 views
Skip to first unread message

Rafael Oliveira

unread,
Feb 16, 2017, 6:09:50 PM2/16/17
to Haxe
I'm making my first hxcpp extern, it's for a c library (raylib), thanks to hxcpp guide I passed the build.xml stage, now I'm making the code. 
My doubt is about structs, I searched in the code of some hxcpp externs, and create something like this:

original c code:

typedef struct Rectangle {
    int x;
    int y;
    int width;
    int height;
} Rectangle;

haxe extern:

@:structAccess
@:native("Rectangle")
extern class Rectangle {
    public var x:Int;
    public var y:Int;
    public var width:Int;
    public var height:Int;
}

It works if I receive the struct from a function, but I don't know how to initialize one. Without the new keyword Haxe will complain the variable was not initialized, and I can't put the new method.
Also, in the raylib examples the struct is initialized in this format, which I didnt saw before in c:

Rectangle player = { 400, 280, 40, 40 };

Could this be possible in Haxe?

This is the reference of the struct and the example:

Hugh

unread,
Feb 17, 2017, 12:17:52 AM2/17/17
to Haxe

@:structAccess
@:native("Rectangle")
extern class Rectangle {
   
public var x:Int;
   
public var y:Int;
   
public var width:Int;
   
public var height:Int;
     

   
@:native("RectanglePrint")
   
public static function print(rect:Rectangle) : Void;

   
@:native("RectangleMake")
   
public static function make(x:Int, y:Int, w:Int, h:Int) : Rectangle;

   
@:native("RectangleWrap")
   
public static function createWrapped(r:Rectangle) : WrappedRectangle;

   
inline public function wrap() : WrappedRectangle
       
return createWrapped(this);
}

// Not structAccess

@:native("cpp::Struct<Rectangle>")
extern class WrappedRectangle extends Rectangle { }

@:headerCode('
#include <stdio.h>


typedef struct Rectangle {
    int x;
    int y;
    int width;
    int height;
} Rectangle;

inline Rectangle RectangleMake(int x, int y, int w, int h) { return {x,y,w,h}; }
inline cpp::Struct<Rectangle> RectangleWrap(const Rectangle &rect) { return rect; }
inline void RectanglePrint(const Rectangle &rect) { printf("rect %d,%d %dx%d\\n", rect.x, rect.y, rect.width, rect.height ); }

'
)
class Test
{
   
public static function main()
   
{
     
var r:Rectangle = Rectangle.make(1,2,3,4);
      trace
("Width " + r.width);
     
Rectangle.print(r);
     
// Dynamic will not work on 'raw' Struct - must be wrapped:
     
//trace("Rect " + r);
     
var w = r.wrap();
      trace
("Rect " + w);
      trace
("Width " + w.width);
     
// Can still pass wrapped off to something that wants a 'Rectangle' since it inherits
     
Rectangle.print(w);
   
}
}



I've use the "headerCode" meta to allow testing from a single file, but you would typically put this stuff in a separate file.

So the trick is to make a native static function to so the work.  This allows haxe to do type checking etc.  you could also make an "Rectangle.empty" extern function.  If you are using c++, you can make a "operator Rectangle = null" function, and use "var x:Rectangle = null;"  But with type inference, "var x=Rectangle.empty()" is only marginally longer, but more descriptive.

If you are doing this, you are bound to run into the problem of passing these native rectangles via Dynamic as some stage (eg, as member variables).  The solution here is to wrap the Rectangle with the native "cpp::Struct" template, like in the example.  You can then differentiate between wrapped and native rectangles.  You can generally use a wrapped rectangle in all the places you would use the normal rectangle, since it auto-casts and if use the inheritance pattern above.

You could be tricky and use abstracts to remove the need for "wrap()" call, and possibly allow "{1,2,3,4}" syntax via macros, but I would get the basics going first.

Rafael Oliveira

unread,
Feb 17, 2017, 1:12:45 PM2/17/17
to Haxe
Hi, thanks for the help.

I had to use this way:

@:headerCode('
   #include <stdio.h>
   #include "raylib.h"
           
    Rectangle RectangleMake(int x, int y, int width, int height);
')
@:cppFileCode('  
   Rectangle RectangleMake(int x, int y, int width, int height) { return { x, y, width, height }; }
')


otherwise the compiler will complain:
aea44ed0_Main.obj : error LNK2005: "struct Rectangle __cdecl RectangleMake(int,int,int,int)" (?RectangleMake@@YA?AURectangle@@HHHH@Z) already defined in 36b76f43_RaylibHx.obj

RaylibHx is the class where is defined all the c methods.

Now is compiling, but I having a problem testing some code. 

var player = Rectangle.make(400, 280, 40, 40);

while (!windowShouldClose())
{
    if (isKeyDown(KEY_RIGHT))
        player.x += 2;
    else if (isKeyDown(KEY_LEFT))
        player.x -= 2;
}

is generating this:

HXLINE(  18)    ::Rectangle player = RectangleMake((int)400,(int)280,(int)40,(int)40);
HXLINE(  20)    while(!(WindowShouldClose())){
HXLINE(  22)        if (IsKeyDown((int)262)) {
HXLINE(  23)            ::Rectangle player1 = player;
HXDLIN(  23)            player1.x = (player1.x + (int)2);
                    }
                    else {
HXLINE(  24)            if (IsKeyDown((int)263)) {
HXLINE(  25)                ::Rectangle player2 = player;
HXDLIN(  25)                player2.x = (player2.x - (int)2);
                        }
                    }
                }

the code is creating a new instance of Rectangle and this is not updating player.

if I change the cpp code manually removing the new rectangle and compile with the build.xml the code will work.

HXLINE(  22)    if (IsKeyDown((int)262)) {
HXDLIN(  23)        player.x = (player.x + (int)2);
                }
                else {
HXLINE(  24)        if (IsKeyDown((int)263)) {
HXDLIN(  25)            player.x = (player.x - (int)2);
                    }
                }

So, how to prevent the compiler to create new instances of the struct?
Reply all
Reply to author
Forward
0 new messages