Use any C++ library in haxe (without writing externs) auto extern gen

I made a python script that you can input a c++ library and it will generate externs for it… it’s not done yet so it doesnt work fully, but ill release the script anyway. Its written in python.

What needs to be done… Make it work with cpp template arguments i dont even know how they work so i will have to do some research.

HOW TO USE:
The python script doesnt actually work on the c++ directly. it works on the xml generated from it when you use the tool “castxml” on it. You just put the xml filename in the script as the variable filename to the name of the xml file.

i feel like haxe is a really cool language and to be able to fully integrate it with cpp so i can write fast stuff to would be really nice without slaving over externs when u can do it automatically theoretically.

step one:
library.cpp —>>> library.xml : with cast xml
step two:
python.py ---->> epic.haxe :done.

Video of showcase

edit: it might not be clear what the script is doing. Every class is trying to imitate something in the c++. For every variable in every c++ class u input, it makes a “feild” class to “Be” it and generate the “haxe” from it.

import os
#os is to generate the file

cppFile = 'library.cpp'

class cppMethod:
    def __init__(self,parent,name,returnType,id):
        self.parent = parent
        self.name = name
        self.typey = "method"
        self.args = []
        self.returnType = returnType
        self.id = id
        self.args = []
    def getPath(self):
        return self.parent.getPath() + "::" + self.name
    

class cppVar:
    def __init__(self,name,type,id,parent):
        self.parent = parent
        self.name = name
        self.type = type
        self.id = id
        self.typey = "var"
    def getPath(self):
        return self.parent.getPath() + "::" + self.name

class cppArg:
    def __init__(self,name,type):
        self.name = name
        self.type = type

class cppClass:
    def __init__(self,parent,name,id) -> None:
        self.stuffInside = []
        self.parent = parent
        self.typey = "class"
        self.name = name
        self.constructorArgs = []
        self.id = id
    def getPath(self):
        return self.parent.getPath() + "::" + self.name

class cppNamespace:
    def __init__(self,parent,name,id) -> None:
        self.parent = parent
        self.name = name
        self.typey = "namespace"
        self.id = id
    def getPath(self):
        return self.parent.getPath() + "::" + self.name

class baseClass:
    def getPath(self):
        return ""
    def __init__(self):
        self.id = "_1"
        self.stuffInside = []































def idToType(id):
    types = doc.getElementsByTagName("FundamentalType")
    for type in types:
        if type.getAttribute("id") == id:
            return type.getAttribute("name")
    types = doc.getElementsByTagName("Typedef")
    for type in types:
        if type.getAttribute("id") == id:
            return type.getAttribute("name")
    types = doc.getElementsByTagName("ReferenceType")
    for type in types:
        if type.getAttribute("id") == id:
            return idToType(type.getAttribute("type"))  

    clazzez = doc.getElementsByTagName("Class")
    for clazz in clazzez:
        if clazz.getAttribute("id") == id:
            return clazz.getAttribute("name")
    clazzez = doc.getElementsByTagName("Struct")
    for clazz in clazzez:
        if clazz.getAttribute("id") == id:
            return clazz.getAttribute("name")
    return "Not found"
    

from xml.dom import minidom
doc = minidom.parse("library.xml")

namespaces = doc.getElementsByTagName("Namespace")
fields = doc.getElementsByTagName("Field")
destructors = doc.getElementsByTagName("Destructor")
constructors = doc.getElementsByTagName("Constructor")
classes = doc.getElementsByTagName("Class")
structs = doc.getElementsByTagName("Struct")
methods = doc.getElementsByTagName("Method")
base = baseClass()
namespaceObjects = [base]
FieldObjects = []
deconstructorObjects = []
constructorObjects = []
classObjects = []
methodObjects = []

def getObjectById(id):
    for namespace in namespaceObjects:
        if(namespace.id == id):
            return namespace
    for clazz in classObjects:
        if(clazz.id == id):
            return clazz
    for Field in FieldObjects:
        if(Field.id == id):
            return Field
    for constructor in constructorObjects:
        if(constructor.id == id):
            return constructor
    for deconstructor in deconstructorObjects:
        if(deconstructor.id == id):
            return deconstructor
    return "Not found"



output = ""



percentDone = 0
for namespace in namespaces:

    newNamespace = cppNamespace(getObjectById(namespace.getAttribute("context")),namespace.getAttribute("name"),namespace.getAttribute("id"))
    if namespace.getAttribute("id") != "_1":
            namespaceObjects.append(newNamespace)
    percentDone += 100/len(namespaces)
    os.system("clear")
    print("namespaces: " + str(percentDone) + "%")
    
