Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Generate bindings via cl-autowrap #12

Open
nagy opened this issue Jul 28, 2021 · 15 comments
Open

Generate bindings via cl-autowrap #12

nagy opened this issue Jul 28, 2021 · 15 comments

Comments

@nagy
Copy link
Contributor

nagy commented Jul 28, 2021

It seems like the raylib.lisp file is a rather manually written binding file to raylib.
Would it make to you to generate these via https://github.com/rpav/cl-autowrap ?

If you want I can draft something out.

@longlene
Copy link
Owner

Thanks, will try it later.

@compufox
Copy link
Contributor

@nagy could also try doing something similar with https://github.com/borodust/claw

@longlene
Copy link
Owner

longlene commented Aug 6, 2021

Thanks all for the recommendation.
I've read some code of both cl-autowrap and claw.
I think the method of the code generation is not the key point of this project now, write manually or generate automatically are both ok to me.

The key issue is that raylib use both struct and pointer to the same struct at the same time, some API can not work at all.
I've got stuck here for a long time.

@nagy
Copy link
Contributor Author

nagy commented Aug 15, 2021

Thank you @longlene for the investigation.

The key issue is that raylib use both struct and pointer to the same struct at the same time, some API can not work at all.

Do you have an example of such a case?

@longlene
Copy link
Owner

@nagy
RLAPI void SetCameraMode(Camera camera, int mode);
RLAPI void UpdateCamera(Camera *camera);
For these two API, I can't find a way to write binding for them.

@nagy
Copy link
Contributor Author

nagy commented Sep 26, 2021

I was investigating this a bit more and found the following. When I create a test file
raylib-test.c like so:

typedef struct Vector3 {
    float x;
    float y;
    float z;
} Vector3;

typedef struct Camera3D {
    Vector3 position;
    Vector3 target;
    Vector3 up;
    float fovy;
    int projection;
} Camera3D;

typedef Camera3D Camera;

void SetCameraMode(Camera camera, int mode);
void UpdateCamera(Camera *camera);

I can use the tool from https://github.com/rpav/c2ffi to generate something that comes close to
actual bindings. In case you are using the nix package manager, you can directly invoke it like so:

$ nix-shell -p c2ffi --run "c2ffi raylib-test.c -D sexp -N cl-raylib"
(in-package :cl-raylib)

;; raylib-test.c:1:16
(struct Vector3
  (x :float)
  (y :float)
  (z :float))

;; raylib-test.c:5:3
(typedef Vector3 (:struct Vector3))

;; raylib-test.c:7:16
(struct Camera3D
  (position Vector3)
  (target Vector3)
  (up Vector3)
  (fovy :float)
  (projection :int))

;; raylib-test.c:13:3
(typedef Camera3D (:struct Camera3D))

;; raylib-test.c:15:18
(typedef Camera Camera3D)

;; raylib-test.c:17:6
(function "SetCameraMode" ((camera Camera) (mode :int)) :void)

;; raylib-test.c:18:6
(function "UpdateCamera" ((camera (:pointer Camera))) :void)

$

As you can see this already generated something that comes close to what is written in
raylib.lisp. The :pointer part is taken care of and it looks like we would just have to adapt to
the typedef s a bit more.

I have not used it yet, but it looks like asdf has already builtin support for this c2ffi output.

https://common-lisp.net/project/cffi/manual/html_node/Groveller-ASDF-Integration.html

All we would need to do is to prepare a parsed version of raylib, such that asdf can integrate it.
But I dont know how yet.

@longlene
Copy link
Owner

longlene commented Oct 14, 2021

Sorry for the late response.
I have created a small dynamic library and cffi binding to test them.
Now I'm sure the code does not work.
Please have a look at https://common-lisp.net/project/cffi/manual/html_node/Foreign-Structure-Types.html

I think we can not read and write a struct in both lisp and c at the same time.

@kiran-kp
Copy link
Contributor

kiran-kp commented Mar 26, 2022

Hello, cl-autowrap does support return-by-value if that's what you're having trouble with. I'm testing using it now but I wanted to make sure I'm not misunderstanding what the issue being discussed here is.

For what it's worth, switching to autowrap will require users to re-write significant amount of code for a decent sized project since allocation/deallocation needs to be done manually and return values for functions using call-by-value needs to passed in as the first argument to the function.

Here's the relevant quote from the cl-autowrap readme:

