Search code examples
swiftpointersopenglglutfreeglut

Freeglut doesn't initialize when using it from Swift


I've tried to use the Freeglut library in a Swift 4 Project. When the

void glutInit(int *argcp, char **argv);

function is shifted to Swift, its declaration is

func glutInit(_ pargc: UnsafeMutablePointer<Int32>!, _ argv: UnsafeMutablePointer<UnsafeMutablePointer<Int8>?>!)

Since I don't need the real arguments from the command line I want to make up the two arguments. I tried to define **argv in the Bridging-Header.h file

#include <OpenGL/gl.h>
#include <GL/glut.h>

char ** argv[1] = {"t"};

and use them in main.swift

func main() {
    var argcp: Int32 = 1
    glutInit(&argcp, argv!)  // EXC_BAD_ACCESS
    glutInitDisplayMode(UInt32(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH));
    glutCreateWindow("my project")
    glutDisplayFunc(display)
    initOpenGL()
    glutMainLoop()

but with that I get Thread 1: EXC_BAD_ACCESS (code=1, address=0x74) at the line with glutInit().

How can I initialize glut properly? How can I get an UnsafeMutablePointer<UnsafeMutablePointer<Int8>?>! so that it works?


Solution

  • The reason the right code in C char * argv[1] = {"t"}; does not work is because Swift imports fixed size C-array as a tuple, not a pointer to the first element.

    But your char ** argv[1] = {"t"}; is completely wrong. Each Element of argv needs to be char **, but you assign char * ("t"). Xcode must have shown you a warning at first build:

    warning: incompatible pointer types initializing 'char **' with an expression of type 'char [2]'

    You should better take incompatible pointer types warning as error, unless you know what you are doing completely.

    Generally, you should better not write some codes generating actual code/data like char * argv[1] = {"t"}; in a header file.


    You can try it with Swift code.

    As you know, when you want to pass a pointer to single element T, you declare a var of type T and pass &varName to the function you call.

    As argcp in your code.

    As well, when you want to pass a pointer to multiple element T, you declare a var of type [T] (Array<T>) and pass &arrName to the function you call.

    (Ignoring immutable case to simplify.)

    The parameter argv matches this case, where T == UnsafeMutablePointer<Int8>?.

    So declare a var of type [UnsafeMutablePointer<Int8>?].

    func main() {
        var argc: Int32 = 1
        var argv: [UnsafeMutablePointer<Int8>?] = [
            strdup("t")
        ]
        defer { argv.forEach{free($0)} }
    
        glutInit(&argc, &argv)
        //...
    }
    

    But I wonder if you really want to pass something to glutInit().

    You can try something like this:

    func main() {
        var argc: Int32 = 0 //<- 0
    
        glutInit(&argc, nil)
        //...
    }
    

    I'm not sure if freeglut accept this, but you can find some articles on the web saying that this works in some implementation of Glut.