COMMUNITY

Haxe and Emscripten

haxe-cpp
haxe-js

(david mouton) #1

Hi,
I want to compile my haxe code to wasm via emscripten.
This is the original cpp code :

#include "screencap.h"
#include <emscripten/bind.h>
using namespace emscripten;

EMSCRIPTEN_BINDINGS(my_module) {
  
  class_<CScreenCapt>("ScreenPressor")
          .constructor<int, int>()
          .function("DecompressFrame", &CScreenCapt::DecompressFrame)
          ;
  
}

How can i do the same in haxe ?
Actrually this code doesn’t export my ScreenPressor :confused:

class Main 
{
	static inline var X = 960;
	static inline var Y = 540;
	static var ScreenPressor;
	
	static function main() 
	{
		ScreenPressor = new ScreenPressor(X,Y);
	}
	
}

(david mouton) #2

@andyli @nadako @singmajesty any idea ?


(Philippe) #3

Found that in the mailing list:
https://groups.google.com/forum/#!topic/haxelang/Pcm38LPFjW0


(david mouton) #4

Ahah i found it earlier and i actually compile to wasm. My point here is to expose a cpp function to javascript.
It’s called Embind :
https://kripken.github.io/emscripten-site/docs/porting/connecting_cpp_and_javascript/embind.html

And i don’t know how to use Embind with my Haxe code.


#5

I’d love to keep this thread alive as I was trying, and mostly failing, to do something similar at the weekend.