Using its own facilities, autowrap now includes autowrap/libffi.
This allows functions that pass and return structs to be called using
autowrap. To use this, just load or :depends-on
cl-autowrap/libffi instead of cl-autowrap:

  :depends-on (... :cl-autowrap/libffi ...)
  ...

Of course, this requires libffi be compiled and available to your
lisp.

Usage mostly identical; functions called via libffi look and act the
same as any others. The one exception is functions that return a
struct by value:

(c-with ((return-value some-struct))
  (some-call-returning-some-struct return-value ...))

Calls returning a struct take the return as their first parameter.

@longlene
Copy link
Owner

Hi, @kiran-kp return-by-value is not the point, the question is that:
There are functions that use the value of the Camera structure and a pointer to the value of the Camera structure as arguments. It is just like that:
#12 (comment)

We did can define functions at the same time, but they can not be called at the same time.

@kiran-kp
Copy link
Contributor

Thanks for responding. I'm not sure I follow though. Neither of the functions you listed take a pointer to the camera and it's member at the same time and I'm not sure I understand what could go wrong if it did.

@kiran-kp
Copy link
Contributor

Separately though, I've been trying out cl-autowrap for raylib and there seems to be issues with how typedefs are handled. I'm specifically having issues with Texture/Texture2D and am investigating a fix.

@longlene
Copy link
Owner

longlene commented Mar 28, 2022

Thanks for responding. I'm not sure I follow though. Neither of the functions you listed take a pointer to the camera and it's member at the same time and I'm not sure I understand what could go wrong if it did.

@kiran-kp
The UpdateCamera function need a pointer to a Camera struct and will modify the struct,
the SetCameraMode require a Camera struct as parameter,
so we can't define a variable in lisp level,
please correct me if I'm wrong.

@nagy
Copy link
Contributor Author

nagy commented Mar 28, 2022

The UpdateCamera function need a pointer to a Camera struct and will modify the struct,
the SetCameraMode require a Camera struct as parameter,
...
please correct me if I'm wrong.

I think you are correct, I understand it the same way.

so we can't define a variable in lisp level,

You could not define a variable using the #'cl:defstruct function. All this memory would be allocated in the c world. But you still get a lisp value back from cffi which holds a pointer to this C memory. Also, the value would even be garbage-collectable, because these lisp-values could have a "finalizer" attached and then the garbage collector would do the cleanup in the C-world.

I don't think we would loose any functionality. In my experiments, the generated code was almost identical with the one that we are currently having.

@kiran-kp
Copy link
Contributor

kiran-kp commented Apr 4, 2022

If I'm understanding the problem correctly, something like this should work:

(defcfun ("UpdateCamera" update-camera%) :void
(camera (:pointer (:struct %camera3d))))

(defmacro update-camera (camera)
 (let ((foreign-camera (gensym)))
   `(cffi:with-foreign-object (,foreign-camera (:struct %camera3d))
      (cffi:translate-into-foreign-memory ,camera %camera3d-tclass ,foreign-camera)
      (update-camera% ,foreign-camera)
      (setf (camera3d-position ,camera) (cffi:foreign-slot-value ,foreign-camera '(:struct %camera3d) 'position))
      (setf (camera3d-target ,camera) (cffi:foreign-slot-value ,foreign-camera '(:struct %camera3d) 'target))
      (setf (camera3d-up ,camera) (cffi:foreign-slot-value ,foreign-camera '(:struct %camera3d) 'up))
      (setf (camera3d-fovy ,camera) (cffi:foreign-slot-value ,foreign-camera '(:struct %camera3d) 'fovy))
      (setf (camera3d-projection ,camera) (cffi:foreign-slot-value ,foreign-camera '(:struct %camera3d) 'projection)))))

It would be nice to have the setf forms be generated by a macro and I'm playing around with something for that so I'll update when I have it working.

I've given up on cl-autowrap for what it's worth. It's very buggy and does not look like it's maintained anymore. I'll continue discussion of this in #15 if you decide to close this issue.

@nagy
Copy link
Contributor Author

nagy commented Apr 4, 2022

I've given up on cl-autowrap for what it's worth. It's very buggy and does not look like it's maintained anymore.

I have come to some of its limits also lately. Namely it hat troubles for me supporting variadic lists. However, it does support a graceful failure mode, meaning that it can try to translate functions and when it cannot, it does not fail. This could allow us to at least use what it was able to figure out correctly and only leave us to keep the implementations that it cant translate. Only of course if we decide to proceed with this.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants