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.
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
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
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
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.
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 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 …
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;
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)