percentDone = 0
for clazz in classes:
    newClass = cppClass(getObjectById(clazz.getAttribute("context")),clazz.getAttribute("name"),clazz.getAttribute("id"))
    classObjects.append(newClass)
    percentDone += 100/len(classes)
    os.system("clear")
    print("classes: " + str(percentDone) + "%")

percentDone = 0
for clazz in structs:
    newClass = cppClass(getObjectById(clazz.getAttribute("context")),clazz.getAttribute("name"),clazz.getAttribute("id"))
    if(newClass.name != ""):
        classObjects.append(newClass)
    percentDone += 100/len(structs)
    os.system("clear")
    print("structs: " + str(percentDone) + "%")

percentDone = 0
for method in methods:
    if method.getAttribute("name") != "" and getObjectById(method.getAttribute("context")) != "Not found" and method.getAttribute("access") == "public":
        newMethod = cppMethod(getObjectById(method.getAttribute("context")),method.getAttribute("name"),idToType(method.getAttribute("returns")),method.getAttribute("id"))
        methodObjects.append(newMethod)
        newMethod.parent.stuffInside.append(newMethod)
        for arg in method.getElementsByTagName("Argument"):
            
            newArg = cppArg(arg.getAttribute("name"),idToType(arg.getAttribute("type")))
            newMethod.args.append(newArg)
    percentDone += 100/len(methods)
    os.system("clear")
    print("methods: " + str(percentDone) + "%")
percentDone = 0
for field in fields:
    
    if field.getAttribute("name") != "" and field.getAttribute("access") == "public" and idToType(field.getAttribute("type")) != "Not found" or field.getAttribute("context") != "_1" and type( getObjectById(field.getAttribute("context"))) != cppNamespace:
        newField = cppVar(field.getAttribute("name"),idToType(field.getAttribute("type")),field.getAttribute("id"),getObjectById(field.getAttribute("context")))
        FieldObjects.append(newField)
        if newField.type != "Not found" and newField.parent != "Not found":
            newField.parent.stuffInside.append(newField)
    percentDone += 100/len(fields)
    os.system("clear")
    print("fields: " + str(percentDone) + "%")
percentDone = 0
for constructor in constructors:
    if not constructor.getAttribute("artificial") and len(constructor.getElementsByTagName("Argument")) > 0 and idToType(constructor.getAttribute("context")) != "Not found":
        for arg in constructor.getElementsByTagName("Argument"):
            
            newArg = cppArg(arg.getAttribute("name"),idToType(arg.getAttribute("type")))
            getObjectById(constructor.getAttribute("context")).constructorArgs.append(newArg)
    percentDone += 100/len(constructors)
    os.system("clear")
    print("constructors: " + str(percentDone) + "%")

    

def ctoh(string):
    if string == "int":
        string == "Int"



    string = string.capitalize()
    if string[0] == "_":
        string = "H" + string
    return string

def classToHaxeExtern(classOb):
    output = '''
@:include("''' + cppFile + '''")
@:structAccess()
\n@:native("''' + classOb.getPath() + '''")
    '''
    output += "extern class " + ctoh(classOb.name) + "{\n"
    
    argList = ""
    for arg in classOb.constructorArgs:
        argList += arg.name + ":" + ctoh(arg.type)
    output += '\t\t@:native("' + classOb.getPath() + '") static public function create(' + argList + '):' + ctoh(classOb.name) + ';\n'
    
    for thing in classOb.stuffInside:
        if thing.typey == "method":
            argList = ""
            for arg in thing.args:
                argList += arg.name + ":" + ctoh(arg.type)
            output += '\t\t@:native("' + thing.name +  '") public function ' + thing.name + '('+ argList +'):' + ctoh(thing.returnType) + ';\n'
        if thing.typey == "var":
            output += '\t\t@:native("' + thing.name + '") var ' + thing.name + ':'+ ctoh(thing.type) + ';\n'

    output += "}\n\n\n\n\n\n"
    return output
#TODO: add class function args into output. Get a life lol!


file = open("Epic.hx","w")
percentDone = 0
for a in classObjects:

    output += classToHaxeExtern(a)
    percentDone += 100/len(classObjects)
    os.system("clear")
    print("Writing Haxe: " + str(percentDone) + "%")
file.write(output) 

 
file.close() 
2 Likes

Hi Trevor, welcome to Haxe! Interesting project. :+1: I work in JS/HTML but like to poke at cpp. I made some comments on your video.

Cheers!
-Jeff

Oh… the “it doesnt do inheritance because that shit is gay” was removed. That was my start quote. :smiley:

Instead of templated c++ classes, i would probably advise that your script handles pointers, these are kinda important in c++ libs and used all over the shop. In fact, i would argue that any haxe → cpp lib needs to think (quite seriously) about pointers… … … … …

coz that shit aint gay bro its standardized c++ memory access :sweat_smile:

Ian