How to compile c with different function name with hxcpp

I want to compile some c program as a haxe library In this program they use some “val_int” “val_float” “val_string” fields. This causes some problems as haxe CFFI uses the same names for the fields.

How can I rename the original c program without modifying the c files. I was thinking of using the gcc preprocessor -D flag

(like in this example)

gcc -c foo.c -Dfoo=foo_renamed

but it doesn’t seem to work. Is there a way through Build.xml to do a grep or search and replace before compiling a file ?

Are you wedded to loading the library dynamically at runtime? Given the original C program presumably doesn’t need to know about haxe types I wonder if you can just use externs and regular C library linking rather than the CFFI system

Here’s a working example (haxe 4.2+):
ExampleLib.c

#include <stdio.h>

int add(int a, int b) {
	return a + b;
}

void sayHello(const char* message) {
	printf("Printing from C: %s\n", message);
}

ExampleLib.h

#ifndef ExampleLib_h
#define ExampleLib_h

#ifdef __cplusplus // when our lib is used from a C++ compiler, we need to wrap with extern "C"
extern "C" {
#endif

int add(int a, int b);

void sayHello(const char* message);

#ifdef __cplusplus
}
#endif

#endif

You can write externs to use this lib like so:

ExampleLib.hx

@:include('./ExampleLib.h')
@:sourceFile('./ExampleLib.c')
extern class ExampleLib {

	// we use native because we don't want ExampleLib:: namespace prefix
	@:native('add')
	static function add(a: Int, b: Int): Int;

	@:native('sayHello')
	static function sayHello(message: cpp.ConstCharStar): Void;

}

The @:sourceFile() meta adds the ExampleLib.c file to the hxcpp compilation so it’s compiled along with everything else.

Alternatively, you might want to compile the C code into a native library binary, either static (.a, .lib) or dynamic (.dll, .dylib, .so). One way to do this is to use @:buildXml to add linker flags that link with your static or dynamic library

1 Like

Good Idea. I’ll give it it a try. I’ve never used externs before, that’s a good opportunity to start !

Good luck :), here’s a working example of the snippet above: https://github.com/haxiomic/haxe-c-bridge/files/6394311/hxcpp-c-link.zip

Hxcpp documentation isn’t as good as it could be so at present one of the best ways of learning about possibilities is by checking out the test/ directory in the hxcpp github: hxcpp/test at master · HaxeFoundation/hxcpp · GitHub

Feel free to ask again here if you run into issues or have any questions :slight_smile:

1 Like

How did you guess I would have issues ? :wink:

My Main.hx

class Main{

public static function main() {

	Flite.flite_init();
}

}

My Flite.hx

@:buildXml('
<files id="haxe">  # I put this because https://wighawag.com/blog/2014/12/hxcpp-externs/
<compilerflag value="-Iinclude"/>
# This part is simplified, there are far more files
    <compilerflag value="-Iinclude/"/>
    <file name="flite/src/synth/cst_utt_utils.c"/>
    <file name="flite/src/synth/cst_ffeatures.c"/>
    <file name="flite/src/synth/cst_ssml.c"/>
    <file name="flite/src/synth/flite.c"/>
    <file name="flite/src/synth/cst_voice.c"/>
    <file name="flite/src/synth/cst_phoneset.c"/>
    <file name="flite/src/synth/cst_synth.c"/>

  </files>
<files id="__lib__">
  <compilerflag value="-Iinclude"/>
</files>
')
@:native('flite')  # I did try this and the folllowing separately
@:sourceFile('flite/src/synth/flite.c')
@:extern('flite')
extern class Flite {
	
	@:native('flite_init')
	static function flite_init() :Int;

}

I have this error, I would have thought it has to do with the native metatag as it’s warning about scopes.

./src/Main.cpp: In static member function ‘static void Main_obj::main()’:
./src/Main.cpp:29:15: error: ‘flite_init’ was not declared in this scope
 HXDLIN(   5)  flite_init();
               ^~~~~~~~~~
Error: Build failed

I think you need to replace the @:sourceFile with @:headerInclude('flite.h') or @:include('flite.h'), not sure which one.

Thanks :slight_smile: Indeed ! I thought for some reason that that I had the files in the BuildXml, I didn’t need @:include(‘flite.h’) ( and then forgot about it …)

With it I don’t have this error any more .
( Now it still doesn’t work, but for normal c reasons )

I say this but …

val_string, val_int and val_float still seem to cause problems.

obj/linux64/da3c452f_CFFI.o : Dans la fonction « val_string » :
CFFI.cpp:(.text+0x1950) : définitions multiples de « val_string »
obj/linux64/a7ec4624_cst_val.o:cst_val.c:(.text+0x4d0) : défini pour la première fois ici
obj/linux64/da3c452f_CFFI.o : Dans la fonction « val_int » :
CFFI.cpp:(.text+0x1ac0) : définitions multiples de « val_int »
obj/linux64/a7ec4624_cst_val.o:cst_val.c:(.text+0x390) : défini pour la première fois ici
obj/linux64/da3c452f_CFFI.o : Dans la fonction « val_float » :
CFFI.cpp:(.text+0x1b60) : définitions multiples de « val_float »
obj/linux64/a7ec4624_cst_val.o:cst_val.c:(.text+0x430) : défini pour la première fois ici

Hmm in fact if
I put

  <compilerflag value="-Dval_string=vval_string" />
    <compilerflag value="-Dval_int=vval_int" />
    <compilerflag value="-Dval_float=vval_float" />

in the @:BuildXml

I don’t have this error anymore.

I still have problems as the @BuildXml don’t act normal. But I’ll post about it tomorrow.

I’ve reselbed all BuildXml problems. Now I’ll post about/edit my posts later.

I’ll add some links to some interesting tutorials/guides I found
https://wighawag.com/blog/2014/12/hxcpp-externs/

Thanks for the tips of the tests.

Sounds like all solved?

Something to note (that you have probably figured out now) is that @:include('./path') and @:sourceFile('./path') is a path relative to the current haxe file, whereas paths injected into buildXml are relative to the cpp output path (e.g. bin/ or whatever you assigned). Sometimes you need haxe-file relative paths injected into build.xml and currently the best way to do that is via a macro. Here’s an example.

yes:) And all the most important functions have been working ( Now if I want to externalise the whole thing, I’m sure I will encounter some difficulties)

