dmcblue
dmcblue

Reputation: 363

How to use a C/C++ library (like NCurses) in Haxe

I have a cli written in Haxe and compiled to a binary via C++ (hxcpp). I would like to use ncurses in it. I have worked with ncurses in C and I've worked with JS externs in Haxe but I'm can't figure out the Haxe/C++ documentation to connection the two together.

I haven't used much more of the HXCPP compiler than the basic haxe command (ie not build files etc), à la:

haxe -lib somelib -cp src --cpp bin/cpp path.to.Main

Basically, I can work with all custom code, but am struggling on externals. So I'm not entirely sure how many steps I am missing towards my goal. But I can see a few major obstacles.

  1. How to include ncurses in the build? ie. cc -lncurses -o out [etc...] in a Makefile.
  2. How to include the proper externs to allow Haxe to compile at all without error. All the extern examples I see involve a class/namespace but NCurses functions don't have a class or namespace. I haven't found any documentation for a bare extern function.
  3. Then of course the basics of including the headers properly (or the haxe compilation equivalent) in my actual Haxe code.

I know this is basically asking for a mini-tutorial, but I can't find examples or documentation that I can put together to accomplish this specific goal.

Thanks for any help you can lend.

Upvotes: 4

Views: 1381

Answers (1)

Giuppe
Giuppe

Reputation: 439

HXCPP uses an xml-based build system. When you launch haxe -cp src --cpp bin/cpp path.to.Main:

  1. Haxe files are transpiled to C++ and a Build.xml is produced in the output directory, i.e. bin/cpp/Build.xml;
  2. everything is then built by HXCPP, merging the newly generated project Build.xml with the global default xml definitions and then calling the compiler toolchain(s).

You can inject compiler flags, libraries to link, includes directories, etc., through the @:buildXml metadata, as described on the manual:

@:buildXml("
    <target id='haxe'>
       <lib name='-lncurses' if='linux'/>
       <lib name='ncurses.lib' if='windows'/>
    </target>
")
class Main{ ... 

These tags will be appended to the project Build.xml. The haxe target is the default target. Keep in mind that every toolchain (MSVC, gcc, Xcode, etc.) has its own syntax. You can see examples in the build.xml of cross-platform low-level projects like Systools or Lime.

You can add -D HXCPP_VERBOSE to the haxe command line to see which commands are actually launched: haxe -D HXCPP_VERBOSE -cp src --cpp bin/cpp path.to.Main.

As for externs, the simpler case is:

  1. you write your C++ code, with #includes and everything needed, inside a @:cppFileCode() block; everything you write here is pasted as-is in the generated cpp file;
  2. you mark one of your haxe functions, defined on one of the haxe classes, as @:native("nameOfTheCppFunction"), and the build system will join them together.
    @:cppFileCode("
        #include <ncurses.h>

        void nativeCppTest(){
             /* here goes your ncurses code */
             return;
        }
    ")

    class Main{
        public static function main()
        {
            myCppTest();
        }

        @:native("nativeCppTest")
        extern static function myCppTest():Void;
    }

If you open the generated file (in this case bin/cpp/src/Main.cpp) you'll see that the haxe myCppTest() call is changed to its native version nativeCppTest().

If you would like to pass function arguments, and receive return values, you'll have to wrap those using the cpp.* standard library types. For example:

    @:cppFileCode("
        #include <ncurses.h>

        void nativeCppTest(const char* myString){
             /* here goes your ncurses code */
             return;
        }
    ")

    class Main{
        public static function main()
        {
            myCppTest("print this");
        }

        @:native("nativeCppTest")
        extern static function myCppTest(myString:cpp.ConstCharStar):Void;
    }

Some of the conversions will be automatic (like, in this case, from a constant string to ConstCharStar), some will require an explicit cast; if you need to pass pointers to the C++ code, you can get it via cpp.RawConstPointer.addressOf(<haxe object>) (or RawPointer if not const):

    public static function main()
    {
        var myString:String = "print this";
        myCppTest(cast cpp.RawConstPointer.addressOf(myString));
    }

Useful references:

Upvotes: 2

Related Questions