Repository: brendanzab/gl-rs (language: rust)

Issue:
RFC: Add a trait to load the OpenGL functions
In addition to `gl::load_with`, add a `gl::load` function that takes a trait:

``` rust
trait GlFunctionsSource {
    fn get_proc_addr(&self, &str) -> *const ();
}
```

That would allow the library to be used with `gl::load(&glfw)`, `gl::load(&sdl)` or `gl::load(&glutin)`.

The trait would need to be defined in its own crate (for example `gl_common`) in order to be usable by `gl_generator`.

Keep `load_with` for retro-compatibility and deprecate it as soon as glfw, sdl and glutin implement the trait.
## Alternatives

Implement the trait `GlFunctionsSource` for `|&str| -> *const ()` in order to be retro-compatible. Unfortunately you can only invoke closures with a `&mut self`, so that would require changing the trait.
## Drawbacks

This requires glfw, glutin and sdl2 to depend on `gl_common` in order to implement the trait.

This also creates a new crate _just for this_.



Discussion / hints:
cc @AngryLawyer who maintains rust-sdl2


Fix patch:
diff --git a/src/gl_common/Cargo.toml b/src/gl_common/Cargo.toml
new file mode 100644
index 00000000..2328ef79
--- /dev/null
+++ b/src/gl_common/Cargo.toml
@@ -0,0 +1,5 @@
+[package]
+name = "gl_common"
+version = "0.0.1"
+authors = ["Pierre Krieger <pierre.krieger1708@gmail.com>"]
+
diff --git a/src/gl_common/src/lib.rs b/src/gl_common/src/lib.rs
new file mode 100644
index 00000000..db7a9c41
--- /dev/null
+++ b/src/gl_common/src/lib.rs
@@ -0,0 +1,11 @@
+/*!
+
+Defines everything used by `gl_generator`.
+
+*/
+
+extern crate libc;
+
+pub trait GlFunctionsSource {
+    fn get_proc_addr(&self, &str) -> *const libc::c_void;
+}
diff --git a/src/gl_generator/Cargo.toml b/src/gl_generator/Cargo.toml
index db7cc4e1..f34163aa 100644
--- a/src/gl_generator/Cargo.toml
+++ b/src/gl_generator/Cargo.toml
@@ -13,6 +13,9 @@ name = "gl_generator"
 path = "lib.rs"
 plugin = true
 
+[dependencies.gl_common]
+path = "../gl_common"
+
 [dependencies.rust-xml]
 git = "https://github.com/netvl/rust-xml"
 
diff --git a/src/gl_generator/generators/global_gen.rs b/src/gl_generator/generators/global_gen.rs
index ce591595..fb0f31d1 100644
--- a/src/gl_generator/generators/global_gen.rs
+++ b/src/gl_generator/generators/global_gen.rs
@@ -34,7 +34,7 @@ impl super::Generator for GlobalGenerator {
         result.push(write_ptrs(ecx, registry));
         result.extend(write_fn_mods(ecx, registry, &ns).into_iter());
         result.push(write_failing_fns(ecx, registry));
-        result.push(write_load_fn(ecx, registry));
+        result.extend(write_load_fn(ecx, registry).into_iter());
         result
     }
 }
@@ -44,6 +44,7 @@ impl super::Generator for GlobalGenerator {
 fn write_header(ecx: &ExtCtxt) -> P<ast::Item> {
     (quote_item!(ecx,
         mod __gl_imports {
+            extern crate gl_common;
             extern crate libc;
             pub use std::mem;
         }
@@ -245,13 +246,13 @@ fn write_failing_fns(ecx: &ExtCtxt, registry: &Registry) -> P<ast::Item> {
 /// Creates the `load_with` function.
 ///
 /// The function calls `load_with` in each module created by `write_fn_mods`.
-fn write_load_fn(ecx: &ExtCtxt, registry: &Registry) -> P<ast::Item> {
+fn write_load_fn(ecx: &ExtCtxt, registry: &Registry) -> Vec<P<ast::Item>> {
     let loadings = registry.cmd_iter().map(|c| {
         let cmd_name = ecx.ident_of(c.proto.ident.as_slice());
         quote_stmt!(ecx, $cmd_name::load_with(|s| loadfn(s));)
     }).collect::<Vec<P<ast::Stmt>>>();
 
-    (quote_item!(ecx,
+    let a = (quote_item!(ecx,
         /// Load each OpenGL symbol using a custom load function. This allows for the
         /// use of functions like `glfwGetProcAddress` or `SDL_GL_GetProcAddress`.
         /// ~~~ignore
@@ -262,5 +263,20 @@ fn write_load_fn(ecx: &ExtCtxt, registry: &Registry) -> P<ast::Item> {
         pub fn load_with(loadfn: |symbol: &str| -> *const __gl_imports::libc::c_void) {
             $loadings
         }
-    )).unwrap()
+    )).unwrap();
+
+    let b = (quote_item!(ecx,
+        /// Load each OpenGL symbol using a custom load function.
+        ///
+        /// ~~~ignore
+        /// gl::load(&glfw);
+        /// ~~~
+        #[unstable]
+        #[allow(dead_code)]
+        pub fn load<T: __gl_imports::gl_common::GlFunctionsSource>(loader: &T) {
+            load_with(|name| loader.get_proc_addr(name));
+        }
+    )).unwrap();
+
+    vec![a, b]
 }
diff --git a/src/gl_generator/generators/struct_gen.rs b/src/gl_generator/generators/struct_gen.rs
index adfce12f..10a00a60 100644
--- a/src/gl_generator/generators/struct_gen.rs
+++ b/src/gl_generator/generators/struct_gen.rs
@@ -42,6 +42,7 @@ impl super::Generator for StructGenerator {
 fn write_header(ecx: &ExtCtxt) -> P<ast::Item> {
     (quote_item!(ecx,
         mod __gl_imports {
+            extern crate gl_common;
             extern crate libc;
             pub use std::mem;
         }
@@ -184,6 +185,18 @@ fn write_impl(ecx: &ExtCtxt, registry: &Registry, ns: &Ns) -> P<ast::Item> {
                 }}
             }}
 
+            /// Load each OpenGL symbol using a custom load function.
+            ///
+            /// ~~~ignore
+            /// let gl = Gl::load(&glfw);
+            /// ~~~
+            #[unstable]
+            #[allow(dead_code)]
+            #[allow(unused_variable)]
+            pub fn load<T: __gl_imports::gl_common::GlFunctionsSource>(loader: &T) -> {ns:c} {{
+                {ns:c}::load_with(|name| loader.get_proc_addr(name))
+            }}
+
             {modules}
         }}",
 


Test patch:
diff --git a/tests/loading.rs b/tests/loading.rs
new file mode 100644
index 00000000..442154cc
--- /dev/null
+++ b/tests/loading.rs
@@ -0,0 +1,16 @@
+extern crate libc;
+extern crate gl;
+extern crate gl_common;
+
+struct X;
+
+impl gl_common::GlFunctionsSource for X {
+    fn get_proc_addr(&self, _: &str) -> *const libc::c_void {
+        std::ptr::null()
+    }
+}
+
+#[test]
+fn main() {
+    gl::load(&X);
+}