I’m new to WASM, Emscripten and indeed the wonderful hxcpp. I found it straightforward to generate and run the WASM, but I also found configuring the compiler and exposing methods to Javascript harder, and I have not yet succeeded. Unfortunately I didn’t record my initial attempts fully but I did discover that:

  • You can include the bind.h header file using the @:cppInclude macro (see https://haxe.org/manual/cr-metadata.html)

  • You can probably(?) inject the EMSCRIPTEN_BINDINGS macro using @:cppFileCode

  • You can pass additional parameters to the compiler and linker (so the --bind flag for example) using hxcpp’s Build.xml which is powerful and well documented: https://github.com/HaxeFoundation/hxcpp/tree/master/docs/build_xml

  • The main static method of a WASM app compiled with the emscripten toolchain seems to be automatically exposed to javascript (as Module._main(scope, arguments)) so you can call and pass arguments to it, which, in turn can be accessed from within the main method using Sys.args – this might be sufficient for many use cases.


(david mouton) #6

Thank you @meek.

package hxcpp;

import hxcpp.ScreenPressor;

@:buildXml("
<linker id='exe' exe='emcc'>
    <flag value='--bind' />
    <flag value='-s' />
 <flag value='-O3' />
    <flag value='TOTAL_MEMORY=33554432' />
	<flag value='-s' />
	<flag value='WASM=1' />
</linker>
")
@:cppFileCode('
#include <emscripten/bind.h>
using namespace emscripten;

EMSCRIPTEN_BINDINGS(my_module) {

  class_<hxcpp::ScreenPressor>("ScreenPressor")
          .constructor<int, int>()
          .function("DecompressFrame", &hxcpp::ScreenPressor::DecompressI)
          ;

}
')
class Main
{
	static inline var X = 960;
	static inline var Y = 540;
	static var SP:ScreenPressor;

	static function main()
	{
		SP = new ScreenPressor(X,Y);
		trace('hello');
	}

}

You are right, i used both @:buildXml and @:cppFileCode.
I’m acualy fighting against a cpp error

Error: ./src/hxcpp/Main.cpp:25:63: error: no member named ‘DecompressI’ in ‘hx::ObjectPtrhxcpp::ScreenPressor_obj
.function(“DecompressFrame”, &hxcpp::ScreenPressor::DecompressI)
~~~~~~~~~~~~~~~~~~~~~~^
In file included from ./src/hxcpp/Main.cpp:18:
/home/tamina/projects/emsdk/emscripten/1.38.5/system/include/emscripten/bind.h:433:24: error: no matching constructor for initialization of ‘hx::ObjectPtr< hxcpp::ScreenPressor_obj>’
return new ClassType(std::forward(args)…);
^ ~~~~~~~~~~~~~~~~~~~~~~~~
/home/tamina/projects/emsdk/emscripten/1.38.5/system/include/emscripten/bind.h:1177:28: note: in instantiation of function template specialization ‘emscrip ten::internal::operator_new<hx::ObjectPtrhxcpp::ScreenPressor_obj, int, int>’ requested here
&internal::operator_new<ClassType, ConstructorArgs…>,
^
./src/hxcpp/Main.cpp:24:12: note: in instantiation of function template specialization ‘emscripten::class_<hx::ObjectPtrhxcpp::ScreenPressor_obj, emscrip ten::internal::NoBaseClass>::constructor<int, int>’ requested here
.constructor<int, int>()

I tried to wrap ScreenPressor but it doesn’t work :confused:

struct ScreenPressorWrapper
{
   hxcpp::ScreenPressor obj;

   ScreenPressorWrapper(int width,int height)
   {
      obj = ???; // create an Haxe Instance
   }

   inline void DecompressI(haxe::io::ArrayBufferViewImpl src, haxe::io::ArrayBufferViewImpl dst)
   {
      obj.DecompressI(src, dst);
   }
};

Source code available here :


(Dmitry Hryppa) #7

Hi,
I didn’t tried to compile your sources, but one thing looks wrong as for me.
Inside cppFileCode macro you trying to call DecompressI through ScreenPressor, but I believe it should be ScreenPressor_obj.
So, try something like this:

@:cppFileCode('
#include <emscripten/bind.h>
using namespace emscripten;

EMSCRIPTEN_BINDINGS(my_module) {

  class_<hxcpp::ScreenPressor_obj>("ScreenPressor") // not sure if name inside "..." shold be replaced too
          .constructor<int, int>()
          .function("DecompressFrame", &hxcpp::ScreenPressor_obj::DecompressI)
          ;

}
')

(david mouton) #8

Thank you.
Not the same error, but still an error

Error: In file included from ./src/hxcpp/Main.cpp:18:
/home/tamina/projects/emsdk/emscripten/1.38.5/system/include/emscripten/bind.h:452:13: error: no suitable member ‘operator delete’ in ‘ScreenPressor_obj’
delete ptr;
^
/home/tamina/projects/emsdk/emscripten/1.38.5/system/include/emscripten/bind.h:1126:32: note: in instantiation of function template specialization ‘emscripten::internal::raw_destructorhxcpp::ScreenPressor_obj’ requested here
auto destructor = &raw_destructor;
^
./src/hxcpp/Main.cpp:23:3: note: in instantiation of member function ‘emscripten::class_<hxcpp::ScreenPressor_obj, emscripten::internal::NoBaseClass>::class_’ requested here
class_hxcpp::ScreenPressor_obj(“ScreenPressor”)
^
/home/tamina/haxelib/hxcpp/3,4,188/include/hx/Object.h:190:9: note: member ‘operator delete’ declared here
void operator delete( void *, bool) { }


(Dmitry Hryppa) #9

Now it’s another problem. Haxe generates C++ classes without destructors but looks like Emscripten needs them.
Not sure if this is a solution, but try to put this thing into ScreenPressor.hx:

@:headerClassCode('
    ~ScreenPressor() { //or ScreenPressor_obj, not sure
         //emty destructor
     }
')
class ScreenPressor {
....
}

Or you can try to wrap your ScreenPressor class and implement destructor in wrapper. Something like you did before.


(david mouton) #10

God, it’s too hard for me, i give up.
I tryed to wrap again my haxe class but i couldn’t do a valid instance.

obj = new ::hxcpp::ScreenPressor_obj(width,height);

-> error: no matching constructor for initialization of ‘::hxcpp::ScreenPressor_obj’

OR

obj = ::hxcpp::ScreenPressor_obj::__alloc( HX_CTX ,(int)960,(int)540);

-> Error: ./src/hxcpp/Main.cpp:29:50: error: use of undeclared identifier ‘_hx_ctx’
obj = ::hxcpp::ScreenPressor_obj::__alloc( HX_CTX ,(int)960,(int)540);
^
haxelib/hxcpp/3,4,188/include/hx/StackContext.h:85:19: note: expanded from macro ‘HX_CTX’
#define HX_CTX hx

I openned an issue here : https://github.com/HaxeFoundation/hxcpp/issues/713


Build a .wasm Class using WebAssembly
(Dmitry Hryppa) #11

Hi, maybe you can try to use something like this:

@:cppFileCode('
#include <emscripten/bind.h>
using namespace emscripten;

EMSCRIPTEN_BINDINGS(my_module) {
	class_<hxcpp::Main>("MyWrapper")
	.class_function("decompress", &hxcpp::Main_obj::DecompressI);
}
')
class Main
{
	static var SP:ScreenPressor;

	static function main()
	{
		
	}

	public static function DecompressI(width:Int, height:Int, src:haxe.io.UInt8Array, dst:haxe.io.Int32Array):Void {
		SP = (SP == null) ? new ScreenPressor(width, height) : SP;
		SP.DecompressI(src, dst);
	}
}

At least it is compiles and I’ve got Main.html and Main.js files in build folder.

Not sure what do you want to do next, but I hope it will help you.


(Brian Tiffin) #12

Just as a passing notice, @damoebius;

You you may not get normal linkage or name resolution for inline modifiers. The compiler may never have to lay down a declaration. See if you can drop the inline from inline void DecompressI.

If you ever struggle on with this.

Have good


(david mouton) #13

Thanks, I’ll try this.


(david mouton) #14

It works :star_struck: thanks to you.

I can now interact with my wasm from the html page like this :

Module.MyWrapper.decompress(X,Y,bytes,dst);

It looks like i still have an UnboundTypeError and have to debug my WASM code :sweat_smile:

  1. UnboundTypeError {name: “UnboundTypeError”, message: “Cannot call MyWrapper.decompress due to unbound ty…x9ObjectPtrIN4haxe2io23ArrayBufferViewImpl_objEEE”, stack: “UnboundTypeError: Cannot call MyWrapper.decompress… at http://localhost/bench/haxeasmjs.html:117:30”}

  2. message:“Cannot call MyWrapper.decompress due to unbound types: N2hx9ObjectPtrIN4haxe2io23ArrayBufferViewImpl_objEEE”

  3. name:“UnboundTypeError”

  4. stack:“UnboundTypeError: Cannot call MyWrapper.decompress due to unbound types: N2hx9ObjectPtrIN4haxe2io23ArrayBufferViewImpl_objEEE↵ at UnboundTypeError.<anonymous> (http://localhost/bench/Main.js:1:101612)↵ at new UnboundTypeError (eval at createNamedFunction (http://localhost/bench/Main.js:1:101318), <anonymous>:4:34)↵ at throwUnboundTypeError (http://localhost/bench/Main.js:1:118068)↵ at Function.unboundTypesHandler [as decompress] (http://localhost/bench/Main.js:1:123985)↵ at http://localhost/bench/haxeasmjs.html:117:30

  5. proto:Error

I should be able to manage this but if someone want to have a look on it, all is on github.


(Brian Tiffin) #15

Grand.

Yeah, bumped into the inline issue with simple COBOL to C interfacing. And your problem is Haxe to C++ to Javascript to … how many layers? :thinking: :slight_smile:

Getting as far as you have is a testament to the quality of the toolchain, a tool chain, in this case, with many many links, the weakest still being pretty strong, it seems.

We live in very interesting times.

Have good, make well