indeed, I’ve noticed the paths weren’t working and I was putting absolute paths for now. Nice trick for the macro. I think I’ll use it.

I had an interesting error, when I used my library with openfl/lime


/home/consonne/Work/Temp/FliteExtension/src/native/flite/include/cst_alloc.h:47:16: error: expected unqualified-id before numeric constant
 #define FALSE (1==0)
                ^
include/lime/media/OpenALAudioContext.h:47:7: note: in expansion of macro ‘FALSE’
   int FALSE;
       ^~~~~

I resolved it with

   <compilerflag value="-DTRUE=TTRUE" />
    <compilerflag value="-DFALSE=FFALSE" />

It feels like cheating. I wonder how you can have your defines separated .

hmm this is an interesting one! I guess #define FALSE is a bit risky but not that uncommon in C libs. Maybe lime could use inline variables there so the word FALSE is never emitted

I can’t see your solution in the post but one solution could be create an intermediate header for your externs and add #undef FALSE after including the flite header (example)

I copy pasted the xml without putting them in preformated text. Corrected now!
Yes, it seems like another interesting way to do it :slight_smile:

I am able to do some externs for most of my simple ( only normal functions) and event some for structs

But for example, for this file ( a reduced .h file)

typedef struct cst_featvalpair_struct {
    const char *name;
    cst_val *val;
    struct cst_featvalpair_struct *next;
} cst_featvalpair;

typedef struct cst_features_struct {
    struct cst_featvalpair_struct *head;
    cst_alloc_context ctx;
    cst_val *owned_strings; /* fnames that are owned by this struct */

    /* Link to other cst_features that we search too */
    const struct cst_features_struct *linked; 
} cst_features;

/* Constructor functions */
cst_features *new_features(void);
cst_features *new_features_local(cst_alloc_context ctx);
void delete_features(cst_features *f);

/* Accessor functions */
int feat_length(const cst_features *f);

When I try to do something simple like ( not even thinking about const structs)

@:structAccess
@:unreflective
@:include('cst_features.h')
@:extern('cst_features')
@:native('cst_features')
extern class CstFeatures {
	@:native("new cst_features")
	public static function create():Star<CstFeatures>;

	@:native("feat_length")
	public static function feat_length(f:ConstPointer<CstFeatures>):Int;

}


@:include('cst_features.h')
extern class CstFeaturesF {
	@:native("feat_length")
	public static function feat_length(f:ConstPointer<CstFeatures>):Int;
}

If I do

var f = CstFeatures.create();
		trace(""+ CstFeaturesF.feat_length(cpp.Pointer.fromStar(f)));

or
var f = CstFeatures.create();
		trace(""+ CstFeatures.feat_length(cpp.Pointer.fromStar(f)));

I have undefined reference to feat_length(cst_features_struct const*) .

What could be wrong. I think I followed the same logic as the other files …

In fact, in you can put extern static functions only to c files header which have "extern “C” "

I can’t see your solution in the post but one solution could be create an intermediate header for your externs and add #undef FALSE after including the flite header ([example](https://stackoverflow.com/a/25046576))

yeah I know, it’s one year later. But my solution wasn’t a good solution, I have to compile twice each time which bothers me.

I do,'t understand how to create an intermediate header
I tried this but it doesn’t work

//@:include('flite.h')   <-- usually I use this
@:headerInclude('


#undef FALSE
#undef TRUE

#include "flite.h"   <---- It doesn't seem to include it

#undef FALSE
#undef TRUE

')
extern class Flite {
	
	@:native('flite_init')
	static function flite_init() :Int;

I’d guess this could be do to with path resolution

Try placing

#undef FALSE
#undef TRUE

#include "flite.h"   <---- It doesn't seem to include it

#undef FALSE
#undef TRUE

In a header file next to flite.h, named flite-intermediate.h and then include with

@:include('flite.h')

In fact,I explored the files:
There were two files,
one already uses a static inline

But in AL.h , it is compiled as

static int FALSE;
static int TRUE;

so it’s is a problem, as the macro will do

static int (1==0);
static int (0==0);

But I don’t understand why it isn’t haxe that does the inlining, is it a compiler bug ? ( I have haxe 4.2.4)

( PS: thanks your solution works, difficult to put in place as there are so many "entry points ", but I think I’ll post on lime github, as many of these variables are unused)