From 37bf4792893c494113e71e6f85dec6732f52de6b Mon Sep 17 00:00:00 2001
From: Yichao Yu <yyc1992@gmail.com>
Date: Mon, 9 Jun 2014 09:32:20 -0400
Subject: [PATCH] split wrapper objects into different files; simplify
 templates

---
 setup.py                        |   14 +-
 src/c_wrapper/buffer.cpp        |  163 ++
 src/c_wrapper/buffer.h          |   34 +
 src/c_wrapper/clhelper.h        |   45 +-
 src/c_wrapper/clobj.h           |   24 +-
 src/c_wrapper/command_queue.cpp |  131 ++
 src/c_wrapper/command_queue.h   |   59 +
 src/c_wrapper/context.cpp       |  117 ++
 src/c_wrapper/context.h         |   29 +
 src/c_wrapper/device.cpp        |  245 +++
 src/c_wrapper/device.h          |  135 ++
 src/c_wrapper/error.h           |    6 +-
 src/c_wrapper/event.cpp         |  177 +++
 src/c_wrapper/event.h           |   68 +
 src/c_wrapper/gl_obj.cpp        |  232 +++
 src/c_wrapper/gl_obj.h          |   57 +
 src/c_wrapper/image.cpp         |  326 ++++
 src/c_wrapper/image.h           |   56 +
 src/c_wrapper/kernel.cpp        |  172 ++
 src/c_wrapper/kernel.h          |   64 +
 src/c_wrapper/memory_map.cpp    |  128 ++
 src/c_wrapper/memory_map.h      |   37 +
 src/c_wrapper/memory_object.cpp |   73 +
 src/c_wrapper/memory_object.h   |  129 ++
 src/c_wrapper/platform.cpp      |   68 +
 src/c_wrapper/platform.h        |   22 +
 src/c_wrapper/program.cpp       |  153 ++
 src/c_wrapper/program.h         |   91 ++
 src/c_wrapper/sampler.cpp       |   50 +
 src/c_wrapper/sampler.h         |   29 +
 src/c_wrapper/wrap_cl.cpp       | 2630 +------------------------------
 31 files changed, 2913 insertions(+), 2651 deletions(-)
 create mode 100644 src/c_wrapper/buffer.cpp
 create mode 100644 src/c_wrapper/buffer.h
 create mode 100644 src/c_wrapper/command_queue.cpp
 create mode 100644 src/c_wrapper/command_queue.h
 create mode 100644 src/c_wrapper/context.cpp
 create mode 100644 src/c_wrapper/context.h
 create mode 100644 src/c_wrapper/device.cpp
 create mode 100644 src/c_wrapper/device.h
 create mode 100644 src/c_wrapper/event.cpp
 create mode 100644 src/c_wrapper/event.h
 create mode 100644 src/c_wrapper/gl_obj.cpp
 create mode 100644 src/c_wrapper/gl_obj.h
 create mode 100644 src/c_wrapper/image.cpp
 create mode 100644 src/c_wrapper/image.h
 create mode 100644 src/c_wrapper/kernel.cpp
 create mode 100644 src/c_wrapper/kernel.h
 create mode 100644 src/c_wrapper/memory_map.cpp
 create mode 100644 src/c_wrapper/memory_map.h
 create mode 100644 src/c_wrapper/memory_object.cpp
 create mode 100644 src/c_wrapper/memory_object.h
 create mode 100644 src/c_wrapper/platform.cpp
 create mode 100644 src/c_wrapper/platform.h
 create mode 100644 src/c_wrapper/program.cpp
 create mode 100644 src/c_wrapper/program.h
 create mode 100644 src/c_wrapper/sampler.cpp
 create mode 100644 src/c_wrapper/sampler.h

diff --git a/setup.py b/setup.py
index 1cdcc559..e2f56902 100644
--- a/setup.py
+++ b/setup.py
@@ -236,7 +236,19 @@ def main():
                                 "src/c_wrapper/bitlog.cpp",
                                 "src/c_wrapper/pyhelper.cpp",
                                 "src/c_wrapper/async.cpp",
-                                # "src/c_wrapper/wrap_mempool.cpp",
+                                "src/c_wrapper/platform.cpp",
+                                "src/c_wrapper/device.cpp",
+                                "src/c_wrapper/context.cpp",
+                                "src/c_wrapper/command_queue.cpp",
+                                "src/c_wrapper/event.cpp",
+                                "src/c_wrapper/memory_object.cpp",
+                                "src/c_wrapper/image.cpp",
+                                "src/c_wrapper/gl_obj.cpp",
+                                "src/c_wrapper/memory_map.cpp",
+                                "src/c_wrapper/buffer.cpp",
+                                "src/c_wrapper/sampler.cpp",
+                                "src/c_wrapper/program.cpp",
+                                "src/c_wrapper/kernel.cpp",
                                 ],
                                include_dirs=(
                                    conf["CL_INC_DIR"]
diff --git a/src/c_wrapper/buffer.cpp b/src/c_wrapper/buffer.cpp
new file mode 100644
index 00000000..6b777fab
--- /dev/null
+++ b/src/c_wrapper/buffer.cpp
@@ -0,0 +1,163 @@
+#include "buffer.h"
+#include "context.h"
+#include "command_queue.h"
+#include "event.h"
+
+namespace pyopencl {
+
+#if PYOPENCL_CL_VERSION >= 0x1010
+PYOPENCL_USE_RESULT buffer*
+buffer::get_sub_region(size_t origin, size_t size, cl_mem_flags flags) const
+{
+    cl_buffer_region region = {origin, size};
+
+    auto mem = retry_mem_error([&] {
+            return pyopencl_call_guarded(clCreateSubBuffer, this, flags,
+                                         CL_BUFFER_CREATE_TYPE_REGION,
+                                         &region);
+        });
+    return new_buffer(mem);
+}
+
+//       buffer *getitem(py::slice slc) const
+//       {
+//         PYOPENCL_BUFFER_SIZE_T start, end, stride, length;
+
+//         size_t my_length;
+//         PYOPENCL_CALL_GUARDED(clGetMemObjectInfo,
+//             (this, CL_MEM_SIZE, sizeof(my_length), &my_length, 0));
+
+// #if PY_VERSION_HEX >= 0x03020000
+//         if (PySlice_GetIndicesEx(slc.ptr(),
+// #else
+//         if (PySlice_GetIndicesEx(reinterpret_cast<PySliceObject *>(slc.ptr()),
+// #endif
+//               my_length, &start, &end, &stride, &length) != 0)
+//           throw py::error_already_set();
+
+//         if (stride != 1)
+//           throw clerror("Buffer.__getitem__", CL_INVALID_VALUE,
+//               "Buffer slice must have stride 1");
+
+//         cl_mem_flags my_flags;
+//         PYOPENCL_CALL_GUARDED(clGetMemObjectInfo,
+//             (this, CL_MEM_FLAGS, sizeof(my_flags), &my_flags, 0));
+
+//         return get_sub_region(start, end, my_flags);
+//       }
+#endif
+
+}
+
+// c wrapper
+// Import all the names in pyopencl namespace for c wrappers.
+using namespace pyopencl;
+
+// Buffer
+error*
+create_buffer(clobj_t *buffer, clobj_t _ctx, cl_mem_flags flags,
+              size_t size, void *hostbuf)
+{
+    auto ctx = static_cast<context*>(_ctx);
+    return c_handle_error([&] {
+            auto mem = retry_mem_error([&] {
+                    return pyopencl_call_guarded(clCreateBuffer, ctx,
+                                                 flags, size, hostbuf);
+                });
+            *buffer = new_buffer(mem, (flags & CL_MEM_USE_HOST_PTR ?
+                                       hostbuf : nullptr));
+        });
+}
+
+error*
+enqueue_read_buffer(clobj_t *_evt, clobj_t _queue, clobj_t _mem,
+                    void *buffer, size_t size, size_t device_offset,
+                    const clobj_t *_wait_for, uint32_t num_wait_for,
+                    int is_blocking, void *pyobj)
+{
+    const auto wait_for = buf_from_class<event>(_wait_for, num_wait_for);
+    auto queue = static_cast<command_queue*>(_queue);
+    auto mem = static_cast<memory_object*>(_mem);
+    return c_handle_error([&] {
+            cl_event evt;
+            retry_mem_error([&] {
+                    pyopencl_call_guarded(
+                        clEnqueueReadBuffer, queue, mem,
+                        cast_bool(is_blocking), device_offset, size,
+                        buffer, wait_for, &evt);
+                });
+            *_evt = new_nanny_event(evt, pyobj);
+        });
+}
+
+error*
+enqueue_write_buffer(clobj_t *_evt, clobj_t _queue, clobj_t _mem,
+                     const void *buffer, size_t size, size_t device_offset,
+                     const clobj_t *_wait_for, uint32_t num_wait_for,
+                     int is_blocking, void *pyobj)
+{
+    const auto wait_for = buf_from_class<event>(_wait_for, num_wait_for);
+    auto queue = static_cast<command_queue*>(_queue);
+    auto mem = static_cast<memory_object*>(_mem);
+    return c_handle_error([&] {
+            cl_event evt;
+            retry_mem_error([&] {
+                    pyopencl_call_guarded(
+                        clEnqueueWriteBuffer, queue, mem,
+                        cast_bool(is_blocking), device_offset, size, buffer,
+                        wait_for, &evt);
+                });
+            *_evt = new_nanny_event(evt, pyobj);
+        });
+}
+
+error*
+enqueue_copy_buffer(clobj_t *_evt, clobj_t _queue, clobj_t _src, clobj_t _dst,
+                    ptrdiff_t byte_count, size_t src_offset, size_t dst_offset,
+                    const clobj_t *_wait_for, uint32_t num_wait_for)
+{
+    auto queue = static_cast<command_queue*>(_queue);
+    auto src = static_cast<memory_object*>(_src);
+    auto dst = static_cast<memory_object*>(_dst);
+    return c_handle_error([&] {
+            if (byte_count < 0) {
+                size_t byte_count_src = 0;
+                size_t byte_count_dst = 0;
+                pyopencl_call_guarded(
+                    clGetMemObjectInfo, src, CL_MEM_SIZE,
+                    sizeof(byte_count), &byte_count_src, nullptr);
+                pyopencl_call_guarded(
+                    clGetMemObjectInfo, src, CL_MEM_SIZE,
+                    sizeof(byte_count), &byte_count_dst, nullptr);
+                byte_count = std::min(byte_count_src, byte_count_dst);
+            }
+            const auto wait_for = buf_from_class<event>(_wait_for,
+                                                        num_wait_for);
+            cl_event evt;
+            retry_mem_error([&] {
+                    pyopencl_call_guarded(
+                        clEnqueueCopyBuffer, queue, src, dst, src_offset,
+                        dst_offset, byte_count, wait_for, &evt);
+                });
+            *_evt = new_event(evt);
+        });
+}
+
+error*
+enqueue_fill_buffer(clobj_t *_evt, clobj_t _queue, clobj_t _mem, void *pattern,
+                    size_t psize, size_t offset, size_t size,
+                    const clobj_t *_wait_for, uint32_t num_wait_for)
+{
+    const auto wait_for = buf_from_class<event>(_wait_for, num_wait_for);
+    auto queue = static_cast<command_queue*>(_queue);
+    auto mem = static_cast<memory_object*>(_mem);
+    return c_handle_error([&] {
+            cl_event evt;
+            retry_mem_error([&] {
+                    pyopencl_call_guarded(
+                        clEnqueueFillBuffer, queue, mem, pattern, psize,
+                        offset, size, wait_for, &evt);
+                });
+            *_evt = new_event(evt);
+        });
+}
diff --git a/src/c_wrapper/buffer.h b/src/c_wrapper/buffer.h
new file mode 100644
index 00000000..d76bbdcd
--- /dev/null
+++ b/src/c_wrapper/buffer.h
@@ -0,0 +1,34 @@
+#include "memory_object.h"
+#include "clhelper.h"
+
+#ifndef __PYOPENCL_BUFFER_H
+#define __PYOPENCL_BUFFER_H
+
+namespace pyopencl {
+
+// {{{ buffer
+
+class buffer : public memory_object {
+public:
+    PYOPENCL_DEF_CL_CLASS(BUFFER);
+    PYOPENCL_INLINE
+    buffer(cl_mem mem, bool retain, void *hostbuf=0)
+        : memory_object(mem, retain, hostbuf)
+    {}
+
+#if PYOPENCL_CL_VERSION >= 0x1010
+    PYOPENCL_USE_RESULT buffer *get_sub_region(size_t origin, size_t size,
+                                               cl_mem_flags flags) const;
+#endif
+};
+PYOPENCL_USE_RESULT static PYOPENCL_INLINE buffer*
+new_buffer(cl_mem mem, void *buff=0)
+{
+    return pyopencl_convert_obj(buffer, clReleaseMemObject, mem, buff);
+}
+
+// }}}
+
+}
+
+#endif
diff --git a/src/c_wrapper/clhelper.h b/src/c_wrapper/clhelper.h
index cec412df..003e23b8 100644
--- a/src/c_wrapper/clhelper.h
+++ b/src/c_wrapper/clhelper.h
@@ -48,52 +48,43 @@ convert_array_info(const char *tname, pyopencl_buf<T> &&_buf)
 #define pyopencl_get_array_info(type, what, args...)                    \
     pyopencl_convert_array_info(type, pyopencl_get_vec_info(type, what, args))
 
-template<typename T, typename Cls>
+template<typename CLObj, typename T>
 PYOPENCL_USE_RESULT static PYOPENCL_INLINE generic_info
-convert_opaque_array_info(pyopencl_buf<T> &buf)
+convert_opaque_array_info(T &&buf)
 {
     generic_info info;
     info.dontfree = 0;
-    info.opaque_class = Cls::get_class_t();
+    info.opaque_class = CLObj::get_class_t();
     info.type = _copy_str(std::string("void*[") + tostring(buf.len()) + "]");
-    info.value = buf_to_base<Cls>(buf).release();
+    info.value = buf_to_base<CLObj>(std::forward<T>(buf)).release();
     return info;
 }
+#define pyopencl_get_opaque_array_info(cls, what, args...)  \
+    pyopencl::convert_opaque_array_info<cls>(               \
+        pyopencl_get_vec_info(cls::cl_type, what, args))
 
-template<typename T, typename Cls>
-PYOPENCL_USE_RESULT static PYOPENCL_INLINE generic_info
-convert_opaque_array_info(pyopencl_buf<T> &&_buf)
-{
-    pyopencl_buf<T> &buf = _buf;
-    return convert_opaque_array_info<T, Cls>(buf);
-}
-#define pyopencl_get_opaque_array_info(type, cls, what, args...)  \
-    pyopencl::convert_opaque_array_info<type, cls>(              \
-        pyopencl_get_vec_info(type, what, args))
-
-template<typename CLType, typename Cls,
-         typename... ArgTypes, typename... ArgTypes2>
+template<typename CLObj, typename... ArgTypes, typename... ArgTypes2>
 PYOPENCL_USE_RESULT static PYOPENCL_INLINE generic_info
 get_opaque_info(cl_int (*func)(ArgTypes...), const char *name,
                 ArgTypes2&&... args)
 {
-    CLType param_value;
+    typename CLObj::cl_type param_value;
     call_guarded(func, name, args..., sizeof(param_value),
                  &param_value, nullptr);
     generic_info info;
     info.dontfree = 0;
-    info.opaque_class = Cls::get_class_t();
+    info.opaque_class = CLObj::get_class_t();
     info.type = "void *";
     if (param_value) {
-        info.value = (void*)(new Cls(param_value, /*retain*/ true));
+        info.value = (void*)(new CLObj(param_value, /*retain*/ true));
     } else {
         info.value = nullptr;
     }
     return info;
 }
-#define pyopencl_get_opaque_info(type, cls, what, args...)              \
-    pyopencl::get_opaque_info<type, cls>(clGet##what##Info,             \
-                                         "clGet" #what "Info", args)
+#define pyopencl_get_opaque_info(clobj, what, args...)              \
+    pyopencl::get_opaque_info<clobj>(clGet##what##Info,             \
+                                     "clGet" #what "Info", args)
 
 template<typename... ArgTypes, typename... ArgTypes2>
 PYOPENCL_USE_RESULT static PYOPENCL_INLINE generic_info
@@ -163,10 +154,12 @@ get_ext_fun(cl_platform_id plat, const char *name, const char *err)
     }
     return func;
 }
+#define pyopencl_get_ext_fun(plat, name)                                \
+    pyopencl::get_ext_fun<name##_fn>(plat, #name, #name " not available")
 #else
 template<typename T>
 PYOPENCL_USE_RESULT static PYOPENCL_INLINE T
-get_ext_fun(cl_platform_id, const char *name, const char *err)
+get_ext_fun(const char *name, const char *err)
 {
     T func = (T)clGetExtensionFunctionAddress(name);
     if (!func) {
@@ -174,9 +167,9 @@ get_ext_fun(cl_platform_id, const char *name, const char *err)
     }
     return func;
 }
-#endif
 #define pyopencl_get_ext_fun(plat, name)                                \
-    pyopencl::get_ext_fun<name##_fn>(plat, #name, #name " not available")
+    pyopencl::get_ext_fun<name##_fn>(#name, #name " not available")
+#endif
 
 // }}}
 
diff --git a/src/c_wrapper/clobj.h b/src/c_wrapper/clobj.h
index b0498b04..66be5a56 100644
--- a/src/c_wrapper/clobj.h
+++ b/src/c_wrapper/clobj.h
@@ -32,6 +32,7 @@ private:
     CLType m_obj;
 public:
     typedef CLType cl_type;
+    PYOPENCL_INLINE
     clobj(CLType obj, bool=false) : m_obj(obj)
     {}
     PYOPENCL_INLINE const CLType&
@@ -91,7 +92,7 @@ clobj_from_int_ptr(intptr_t ptr)
 
 template<typename T, typename T2>
 PYOPENCL_USE_RESULT static PYOPENCL_INLINE pyopencl_buf<typename T::cl_type>
-buf_from_class(const T2 *buf2, size_t len)
+buf_from_class(T2 *buf2, size_t len)
 {
     pyopencl_buf<typename T::cl_type> buf(len);
     for (size_t i = 0;i < len;i++) {
@@ -102,21 +103,14 @@ buf_from_class(const T2 *buf2, size_t len)
 
 template<typename T, typename T2>
 PYOPENCL_USE_RESULT static PYOPENCL_INLINE pyopencl_buf<typename T::cl_type>
-buf_from_class(const pyopencl_buf<T2> &&buf)
-{
-    return buf_from_class(buf.get(), buf.len());
-}
-
-template<typename T, typename T2>
-PYOPENCL_USE_RESULT static PYOPENCL_INLINE pyopencl_buf<typename T::cl_type>
-buf_from_class(const pyopencl_buf<T2> &buf)
+buf_from_class(T2 &&buf)
 {
     return buf_from_class(buf.get(), buf.len());
 }
 
 template<typename T, typename T2, typename... ArgTypes>
 PYOPENCL_USE_RESULT static PYOPENCL_INLINE pyopencl_buf<clbase*>
-buf_to_base(const T2 *buf2, size_t len, ArgTypes&&... args)
+buf_to_base(T2 *buf2, size_t len, ArgTypes&&... args)
 {
     pyopencl_buf<clbase*> buf(len);
     size_t i = 0;
@@ -137,20 +131,12 @@ buf_to_base(const T2 *buf2, size_t len, ArgTypes&&... args)
 
 template<typename T, typename T2, typename... ArgTypes>
 PYOPENCL_USE_RESULT static PYOPENCL_INLINE pyopencl_buf<clbase*>
-buf_to_base(const pyopencl_buf<T2> &&buf2, ArgTypes&&... args)
+buf_to_base(T2 &&buf2, ArgTypes&&... args)
 {
     return buf_to_base<T>(buf2.get(), buf2.len(),
                            std::forward<ArgTypes>(args)...);
 }
 
-template<typename T, typename T2, typename... ArgTypes>
-PYOPENCL_USE_RESULT static PYOPENCL_INLINE pyopencl_buf<clbase*>
-buf_to_base(const pyopencl_buf<T2> &buf2, ArgTypes&&... args)
-{
-    return buf_to_base<T>(buf2.get(), buf2.len(),
-                          std::forward<ArgTypes>(args)...);
-}
-
 }
 
 #endif
diff --git a/src/c_wrapper/command_queue.cpp b/src/c_wrapper/command_queue.cpp
new file mode 100644
index 00000000..4e2ee733
--- /dev/null
+++ b/src/c_wrapper/command_queue.cpp
@@ -0,0 +1,131 @@
+#include "command_queue.h"
+#include "device.h"
+#include "context.h"
+#include "event.h"
+#include "clhelper.h"
+
+namespace pyopencl {
+
+command_queue::~command_queue()
+{
+    pyopencl_call_guarded_cleanup(clReleaseCommandQueue, this);
+}
+
+generic_info
+command_queue::get_info(cl_uint param_name) const
+{
+    switch ((cl_command_queue_info)param_name) {
+    case CL_QUEUE_CONTEXT:
+        return pyopencl_get_opaque_info(context, CommandQueue,
+                                        this, param_name);
+    case CL_QUEUE_DEVICE:
+        return pyopencl_get_opaque_info(device, CommandQueue, this, param_name);
+    case CL_QUEUE_REFERENCE_COUNT:
+        return pyopencl_get_int_info(cl_uint, CommandQueue,
+                                     this, param_name);
+    case CL_QUEUE_PROPERTIES:
+        return pyopencl_get_int_info(cl_command_queue_properties,
+                                     CommandQueue, this, param_name);
+    default:
+        throw clerror("CommandQueue.get_info", CL_INVALID_VALUE);
+    }
+}
+
+}
+
+// c wrapper
+// Import all the names in pyopencl namespace for c wrappers.
+using namespace pyopencl;
+
+// Command Queue
+error*
+create_command_queue(clobj_t *queue, clobj_t _ctx,
+                     clobj_t _dev, cl_command_queue_properties props)
+{
+    auto ctx = static_cast<context*>(_ctx);
+    auto py_dev = static_cast<device*>(_dev);
+    return c_handle_error([&] {
+            cl_device_id dev;
+            if (py_dev) {
+                dev = py_dev->data();
+            } else {
+                auto devs = pyopencl_get_vec_info(cl_device_id, Context,
+                                                  ctx, CL_CONTEXT_DEVICES);
+                if (devs.len() == 0) {
+                    throw clerror("CommandQueue", CL_INVALID_VALUE,
+                                  "context doesn't have any devices? -- "
+                                  "don't know which one to default to");
+                }
+                dev = devs[0];
+            }
+            cl_command_queue cl_queue =
+                pyopencl_call_guarded(clCreateCommandQueue, ctx, dev, props);
+            *queue = new command_queue(cl_queue, false);
+        });
+}
+
+error*
+command_queue__finish(clobj_t queue)
+{
+    return c_handle_error([&] {
+            pyopencl_call_guarded(clFinish, static_cast<command_queue*>(queue));
+        });
+}
+
+error*
+command_queue__flush(clobj_t queue)
+{
+    return c_handle_error([&] {
+            pyopencl_call_guarded(clFlush, static_cast<command_queue*>(queue));
+        });
+}
+
+#if PYOPENCL_CL_VERSION >= 0x1020
+error*
+enqueue_marker_with_wait_list(clobj_t *_evt, clobj_t _queue,
+                              const clobj_t *_wait_for, uint32_t num_wait_for)
+{
+    auto queue = static_cast<command_queue*>(_queue);
+    const auto wait_for = buf_from_class<event>(_wait_for, num_wait_for);
+    return c_handle_error([&] {
+            cl_event evt;
+            pyopencl_call_guarded(clEnqueueMarkerWithWaitList, queue,
+                                  wait_for, &evt);
+            *_evt = new_event(evt);
+        });
+}
+
+error*
+enqueue_barrier_with_wait_list(clobj_t *_evt, clobj_t _queue,
+                               const clobj_t *_wait_for, uint32_t num_wait_for)
+{
+    auto queue = static_cast<command_queue*>(_queue);
+    const auto wait_for = buf_from_class<event>(_wait_for, num_wait_for);
+    return c_handle_error([&] {
+            cl_event evt;
+            pyopencl_call_guarded(clEnqueueBarrierWithWaitList, queue,
+                                  wait_for, &evt);
+            *_evt = new_event(evt);
+        });
+}
+#endif
+
+error*
+enqueue_marker(clobj_t *_evt, clobj_t _queue)
+{
+    auto queue = static_cast<command_queue*>(_queue);
+    return c_handle_error([&] {
+            cl_event evt;
+            pyopencl_call_guarded(clEnqueueMarker, queue, &evt);
+            *_evt = new_event(evt);
+        });
+}
+
+error*
+enqueue_barrier(clobj_t _queue)
+{
+    auto queue = static_cast<command_queue*>(_queue);
+    return c_handle_error([&] {
+            pyopencl_call_guarded(clEnqueueBarrier, queue);
+        });
+}
diff --git a/src/c_wrapper/command_queue.h b/src/c_wrapper/command_queue.h
new file mode 100644
index 00000000..d7fcd08c
--- /dev/null
+++ b/src/c_wrapper/command_queue.h
@@ -0,0 +1,59 @@
+#include "error.h"
+
+#ifndef __PYOPENCL_COMMAND_QUEUE_H
+#define __PYOPENCL_COMMAND_QUEUE_H
+
+namespace pyopencl {
+
+// {{{ command_queue
+
+class command_queue : public clobj<cl_command_queue> {
+public:
+    PYOPENCL_DEF_CL_CLASS(COMMAND_QUEUE);
+    PYOPENCL_INLINE
+    command_queue(cl_command_queue q, bool retain)
+        : clobj(q)
+    {
+        if (retain) {
+            pyopencl_call_guarded(clRetainCommandQueue, this);
+        }
+    }
+    PYOPENCL_INLINE
+    command_queue(const command_queue &queue)
+        : command_queue(queue.data(), true)
+    {}
+    ~command_queue();
+
+    generic_info get_info(cl_uint param_name) const;
+
+#if 0
+
+    PYOPENCL_USE_RESULT std::unique_ptr<context>
+    get_context() const
+    {
+        cl_context param_value;
+        pyopencl_call_guarded(clGetCommandQueueInfo, this, CL_QUEUE_CONTEXT,
+                              make_sizearg(param_value), nullptr);
+        return std::unique_ptr<context>(
+            new context(param_value, /*retain*/ true));
+    }
+
+#if PYOPENCL_CL_VERSION < 0x1010
+    cl_command_queue_properties
+    set_property(cl_command_queue_properties prop, bool enable) const
+    {
+        cl_command_queue_properties old_prop;
+        pyopencl_call_guarded(clSetCommandQueueProperty, this, prop,
+                              cast_bool(enable), make_argbuf(old_prop));
+        return old_prop;
+    }
+#endif
+
+#endif
+};
+
+// }}}
+
+}
+
+#endif
diff --git a/src/c_wrapper/context.cpp b/src/c_wrapper/context.cpp
new file mode 100644
index 00000000..9b16c87c
--- /dev/null
+++ b/src/c_wrapper/context.cpp
@@ -0,0 +1,117 @@
+#include "context.h"
+#include "device.h"
+#include "platform.h"
+#include "clhelper.h"
+
+namespace pyopencl {
+
+context::~context()
+{
+    pyopencl_call_guarded_cleanup(clReleaseContext, this);
+}
+
+generic_info
+context::get_info(cl_uint param_name) const
+{
+    switch ((cl_context_info)param_name) {
+    case CL_CONTEXT_REFERENCE_COUNT:
+        return pyopencl_get_int_info(cl_uint, Context,
+                                     this, param_name);
+    case CL_CONTEXT_DEVICES:
+        return pyopencl_get_opaque_array_info(device, Context,
+                                              this, param_name);
+    case CL_CONTEXT_PROPERTIES: {
+        auto result = pyopencl_get_vec_info(
+            cl_context_properties, Context, this, param_name);
+        pyopencl_buf<generic_info> py_result(result.len() / 2);
+        size_t i = 0;
+        for (;i < py_result.len();i++) {
+            cl_context_properties key = result[i * 2];
+            if (key == 0)
+                break;
+            cl_context_properties value = result[i * 2 + 1];
+            generic_info &info = py_result[i];
+            info.dontfree = 0;
+            info.opaque_class = CLASS_NONE;
+            switch (key) {
+            case CL_CONTEXT_PLATFORM:
+                info.opaque_class = CLASS_PLATFORM;
+                info.type = "void *";
+                info.value = new platform(
+                    reinterpret_cast<cl_platform_id>(value));
+                break;
+
+#if defined(PYOPENCL_GL_SHARING_VERSION) && (PYOPENCL_GL_SHARING_VERSION >= 1)
+#if defined(__APPLE__) && defined(HAVE_GL)
+            case CL_CONTEXT_PROPERTY_USE_CGL_SHAREGROUP_APPLE:
+#else
+            case CL_GL_CONTEXT_KHR:
+            case CL_EGL_DISPLAY_KHR:
+            case CL_GLX_DISPLAY_KHR:
+            case CL_WGL_HDC_KHR:
+            case CL_CGL_SHAREGROUP_KHR:
+#endif
+                info.type = "intptr_t *";
+                info.value = (void*)value;
+                // we do not own this object
+                info.dontfree = 1;
+                break;
+
+#endif
+            default:
+                throw clerror("Context.get_info", CL_INVALID_VALUE,
+                              "unknown context_property key encountered");
+            }
+        }
+        py_result.resize(i);
+        return pyopencl_convert_array_info(generic_info, py_result);
+    }
+
+#if PYOPENCL_CL_VERSION >= 0x1010
+    case CL_CONTEXT_NUM_DEVICES:
+        return pyopencl_get_int_info(cl_uint, Context,
+                                     this, param_name);
+#endif
+
+    default:
+        throw clerror("Context.get_info", CL_INVALID_VALUE);
+    }
+}
+
+}
+
+// c wrapper
+// Import all the names in pyopencl namespace for c wrappers.
+using namespace pyopencl;
+
+// Context
+error*
+create_context(clobj_t *_ctx, const cl_context_properties *properties,
+               cl_uint num_devices, const clobj_t *_devices)
+{
+    return c_handle_error([&] {
+            const auto devices = buf_from_class<device>(_devices, num_devices);
+            *_ctx = new context(
+                pyopencl_call_guarded(
+                    clCreateContext,
+                    const_cast<cl_context_properties*>(properties),
+                    devices, nullptr, nullptr), false);
+        });
+}
+
+error*
+context__get_supported_image_formats(clobj_t _ctx, cl_mem_flags flags,
+                                     cl_mem_object_type image_type,
+                                     generic_info *out)
+{
+    auto ctx = static_cast<context*>(_ctx);
+    return c_handle_error([&] {
+            cl_uint num;
+            pyopencl_call_guarded(clGetSupportedImageFormats, ctx, flags,
+                                  image_type, 0, nullptr, make_argbuf(num));
+            pyopencl_buf<cl_image_format> formats(num);
+            pyopencl_call_guarded(clGetSupportedImageFormats, ctx, flags,
+                                  image_type, formats, make_argbuf(num));
+            *out = pyopencl_convert_array_info(cl_image_format, formats);
+        });
+}
diff --git a/src/c_wrapper/context.h b/src/c_wrapper/context.h
new file mode 100644
index 00000000..74a108a5
--- /dev/null
+++ b/src/c_wrapper/context.h
@@ -0,0 +1,29 @@
+#include "error.h"
+
+#ifndef __PYOPENCL_CONTEXT_H
+#define __PYOPENCL_CONTEXT_H
+
+namespace pyopencl {
+
+// {{{ context
+
+class context : public clobj<cl_context> {
+public:
+    PYOPENCL_DEF_CL_CLASS(CONTEXT);
+    PYOPENCL_INLINE
+    context(cl_context ctx, bool retain)
+        : clobj(ctx)
+    {
+        if (retain) {
+            pyopencl_call_guarded(clRetainContext, this);
+        }
+    }
+    ~context();
+    generic_info get_info(cl_uint param_name) const;
+};
+
+// }}}
+
+}
+
+#endif
diff --git a/src/c_wrapper/device.cpp b/src/c_wrapper/device.cpp
new file mode 100644
index 00000000..495c2152
--- /dev/null
+++ b/src/c_wrapper/device.cpp
@@ -0,0 +1,245 @@
+#include "device.h"
+#include "platform.h"
+
+namespace pyopencl {
+
+device::~device()
+{
+    if (false) {
+    }
+#if defined(cl_ext_device_fission) && defined(PYOPENCL_USE_DEVICE_FISSION)
+    else if (m_ref_type == REF_FISSION_EXT) {
+#if PYOPENCL_CL_VERSION >= 0x1020
+        cl_platform_id plat;
+        pyopencl_call_guarded_cleanup(clGetDeviceInfo, this, CL_DEVICE_PLATFORM,
+                                      make_sizearg(plat), nullptr);
+#endif
+        pyopencl_call_guarded_cleanup(
+            pyopencl_get_ext_fun(plat, clReleaseDeviceEXT), this);
+    }
+#endif
+#if PYOPENCL_CL_VERSION >= 0x1020
+    else if (m_ref_type == REF_CL_1_2) {
+        pyopencl_call_guarded_cleanup(clReleaseDevice, this);
+    }
+#endif
+}
+
+generic_info
+device::get_info(cl_uint param_name) const
+{
+#define DEV_GET_INT_INF(TYPE)                                   \
+    pyopencl_get_int_info(TYPE, Device, this, param_name)
+
+    switch ((cl_device_info)param_name) {
+    case CL_DEVICE_TYPE:
+        return DEV_GET_INT_INF(cl_device_type);
+    case CL_DEVICE_MAX_WORK_GROUP_SIZE:
+        return DEV_GET_INT_INF(size_t);
+    case CL_DEVICE_MAX_WORK_ITEM_DIMENSIONS:
+    case CL_DEVICE_MAX_COMPUTE_UNITS:
+    case CL_DEVICE_VENDOR_ID:
+        return DEV_GET_INT_INF(cl_uint);
+
+    case CL_DEVICE_MAX_WORK_ITEM_SIZES:
+        return pyopencl_get_array_info(size_t, Device, this, param_name);
+
+    case CL_DEVICE_PREFERRED_VECTOR_WIDTH_CHAR:
+    case CL_DEVICE_PREFERRED_VECTOR_WIDTH_SHORT:
+    case CL_DEVICE_PREFERRED_VECTOR_WIDTH_INT:
+    case CL_DEVICE_PREFERRED_VECTOR_WIDTH_LONG:
+    case CL_DEVICE_PREFERRED_VECTOR_WIDTH_FLOAT:
+    case CL_DEVICE_PREFERRED_VECTOR_WIDTH_DOUBLE:
+
+    case CL_DEVICE_MAX_CLOCK_FREQUENCY:
+    case CL_DEVICE_ADDRESS_BITS:
+    case CL_DEVICE_MAX_READ_IMAGE_ARGS:
+    case CL_DEVICE_MAX_WRITE_IMAGE_ARGS:
+    case CL_DEVICE_MAX_SAMPLERS:
+    case CL_DEVICE_MEM_BASE_ADDR_ALIGN:
+    case CL_DEVICE_MIN_DATA_TYPE_ALIGN_SIZE:
+        return DEV_GET_INT_INF(cl_uint);
+
+    case CL_DEVICE_MAX_MEM_ALLOC_SIZE:
+        return DEV_GET_INT_INF(cl_ulong);
+
+    case CL_DEVICE_IMAGE2D_MAX_WIDTH:
+    case CL_DEVICE_IMAGE2D_MAX_HEIGHT:
+    case CL_DEVICE_IMAGE3D_MAX_WIDTH:
+    case CL_DEVICE_IMAGE3D_MAX_HEIGHT:
+    case CL_DEVICE_IMAGE3D_MAX_DEPTH:
+    case CL_DEVICE_MAX_PARAMETER_SIZE:
+        return DEV_GET_INT_INF(size_t);
+
+    case CL_DEVICE_IMAGE_SUPPORT:
+        return DEV_GET_INT_INF(cl_bool);
+#ifdef CL_DEVICE_DOUBLE_FP_CONFIG
+    case CL_DEVICE_DOUBLE_FP_CONFIG:
+#endif
+#ifdef CL_DEVICE_HALF_FP_CONFIG
+    case CL_DEVICE_HALF_FP_CONFIG:
+#endif
+    case CL_DEVICE_SINGLE_FP_CONFIG:
+        return DEV_GET_INT_INF(cl_device_fp_config);
+
+    case CL_DEVICE_GLOBAL_MEM_CACHE_TYPE:
+        return DEV_GET_INT_INF(cl_device_mem_cache_type);
+    case CL_DEVICE_GLOBAL_MEM_CACHELINE_SIZE:
+        return DEV_GET_INT_INF(cl_uint);
+    case CL_DEVICE_GLOBAL_MEM_CACHE_SIZE:
+    case CL_DEVICE_GLOBAL_MEM_SIZE:
+    case CL_DEVICE_LOCAL_MEM_SIZE:
+    case CL_DEVICE_MAX_CONSTANT_BUFFER_SIZE:
+        return DEV_GET_INT_INF(cl_ulong);
+
+    case CL_DEVICE_MAX_CONSTANT_ARGS:
+        return DEV_GET_INT_INF(cl_uint);
+    case CL_DEVICE_LOCAL_MEM_TYPE:
+        return DEV_GET_INT_INF(cl_device_local_mem_type);
+    case CL_DEVICE_PROFILING_TIMER_RESOLUTION:
+        return DEV_GET_INT_INF(size_t);
+    case CL_DEVICE_ENDIAN_LITTLE:
+    case CL_DEVICE_AVAILABLE:
+    case CL_DEVICE_COMPILER_AVAILABLE:
+    case CL_DEVICE_ERROR_CORRECTION_SUPPORT:
+        return DEV_GET_INT_INF(cl_bool);
+    case CL_DEVICE_EXECUTION_CAPABILITIES:
+        return DEV_GET_INT_INF(cl_device_exec_capabilities);
+    case CL_DEVICE_QUEUE_PROPERTIES:
+        return DEV_GET_INT_INF(cl_command_queue_properties);
+
+    case CL_DEVICE_NAME:
+    case CL_DEVICE_VENDOR:
+    case CL_DRIVER_VERSION:
+    case CL_DEVICE_PROFILE:
+    case CL_DEVICE_VERSION:
+    case CL_DEVICE_EXTENSIONS:
+        return pyopencl_get_str_info(Device, this, param_name);
+
+    case CL_DEVICE_PLATFORM:
+        return pyopencl_get_opaque_info(platform, Device, this, param_name);
+#if PYOPENCL_CL_VERSION >= 0x1010
+    case CL_DEVICE_PREFERRED_VECTOR_WIDTH_HALF:
+    case CL_DEVICE_NATIVE_VECTOR_WIDTH_CHAR:
+    case CL_DEVICE_NATIVE_VECTOR_WIDTH_SHORT:
+    case CL_DEVICE_NATIVE_VECTOR_WIDTH_INT:
+    case CL_DEVICE_NATIVE_VECTOR_WIDTH_LONG:
+    case CL_DEVICE_NATIVE_VECTOR_WIDTH_FLOAT:
+    case CL_DEVICE_NATIVE_VECTOR_WIDTH_DOUBLE:
+    case CL_DEVICE_NATIVE_VECTOR_WIDTH_HALF:
+        return DEV_GET_INT_INF(cl_uint);
+
+    case CL_DEVICE_HOST_UNIFIED_MEMORY:
+        return DEV_GET_INT_INF(cl_bool);
+    case CL_DEVICE_OPENCL_C_VERSION:
+        return pyopencl_get_str_info(Device, this, param_name);
+#endif
+#ifdef CL_DEVICE_COMPUTE_CAPABILITY_MAJOR_NV
+    case CL_DEVICE_COMPUTE_CAPABILITY_MAJOR_NV:
+    case CL_DEVICE_COMPUTE_CAPABILITY_MINOR_NV:
+    case CL_DEVICE_REGISTERS_PER_BLOCK_NV:
+    case CL_DEVICE_WARP_SIZE_NV:
+        return DEV_GET_INT_INF(cl_uint);
+    case CL_DEVICE_GPU_OVERLAP_NV:
+    case CL_DEVICE_KERNEL_EXEC_TIMEOUT_NV:
+    case CL_DEVICE_INTEGRATED_MEMORY_NV:
+        return DEV_GET_INT_INF(cl_bool);
+#endif
+#if defined(cl_ext_device_fission) && defined(PYOPENCL_USE_DEVICE_FISSION)
+    case CL_DEVICE_PARENT_DEVICE_EXT:
+        return pyopencl_get_opaque_info(device, Device, this, param_name);
+    case CL_DEVICE_PARTITION_TYPES_EXT:
+    case CL_DEVICE_AFFINITY_DOMAINS_EXT:
+    case CL_DEVICE_PARTITION_STYLE_EXT:
+        return pyopencl_get_array_info(cl_device_partition_property_ext,
+                                       Device, this, param_name);
+    case CL_DEVICE_REFERENCE_COUNT_EXT:
+        return DEV_GET_INT_INF(cl_uint);
+#endif
+#if PYOPENCL_CL_VERSION >= 0x1020
+    case CL_DEVICE_LINKER_AVAILABLE:
+        return DEV_GET_INT_INF(cl_bool);
+    case CL_DEVICE_BUILT_IN_KERNELS:
+        return pyopencl_get_str_info(Device, this, param_name);
+    case CL_DEVICE_IMAGE_MAX_BUFFER_SIZE:
+    case CL_DEVICE_IMAGE_MAX_ARRAY_SIZE:
+        return DEV_GET_INT_INF(size_t);
+    case CL_DEVICE_PARENT_DEVICE:
+        return pyopencl_get_opaque_info(device, Device, this, param_name);
+    case CL_DEVICE_PARTITION_MAX_SUB_DEVICES:
+        return DEV_GET_INT_INF(cl_uint);
+    case CL_DEVICE_PARTITION_TYPE:
+    case CL_DEVICE_PARTITION_PROPERTIES:
+        return pyopencl_get_array_info(cl_device_partition_property,
+                                       Device, this, param_name);
+    case CL_DEVICE_PARTITION_AFFINITY_DOMAIN:
+        return pyopencl_get_array_info(cl_device_affinity_domain,
+                                       Device, this, param_name);
+    case CL_DEVICE_REFERENCE_COUNT:
+        return DEV_GET_INT_INF(cl_uint);
+    case CL_DEVICE_PREFERRED_INTEROP_USER_SYNC:
+    case CL_DEVICE_PRINTF_BUFFER_SIZE:
+        return DEV_GET_INT_INF(cl_bool);
+#endif
+        // {{{ AMD dev attrs
+        //
+        // types of AMD dev attrs divined from
+        // https://www.khronos.org/registry/cl/api/1.2/cl.hpp
+#ifdef CL_DEVICE_PROFILING_TIMER_OFFSET_AMD
+    case CL_DEVICE_PROFILING_TIMER_OFFSET_AMD:
+        return DEV_GET_INT_INF(cl_ulong);
+#endif
+        /* FIXME
+           #ifdef CL_DEVICE_TOPOLOGY_AMD
+           case CL_DEVICE_TOPOLOGY_AMD:
+           #endif
+        */
+#ifdef CL_DEVICE_BOARD_NAME_AMD
+    case CL_DEVICE_BOARD_NAME_AMD: ;
+        return pyopencl_get_str_info(Device, this, param_name);
+#endif
+#ifdef CL_DEVICE_GLOBAL_FREE_MEMORY_AMD
+    case CL_DEVICE_GLOBAL_FREE_MEMORY_AMD:
+        return pyopencl_get_array_info(size_t, Device,
+                                       this, param_name);
+#endif
+#ifdef CL_DEVICE_SIMD_PER_COMPUTE_UNIT_AMD
+    case CL_DEVICE_SIMD_PER_COMPUTE_UNIT_AMD:
+#endif
+#ifdef CL_DEVICE_SIMD_WIDTH_AMD
+    case CL_DEVICE_SIMD_WIDTH_AMD:
+#endif
+#ifdef CL_DEVICE_SIMD_INSTRUCTION_WIDTH_AMD
+    case CL_DEVICE_SIMD_INSTRUCTION_WIDTH_AMD:
+#endif
+#ifdef CL_DEVICE_WAVEFRONT_WIDTH_AMD
+    case CL_DEVICE_WAVEFRONT_WIDTH_AMD:
+#endif
+#ifdef CL_DEVICE_GLOBAL_MEM_CHANNELS_AMD
+    case CL_DEVICE_GLOBAL_MEM_CHANNELS_AMD:
+#endif
+#ifdef CL_DEVICE_GLOBAL_MEM_CHANNEL_BANKS_AMD
+    case CL_DEVICE_GLOBAL_MEM_CHANNEL_BANKS_AMD:
+#endif
+#ifdef CL_DEVICE_GLOBAL_MEM_CHANNEL_BANK_WIDTH_AMD
+    case CL_DEVICE_GLOBAL_MEM_CHANNEL_BANK_WIDTH_AMD:
+#endif
+#ifdef CL_DEVICE_LOCAL_MEM_SIZE_PER_COMPUTE_UNIT_AMD
+    case CL_DEVICE_LOCAL_MEM_SIZE_PER_COMPUTE_UNIT_AMD:
+#endif
+#ifdef CL_DEVICE_LOCAL_MEM_BANKS_AMD
+    case CL_DEVICE_LOCAL_MEM_BANKS_AMD:
+#endif
+
+#ifdef CL_DEVICE_MAX_ATOMIC_COUNTERS_EXT
+    case CL_DEVICE_MAX_ATOMIC_COUNTERS_EXT:
+#endif
+        return DEV_GET_INT_INF(cl_uint);
+        // }}}
+
+    default:
+        throw clerror("Device.get_info", CL_INVALID_VALUE);
+    }
+}
+
+}
diff --git a/src/c_wrapper/device.h b/src/c_wrapper/device.h
new file mode 100644
index 00000000..9e7ffd5e
--- /dev/null
+++ b/src/c_wrapper/device.h
@@ -0,0 +1,135 @@
+#include "clhelper.h"
+
+#ifndef __PYOPENCL_DEVICE_H
+#define __PYOPENCL_DEVICE_H
+
+namespace pyopencl {
+
+// {{{ device
+
+class device : public clobj<cl_device_id> {
+public:
+    PYOPENCL_DEF_CL_CLASS(DEVICE);
+    enum reference_type_t {
+        REF_NOT_OWNABLE,
+        REF_FISSION_EXT,
+#if PYOPENCL_CL_VERSION >= 0x1020
+        REF_CL_1_2,
+#endif
+    };
+
+private:
+    reference_type_t m_ref_type;
+
+public:
+    device(cl_device_id did, bool retain=false,
+           reference_type_t ref_type=REF_NOT_OWNABLE)
+        : clobj(did), m_ref_type(ref_type)
+    {
+        if (retain && ref_type != REF_NOT_OWNABLE) {
+            if (false) {
+            }
+#if (defined(cl_ext_device_fission) && defined(PYOPENCL_USE_DEVICE_FISSION))
+            else if (ref_type == REF_FISSION_EXT) {
+#if PYOPENCL_CL_VERSION >= 0x1020
+                cl_platform_id plat;
+                pyopencl_call_guarded(clGetDeviceInfo, this,
+                                      CL_DEVICE_PLATFORM, make_sizearg(plat),
+                                      nullptr);
+#endif
+                pyopencl_call_guarded(
+                    pyopencl_get_ext_fun(plat, clRetainDeviceEXT), this);
+            }
+#endif
+#if PYOPENCL_CL_VERSION >= 0x1020
+            else if (ref_type == REF_CL_1_2) {
+                pyopencl_call_guarded(clRetainDevice, this);
+            }
+#endif
+
+            else {
+                throw clerror("Device", CL_INVALID_VALUE,
+                              "cannot own references to devices when device "
+                              "fission or CL 1.2 is not available");
+            }
+        }
+    }
+
+    ~device();
+
+    generic_info get_info(cl_uint param_name) const;
+    // TODO: sub-devices
+    // #if PYOPENCL_CL_VERSION >= 0x1020
+    //       py::list create_sub_devices(py::object py_properties)
+    //       {
+    //         std::vector<cl_device_partition_property> properties;
+
+    //         COPY_PY_LIST(cl_device_partition_property, properties);
+    //         properties.push_back(0);
+
+    //         cl_device_partition_property *props_ptr
+    //           = properties.empty( ) ? nullptr : &properties.front();
+
+    //         cl_uint num_entries;
+    //         PYOPENCL_CALL_GUARDED(clCreateSubDevices,
+    //             (m_device, props_ptr, 0, nullptr, &num_entries));
+
+    //         std::vector<cl_device_id> result;
+    //         result.resize(num_entries);
+
+    //         PYOPENCL_CALL_GUARDED(clCreateSubDevices,
+    //             (m_device, props_ptr, num_entries, &result.front(), nullptr));
+
+    //         py::list py_result;
+    //         BOOST_FOREACH(cl_device_id did, result)
+    //           py_result.append(handle_from_new_ptr(
+    //                 new pyopencl::device(did, /*retain*/true,
+    //                   device::REF_CL_1_2)));
+    //         return py_result;
+    //       }
+    // #endif
+
+    // #if defined(cl_ext_device_fission) && defined(PYOPENCL_USE_DEVICE_FISSION)
+    //       py::list create_sub_devices_ext(py::object py_properties)
+    //       {
+    //         std::vector<cl_device_partition_property_ext> properties;
+
+    // #if PYOPENCL_CL_VERSION >= 0x1020
+    //         cl_platform_id plat;
+    //         PYOPENCL_CALL_GUARDED(clGetDeviceInfo, (m_device, CL_DEVICE_PLATFORM,
+    //               sizeof(plat), &plat, nullptr));
+    // #endif
+
+    //         PYOPENCL_GET_EXT_FUN(plat, clCreateSubDevicesEXT, create_sub_dev);
+
+    //         COPY_PY_LIST(cl_device_partition_property_ext, properties);
+    //         properties.push_back(CL_PROPERTIES_LIST_END_EXT);
+
+    //         cl_device_partition_property_ext *props_ptr
+    //           = properties.empty( ) ? nullptr : &properties.front();
+
+    //         cl_uint num_entries;
+    //         PYOPENCL_CALL_GUARDED(create_sub_dev,
+    //             (m_device, props_ptr, 0, nullptr, &num_entries));
+
+    //         std::vector<cl_device_id> result;
+    //         result.resize(num_entries);
+
+    //         PYOPENCL_CALL_GUARDED(create_sub_dev,
+    //             (m_device, props_ptr, num_entries, &result.front(), nullptr));
+
+    //         py::list py_result;
+    //         BOOST_FOREACH(cl_device_id did, result)
+    //           py_result.append(handle_from_new_ptr(
+    //                 new pyopencl::device(did, /*retain*/true,
+    //                   device::REF_FISSION_EXT)));
+    //         return py_result;
+    //       }
+    // #endif
+};
+
+// }}}
+
+}
+
+#endif
diff --git a/src/c_wrapper/error.h b/src/c_wrapper/error.h
index db2b2587..8eea7a01 100644
--- a/src/c_wrapper/error.h
+++ b/src/c_wrapper/error.h
@@ -173,9 +173,9 @@ c_handle_error(std::function<void()> func) noexcept
     }
 }
 
-template<typename T>
-static PYOPENCL_INLINE T
-retry_mem_error(std::function<T()> func)
+template<typename Func>
+static PYOPENCL_INLINE auto
+retry_mem_error(Func func) -> decltype(func())
 {
     try {
         return func();
diff --git a/src/c_wrapper/event.cpp b/src/c_wrapper/event.cpp
new file mode 100644
index 00000000..8dffa034
--- /dev/null
+++ b/src/c_wrapper/event.cpp
@@ -0,0 +1,177 @@
+#include "event.h"
+#include "command_queue.h"
+#include "context.h"
+#include "async.h"
+#include "pyhelper.h"
+
+namespace pyopencl {
+#if PYOPENCL_CL_VERSION >= 0x1010
+class event_callback {
+    std::function<void(cl_int)> m_func;
+    event_callback(const std::function<void(cl_int)> &func)
+        : m_func(func)
+    {}
+    static void
+    cl_call_and_free(cl_event, cl_int status, void *data)
+    {
+        auto cb = static_cast<event_callback*>(data);
+        auto func = cb->m_func;
+        try {
+            call_async([func, status] {func(status);});
+        } catch (...) {
+        }
+        delete cb;
+    }
+
+    friend class event;
+};
+#endif
+
+event::~event()
+{
+    pyopencl_call_guarded_cleanup(clReleaseEvent, this);
+}
+
+generic_info
+event::get_info(cl_uint param_name) const
+{
+    switch ((cl_event_info)param_name) {
+    case CL_EVENT_COMMAND_QUEUE:
+        return pyopencl_get_opaque_info(command_queue, Event, this, param_name);
+    case CL_EVENT_COMMAND_TYPE:
+        return pyopencl_get_int_info(cl_command_type, Event,
+                                     this, param_name);
+    case CL_EVENT_COMMAND_EXECUTION_STATUS:
+        return pyopencl_get_int_info(cl_int, Event, this, param_name);
+    case CL_EVENT_REFERENCE_COUNT:
+        return pyopencl_get_int_info(cl_uint, Event, this, param_name);
+#if PYOPENCL_CL_VERSION >= 0x1010
+    case CL_EVENT_CONTEXT:
+        return pyopencl_get_opaque_info(context, Event, this, param_name);
+#endif
+
+    default:
+        throw clerror("Event.get_info", CL_INVALID_VALUE);
+    }
+}
+
+generic_info
+event::get_profiling_info(cl_profiling_info param) const
+{
+    switch (param) {
+    case CL_PROFILING_COMMAND_QUEUED:
+    case CL_PROFILING_COMMAND_SUBMIT:
+    case CL_PROFILING_COMMAND_START:
+    case CL_PROFILING_COMMAND_END:
+        return pyopencl_get_int_info(cl_ulong, EventProfiling, this, param);
+    default:
+        throw clerror("Event.get_profiling_info", CL_INVALID_VALUE);
+    }
+}
+
+void
+event::wait()
+{
+    pyopencl_call_guarded(clWaitForEvents,
+                          make_argbuf<ArgType::Length>(data()));
+    finished();
+}
+
+#if PYOPENCL_CL_VERSION >= 0x1010
+void
+event::set_callback(cl_int type, const std::function<void(cl_int)> &func)
+{
+    auto cb = new event_callback(func);
+    try {
+        pyopencl_call_guarded(clSetEventCallback, this, type,
+                              &event_callback::cl_call_and_free, cb);
+    } catch (...) {
+        delete cb;
+        throw;
+    }
+    init_async();
+}
+#endif
+
+nanny_event::~nanny_event()
+{
+    if (m_ward) {
+        wait();
+    }
+}
+
+void
+nanny_event::finished()
+{
+    // No lock needed because multiple release is safe here.
+    void *ward = m_ward;
+    m_ward = nullptr;
+    py::deref(ward);
+}
+
+}
+
+// c wrapper
+// Import all the names in pyopencl namespace for c wrappers.
+using namespace pyopencl;
+
+// Event
+error*
+event__get_profiling_info(clobj_t _evt, cl_profiling_info param,
+                          generic_info *out)
+{
+    auto evt = static_cast<event*>(_evt);
+    return c_handle_error([&] {
+            *out = evt->get_profiling_info(param);
+        });
+}
+
+error*
+event__wait(clobj_t evt)
+{
+    return c_handle_error([&] {
+            static_cast<event*>(evt)->wait();
+        });
+}
+
+#if PYOPENCL_CL_VERSION >= 0x1010
+error*
+event__set_callback(clobj_t _evt, cl_int type, void *pyobj)
+{
+    auto evt = static_cast<event*>(_evt);
+    return c_handle_error([&] {
+            evt->set_callback(type, [=] (cl_int status) {
+                    py::call(pyobj, status);
+                    py::deref(pyobj);
+                });
+            py::ref(pyobj);
+        });
+}
+#endif
+
+// Nanny Event
+void*
+nanny_event__get_ward(clobj_t evt)
+{
+    return static_cast<nanny_event*>(evt)->get_ward();
+}
+
+error*
+wait_for_events(const clobj_t *_wait_for, uint32_t num_wait_for)
+{
+    const auto wait_for = buf_from_class<event>(_wait_for, num_wait_for);
+    return c_handle_error([&] {
+            pyopencl_call_guarded(clWaitForEvents, wait_for);
+        });
+}
+
+error*
+enqueue_wait_for_events(clobj_t _queue, const clobj_t *_wait_for,
+                        uint32_t num_wait_for)
+{
+    auto queue = static_cast<command_queue*>(_queue);
+    const auto wait_for = buf_from_class<event>(_wait_for, num_wait_for);
+    return c_handle_error([&] {
+            pyopencl_call_guarded(clEnqueueWaitForEvents, queue, wait_for);
+        });
+}
diff --git a/src/c_wrapper/event.h b/src/c_wrapper/event.h
new file mode 100644
index 00000000..57517e2f
--- /dev/null
+++ b/src/c_wrapper/event.h
@@ -0,0 +1,68 @@
+#include "clhelper.h"
+
+#ifndef __PYOPENCL_EVENT_H
+#define __PYOPENCL_EVENT_H
+
+namespace pyopencl {
+
+// {{{ event
+
+class event : public clobj<cl_event> {
+public:
+    PYOPENCL_DEF_CL_CLASS(EVENT);
+    PYOPENCL_INLINE
+    event(cl_event event, bool retain)
+        : clobj(event)
+    {
+        if (retain) {
+            pyopencl_call_guarded(clRetainEvent, this);
+        }
+    }
+    ~event();
+    generic_info get_info(cl_uint param) const;
+    PYOPENCL_USE_RESULT generic_info
+    get_profiling_info(cl_profiling_info param) const;
+    virtual void
+    finished()
+    {}
+    void wait();
+#if PYOPENCL_CL_VERSION >= 0x1010
+    void set_callback(cl_int type, const std::function<void(cl_int)> &func);
+#endif
+};
+PYOPENCL_USE_RESULT static PYOPENCL_INLINE event*
+new_event(cl_event evt)
+{
+    return pyopencl_convert_obj(event, clReleaseEvent, evt);
+}
+
+class nanny_event : public event {
+private:
+    void *m_ward;
+public:
+    nanny_event(cl_event evt, bool retain, void *ward=nullptr)
+        : event(evt, retain), m_ward(ward)
+    {
+        if (ward) {
+            py::ref(ward);
+        }
+    }
+    ~nanny_event();
+    PYOPENCL_USE_RESULT PYOPENCL_INLINE void*
+    get_ward() const
+    {
+        return m_ward;
+    }
+    void finished();
+};
+PYOPENCL_USE_RESULT static PYOPENCL_INLINE event*
+new_nanny_event(cl_event evt, void *ward)
+{
+    return pyopencl_convert_obj(nanny_event, clReleaseEvent, evt, ward);
+}
+
+// }}}
+
+}
+
+#endif
diff --git a/src/c_wrapper/gl_obj.cpp b/src/c_wrapper/gl_obj.cpp
new file mode 100644
index 00000000..682c630d
--- /dev/null
+++ b/src/c_wrapper/gl_obj.cpp
@@ -0,0 +1,232 @@
+#include "gl_obj.h"
+#include "context.h"
+#include "command_queue.h"
+#include "event.h"
+#include "clhelper.h"
+
+#ifdef HAVE_GL
+
+namespace pyopencl {
+
+generic_info
+gl_texture::get_gl_texture_info(cl_gl_texture_info param_name) const
+{
+    switch (param_name) {
+    case CL_GL_TEXTURE_TARGET:
+        return pyopencl_get_int_info(GLenum, GLTexture, this, param_name);
+    case CL_GL_MIPMAP_LEVEL:
+        return pyopencl_get_int_info(GLint, GLTexture, this, param_name);
+    default:
+        throw clerror("MemoryObject.get_gl_texture_info", CL_INVALID_VALUE);
+    }
+}
+
+#if 0
+PYOPENCL_USE_RESULT static gl_texture*
+create_from_gl_texture(const context *ctx, cl_mem_flags flags,
+                       GLenum texture_target, GLint miplevel,
+                       GLuint texture, unsigned dims)
+{
+    if (dims == 2) {
+        cl_mem mem = pyopencl_call_guarded(clCreateFromGLTexture2D,
+                                           ctx, flags, texture_target,
+                                           miplevel, texture);
+        return pyopencl_convert_obj(gl_texture, clReleaseMemObject, mem);
+    } else if (dims == 3) {
+        cl_mem mem = pyopencl_call_guarded(clCreateFromGLTexture3D,
+                                           ctx, flags, texture_target,
+                                           miplevel, texture);
+        return pyopencl_convert_obj(gl_texture, clReleaseMemObject, mem);
+    } else {
+        throw clerror("Image", CL_INVALID_VALUE, "invalid dimension");
+    }
+}
+#endif
+
+// TODO:
+// PYOPENCL_INLINE
+// py::tuple get_gl_object_info(memory_object_holder const &mem)
+// {
+//   cl_gl_object_type otype;
+//   GLuint gl_name;
+//   PYOPENCL_CALL_GUARDED(clGetGLObjectInfo, (mem, &otype, &gl_name));
+//   return py::make_tuple(otype, gl_name);
+// }
+
+typedef cl_int (*clEnqueueGLObjectFunc)(cl_command_queue, cl_uint,
+                                        const cl_mem*, cl_uint,
+                                        const cl_event*, cl_event*);
+
+PYOPENCL_USE_RESULT static PYOPENCL_INLINE event*
+enqueue_gl_objects(clEnqueueGLObjectFunc func, const char *name,
+                   command_queue *cq, const clobj_t *mem_objects,
+                   uint32_t num_mem_objects, const clobj_t *wait_for,
+                   uint32_t num_wait_for)
+{
+    const auto _wait_for = buf_from_class<event>(wait_for, num_wait_for);
+    const auto _mem_objs = buf_from_class<memory_object>(
+        mem_objects, num_mem_objects);
+    cl_event evt;
+    call_guarded(func, name, cq, _mem_objs, _wait_for, &evt);
+    return new_event(evt);
+}
+#define enqueue_gl_objects(what, args...)                       \
+    enqueue_gl_objects(clEnqueue##what##GLObjects,              \
+                       "clEnqueue" #what "GLObjects", args)
+
+// #if defined(cl_khr_gl_sharing) && (cl_khr_gl_sharing >= 1)
+//   PYOPENCL_INLINE
+//   py::object get_gl_context_info_khr(
+//       py::object py_properties,
+//       cl_gl_context_info param_name,
+//       py::object py_platform
+//       )
+//   {
+//     std::vector<cl_context_properties> props
+//       = parse_context_properties(py_properties);
+
+//     typedef CL_API_ENTRY cl_int (CL_API_CALL
+//       *func_ptr_type)(const cl_context_properties * /* properties */,
+//           cl_gl_context_info            /* param_name */,
+//           size_t                        /* param_value_size */,
+//           void *                        /* param_value */,
+//           size_t *                      /* param_value_size_ret */) CL_API_SUFFIX__VERSION_1_0;
+
+//     func_ptr_type func_ptr;
+
+// #if PYOPENCL_CL_VERSION >= 0x1020
+//     if (py_platform.ptr() != Py_None)
+//     {
+//       platform &plat = py::extract<platform &>(py_platform);
+
+//       func_ptr = (func_ptr_type) clGetExtensionFunctionAddressForPlatform(
+//             plat.data(), "clGetGLContextInfoKHR");
+//     }
+//     else
+//     {
+//       PYOPENCL_DEPRECATED("get_gl_context_info_khr with platform=None", "2013.1", );
+
+//       func_ptr = (func_ptr_type) clGetExtensionFunctionAddress(
+//             "clGetGLContextInfoKHR");
+//     }
+// #else
+//     func_ptr = (func_ptr_type) clGetExtensionFunctionAddress(
+//           "clGetGLContextInfoKHR");
+// #endif
+
+
+//     if (!func_ptr)
+//       throw error("Context.get_info", CL_INVALID_PLATFORM,
+//           "clGetGLContextInfoKHR extension function not present");
+
+//     cl_context_properties *props_ptr
+//       = props.empty( ) ? nullptr : &props.front();
+
+//     switch (param_name)
+//     {
+//       case CL_CURRENT_DEVICE_FOR_GL_CONTEXT_KHR:
+//         {
+//           cl_device_id param_value;
+//           PYOPENCL_CALL_GUARDED(func_ptr,
+//               (props_ptr, param_name, sizeof(param_value), &param_value, 0));
+//           return py::object(handle_from_new_ptr( new device(param_value, /*retain*/ true)));
+//         }
+
+//       case CL_DEVICES_FOR_GL_CONTEXT_KHR:
+//         {
+//           size_t size;
+//           PYOPENCL_CALL_GUARDED(func_ptr,
+//               (props_ptr, param_name, 0, 0, &size));
+
+//           std::vector<cl_device_id> devices;
+
+//           devices.resize(size / sizeof(devices.front()));
+
+//           PYOPENCL_CALL_GUARDED(func_ptr,
+//               (props_ptr, param_name, size,
+//                devices.empty( ) ? nullptr : &devices.front(), &size));
+
+//           py::list result;
+//           BOOST_FOREACH(cl_device_id did, devices)
+//             result.append(handle_from_new_ptr(
+//                   new device(did)));
+
+//           return result;
+//         }
+
+//       default:
+//         throw error("get_gl_context_info_khr", CL_INVALID_VALUE);
+//     }
+//   }
+
+// #endif
+
+}
+
+// c wrapper
+// Import all the names in pyopencl namespace for c wrappers.
+using namespace pyopencl;
+
+error*
+create_from_gl_buffer(clobj_t *ptr, clobj_t _ctx,
+                      cl_mem_flags flags, GLuint bufobj)
+{
+    auto ctx = static_cast<context*>(_ctx);
+    return c_handle_error([&] {
+            cl_mem mem = pyopencl_call_guarded(clCreateFromGLBuffer,
+                                               ctx, flags, bufobj);
+            *ptr = pyopencl_convert_obj(gl_buffer,
+                                        clReleaseMemObject, mem);
+        });
+}
+
+error*
+create_from_gl_renderbuffer(clobj_t *ptr, clobj_t _ctx,
+                            cl_mem_flags flags, GLuint bufobj)
+{
+    auto ctx = static_cast<context*>(_ctx);
+    return c_handle_error([&] {
+            cl_mem mem = pyopencl_call_guarded(clCreateFromGLRenderbuffer,
+                                               ctx, flags, bufobj);
+            *ptr = pyopencl_convert_obj(gl_renderbuffer,
+                                        clReleaseMemObject, mem);
+        });
+}
+
+error*
+enqueue_acquire_gl_objects(clobj_t *_evt, clobj_t queue,
+                           const clobj_t *mem_objects,
+                           uint32_t num_mem_objects,
+                           const clobj_t *wait_for, uint32_t num_wait_for)
+{
+    return c_handle_error([&] {
+            *_evt = enqueue_gl_objects(
+                Acquire, static_cast<command_queue*>(queue),
+                mem_objects, num_mem_objects, wait_for, num_wait_for);
+        });
+}
+
+error*
+enqueue_release_gl_objects(clobj_t *event, clobj_t queue,
+                           const clobj_t *mem_objects,
+                           uint32_t num_mem_objects,
+                           const clobj_t *wait_for, uint32_t num_wait_for)
+{
+    return c_handle_error([&] {
+            *event = enqueue_gl_objects(
+                Release, static_cast<command_queue*>(queue),
+                mem_objects, num_mem_objects, wait_for, num_wait_for);
+        });
+}
+
+#endif
+
+int
+have_gl()
+{
+#ifdef HAVE_GL
+    return 1;
+#else
+    return 0;
+#endif
+}
diff --git a/src/c_wrapper/gl_obj.h b/src/c_wrapper/gl_obj.h
new file mode 100644
index 00000000..09ebc507
--- /dev/null
+++ b/src/c_wrapper/gl_obj.h
@@ -0,0 +1,57 @@
+#include "image.h"
+
+#ifndef __PYOPENCL_GL_OBJ_H
+#define __PYOPENCL_GL_OBJ_H
+
+#ifdef HAVE_GL
+
+namespace pyopencl {
+
+// {{{ gl interop
+
+#ifdef __APPLE__
+static PYOPENCL_INLINE cl_context_properties
+get_apple_cgl_share_group()
+{
+    CGLContextObj kCGLContext = CGLGetCurrentContext();
+    CGLShareGroupObj kCGLShareGroup = CGLGetShareGroup(kCGLContext);
+
+    return (cl_context_properties)kCGLShareGroup;
+}
+#endif /* __APPLE__ */
+
+class gl_buffer : public memory_object {
+public:
+    PYOPENCL_DEF_CL_CLASS(GL_BUFFER);
+    PYOPENCL_INLINE
+    gl_buffer(cl_mem mem, bool retain, void *hostbuf=0)
+        : memory_object(mem, retain, hostbuf)
+    {}
+};
+
+class gl_renderbuffer : public memory_object {
+public:
+    PYOPENCL_DEF_CL_CLASS(GL_RENDERBUFFER);
+    PYOPENCL_INLINE
+    gl_renderbuffer(cl_mem mem, bool retain, void *hostbuf=0)
+        : memory_object(mem, retain, hostbuf)
+    {}
+};
+
+class gl_texture : public image {
+  public:
+    PYOPENCL_INLINE
+    gl_texture(cl_mem mem, bool retain, void *hostbuf=0)
+      : image(mem, retain, hostbuf)
+    {}
+    PYOPENCL_USE_RESULT generic_info
+    get_gl_texture_info(cl_gl_texture_info param_name) const;
+};
+
+// }}}
+
+#endif
+
+}
+
+#endif
diff --git a/src/c_wrapper/image.cpp b/src/c_wrapper/image.cpp
new file mode 100644
index 00000000..49d20ec6
--- /dev/null
+++ b/src/c_wrapper/image.cpp
@@ -0,0 +1,326 @@
+#include "image.h"
+#include "context.h"
+#include "command_queue.h"
+#include "event.h"
+
+namespace pyopencl {
+
+generic_info
+image::get_image_info(cl_image_info param) const
+{
+    switch (param) {
+    case CL_IMAGE_FORMAT:
+        return pyopencl_get_int_info(cl_image_format, Image, this, param);
+    case CL_IMAGE_ELEMENT_SIZE:
+    case CL_IMAGE_ROW_PITCH:
+    case CL_IMAGE_SLICE_PITCH:
+    case CL_IMAGE_WIDTH:
+    case CL_IMAGE_HEIGHT:
+    case CL_IMAGE_DEPTH:
+#if PYOPENCL_CL_VERSION >= 0x1020
+    case CL_IMAGE_ARRAY_SIZE:
+#endif
+        return pyopencl_get_int_info(size_t, Image, this, param);
+
+#if PYOPENCL_CL_VERSION >= 0x1020
+        // TODO:
+        //    case CL_IMAGE_BUFFER:
+        //      {
+        //        cl_mem param_value;
+        //        PYOPENCL_CALL_GUARDED(clGetImageInfo, (this, param, sizeof(param_value), &param_value, 0));
+        //        if (param_value == 0)
+        //               {
+        //                 // no associated memory object? no problem.
+        //                 return py::object();
+        //               }
+        //        return create_mem_object_wrapper(param_value);
+        //      }
+    case CL_IMAGE_NUM_MIP_LEVELS:
+    case CL_IMAGE_NUM_SAMPLES:
+        return pyopencl_get_int_info(cl_uint, Image, this, param);
+#endif
+    default:
+        throw clerror("Image.get_image_info", CL_INVALID_VALUE);
+    }
+}
+
+// {{{ image creation
+
+// #if PYOPENCL_CL_VERSION >= 0x1020
+
+//   PYOPENCL_INLINE
+//   image *create_image_from_desc(
+//       context const &ctx,
+//       cl_mem_flags flags,
+//       cl_image_format const &fmt,
+//       cl_image_desc &desc,
+//       py::object buffer)
+//   {
+//     if (buffer.ptr() != Py_None &&
+//         !(flags & (CL_MEM_USE_HOST_PTR | CL_MEM_COPY_HOST_PTR)))
+//       PyErr_Warn(PyExc_UserWarning, "'hostbuf' was passed, "
+//           "but no memory flags to make use of it.");
+
+//     void *buf = 0;
+//     PYOPENCL_BUFFER_SIZE_T len;
+//     py::object *retained_buf_obj = 0;
+
+//     if (buffer.ptr() != Py_None)
+//     {
+//       if (flags & CL_MEM_USE_HOST_PTR)
+//       {
+//         if (PyObject_AsWriteBuffer(buffer.ptr(), &buf, &len))
+//           throw py::error_already_set();
+//       }
+//       else
+//       {
+//         if (PyObject_AsReadBuffer(
+//               buffer.ptr(), const_cast<const void **>(&buf), &len))
+//           throw py::error_already_set();
+//       }
+
+//       if (flags & CL_MEM_USE_HOST_PTR)
+//         retained_buf_obj = &buffer;
+//     }
+
+//     PYOPENCL_PRINT_CALL_TRACE("clCreateImage");
+//     cl_int status_code;
+//     cl_mem mem = clCreateImage(ctx.data(), flags, &fmt, &desc, buf, &status_code);
+//     if (status_code != CL_SUCCESS)
+//       throw clerror("clCreateImage", status_code);
+
+//     try
+//     {
+//       return new image(mem, false, retained_buf_obj);
+//     }
+//     catch (...)
+//     {
+//       PYOPENCL_CALL_GUARDED(clReleaseMemObject, (mem));
+//       throw;
+//     }
+//   }
+
+// #endif
+
+// }}}
+
+// {{{ image transfers
+
+//   PYOPENCL_INLINE
+//   event *enqueue_copy_image_to_buffer(
+//       command_queue &cq,
+//       memory_object_holder &src,
+//       memory_object_holder &dest,
+//       py::object py_origin,
+//       py::object py_region,
+//       size_t offset,
+//       py::object py_wait_for
+//       )
+//   {
+//     PYOPENCL_PARSE_WAIT_FOR;
+//     COPY_PY_COORD_TRIPLE(origin);
+//     COPY_PY_REGION_TRIPLE(region);
+
+//     cl_event evt;
+//     PYOPENCL_RETRY_IF_MEM_ERROR(
+//       PYOPENCL_CALL_GUARDED(clEnqueueCopyImageToBuffer, (
+//             cq.data(), src.data(), dest.data(),
+//             origin, region, offset,
+//             PYOPENCL_WAITLIST_ARGS, &evt
+//             ));
+//       );
+//     PYOPENCL_RETURN_NEW_EVENT(evt);
+//   }
+
+//   PYOPENCL_INLINE
+//   event *enqueue_copy_buffer_to_image(
+//       command_queue &cq,
+//       memory_object_holder &src,
+//       memory_object_holder &dest,
+//       size_t offset,
+//       py::object py_origin,
+//       py::object py_region,
+//       py::object py_wait_for
+//       )
+//   {
+//     PYOPENCL_PARSE_WAIT_FOR;
+//     COPY_PY_COORD_TRIPLE(origin);
+//     COPY_PY_REGION_TRIPLE(region);
+
+//     cl_event evt;
+//     PYOPENCL_RETRY_IF_MEM_ERROR(
+//       PYOPENCL_CALL_GUARDED(clEnqueueCopyBufferToImage, (
+//             cq.data(), src.data(), dest.data(),
+//             offset, origin, region,
+//             PYOPENCL_WAITLIST_ARGS, &evt
+//             ));
+//       );
+//     PYOPENCL_RETURN_NEW_EVENT(evt);
+//   }
+
+// }}}
+
+}
+
+// c wrapper
+// Import all the names in pyopencl namespace for c wrappers.
+using namespace pyopencl;
+
+// Image
+error*
+create_image_2d(clobj_t *img, clobj_t _ctx, cl_mem_flags flags,
+                cl_image_format *fmt, size_t width, size_t height,
+                size_t pitch, void *buffer)
+{
+    auto ctx = static_cast<context*>(_ctx);
+    return c_handle_error([&] {
+            auto mem = retry_mem_error([&] {
+                    return pyopencl_call_guarded(
+                        clCreateImage2D, ctx, flags,
+                        fmt, width, height, pitch, buffer);
+                });
+            *img = new_image(mem, (flags & CL_MEM_USE_HOST_PTR ?
+                                   buffer : nullptr), fmt);
+        });
+}
+
+error*
+create_image_3d(clobj_t *img, clobj_t _ctx, cl_mem_flags flags,
+                cl_image_format *fmt, size_t width, size_t height,
+                size_t depth, size_t pitch_x, size_t pitch_y, void *buffer)
+{
+    auto ctx = static_cast<context*>(_ctx);
+    return c_handle_error([&] {
+            auto mem = retry_mem_error([&] {
+                    return pyopencl_call_guarded(
+                        clCreateImage3D, ctx, flags, fmt, width,
+                        height, depth, pitch_x, pitch_y, buffer);
+                });
+            *img = new_image(mem, (flags & CL_MEM_USE_HOST_PTR ?
+                                   buffer : nullptr), fmt);
+        });
+}
+
+error*
+image__get_image_info(clobj_t _img, cl_image_info param, generic_info *out)
+{
+    auto img = static_cast<image*>(_img);
+    return c_handle_error([&] {
+            *out = img->get_image_info(param);
+        });
+}
+
+type_t
+image__get_fill_type(clobj_t img)
+{
+    return static_cast<image*>(img)->get_fill_type();
+}
+
+error*
+enqueue_read_image(clobj_t *_evt, clobj_t _queue, clobj_t _mem,
+                   const size_t *_origin, size_t origin_l,
+                   const size_t *_region, size_t region_l, void *buffer,
+                   size_t row_pitch, size_t slice_pitch,
+                   const clobj_t *_wait_for, uint32_t num_wait_for,
+                   int is_blocking, void *pyobj)
+{
+    const auto wait_for = buf_from_class<event>(_wait_for, num_wait_for);
+    auto queue = static_cast<command_queue*>(_queue);
+    auto img = static_cast<image*>(_mem);
+    ConstBuffer<size_t, 3> origin(_origin, origin_l);
+    ConstBuffer<size_t, 3> region(_region, region_l);
+    return c_handle_error([&] {
+            cl_event evt;
+            retry_mem_error([&] {
+                    pyopencl_call_guarded(
+                        clEnqueueReadImage, queue, img,
+                        cast_bool(is_blocking), origin, region, row_pitch,
+                        slice_pitch, buffer, wait_for, &evt);
+                });
+            *_evt = new_nanny_event(evt, pyobj);
+        });
+}
+
+error*
+enqueue_copy_image(clobj_t *_evt, clobj_t _queue, clobj_t _src, clobj_t _dst,
+                   const size_t *_src_origin, size_t src_origin_l,
+                   const size_t *_dst_origin, size_t dst_origin_l,
+                   const size_t *_region, size_t region_l,
+                   const clobj_t *_wait_for, uint32_t num_wait_for)
+{
+    const auto wait_for = buf_from_class<event>(_wait_for, num_wait_for);
+    auto queue = static_cast<command_queue*>(_queue);
+    auto src = static_cast<image*>(_src);
+    auto dst = static_cast<image*>(_dst);
+    ConstBuffer<size_t, 3> src_origin(_src_origin, src_origin_l);
+    ConstBuffer<size_t, 3> dst_origin(_dst_origin, dst_origin_l);
+    ConstBuffer<size_t, 3> region(_region, region_l);
+    return c_handle_error([&] {
+            cl_event evt;
+            retry_mem_error([&] {
+                    pyopencl_call_guarded(
+                        clEnqueueCopyImage, queue, src, dst, src_origin,
+                        dst_origin, region, wait_for, &evt);
+                });
+            *_evt = new_event(evt);
+        });
+}
+
+error*
+enqueue_write_image(clobj_t *_evt, clobj_t _queue, clobj_t _mem,
+                    const size_t *_origin, size_t origin_l,
+                    const size_t *_region, size_t region_l,
+                    const void *buffer, size_t row_pitch, size_t slice_pitch,
+                    const clobj_t *_wait_for, uint32_t num_wait_for,
+                    int is_blocking, void *pyobj)
+{
+    auto queue = static_cast<command_queue*>(_queue);
+    auto img = static_cast<image*>(_mem);
+    const auto wait_for = buf_from_class<event>(_wait_for, num_wait_for);
+    ConstBuffer<size_t, 3> origin(_origin, origin_l);
+    ConstBuffer<size_t, 3> region(_region, region_l);
+    return c_handle_error([&] {
+            cl_event evt;
+            retry_mem_error([&] {
+                    pyopencl_call_guarded(
+                        clEnqueueWriteImage, queue, img,
+                        cast_bool(is_blocking), origin, region, row_pitch,
+                        slice_pitch, buffer, wait_for, &evt);
+                });
+            *_evt = new_nanny_event(evt, pyobj);
+        });
+}
+
+#if PYOPENCL_CL_VERSION >= 0x1020
+//   PYOPENCL_INLINE
+//   event *enqueue_fill_image(
+//       command_queue &cq,
+//       memory_object_holder &mem,
+//       py::object color,
+//       py::object py_origin, py::object py_region,
+//       py::object py_wait_for
+//       )
+//   {
+//     PYOPENCL_PARSE_WAIT_FOR;
+
+//     COPY_PY_COORD_TRIPLE(origin);
+//     COPY_PY_REGION_TRIPLE(region);
+
+//     const void *color_buf;
+//     PYOPENCL_BUFFER_SIZE_T color_len;
+
+//     if (PyObject_AsReadBuffer(color.ptr(), &color_buf, &color_len))
+//       throw py::error_already_set();
+
+//     cl_event evt;
+//     PYOPENCL_RETRY_IF_MEM_ERROR(
+//       PYOPENCL_CALL_GUARDED(clEnqueueFillImage, (
+//             cq.data(),
+//             mem.data(),
+//             color_buf, origin, region,
+//             PYOPENCL_WAITLIST_ARGS, &evt
+//             ));
+//       );
+//     PYOPENCL_RETURN_NEW_EVENT(evt);
+//   }
+#endif
diff --git a/src/c_wrapper/image.h b/src/c_wrapper/image.h
new file mode 100644
index 00000000..ee96d588
--- /dev/null
+++ b/src/c_wrapper/image.h
@@ -0,0 +1,56 @@
+#include "memory_object.h"
+#include "clhelper.h"
+
+#ifndef __PYOPENCL_IMAGE_H
+#define __PYOPENCL_IMAGE_H
+
+namespace pyopencl {
+
+// {{{ image
+
+class image : public memory_object {
+private:
+    cl_image_format m_format;
+public:
+    PYOPENCL_DEF_CL_CLASS(IMAGE);
+    PYOPENCL_INLINE
+    image(cl_mem mem, bool retain, void *hostbuf=0,
+          const cl_image_format *fmt=0)
+        : memory_object(mem, retain, hostbuf)
+    {
+        if (fmt) {
+            m_format = *fmt;
+        } else {
+            pyopencl_call_guarded(clGetImageInfo, this, CL_IMAGE_FORMAT,
+                                  make_sizearg(m_format), nullptr);
+        }
+    }
+    PYOPENCL_USE_RESULT generic_info get_image_info(cl_image_info param) const;
+    PYOPENCL_INLINE type_t
+    get_fill_type()
+    {
+        switch (m_format.image_channel_data_type) {
+        case CL_SIGNED_INT8:
+        case CL_SIGNED_INT16:
+        case CL_SIGNED_INT32:
+            return TYPE_INT;
+        case CL_UNSIGNED_INT8:
+        case CL_UNSIGNED_INT16:
+        case CL_UNSIGNED_INT32:
+            return TYPE_UINT;
+        default:
+            return TYPE_FLOAT;
+        }
+    }
+};
+PYOPENCL_USE_RESULT static PYOPENCL_INLINE image*
+new_image(cl_mem mem, void *buff, const cl_image_format *fmt)
+{
+    return pyopencl_convert_obj(image, clReleaseMemObject, mem, buff, fmt);
+}
+
+// }}}
+
+}
+
+#endif
diff --git a/src/c_wrapper/kernel.cpp b/src/c_wrapper/kernel.cpp
new file mode 100644
index 00000000..b7f55ecb
--- /dev/null
+++ b/src/c_wrapper/kernel.cpp
@@ -0,0 +1,172 @@
+#include "kernel.h"
+#include "context.h"
+#include "device.h"
+#include "program.h"
+#include "memory_object.h"
+#include "sampler.h"
+#include "command_queue.h"
+#include "event.h"
+#include "clhelper.h"
+
+namespace pyopencl {
+
+kernel::~kernel()
+{
+    pyopencl_call_guarded_cleanup(clReleaseKernel, this);
+}
+
+generic_info
+kernel::get_info(cl_uint param) const
+{
+    switch ((cl_kernel_info)param) {
+    case CL_KERNEL_FUNCTION_NAME:
+        return pyopencl_get_str_info(Kernel, this, param);
+    case CL_KERNEL_NUM_ARGS:
+    case CL_KERNEL_REFERENCE_COUNT:
+        return pyopencl_get_int_info(cl_uint, Kernel, this, param);
+    case CL_KERNEL_CONTEXT:
+        return pyopencl_get_opaque_info(context, Kernel, this, param);
+    case CL_KERNEL_PROGRAM:
+        return pyopencl_get_opaque_info(program, Kernel, this, param);
+#if PYOPENCL_CL_VERSION >= 0x1020
+    case CL_KERNEL_ATTRIBUTES:
+        return pyopencl_get_str_info(Kernel, this, param);
+#endif
+    default:
+        throw clerror("Kernel.get_info", CL_INVALID_VALUE);
+    }
+}
+
+generic_info
+kernel::get_work_group_info(cl_kernel_work_group_info param,
+                            const device *dev) const
+{
+    switch (param) {
+#if PYOPENCL_CL_VERSION >= 0x1010
+    case CL_KERNEL_PREFERRED_WORK_GROUP_SIZE_MULTIPLE:
+#endif
+    case CL_KERNEL_WORK_GROUP_SIZE:
+        return pyopencl_get_int_info(size_t, KernelWorkGroup, this, dev, param);
+    case CL_KERNEL_COMPILE_WORK_GROUP_SIZE:
+        return pyopencl_get_array_info(size_t, KernelWorkGroup,
+                                       this, dev, param);
+    case CL_KERNEL_LOCAL_MEM_SIZE:
+#if PYOPENCL_CL_VERSION >= 0x1010
+    case CL_KERNEL_PRIVATE_MEM_SIZE:
+#endif
+        return pyopencl_get_int_info(cl_ulong, KernelWorkGroup,
+                                     this, dev, param);
+    default:
+        throw clerror("Kernel.get_work_group_info", CL_INVALID_VALUE);
+    }
+}
+
+}
+
+// c wrapper
+// Import all the names in pyopencl namespace for c wrappers.
+using namespace pyopencl;
+
+// Kernel
+error*
+create_kernel(clobj_t *knl, clobj_t _prog, const char *name)
+{
+    auto prog = static_cast<const program*>(_prog);
+    return c_handle_error([&] {
+            *knl = new kernel(pyopencl_call_guarded(clCreateKernel, prog,
+                                                    name), false);
+        });
+}
+
+error*
+kernel__set_arg_null(clobj_t _knl, cl_uint arg_index)
+{
+    auto knl = static_cast<kernel*>(_knl);
+    return c_handle_error([&] {
+            const cl_mem m = 0;
+            pyopencl_call_guarded(clSetKernelArg, knl,
+                                  arg_index, make_sizearg(m));
+        });
+}
+
+error*
+kernel__set_arg_mem(clobj_t _knl, cl_uint arg_index, clobj_t _mem)
+{
+    auto knl = static_cast<kernel*>(_knl);
+    auto mem = static_cast<memory_object*>(_mem);
+    return c_handle_error([&] {
+            pyopencl_call_guarded(clSetKernelArg, knl, arg_index,
+                                  make_sizearg(mem->data()));
+        });
+}
+
+error*
+kernel__set_arg_sampler(clobj_t _knl, cl_uint arg_index, clobj_t _samp)
+{
+    auto knl = static_cast<kernel*>(_knl);
+    auto samp = static_cast<sampler*>(_samp);
+    return c_handle_error([&] {
+            pyopencl_call_guarded(clSetKernelArg, knl, arg_index,
+                                  make_sizearg(samp->data()));
+        });
+}
+
+error*
+kernel__set_arg_buf(clobj_t _knl, cl_uint arg_index,
+                    const void *buffer, size_t size)
+{
+    auto knl = static_cast<kernel*>(_knl);
+    return c_handle_error([&] {
+            pyopencl_call_guarded(clSetKernelArg, knl, arg_index, size, buffer);
+        });
+}
+
+error*
+kernel__get_work_group_info(clobj_t _knl, cl_kernel_work_group_info param,
+                            clobj_t _dev, generic_info *out)
+{
+    auto knl = static_cast<kernel*>(_knl);
+    auto dev = static_cast<device*>(_dev);
+    return c_handle_error([&] {
+            *out = knl->get_work_group_info(param, dev);
+        });
+}
+
+error*
+enqueue_nd_range_kernel(clobj_t *_evt, clobj_t _queue, clobj_t _knl,
+                        cl_uint work_dim, const size_t *global_work_offset,
+                        const size_t *global_work_size,
+                        const size_t *local_work_size,
+                        const clobj_t *_wait_for, uint32_t num_wait_for)
+{
+    auto queue = static_cast<command_queue*>(_queue);
+    auto knl = static_cast<kernel*>(_knl);
+    const auto wait_for = buf_from_class<event>(_wait_for, num_wait_for);
+    return c_handle_error([&] {
+            cl_event evt;
+            retry_mem_error([&] {
+                    pyopencl_call_guarded(
+                        clEnqueueNDRangeKernel, queue, knl,
+                        work_dim, global_work_offset, global_work_size,
+                        local_work_size, wait_for, &evt);
+                });
+            *_evt = new_event(evt);
+        });
+}
+
+error*
+enqueue_task(clobj_t *_evt, clobj_t _queue, clobj_t _knl,
+             const clobj_t *_wait_for, uint32_t num_wait_for)
+{
+    auto queue = static_cast<command_queue*>(_queue);
+    auto knl = static_cast<kernel*>(_knl);
+    const auto wait_for = buf_from_class<event>(_wait_for, num_wait_for);
+    return c_handle_error([&] {
+            cl_event evt;
+            retry_mem_error([&] {
+                    pyopencl_call_guarded(
+                        clEnqueueTask, queue, knl, wait_for, &evt);
+                });
+            *_evt = new_event(evt);
+        });
+}
diff --git a/src/c_wrapper/kernel.h b/src/c_wrapper/kernel.h
new file mode 100644
index 00000000..5c52bdcc
--- /dev/null
+++ b/src/c_wrapper/kernel.h
@@ -0,0 +1,64 @@
+#include "error.h"
+
+#ifndef __PYOPENCL_KERNEL_H
+#define __PYOPENCL_KERNEL_H
+
+namespace pyopencl {
+
+class device;
+
+// {{{ kernel
+
+class kernel : public clobj<cl_kernel> {
+public:
+    PYOPENCL_DEF_CL_CLASS(KERNEL);
+    PYOPENCL_INLINE
+    kernel(cl_kernel knl, bool retain)
+        : clobj(knl)
+    {
+        if (retain) {
+            pyopencl_call_guarded(clRetainKernel, this);
+        }
+    }
+    ~kernel();
+    generic_info get_info(cl_uint param) const;
+
+    PYOPENCL_USE_RESULT generic_info
+    get_work_group_info(cl_kernel_work_group_info param,
+                        const device *dev) const;
+
+    // #if PYOPENCL_CL_VERSION >= 0x1020
+    //       py::object get_arg_info(
+    //           cl_uint arg_index,
+    //           cl_kernel_arg_info param_name
+    //           ) const
+    //       {
+    //         switch (param_name)
+    //         {
+    // #define PYOPENCL_FIRST_ARG data(), arg_index // hackety hack
+    //           case CL_KERNEL_ARG_ADDRESS_QUALIFIER:
+    //             PYOPENCL_GET_INTEGRAL_INFO(KernelArg,
+    //                 PYOPENCL_FIRST_ARG, param_name,
+    //                 cl_kernel_arg_address_qualifier);
+
+    //           case CL_KERNEL_ARG_ACCESS_QUALIFIER:
+    //             PYOPENCL_GET_INTEGRAL_INFO(KernelArg,
+    //                 PYOPENCL_FIRST_ARG, param_name,
+    //                 cl_kernel_arg_access_qualifier);
+
+    //           case CL_KERNEL_ARG_TYPE_NAME:
+    //           case CL_KERNEL_ARG_NAME:
+    //             PYOPENCL_GET_STR_INFO(KernelArg, PYOPENCL_FIRST_ARG, param_name);
+    // #undef PYOPENCL_FIRST_ARG
+    //           default:
+    //             throw error("Kernel.get_arg_info", CL_INVALID_VALUE);
+    //         }
+    //       }
+    // #endif
+};
+
+// }}}
+
+}
+
+#endif
diff --git a/src/c_wrapper/memory_map.cpp b/src/c_wrapper/memory_map.cpp
new file mode 100644
index 00000000..3826074c
--- /dev/null
+++ b/src/c_wrapper/memory_map.cpp
@@ -0,0 +1,128 @@
+#include "memory_map.h"
+#include "image.h"
+#include "buffer.h"
+#include "event.h"
+#include "clhelper.h"
+
+namespace pyopencl {
+
+memory_map::~memory_map()
+{
+    if (!m_valid.exchange(false))
+        return;
+    pyopencl_call_guarded_cleanup(clEnqueueUnmapMemObject, m_queue,
+                                  m_mem, this, 0, nullptr, nullptr);
+}
+
+event*
+memory_map::release(const command_queue *queue, const clobj_t *_wait_for,
+                    uint32_t num_wait_for) const
+{
+    if (!m_valid.exchange(false)) {
+        throw clerror("MemoryMap.release", CL_INVALID_VALUE,
+                      "trying to double-unref mem map");
+    }
+    const auto wait_for = buf_from_class<event>(_wait_for, num_wait_for);
+    queue = queue ? queue : &m_queue;
+    cl_event evt;
+    pyopencl_call_guarded(clEnqueueUnmapMemObject, queue,
+                          m_mem, this, wait_for, &evt);
+    return new_event(evt);
+}
+
+generic_info
+memory_map::get_info(cl_uint) const
+{
+    throw clerror("MemoryMap.get_info", CL_INVALID_VALUE);
+}
+
+intptr_t
+memory_map::intptr() const
+{
+    return m_valid ? (intptr_t)data() : 0;
+}
+
+}
+
+// c wrapper
+// Import all the names in pyopencl namespace for c wrappers.
+using namespace pyopencl;
+
+// Memory Map
+error*
+memory_map__release(clobj_t _map, clobj_t _queue, const clobj_t *_wait_for,
+                    uint32_t num_wait_for, clobj_t *evt)
+{
+    auto map = static_cast<memory_map*>(_map);
+    auto queue = static_cast<command_queue*>(_queue);
+    return c_handle_error([&] {
+            *evt = map->release(queue, _wait_for, num_wait_for);
+        });
+}
+
+void*
+memory_map__data(clobj_t _map)
+{
+    return static_cast<memory_map*>(_map)->data();
+}
+
+memory_map*
+memory_map::convert(clobj_t *_evt, cl_event evt, command_queue *queue,
+                    memory_object *buf, void *res)
+{
+    try {
+        *_evt = new event(evt, false);
+        evt = 0;
+        return new memory_map(queue, buf, res);
+    } catch (...) {
+        if (evt) {
+            pyopencl_call_guarded_cleanup(clReleaseEvent, evt);
+        }
+        pyopencl_call_guarded_cleanup(clEnqueueUnmapMemObject, queue,
+                                      buf, res, 0, nullptr, nullptr);
+        throw;
+    }
+}
+
+error*
+enqueue_map_image(clobj_t *_evt, clobj_t *map, clobj_t _queue, clobj_t _mem,
+                  cl_map_flags flags, const size_t *_origin, size_t origin_l,
+                  const size_t *_region, size_t region_l, size_t *row_pitch,
+                  size_t *slice_pitch, const clobj_t *_wait_for,
+                  uint32_t num_wait_for, int block)
+{
+    auto queue = static_cast<command_queue*>(_queue);
+    auto img = static_cast<image*>(_mem);
+    const auto wait_for = buf_from_class<event>(_wait_for, num_wait_for);
+    ConstBuffer<size_t, 3> origin(_origin, origin_l);
+    ConstBuffer<size_t, 3> region(_region, region_l);
+    return c_handle_error([&] {
+            cl_event evt;
+            void *res = retry_mem_error([&] {
+                    return pyopencl_call_guarded(
+                        clEnqueueMapImage, queue, img, cast_bool(block), flags,
+                        origin, region, row_pitch, slice_pitch, wait_for, &evt);
+                });
+            *map = memory_map::convert(_evt, evt, queue, img, res);
+        });
+}
+
+error*
+enqueue_map_buffer(clobj_t *_evt, clobj_t *map, clobj_t _queue, clobj_t _mem,
+                   cl_map_flags flags, size_t offset, size_t size,
+                   const clobj_t *_wait_for, uint32_t num_wait_for,
+                   int block)
+{
+    auto queue = static_cast<command_queue*>(_queue);
+    auto buf = static_cast<buffer*>(_mem);
+    const auto wait_for = buf_from_class<event>(_wait_for, num_wait_for);
+    return c_handle_error([&] {
+            cl_event evt;
+            void *res = retry_mem_error([&] {
+                    return pyopencl_call_guarded(
+                        clEnqueueMapBuffer, queue, buf, cast_bool(block),
+                        flags, offset, size, wait_for, &evt);
+                });
+            *map = memory_map::convert(_evt, evt, queue, buf, res);
+        });
+}
diff --git a/src/c_wrapper/memory_map.h b/src/c_wrapper/memory_map.h
new file mode 100644
index 00000000..6c3d6c8d
--- /dev/null
+++ b/src/c_wrapper/memory_map.h
@@ -0,0 +1,37 @@
+#include "error.h"
+#include "command_queue.h"
+#include "memory_object.h"
+
+#ifndef __PYOPENCL_MEMORY_MAP_H
+#define __PYOPENCL_MEMORY_MAP_H
+
+namespace pyopencl {
+
+class event;
+
+// {{{ memory_map
+
+class memory_map : public clobj<void*> {
+private:
+    mutable volatile std::atomic_bool m_valid;
+    command_queue m_queue;
+    memory_object m_mem;
+public:
+    PYOPENCL_INLINE
+    memory_map(const command_queue *queue, const memory_object *mem, void *ptr)
+        : clobj(ptr), m_valid(true), m_queue(*queue), m_mem(*mem)
+    {}
+    ~memory_map();
+    event *release(const command_queue *queue, const clobj_t *_wait_for,
+                   uint32_t num_wait_for) const;
+    generic_info get_info(cl_uint) const;
+    intptr_t intptr() const;
+    static memory_map *convert(clobj_t*, cl_event, command_queue *queue,
+                               memory_object *buf, void *res);
+};
+
+// }}}
+
+}
+
+#endif
diff --git a/src/c_wrapper/memory_object.cpp b/src/c_wrapper/memory_object.cpp
new file mode 100644
index 00000000..a6945b21
--- /dev/null
+++ b/src/c_wrapper/memory_object.cpp
@@ -0,0 +1,73 @@
+#include "memory_object.h"
+#include "context.h"
+#include "clhelper.h"
+
+namespace pyopencl {
+
+generic_info
+memory_object::get_info(cl_uint param_name) const
+{
+    switch ((cl_mem_info)param_name) {
+    case CL_MEM_TYPE:
+        return pyopencl_get_int_info(cl_mem_object_type, MemObject,
+                                     this, param_name);
+    case CL_MEM_FLAGS:
+        return pyopencl_get_int_info(cl_mem_flags, MemObject,
+                                     this, param_name);
+    case CL_MEM_SIZE:
+        return pyopencl_get_int_info(size_t, MemObject, this, param_name);
+    case CL_MEM_HOST_PTR:
+        throw clerror("MemoryObject.get_info", CL_INVALID_VALUE,
+                      "Use MemoryObject.get_host_array to get "
+                      "host pointer.");
+    case CL_MEM_MAP_COUNT:
+    case CL_MEM_REFERENCE_COUNT:
+        return pyopencl_get_int_info(cl_uint, MemObject,
+                                     this, param_name);
+    case CL_MEM_CONTEXT:
+        return pyopencl_get_opaque_info(context, MemObject, this, param_name);
+
+#if PYOPENCL_CL_VERSION >= 0x1010
+        // TODO
+        //       case CL_MEM_ASSOCIATED_MEMOBJECT:
+        //      {
+        //        cl_mem param_value;
+        //        PYOPENCL_CALL_GUARDED(clGetMemObjectInfo, (this, param_name, sizeof(param_value), &param_value, 0));
+        //        if (param_value == 0)
+        //          {
+        //            // no associated memory object? no problem.
+        //            return py::object();
+        //          }
+
+        //        return create_mem_object_wrapper(param_value);
+        //      }
+    case CL_MEM_OFFSET:
+        return pyopencl_get_int_info(size_t, MemObject, this, param_name);
+#endif
+
+    default:
+        throw clerror("MemoryObject.get_info", CL_INVALID_VALUE);
+    }
+}
+
+memory_object::~memory_object()
+{
+    if (!m_valid.exchange(false))
+        return;
+    pyopencl_call_guarded_cleanup(clReleaseMemObject, this);
+}
+
+}
+
+// c wrapper
+// Import all the names in pyopencl namespace for c wrappers.
+using namespace pyopencl;
+
+// Memory Object
+error*
+memory_object__release(clobj_t obj)
+{
+    return c_handle_error([&] {
+            static_cast<memory_object*>(obj)->release();
+        });
+}
diff --git a/src/c_wrapper/memory_object.h b/src/c_wrapper/memory_object.h
new file mode 100644
index 00000000..0a59a476
--- /dev/null
+++ b/src/c_wrapper/memory_object.h
@@ -0,0 +1,129 @@
+#include "error.h"
+#include <atomic>
+
+#ifndef __PYOPENCL_MEMORY_OBJECT_H
+#define __PYOPENCL_MEMORY_OBJECT_H
+
+namespace pyopencl {
+
+// {{{ memory_object
+
+class memory_object : public clobj<cl_mem> {
+private:
+    mutable volatile std::atomic_bool m_valid;
+    void *m_hostbuf;
+public:
+    PYOPENCL_INLINE
+    memory_object(cl_mem mem, bool retain, void *hostbuf=0)
+        : clobj(mem), m_valid(true)
+    {
+        if (retain) {
+            pyopencl_call_guarded(clRetainMemObject, this);
+        }
+        m_hostbuf = hostbuf;
+    }
+    PYOPENCL_INLINE
+    memory_object(const memory_object &mem)
+        : memory_object(mem.data(), true)
+    {}
+    ~memory_object();
+    generic_info get_info(cl_uint param_name) const;
+    void
+    release() const
+    {
+        if (!m_valid.exchange(false)) {
+            throw clerror("MemoryObject.release", CL_INVALID_VALUE,
+                          "trying to double-unref mem object");
+        }
+        pyopencl_call_guarded(clReleaseMemObject, this);
+    }
+#if 0
+    PYOPENCL_USE_RESULT PYOPENCL_INLINE void*
+    hostbuf() const
+    {
+        return m_hostbuf;
+    }
+    PYOPENCL_USE_RESULT size_t
+    size() const
+    {
+        size_t param_value;
+        pyopencl_call_guarded(clGetMemObjectInfo, this, CL_MEM_SIZE,
+                              make_sizearg(param_value), nullptr);
+        return param_value;
+    }
+#endif
+};
+
+// #if PYOPENCL_CL_VERSION >= 0x1020
+//   PYOPENCL_INLINE
+//   event *enqueue_migrate_mem_objects(
+//       command_queue &cq,
+//       py::object py_mem_objects,
+//       cl_mem_migration_flags flags,
+//       py::object py_wait_for)
+//   {
+//     PYOPENCL_PARSE_WAIT_FOR;
+
+//     std::vector<cl_mem> mem_objects;
+//     PYTHON_FOREACH(mo, py_mem_objects)
+//       mem_objects.push_back(py::extract<memory_object &>(mo)().data());
+
+//     cl_event evt;
+//     PYOPENCL_RETRY_IF_MEM_ERROR(
+//       PYOPENCL_CALL_GUARDED(clEnqueueMigrateMemObjects, (
+//             cq.data(),
+//             mem_objects.size(), mem_objects.empty( ) ? nullptr : &mem_objects.front(),
+//             flags,
+//             PYOPENCL_WAITLIST_ARGS, &evt
+//             ));
+//       );
+//     PYOPENCL_RETURN_NEW_EVENT(evt);
+//   }
+// #endif
+
+// #ifdef cl_ext_migrate_memobject
+//   PYOPENCL_INLINE
+//   event *enqueue_migrate_mem_object_ext(
+//       command_queue &cq,
+//       py::object py_mem_objects,
+//       cl_mem_migration_flags_ext flags,
+//       py::object py_wait_for)
+//   {
+//     PYOPENCL_PARSE_WAIT_FOR;
+
+// #if PYOPENCL_CL_VERSION >= 0x1020
+//     // {{{ get platform
+//     cl_device_id dev;
+//     PYOPENCL_CALL_GUARDED(clGetCommandQueueInfo, (cq.data(), CL_QUEUE_DEVICE,
+//           sizeof(dev), &dev, nullptr));
+//     cl_platform_id plat;
+//     PYOPENCL_CALL_GUARDED(clGetDeviceInfo, (cq.data(), CL_DEVICE_PLATFORM,
+//           sizeof(plat), &plat, nullptr));
+//     // }}}
+// #endif
+
+//     PYOPENCL_GET_EXT_FUN(plat,
+//         clEnqueueMigrateMemObjectEXT, enqueue_migrate_fn);
+
+//     std::vector<cl_mem> mem_objects;
+//     PYTHON_FOREACH(mo, py_mem_objects)
+//       mem_objects.push_back(py::extract<memory_object &>(mo)().data());
+
+//     cl_event evt;
+//     PYOPENCL_RETRY_IF_MEM_ERROR(
+//       PYOPENCL_CALL_GUARDED(enqueue_migrate_fn, (
+//             cq.data(),
+//             mem_objects.size(), mem_objects.empty( ) ? nullptr : &mem_objects.front(),
+//             flags,
+//             PYOPENCL_WAITLIST_ARGS, &evt
+//             ));
+//       );
+//     PYOPENCL_RETURN_NEW_EVENT(evt);
+//   }
+// #endif
+
+// }}}
+
+}
+
+#endif
diff --git a/src/c_wrapper/platform.cpp b/src/c_wrapper/platform.cpp
new file mode 100644
index 00000000..2a0892b8
--- /dev/null
+++ b/src/c_wrapper/platform.cpp
@@ -0,0 +1,68 @@
+#include "platform.h"
+#include "device.h"
+#include "clhelper.h"
+
+namespace pyopencl {
+
+generic_info
+platform::get_info(cl_uint param_name) const
+{
+    switch ((cl_platform_info)param_name) {
+    case CL_PLATFORM_PROFILE:
+    case CL_PLATFORM_VERSION:
+    case CL_PLATFORM_NAME:
+    case CL_PLATFORM_VENDOR:
+#if !(defined(CL_PLATFORM_NVIDIA) && CL_PLATFORM_NVIDIA == 0x3001)
+    case CL_PLATFORM_EXTENSIONS:
+#endif
+        return pyopencl_get_str_info(Platform, this, param_name);
+    default:
+        throw clerror("Platform.get_info", CL_INVALID_VALUE);
+    }
+}
+
+}
+
+// c wrapper
+// Import all the names in pyopencl namespace for c wrappers.
+using namespace pyopencl;
+
+error*
+get_platforms(clobj_t **_platforms, uint32_t *num_platforms)
+{
+    return c_handle_error([&] {
+            *num_platforms = 0;
+            pyopencl_call_guarded(clGetPlatformIDs, 0, nullptr,
+                                  make_argbuf(*num_platforms));
+            pyopencl_buf<cl_platform_id> platforms(*num_platforms);
+            pyopencl_call_guarded(clGetPlatformIDs, platforms,
+                                  make_argbuf(*num_platforms));
+            *_platforms = buf_to_base<platform>(platforms).release();
+        });
+}
+
+error*
+platform__get_devices(clobj_t _plat, clobj_t **_devices,
+                      uint32_t *num_devices, cl_device_type devtype)
+{
+    auto plat = static_cast<platform*>(_plat);
+    return c_handle_error([&] {
+            *num_devices = 0;
+            try {
+                pyopencl_call_guarded(clGetDeviceIDs, plat, devtype, 0, nullptr,
+                                      make_argbuf(*num_devices));
+            } catch (const clerror &e) {
+                if (e.code() != CL_DEVICE_NOT_FOUND)
+                    throw e;
+                *num_devices = 0;
+            }
+            if (*num_devices == 0) {
+                *_devices = nullptr;
+                return;
+            }
+            pyopencl_buf<cl_device_id> devices(*num_devices);
+            pyopencl_call_guarded(clGetDeviceIDs, plat, devtype, devices,
+                                  make_argbuf(*num_devices));
+            *_devices = buf_to_base<device>(devices).release();
+        });
+}
diff --git a/src/c_wrapper/platform.h b/src/c_wrapper/platform.h
new file mode 100644
index 00000000..3474c227
--- /dev/null
+++ b/src/c_wrapper/platform.h
@@ -0,0 +1,22 @@
+#include "error.h"
+
+#ifndef __PYOPENCL_PLATFORM_H
+#define __PYOPENCL_PLATFORM_H
+
+namespace pyopencl {
+
+// {{{ platform
+
+class platform : public clobj<cl_platform_id> {
+public:
+    using clobj::clobj;
+    PYOPENCL_DEF_CL_CLASS(PLATFORM);
+
+    generic_info get_info(cl_uint param_name) const;
+};
+
+// }}}
+
+}
+
+#endif
diff --git a/src/c_wrapper/program.cpp b/src/c_wrapper/program.cpp
new file mode 100644
index 00000000..b1f6d668
--- /dev/null
+++ b/src/c_wrapper/program.cpp
@@ -0,0 +1,153 @@
+#include "program.h"
+#include "device.h"
+#include "context.h"
+#include "clhelper.h"
+
+namespace pyopencl {
+
+program::~program()
+{
+    pyopencl_call_guarded_cleanup(clReleaseProgram, this);
+}
+
+generic_info
+program::get_info(cl_uint param) const
+{
+    switch ((cl_program_info)param) {
+    case CL_PROGRAM_CONTEXT:
+        return pyopencl_get_opaque_info(context, Program, this, param);
+    case CL_PROGRAM_REFERENCE_COUNT:
+    case CL_PROGRAM_NUM_DEVICES:
+        return pyopencl_get_int_info(cl_uint, Program, this, param);
+    case CL_PROGRAM_DEVICES:
+        return pyopencl_get_opaque_array_info(device, Program, this, param);
+    case CL_PROGRAM_SOURCE:
+        return pyopencl_get_str_info(Program, this, param);
+    case CL_PROGRAM_BINARY_SIZES:
+        return pyopencl_get_array_info(size_t, Program, this, param);
+    case CL_PROGRAM_BINARIES: {
+        auto sizes = pyopencl_get_vec_info(size_t, Program, this,
+                                           CL_PROGRAM_BINARY_SIZES);
+        pyopencl_buf<char*> result_ptrs(sizes.len());
+        for (size_t i  = 0;i < sizes.len();i++) {
+            result_ptrs[i] = (char*)malloc(sizes[i]);
+        }
+        try {
+            pyopencl_call_guarded(clGetProgramInfo, this, CL_PROGRAM_BINARIES,
+                                  sizes.len() * sizeof(char*),
+                                  result_ptrs.get(), nullptr);
+        } catch (...) {
+            for (size_t i  = 0;i < sizes.len();i++) {
+                free(result_ptrs[i]);
+            }
+        }
+        pyopencl_buf<generic_info> gis(sizes.len());
+        for (size_t i  = 0;i < sizes.len();i++) {
+            gis[i].value = result_ptrs[i];
+            gis[i].dontfree = 0;
+            gis[i].opaque_class = CLASS_NONE;
+            gis[i].type =  _copy_str(std::string("char[") +
+                                     tostring(sizes[i]) + "]");
+        }
+        return pyopencl_convert_array_info(generic_info, gis);
+    }
+
+#if PYOPENCL_CL_VERSION >= 0x1020
+    case CL_PROGRAM_NUM_KERNELS:
+        return pyopencl_get_int_info(size_t, Program, this, param);
+    case CL_PROGRAM_KERNEL_NAMES:
+        return pyopencl_get_str_info(Program, this, param);
+#endif
+    default:
+        throw clerror("Program.get_info", CL_INVALID_VALUE);
+    }
+}
+
+generic_info
+program::get_build_info(const device *dev, cl_program_build_info param) const
+{
+    switch (param) {
+    case CL_PROGRAM_BUILD_STATUS:
+        return pyopencl_get_int_info(cl_build_status, ProgramBuild,
+                                     this, dev, param);
+    case CL_PROGRAM_BUILD_OPTIONS:
+    case CL_PROGRAM_BUILD_LOG:
+        return pyopencl_get_str_info(ProgramBuild, this, dev, param);
+#if PYOPENCL_CL_VERSION >= 0x1020
+    case CL_PROGRAM_BINARY_TYPE:
+        return pyopencl_get_int_info(cl_program_binary_type, ProgramBuild,
+                                     this, dev, param);
+#endif
+    default:
+        throw clerror("Program.get_build_info", CL_INVALID_VALUE);
+    }
+}
+
+}
+
+// c wrapper
+// Import all the names in pyopencl namespace for c wrappers.
+using namespace pyopencl;
+
+// Program
+error*
+create_program_with_source(clobj_t *prog, clobj_t _ctx, const char *src)
+{
+    auto ctx = static_cast<context*>(_ctx);
+    return c_handle_error([&] {
+            size_t length = strlen(src);
+            cl_program result = pyopencl_call_guarded(
+                clCreateProgramWithSource, ctx, 1, &src, &length);
+            *prog = new_program(result, KND_SOURCE);
+        });
+}
+
+error*
+create_program_with_binary(clobj_t *prog, clobj_t _ctx,
+                           cl_uint num_devices, const clobj_t *devices,
+                           char **binaries, size_t *binary_sizes)
+{
+    auto ctx = static_cast<context*>(_ctx);
+    const auto devs = buf_from_class<device>(devices, num_devices);
+    pyopencl_buf<cl_int> binary_statuses(num_devices);
+    return c_handle_error([&] {
+            cl_program result = pyopencl_call_guarded(
+                clCreateProgramWithBinary, ctx, devs,
+                binary_sizes, reinterpret_cast<const unsigned char**>(
+                    const_cast<const char**>(binaries)), binary_statuses.get());
+            // for (cl_uint i = 0; i < num_devices; ++i)
+            //   std::cout << i << ":" << binary_statuses[i] << std::endl;
+            *prog = new_program(result, KND_BINARY);
+        });
+}
+
+error*
+program__build(clobj_t _prog, const char *options,
+               cl_uint num_devices, const clobj_t *_devices)
+{
+    auto prog = static_cast<const program*>(_prog);
+    const auto devices = buf_from_class<device>(_devices, num_devices);
+    return c_handle_error([&] {
+            pyopencl_call_guarded(clBuildProgram, prog, devices, options,
+                                  nullptr, nullptr);
+        });
+}
+
+error*
+program__kind(clobj_t prog, int *kind)
+{
+    return c_handle_error([&] {
+            *kind = static_cast<program*>(prog)->kind();
+        });
+}
+
+error*
+program__get_build_info(clobj_t _prog, clobj_t _dev,
+                        cl_program_build_info param, generic_info *out)
+{
+    auto prog = static_cast<program*>(_prog);
+    auto dev = static_cast<device*>(_dev);
+    return c_handle_error([&] {
+            *out = prog->get_build_info(dev, param);
+        });
+}
diff --git a/src/c_wrapper/program.h b/src/c_wrapper/program.h
new file mode 100644
index 00000000..21e93bd5
--- /dev/null
+++ b/src/c_wrapper/program.h
@@ -0,0 +1,91 @@
+#include "clhelper.h"
+
+#ifndef __PYOPENCL_PROGRAM_H
+#define __PYOPENCL_PROGRAM_H
+
+namespace pyopencl {
+
+class device;
+
+// {{{ program
+
+class program : public clobj<cl_program> {
+private:
+    program_kind_type m_program_kind;
+
+public:
+    PYOPENCL_DEF_CL_CLASS(PROGRAM);
+    PYOPENCL_INLINE
+    program(cl_program prog, bool retain,
+            program_kind_type progkind=KND_UNKNOWN)
+        : clobj(prog), m_program_kind(progkind)
+    {
+        if (retain) {
+            pyopencl_call_guarded(clRetainProgram, this);
+        }
+    }
+    ~program();
+    PYOPENCL_USE_RESULT PYOPENCL_INLINE program_kind_type
+    kind() const
+    {
+        return m_program_kind;
+    }
+    PYOPENCL_USE_RESULT pyopencl_buf<cl_device_id>
+    get_info__devices() const
+    {
+        return pyopencl_get_vec_info(cl_device_id, Program, this,
+                                     CL_PROGRAM_DEVICES);
+    }
+    generic_info get_info(cl_uint param_name) const;
+    PYOPENCL_USE_RESULT generic_info
+    get_build_info(const device *dev, cl_program_build_info param_name) const;
+
+    // #if PYOPENCL_CL_VERSION >= 0x1020
+    //       void compile(std::string options, py::object py_devices,
+    //           py::object py_headers)
+    //       {
+    //         PYOPENCL_PARSE_PY_DEVICES;
+
+    //         // {{{ pick apart py_headers
+    //         // py_headers is a list of tuples *(name, program)*
+
+    //         std::vector<std::string> header_names;
+    //         std::vector<cl_program> programs;
+    //         PYTHON_FOREACH(name_hdr_tup, py_headers)
+    //         {
+    //           if (py::len(name_hdr_tup) != 2)
+    //             throw error("Program.compile", CL_INVALID_VALUE,
+    //                 "epxected (name, header) tuple in headers list");
+    //           std::string name = py::extract<std::string const &>(name_hdr_tup[0]);
+    //           program &prg = py::extract<program &>(name_hdr_tup[1]);
+
+    //           header_names.push_back(name);
+    //           programs.push_back(prg.data());
+    //         }
+
+    //         std::vector<const char *> header_name_ptrs;
+    //         BOOST_FOREACH(std::string const &name, header_names)
+    //           header_name_ptrs.push_back(name.c_str());
+
+    //         // }}}
+
+    //         PYOPENCL_CALL_GUARDED(clCompileProgram,
+    //             (this, num_devices, devices,
+    //              options.c_str(), header_names.size(),
+    //              programs.empty() ? nullptr : &programs.front(),
+    //              header_name_ptrs.empty() ? nullptr : &header_name_ptrs.front(),
+    //              0, 0));
+    //       }
+    // #endif
+};
+PYOPENCL_USE_RESULT static PYOPENCL_INLINE program*
+new_program(cl_program prog, program_kind_type progkind=KND_UNKNOWN)
+{
+    return pyopencl_convert_obj(program, clReleaseProgram, prog, progkind);
+}
+
+// }}}
+
+}
+
+#endif
diff --git a/src/c_wrapper/sampler.cpp b/src/c_wrapper/sampler.cpp
new file mode 100644
index 00000000..0c6cb902
--- /dev/null
+++ b/src/c_wrapper/sampler.cpp
@@ -0,0 +1,50 @@
+#include "sampler.h"
+#include "context.h"
+#include "clhelper.h"
+
+namespace pyopencl {
+
+sampler::~sampler()
+{
+    pyopencl_call_guarded_cleanup(clReleaseSampler, this);
+}
+
+generic_info
+sampler::get_info(cl_uint param_name) const
+{
+    switch ((cl_sampler_info)param_name) {
+    case CL_SAMPLER_REFERENCE_COUNT:
+        return pyopencl_get_int_info(cl_uint, Sampler, this, param_name);
+    case CL_SAMPLER_CONTEXT:
+        return pyopencl_get_opaque_info(context, Sampler, this, param_name);
+    case CL_SAMPLER_ADDRESSING_MODE:
+        return pyopencl_get_int_info(cl_addressing_mode, Sampler,
+                                     this, param_name);
+    case CL_SAMPLER_FILTER_MODE:
+        return pyopencl_get_int_info(cl_filter_mode, Sampler, this, param_name);
+    case CL_SAMPLER_NORMALIZED_COORDS:
+        return pyopencl_get_int_info(cl_bool, Sampler, this, param_name);
+
+    default:
+        throw clerror("Sampler.get_info", CL_INVALID_VALUE);
+    }
+}
+
+}
+
+// c wrapper
+// Import all the names in pyopencl namespace for c wrappers.
+using namespace pyopencl;
+
+// Sampler
+error*
+create_sampler(clobj_t *samp, clobj_t _ctx, int norm_coords,
+               cl_addressing_mode am, cl_filter_mode fm)
+{
+    auto ctx = static_cast<context*>(_ctx);
+    return c_handle_error([&] {
+            *samp = new sampler(pyopencl_call_guarded(clCreateSampler, ctx,
+                                                      norm_coords, am, fm),
+                                false);
+        });
+}
diff --git a/src/c_wrapper/sampler.h b/src/c_wrapper/sampler.h
new file mode 100644
index 00000000..ed934f75
--- /dev/null
+++ b/src/c_wrapper/sampler.h
@@ -0,0 +1,29 @@
+#include "error.h"
+
+#ifndef __PYOPENCL_SAMPLER_H
+#define __PYOPENCL_SAMPLER_H
+
+namespace pyopencl {
+
+// {{{ sampler
+
+class sampler : public clobj<cl_sampler> {
+public:
+    PYOPENCL_DEF_CL_CLASS(SAMPLER);
+    PYOPENCL_INLINE
+    sampler(cl_sampler samp, bool retain)
+        : clobj(samp)
+    {
+        if (retain) {
+            pyopencl_call_guarded(clRetainSampler, this);
+        }
+    }
+    ~sampler();
+    generic_info get_info(cl_uint param_name) const;
+};
+
+// }}}
+
+}
+
+#endif
diff --git a/src/c_wrapper/wrap_cl.cpp b/src/c_wrapper/wrap_cl.cpp
index 4e05e729..e9069c37 100644
--- a/src/c_wrapper/wrap_cl.cpp
+++ b/src/c_wrapper/wrap_cl.cpp
@@ -1,2561 +1,45 @@
-#include "async.h"
 #include "pyhelper.h"
 #include "clhelper.h"
+#include "platform.h"
+#include "device.h"
+#include "context.h"
+#include "command_queue.h"
+#include "event.h"
+#include "memory_object.h"
+#include "image.h"
+#include "gl_obj.h"
+#include "memory_map.h"
+#include "buffer.h"
+#include "sampler.h"
+#include "program.h"
+#include "kernel.h"
 
-#include <stdlib.h>
-
-namespace pyopencl {
-
-// {{{ platform
-
-class platform : public clobj<cl_platform_id> {
-public:
-    using clobj::clobj;
-    PYOPENCL_DEF_CL_CLASS(PLATFORM);
-    generic_info
-    get_info(cl_uint param_name) const
-    {
-        switch ((cl_platform_info)param_name) {
-        case CL_PLATFORM_PROFILE:
-        case CL_PLATFORM_VERSION:
-        case CL_PLATFORM_NAME:
-        case CL_PLATFORM_VENDOR:
-#if !(defined(CL_PLATFORM_NVIDIA) && CL_PLATFORM_NVIDIA == 0x3001)
-        case CL_PLATFORM_EXTENSIONS:
-#endif
-            return pyopencl_get_str_info(Platform, this, param_name);
-
-        default:
-            throw clerror("Platform.get_info", CL_INVALID_VALUE);
-        }
-    }
-
-    PYOPENCL_USE_RESULT PYOPENCL_INLINE pyopencl_buf<cl_device_id>
-    get_devices(cl_device_type devtype) const;
-};
-
-
-pyopencl_buf<cl_device_id>
-platform::get_devices(cl_device_type devtype) const
-{
-    cl_uint num_devices = 0;
-    try {
-        pyopencl_call_guarded(clGetDeviceIDs, this, devtype, 0, nullptr,
-                              make_argbuf(num_devices));
-    } catch (const clerror &e) {
-        if (e.code() != CL_DEVICE_NOT_FOUND)
-            throw e;
-        num_devices = 0;
-    }
-    pyopencl_buf<cl_device_id> devices(num_devices);
-    if (num_devices == 0)
-        return devices;
-    pyopencl_call_guarded(clGetDeviceIDs, this, devtype, devices,
-                          make_argbuf(num_devices));
-    return devices;
-}
-
-// }}}
-
-
-// {{{ device
-
-class device : public clobj<cl_device_id> {
-public:
-    PYOPENCL_DEF_CL_CLASS(DEVICE);
-    enum reference_type_t {
-        REF_NOT_OWNABLE,
-        REF_FISSION_EXT,
-#if PYOPENCL_CL_VERSION >= 0x1020
-        REF_CL_1_2,
-#endif
-    };
-
-private:
-    reference_type_t m_ref_type;
-
-public:
-    device(cl_device_id did, bool retain=false,
-           reference_type_t ref_type=REF_NOT_OWNABLE)
-        : clobj(did), m_ref_type(ref_type)
-    {
-        if (retain && ref_type != REF_NOT_OWNABLE) {
-            if (false) {
-            }
-#if (defined(cl_ext_device_fission) && defined(PYOPENCL_USE_DEVICE_FISSION))
-            else if (ref_type == REF_FISSION_EXT) {
-#if PYOPENCL_CL_VERSION >= 0x1020
-                cl_platform_id plat;
-                pyopencl_call_guarded(clGetDeviceInfo, this,
-                                      CL_DEVICE_PLATFORM, make_sizearg(plat),
-                                      nullptr);
-#endif
-                pyopencl_call_guarded(
-                    pyopencl_get_ext_fun(plat, clRetainDeviceEXT), this);
-            }
-#endif
-#if PYOPENCL_CL_VERSION >= 0x1020
-            else if (ref_type == REF_CL_1_2) {
-                pyopencl_call_guarded(clRetainDevice, this);
-            }
-#endif
-
-            else {
-                throw clerror("Device", CL_INVALID_VALUE,
-                              "cannot own references to devices when device "
-                              "fission or CL 1.2 is not available");
-            }
-        }
-    }
-
-    ~device()
-    {
-        if (false) {
-        }
-#if defined(cl_ext_device_fission) && defined(PYOPENCL_USE_DEVICE_FISSION)
-        else if (m_ref_type == REF_FISSION_EXT) {
-#if PYOPENCL_CL_VERSION >= 0x1020
-            cl_platform_id plat;
-            pyopencl_call_guarded_cleanup(
-                clGetDeviceInfo, this, CL_DEVICE_PLATFORM,
-                make_sizearg(plat), nullptr);
-#endif
-            pyopencl_call_guarded_cleanup(
-                pyopencl_get_ext_fun(plat, clReleaseDeviceEXT), this);
-        }
-#endif
-#if PYOPENCL_CL_VERSION >= 0x1020
-        else if (m_ref_type == REF_CL_1_2) {
-            pyopencl_call_guarded_cleanup(clReleaseDevice, this);
-        }
-#endif
-    }
-    generic_info
-    get_info(cl_uint param_name) const
-    {
-#define DEV_GET_INT_INF(TYPE)                                           \
-        pyopencl_get_int_info(TYPE, Device, this, param_name)
-
-        switch ((cl_device_info)param_name) {
-        case CL_DEVICE_TYPE:
-            return DEV_GET_INT_INF(cl_device_type);
-        case CL_DEVICE_MAX_WORK_GROUP_SIZE:
-            return DEV_GET_INT_INF(size_t);
-        case CL_DEVICE_MAX_WORK_ITEM_DIMENSIONS:
-        case CL_DEVICE_MAX_COMPUTE_UNITS:
-        case CL_DEVICE_VENDOR_ID:
-            return DEV_GET_INT_INF(cl_uint);
-
-        case CL_DEVICE_MAX_WORK_ITEM_SIZES:
-            return pyopencl_get_array_info(size_t, Device, this, param_name);
-
-        case CL_DEVICE_PREFERRED_VECTOR_WIDTH_CHAR:
-        case CL_DEVICE_PREFERRED_VECTOR_WIDTH_SHORT:
-        case CL_DEVICE_PREFERRED_VECTOR_WIDTH_INT:
-        case CL_DEVICE_PREFERRED_VECTOR_WIDTH_LONG:
-        case CL_DEVICE_PREFERRED_VECTOR_WIDTH_FLOAT:
-        case CL_DEVICE_PREFERRED_VECTOR_WIDTH_DOUBLE:
-
-        case CL_DEVICE_MAX_CLOCK_FREQUENCY:
-        case CL_DEVICE_ADDRESS_BITS:
-        case CL_DEVICE_MAX_READ_IMAGE_ARGS:
-        case CL_DEVICE_MAX_WRITE_IMAGE_ARGS:
-        case CL_DEVICE_MAX_SAMPLERS:
-        case CL_DEVICE_MEM_BASE_ADDR_ALIGN:
-        case CL_DEVICE_MIN_DATA_TYPE_ALIGN_SIZE:
-            return DEV_GET_INT_INF(cl_uint);
-
-        case CL_DEVICE_MAX_MEM_ALLOC_SIZE:
-            return DEV_GET_INT_INF(cl_ulong);
-
-        case CL_DEVICE_IMAGE2D_MAX_WIDTH:
-        case CL_DEVICE_IMAGE2D_MAX_HEIGHT:
-        case CL_DEVICE_IMAGE3D_MAX_WIDTH:
-        case CL_DEVICE_IMAGE3D_MAX_HEIGHT:
-        case CL_DEVICE_IMAGE3D_MAX_DEPTH:
-        case CL_DEVICE_MAX_PARAMETER_SIZE:
-            return DEV_GET_INT_INF(size_t);
-
-        case CL_DEVICE_IMAGE_SUPPORT:
-            return DEV_GET_INT_INF(cl_bool);
-#ifdef CL_DEVICE_DOUBLE_FP_CONFIG
-        case CL_DEVICE_DOUBLE_FP_CONFIG:
-#endif
-#ifdef CL_DEVICE_HALF_FP_CONFIG
-        case CL_DEVICE_HALF_FP_CONFIG:
-#endif
-        case CL_DEVICE_SINGLE_FP_CONFIG:
-            return DEV_GET_INT_INF(cl_device_fp_config);
-
-        case CL_DEVICE_GLOBAL_MEM_CACHE_TYPE:
-            return DEV_GET_INT_INF(cl_device_mem_cache_type);
-        case CL_DEVICE_GLOBAL_MEM_CACHELINE_SIZE:
-            return DEV_GET_INT_INF(cl_uint);
-        case CL_DEVICE_GLOBAL_MEM_CACHE_SIZE:
-        case CL_DEVICE_GLOBAL_MEM_SIZE:
-        case CL_DEVICE_LOCAL_MEM_SIZE:
-        case CL_DEVICE_MAX_CONSTANT_BUFFER_SIZE:
-            return DEV_GET_INT_INF(cl_ulong);
-
-        case CL_DEVICE_MAX_CONSTANT_ARGS:
-            return DEV_GET_INT_INF(cl_uint);
-        case CL_DEVICE_LOCAL_MEM_TYPE:
-            return DEV_GET_INT_INF(cl_device_local_mem_type);
-        case CL_DEVICE_PROFILING_TIMER_RESOLUTION:
-            return DEV_GET_INT_INF(size_t);
-        case CL_DEVICE_ENDIAN_LITTLE:
-        case CL_DEVICE_AVAILABLE:
-        case CL_DEVICE_COMPILER_AVAILABLE:
-        case CL_DEVICE_ERROR_CORRECTION_SUPPORT:
-            return DEV_GET_INT_INF(cl_bool);
-        case CL_DEVICE_EXECUTION_CAPABILITIES:
-            return DEV_GET_INT_INF(cl_device_exec_capabilities);
-        case CL_DEVICE_QUEUE_PROPERTIES:
-            return DEV_GET_INT_INF(cl_command_queue_properties);
-
-        case CL_DEVICE_NAME:
-        case CL_DEVICE_VENDOR:
-        case CL_DRIVER_VERSION:
-        case CL_DEVICE_PROFILE:
-        case CL_DEVICE_VERSION:
-        case CL_DEVICE_EXTENSIONS:
-            return pyopencl_get_str_info(Device, this, param_name);
-
-        case CL_DEVICE_PLATFORM:
-            return pyopencl_get_opaque_info(cl_platform_id, platform,
-                                            Device, this, param_name);
-#if PYOPENCL_CL_VERSION >= 0x1010
-        case CL_DEVICE_PREFERRED_VECTOR_WIDTH_HALF:
-        case CL_DEVICE_NATIVE_VECTOR_WIDTH_CHAR:
-        case CL_DEVICE_NATIVE_VECTOR_WIDTH_SHORT:
-        case CL_DEVICE_NATIVE_VECTOR_WIDTH_INT:
-        case CL_DEVICE_NATIVE_VECTOR_WIDTH_LONG:
-        case CL_DEVICE_NATIVE_VECTOR_WIDTH_FLOAT:
-        case CL_DEVICE_NATIVE_VECTOR_WIDTH_DOUBLE:
-        case CL_DEVICE_NATIVE_VECTOR_WIDTH_HALF:
-            return DEV_GET_INT_INF(cl_uint);
-
-        case CL_DEVICE_HOST_UNIFIED_MEMORY:
-            return DEV_GET_INT_INF(cl_bool);
-        case CL_DEVICE_OPENCL_C_VERSION:
-            return pyopencl_get_str_info(Device, this, param_name);
-#endif
-#ifdef CL_DEVICE_COMPUTE_CAPABILITY_MAJOR_NV
-        case CL_DEVICE_COMPUTE_CAPABILITY_MAJOR_NV:
-        case CL_DEVICE_COMPUTE_CAPABILITY_MINOR_NV:
-        case CL_DEVICE_REGISTERS_PER_BLOCK_NV:
-        case CL_DEVICE_WARP_SIZE_NV:
-            return DEV_GET_INT_INF(cl_uint);
-        case CL_DEVICE_GPU_OVERLAP_NV:
-        case CL_DEVICE_KERNEL_EXEC_TIMEOUT_NV:
-        case CL_DEVICE_INTEGRATED_MEMORY_NV:
-            return DEV_GET_INT_INF(cl_bool);
-#endif
-#if defined(cl_ext_device_fission) && defined(PYOPENCL_USE_DEVICE_FISSION)
-        case CL_DEVICE_PARENT_DEVICE_EXT:
-            return pyopencl_get_opaque_info(cl_device_id, device,
-                                            Device, this, param_name);
-        case CL_DEVICE_PARTITION_TYPES_EXT:
-        case CL_DEVICE_AFFINITY_DOMAINS_EXT:
-        case CL_DEVICE_PARTITION_STYLE_EXT:
-            return pyopencl_get_array_info(cl_device_partition_property_ext,
-                                           Device, this, param_name);
-        case CL_DEVICE_REFERENCE_COUNT_EXT:
-            return DEV_GET_INT_INF(cl_uint);
-#endif
-#if PYOPENCL_CL_VERSION >= 0x1020
-        case CL_DEVICE_LINKER_AVAILABLE:
-            return DEV_GET_INT_INF(cl_bool);
-        case CL_DEVICE_BUILT_IN_KERNELS:
-            return pyopencl_get_str_info(Device, this, param_name);
-        case CL_DEVICE_IMAGE_MAX_BUFFER_SIZE:
-        case CL_DEVICE_IMAGE_MAX_ARRAY_SIZE:
-            return DEV_GET_INT_INF(size_t);
-        case CL_DEVICE_PARENT_DEVICE:
-            return pyopencl_get_opaque_info(cl_device_id, device,
-                                            Device, this, param_name);
-        case CL_DEVICE_PARTITION_MAX_SUB_DEVICES:
-            return DEV_GET_INT_INF(cl_uint);
-        case CL_DEVICE_PARTITION_TYPE:
-        case CL_DEVICE_PARTITION_PROPERTIES:
-            return pyopencl_get_array_info(cl_device_partition_property,
-                                           Device, this, param_name);
-        case CL_DEVICE_PARTITION_AFFINITY_DOMAIN:
-            return pyopencl_get_array_info(cl_device_affinity_domain,
-                                           Device, this, param_name);
-        case CL_DEVICE_REFERENCE_COUNT:
-            return DEV_GET_INT_INF(cl_uint);
-        case CL_DEVICE_PREFERRED_INTEROP_USER_SYNC:
-        case CL_DEVICE_PRINTF_BUFFER_SIZE:
-            return DEV_GET_INT_INF(cl_bool);
-#endif
-            // {{{ AMD dev attrs
-            //
-            // types of AMD dev attrs divined from
-            // https://www.khronos.org/registry/cl/api/1.2/cl.hpp
-#ifdef CL_DEVICE_PROFILING_TIMER_OFFSET_AMD
-        case CL_DEVICE_PROFILING_TIMER_OFFSET_AMD:
-            return DEV_GET_INT_INF(cl_ulong);
-#endif
-            /* FIXME
-               #ifdef CL_DEVICE_TOPOLOGY_AMD
-               case CL_DEVICE_TOPOLOGY_AMD:
-               #endif
-            */
-#ifdef CL_DEVICE_BOARD_NAME_AMD
-        case CL_DEVICE_BOARD_NAME_AMD: ;
-            return pyopencl_get_str_info(Device, this, param_name);
-#endif
-#ifdef CL_DEVICE_GLOBAL_FREE_MEMORY_AMD
-        case CL_DEVICE_GLOBAL_FREE_MEMORY_AMD:
-            return pyopencl_get_array_info(size_t, Device,
-                                           this, param_name);
-#endif
-#ifdef CL_DEVICE_SIMD_PER_COMPUTE_UNIT_AMD
-        case CL_DEVICE_SIMD_PER_COMPUTE_UNIT_AMD:
-#endif
-#ifdef CL_DEVICE_SIMD_WIDTH_AMD
-        case CL_DEVICE_SIMD_WIDTH_AMD:
-#endif
-#ifdef CL_DEVICE_SIMD_INSTRUCTION_WIDTH_AMD
-        case CL_DEVICE_SIMD_INSTRUCTION_WIDTH_AMD:
-#endif
-#ifdef CL_DEVICE_WAVEFRONT_WIDTH_AMD
-        case CL_DEVICE_WAVEFRONT_WIDTH_AMD:
-#endif
-#ifdef CL_DEVICE_GLOBAL_MEM_CHANNELS_AMD
-        case CL_DEVICE_GLOBAL_MEM_CHANNELS_AMD:
-#endif
-#ifdef CL_DEVICE_GLOBAL_MEM_CHANNEL_BANKS_AMD
-        case CL_DEVICE_GLOBAL_MEM_CHANNEL_BANKS_AMD:
-#endif
-#ifdef CL_DEVICE_GLOBAL_MEM_CHANNEL_BANK_WIDTH_AMD
-        case CL_DEVICE_GLOBAL_MEM_CHANNEL_BANK_WIDTH_AMD:
-#endif
-#ifdef CL_DEVICE_LOCAL_MEM_SIZE_PER_COMPUTE_UNIT_AMD
-        case CL_DEVICE_LOCAL_MEM_SIZE_PER_COMPUTE_UNIT_AMD:
-#endif
-#ifdef CL_DEVICE_LOCAL_MEM_BANKS_AMD
-        case CL_DEVICE_LOCAL_MEM_BANKS_AMD:
-#endif
-
-#ifdef CL_DEVICE_MAX_ATOMIC_COUNTERS_EXT
-        case CL_DEVICE_MAX_ATOMIC_COUNTERS_EXT:
-#endif
-            return DEV_GET_INT_INF(cl_uint);
-            // }}}
-
-        default:
-            throw clerror("Device.get_info", CL_INVALID_VALUE);
-        }
-    }
-
-    // TODO: sub-devices
-    // #if PYOPENCL_CL_VERSION >= 0x1020
-    //       py::list create_sub_devices(py::object py_properties)
-    //       {
-    //         std::vector<cl_device_partition_property> properties;
-
-    //         COPY_PY_LIST(cl_device_partition_property, properties);
-    //         properties.push_back(0);
-
-    //         cl_device_partition_property *props_ptr
-    //           = properties.empty( ) ? nullptr : &properties.front();
-
-    //         cl_uint num_entries;
-    //         PYOPENCL_CALL_GUARDED(clCreateSubDevices,
-    //             (m_device, props_ptr, 0, nullptr, &num_entries));
-
-    //         std::vector<cl_device_id> result;
-    //         result.resize(num_entries);
-
-    //         PYOPENCL_CALL_GUARDED(clCreateSubDevices,
-    //             (m_device, props_ptr, num_entries, &result.front(), nullptr));
-
-    //         py::list py_result;
-    //         BOOST_FOREACH(cl_device_id did, result)
-    //           py_result.append(handle_from_new_ptr(
-    //                 new pyopencl::device(did, /*retain*/true,
-    //                   device::REF_CL_1_2)));
-    //         return py_result;
-    //       }
-    // #endif
-
-    // #if defined(cl_ext_device_fission) && defined(PYOPENCL_USE_DEVICE_FISSION)
-    //       py::list create_sub_devices_ext(py::object py_properties)
-    //       {
-    //         std::vector<cl_device_partition_property_ext> properties;
-
-    // #if PYOPENCL_CL_VERSION >= 0x1020
-    //         cl_platform_id plat;
-    //         PYOPENCL_CALL_GUARDED(clGetDeviceInfo, (m_device, CL_DEVICE_PLATFORM,
-    //               sizeof(plat), &plat, nullptr));
-    // #endif
-
-    //         PYOPENCL_GET_EXT_FUN(plat, clCreateSubDevicesEXT, create_sub_dev);
-
-    //         COPY_PY_LIST(cl_device_partition_property_ext, properties);
-    //         properties.push_back(CL_PROPERTIES_LIST_END_EXT);
-
-    //         cl_device_partition_property_ext *props_ptr
-    //           = properties.empty( ) ? nullptr : &properties.front();
-
-    //         cl_uint num_entries;
-    //         PYOPENCL_CALL_GUARDED(create_sub_dev,
-    //             (m_device, props_ptr, 0, nullptr, &num_entries));
-
-    //         std::vector<cl_device_id> result;
-    //         result.resize(num_entries);
-
-    //         PYOPENCL_CALL_GUARDED(create_sub_dev,
-    //             (m_device, props_ptr, num_entries, &result.front(), nullptr));
-
-    //         py::list py_result;
-    //         BOOST_FOREACH(cl_device_id did, result)
-    //           py_result.append(handle_from_new_ptr(
-    //                 new pyopencl::device(did, /*retain*/true,
-    //                   device::REF_FISSION_EXT)));
-    //         return py_result;
-    //       }
-    // #endif
-};
-
-// }}}
-
-
-// {{{ context
-
-class context : public clobj<cl_context> {
-public:
-    PYOPENCL_DEF_CL_CLASS(CONTEXT);
-    context(cl_context ctx, bool retain)
-        : clobj(ctx)
-    {
-        if (retain) {
-            pyopencl_call_guarded(clRetainContext, this);
-        }
-    }
-    ~context()
-    {
-        pyopencl_call_guarded_cleanup(clReleaseContext, this);
-    }
-    generic_info
-    get_info(cl_uint param_name) const
-    {
-        switch ((cl_context_info)param_name) {
-        case CL_CONTEXT_REFERENCE_COUNT:
-            return pyopencl_get_int_info(cl_uint, Context,
-                                         this, param_name);
-        case CL_CONTEXT_DEVICES:
-            return pyopencl_get_opaque_array_info(
-                cl_device_id, device, Context, this, param_name);
-        case CL_CONTEXT_PROPERTIES: {
-            auto result = pyopencl_get_vec_info(
-                cl_context_properties, Context, this, param_name);
-            pyopencl_buf<generic_info> py_result(result.len() / 2);
-            size_t i = 0;
-            for (;i < py_result.len();i++) {
-                cl_context_properties key = result[i * 2];
-                if (key == 0)
-                    break;
-                cl_context_properties value = result[i * 2 + 1];
-                generic_info &info = py_result[i];
-                info.dontfree = 0;
-                info.opaque_class = CLASS_NONE;
-                switch (key) {
-                case CL_CONTEXT_PLATFORM:
-                    info.opaque_class = CLASS_PLATFORM;
-                    info.type = "void *";
-                    info.value = new platform(
-                        reinterpret_cast<cl_platform_id>(value));
-                    break;
-
-#if defined(PYOPENCL_GL_SHARING_VERSION) && (PYOPENCL_GL_SHARING_VERSION >= 1)
-#if defined(__APPLE__) && defined(HAVE_GL)
-                case CL_CONTEXT_PROPERTY_USE_CGL_SHAREGROUP_APPLE:
-#else
-                case CL_GL_CONTEXT_KHR:
-                case CL_EGL_DISPLAY_KHR:
-                case CL_GLX_DISPLAY_KHR:
-                case CL_WGL_HDC_KHR:
-                case CL_CGL_SHAREGROUP_KHR:
-#endif
-                    info.type = "intptr_t *";
-                    info.value = (void*)value;
-                    // we do not own this object
-                    info.dontfree = 1;
-                    break;
-
-#endif
-                default:
-                    throw clerror("Context.get_info", CL_INVALID_VALUE,
-                                  "unknown context_property key encountered");
-                }
-            }
-            py_result.resize(i);
-            return pyopencl_convert_array_info(generic_info, py_result);
-        }
-
-#if PYOPENCL_CL_VERSION >= 0x1010
-        case CL_CONTEXT_NUM_DEVICES:
-            return pyopencl_get_int_info(cl_uint, Context,
-                                         this, param_name);
-#endif
-
-        default:
-            throw clerror("Context.get_info", CL_INVALID_VALUE);
-        }
-    }
-    PYOPENCL_USE_RESULT PYOPENCL_INLINE generic_info
-    get_supported_image_formats(cl_mem_flags flags,
-                                cl_mem_object_type image_type) const
-    {
-        cl_uint num;
-        pyopencl_call_guarded(clGetSupportedImageFormats, this, flags,
-                              image_type, 0, nullptr,
-                              make_argbuf(num));
-        pyopencl_buf<cl_image_format> formats(num);
-        pyopencl_call_guarded(clGetSupportedImageFormats, this, flags,
-                              image_type, formats, make_argbuf(num));
-        return pyopencl_convert_array_info(cl_image_format, formats);
-    }
-};
-
-// }}}
-
-
-// {{{ command_queue
-
-class command_queue : public clobj<cl_command_queue> {
-private:
-    static cl_command_queue
-    create_cl_command_queue(const context *ctx, const device *py_dev,
-                            cl_command_queue_properties props)
-    {
-        cl_device_id dev;
-        if (py_dev) {
-            dev = py_dev->data();
-        } else {
-            auto devs = pyopencl_get_vec_info(cl_device_id, Context,
-                                              ctx, CL_CONTEXT_DEVICES);
-            if (devs.len() == 0) {
-                throw clerror("CommandQueue", CL_INVALID_VALUE,
-                              "context doesn't have any devices? -- "
-                              "don't know which one to default to");
-            }
-            dev = devs[0];
-        }
-        return pyopencl_call_guarded(clCreateCommandQueue, ctx, dev, props);
-    }
-public:
-    PYOPENCL_DEF_CL_CLASS(COMMAND_QUEUE);
-    command_queue(cl_command_queue q, bool retain)
-        : clobj(q)
-    {
-        if (retain) {
-            pyopencl_call_guarded(clRetainCommandQueue, this);
-        }
-    }
-    command_queue(const context *ctx, const device *py_dev=0,
-                  cl_command_queue_properties props=0)
-        : clobj(create_cl_command_queue(ctx, py_dev, props))
-    {}
-    command_queue(const command_queue &queue)
-        : command_queue(queue.data(), true)
-    {}
-    ~command_queue()
-    {
-        pyopencl_call_guarded_cleanup(clReleaseCommandQueue, this);
-    }
-
-    generic_info
-    get_info(cl_uint param_name) const
-    {
-        switch ((cl_command_queue_info)param_name) {
-        case CL_QUEUE_CONTEXT:
-            return pyopencl_get_opaque_info(cl_context, context,
-                                            CommandQueue, this, param_name);
-        case CL_QUEUE_DEVICE:
-            return pyopencl_get_opaque_info(cl_device_id, device,
-                                            CommandQueue, this, param_name);
-        case CL_QUEUE_REFERENCE_COUNT:
-            return pyopencl_get_int_info(cl_uint, CommandQueue,
-                                         this, param_name);
-        case CL_QUEUE_PROPERTIES:
-            return pyopencl_get_int_info(cl_command_queue_properties,
-                                         CommandQueue, this, param_name);
-        default:
-            throw clerror("CommandQueue.get_info", CL_INVALID_VALUE);
-        }
-    }
-
-    PYOPENCL_USE_RESULT std::unique_ptr<context>
-    get_context() const
-    {
-        cl_context param_value;
-        pyopencl_call_guarded(clGetCommandQueueInfo, this, CL_QUEUE_CONTEXT,
-                              make_sizearg(param_value), nullptr);
-        return std::unique_ptr<context>(
-            new context(param_value, /*retain*/ true));
-    }
-
-#if PYOPENCL_CL_VERSION < 0x1010
-    cl_command_queue_properties
-    set_property(cl_command_queue_properties prop, bool enable) const
-    {
-        cl_command_queue_properties old_prop;
-        pyopencl_call_guarded(clSetCommandQueueProperty, this, prop,
-                              cast_bool(enable), make_argbuf(old_prop));
-        return old_prop;
-    }
-#endif
-    void
-    flush() const
-    {
-        pyopencl_call_guarded(clFlush, this);
-    }
-    void
-    finish() const
-    {
-        pyopencl_call_guarded(clFinish, this);
-    }
-};
-// }}}
-
-
-// {{{ event
-
-class event : public clobj<cl_event> {
-private:
-#if PYOPENCL_CL_VERSION >= 0x1010
-    class event_callback {
-    private:
-        std::function<void(cl_int)> m_func;
-    public:
-        event_callback(const std::function<void(cl_int)> &func)
-            : m_func(func)
-        {}
-        static void
-        cl_call_and_free(cl_event, cl_int status, void *data)
-        {
-            auto cb = static_cast<event_callback*>(data);
-            auto func = cb->m_func;
-            try {
-                call_async([func, status] {func(status);});
-            } catch (...) {
-            }
-            delete cb;
-        }
-    };
-#endif
-public:
-    PYOPENCL_DEF_CL_CLASS(EVENT);
-    event(cl_event event, bool retain)
-        : clobj(event)
-    {
-        if (retain) {
-            pyopencl_call_guarded(clRetainEvent, this);
-        }
-    }
-    ~event()
-    {
-        pyopencl_call_guarded_cleanup(clReleaseEvent, this);
-    }
-    generic_info
-    get_info(cl_uint param_name) const
-    {
-        switch ((cl_event_info)param_name) {
-        case CL_EVENT_COMMAND_QUEUE:
-            return pyopencl_get_opaque_info(cl_command_queue, command_queue,
-                                            Event, this, param_name);
-        case CL_EVENT_COMMAND_TYPE:
-            return pyopencl_get_int_info(cl_command_type, Event,
-                                         this, param_name);
-        case CL_EVENT_COMMAND_EXECUTION_STATUS:
-            return pyopencl_get_int_info(cl_int, Event, this, param_name);
-        case CL_EVENT_REFERENCE_COUNT:
-            return pyopencl_get_int_info(cl_uint, Event, this, param_name);
-#if PYOPENCL_CL_VERSION >= 0x1010
-        case CL_EVENT_CONTEXT:
-            return pyopencl_get_opaque_info(cl_context, context,
-                                            Event, this, param_name);
-#endif
-
-        default:
-            throw clerror("Event.get_info", CL_INVALID_VALUE);
-        }
-    }
-    PYOPENCL_USE_RESULT generic_info
-    get_profiling_info(cl_profiling_info param_name) const
-    {
-        switch (param_name) {
-        case CL_PROFILING_COMMAND_QUEUED:
-        case CL_PROFILING_COMMAND_SUBMIT:
-        case CL_PROFILING_COMMAND_START:
-        case CL_PROFILING_COMMAND_END:
-            return pyopencl_get_int_info(cl_ulong, EventProfiling,
-                                         this, param_name);
-        default:
-            throw clerror("Event.get_profiling_info", CL_INVALID_VALUE);
-        }
-    }
-    virtual void
-    finished()
-    {}
-    void
-    wait()
-    {
-        const cl_event *evt = &data();
-        pyopencl_call_guarded(clWaitForEvents,
-                              make_argbuf<ArgType::Length>(evt, 1));
-        finished();
-    }
-#if PYOPENCL_CL_VERSION >= 0x1010
-    void
-    set_callback(cl_int type, const std::function<void(cl_int)> &func)
-    {
-        auto cb = new event_callback(func);
-        try {
-            pyopencl_call_guarded(clSetEventCallback, this, type,
-                                  &event_callback::cl_call_and_free, cb);
-        } catch (...) {
-            delete cb;
-            throw;
-        }
-        init_async();
-    }
-#endif
-};
-static PYOPENCL_INLINE event*
-new_event(cl_event evt)
-{
-    return pyopencl_convert_obj(event, clReleaseEvent, evt);
-}
-
-class nanny_event : public event {
-private:
-    void *m_ward;
-public:
-    nanny_event(cl_event evt, bool retain, void *ward=nullptr)
-        : event(evt, retain), m_ward(ward)
-    {
-        if (ward) {
-            py::ref(ward);
-        }
-    }
-    ~nanny_event()
-    {
-        if (m_ward) {
-            wait();
-        }
-    }
-    void*
-    get_ward() const
-    {
-        return m_ward;
-    }
-    void
-    finished()
-    {
-        // No lock needed because multiple release is safe here.
-        void *ward = m_ward;
-        m_ward = nullptr;
-        py::deref(ward);
-    }
-};
-static PYOPENCL_INLINE event*
-new_nanny_event(cl_event evt, void *ward)
-{
-    return pyopencl_convert_obj(nanny_event, clReleaseEvent, evt, ward);
-}
-
-// }}}
-
-// {{{ memory_object
-
-//py::object create_mem_object_wrapper(cl_mem mem);
-
-class memory_object_holder : public clobj<cl_mem> {
-public:
-    using clobj::clobj;
-    size_t size() const
-    {
-        size_t param_value;
-        pyopencl_call_guarded(clGetMemObjectInfo, this, CL_MEM_SIZE,
-                              make_sizearg(param_value), nullptr);
-        return param_value;
-    }
-    generic_info
-    get_info(cl_uint param_name) const
-    {
-        switch ((cl_mem_info)param_name){
-        case CL_MEM_TYPE:
-            return pyopencl_get_int_info(cl_mem_object_type, MemObject,
-                                         this, param_name);
-        case CL_MEM_FLAGS:
-            return pyopencl_get_int_info(cl_mem_flags, MemObject,
-                                         this, param_name);
-        case CL_MEM_SIZE:
-            return pyopencl_get_int_info(size_t, MemObject, this, param_name);
-        case CL_MEM_HOST_PTR:
-            throw clerror("MemoryObject.get_info", CL_INVALID_VALUE,
-                          "Use MemoryObject.get_host_array to get "
-                          "host pointer.");
-        case CL_MEM_MAP_COUNT:
-        case CL_MEM_REFERENCE_COUNT:
-            return pyopencl_get_int_info(cl_uint, MemObject,
-                                         this, param_name);
-        case CL_MEM_CONTEXT:
-            return pyopencl_get_opaque_info(cl_context, context,
-                                            MemObject, this, param_name);
-
-#if PYOPENCL_CL_VERSION >= 0x1010
-            // TODO
-            //       case CL_MEM_ASSOCIATED_MEMOBJECT:
-            //      {
-            //        cl_mem param_value;
-            //        PYOPENCL_CALL_GUARDED(clGetMemObjectInfo, (this, param_name, sizeof(param_value), &param_value, 0));
-            //        if (param_value == 0)
-            //          {
-            //            // no associated memory object? no problem.
-            //            return py::object();
-            //          }
-
-            //        return create_mem_object_wrapper(param_value);
-            //      }
-        case CL_MEM_OFFSET:
-            return pyopencl_get_int_info(size_t, MemObject, this, param_name);
-#endif
-
-        default:
-            throw clerror("MemoryObjectHolder.get_info", CL_INVALID_VALUE);
-        }
-    }
-};
-
-class memory_object : public memory_object_holder {
-private:
-    mutable volatile std::atomic_bool m_valid;
-    void *m_hostbuf;
-public:
-    memory_object(cl_mem mem, bool retain, void *hostbuf=0)
-        : memory_object_holder(mem), m_valid(true)
-    {
-        if (retain) {
-            pyopencl_call_guarded(clRetainMemObject, this);
-        }
-        if (hostbuf) {
-            m_hostbuf = hostbuf;
-        }
-    }
-    memory_object(const memory_object &mem)
-        : memory_object(mem.data(), true)
-    {}
-    ~memory_object()
-    {
-        if (!m_valid.exchange(false))
-            return;
-        pyopencl_call_guarded_cleanup(clReleaseMemObject, this);
-    }
-    void
-    release() const
-    {
-        if (!m_valid.exchange(false)) {
-            throw clerror("MemoryObject.release", CL_INVALID_VALUE,
-                          "trying to double-unref mem object");
-        }
-        pyopencl_call_guarded(clReleaseMemObject, this);
-    }
-    void*
-    hostbuf() const
-    {
-        return m_hostbuf;
-    }
-};
-
-// #if PYOPENCL_CL_VERSION >= 0x1020
-//   PYOPENCL_INLINE
-//   event *enqueue_migrate_mem_objects(
-//       command_queue &cq,
-//       py::object py_mem_objects,
-//       cl_mem_migration_flags flags,
-//       py::object py_wait_for)
-//   {
-//     PYOPENCL_PARSE_WAIT_FOR;
-
-//     std::vector<cl_mem> mem_objects;
-//     PYTHON_FOREACH(mo, py_mem_objects)
-//       mem_objects.push_back(py::extract<memory_object &>(mo)().data());
-
-//     cl_event evt;
-//     PYOPENCL_RETRY_IF_MEM_ERROR(
-//       PYOPENCL_CALL_GUARDED(clEnqueueMigrateMemObjects, (
-//             cq.data(),
-//             mem_objects.size(), mem_objects.empty( ) ? nullptr : &mem_objects.front(),
-//             flags,
-//             PYOPENCL_WAITLIST_ARGS, &evt
-//             ));
-//       );
-//     PYOPENCL_RETURN_NEW_EVENT(evt);
-//   }
-// #endif
-
-// #ifdef cl_ext_migrate_memobject
-//   PYOPENCL_INLINE
-//   event *enqueue_migrate_mem_object_ext(
-//       command_queue &cq,
-//       py::object py_mem_objects,
-//       cl_mem_migration_flags_ext flags,
-//       py::object py_wait_for)
-//   {
-//     PYOPENCL_PARSE_WAIT_FOR;
-
-// #if PYOPENCL_CL_VERSION >= 0x1020
-//     // {{{ get platform
-//     cl_device_id dev;
-//     PYOPENCL_CALL_GUARDED(clGetCommandQueueInfo, (cq.data(), CL_QUEUE_DEVICE,
-//           sizeof(dev), &dev, nullptr));
-//     cl_platform_id plat;
-//     PYOPENCL_CALL_GUARDED(clGetDeviceInfo, (cq.data(), CL_DEVICE_PLATFORM,
-//           sizeof(plat), &plat, nullptr));
-//     // }}}
-// #endif
-
-//     PYOPENCL_GET_EXT_FUN(plat,
-//         clEnqueueMigrateMemObjectEXT, enqueue_migrate_fn);
-
-//     std::vector<cl_mem> mem_objects;
-//     PYTHON_FOREACH(mo, py_mem_objects)
-//       mem_objects.push_back(py::extract<memory_object &>(mo)().data());
-
-//     cl_event evt;
-//     PYOPENCL_RETRY_IF_MEM_ERROR(
-//       PYOPENCL_CALL_GUARDED(enqueue_migrate_fn, (
-//             cq.data(),
-//             mem_objects.size(), mem_objects.empty( ) ? nullptr : &mem_objects.front(),
-//             flags,
-//             PYOPENCL_WAITLIST_ARGS, &evt
-//             ));
-//       );
-//     PYOPENCL_RETURN_NEW_EVENT(evt);
-//   }
-// #endif
-
-// }}}
-
-// {{{ image
-
-class image : public memory_object {
-private:
-    cl_image_format m_format;
-public:
-    PYOPENCL_DEF_CL_CLASS(IMAGE);
-    image(cl_mem mem, bool retain, void *hostbuf=0,
-          const cl_image_format *fmt=0)
-        : memory_object(mem, retain, hostbuf)
-    {
-        if (fmt) {
-            memcpy(&m_format, fmt, sizeof(m_format));
-        } else {
-            pyopencl_call_guarded(clGetImageInfo, this, CL_IMAGE_FORMAT,
-                                  make_sizearg(m_format), nullptr);
-        }
-    }
-    generic_info
-    get_image_info(cl_image_info param_name) const
-    {
-        switch (param_name) {
-        case CL_IMAGE_FORMAT:
-            return pyopencl_get_int_info(cl_image_format, Image,
-                                         this, param_name);
-        case CL_IMAGE_ELEMENT_SIZE:
-        case CL_IMAGE_ROW_PITCH:
-        case CL_IMAGE_SLICE_PITCH:
-        case CL_IMAGE_WIDTH:
-        case CL_IMAGE_HEIGHT:
-        case CL_IMAGE_DEPTH:
-#if PYOPENCL_CL_VERSION >= 0x1020
-        case CL_IMAGE_ARRAY_SIZE:
-#endif
-            return pyopencl_get_int_info(size_t, Image, this, param_name);
-
-#if PYOPENCL_CL_VERSION >= 0x1020
-            // TODO:
-            //    case CL_IMAGE_BUFFER:
-            //      {
-            //        cl_mem param_value;
-            //        PYOPENCL_CALL_GUARDED(clGetImageInfo, (this, param_name, sizeof(param_value), &param_value, 0));
-            //        if (param_value == 0)
-            //               {
-            //                 // no associated memory object? no problem.
-            //                 return py::object();
-            //               }
-
-            //        return create_mem_object_wrapper(param_value);
-            //      }
-
-        case CL_IMAGE_NUM_MIP_LEVELS:
-        case CL_IMAGE_NUM_SAMPLES:
-            return pyopencl_get_int_info(cl_uint, Image, this, param_name);
-#endif
-
-        default:
-            throw clerror("Image.get_image_info", CL_INVALID_VALUE);
-        }
-    }
-    PYOPENCL_INLINE type_t
-    get_fill_type()
-    {
-        switch (m_format.image_channel_data_type) {
-        case CL_SIGNED_INT8:
-        case CL_SIGNED_INT16:
-        case CL_SIGNED_INT32:
-            return TYPE_INT;
-        case CL_UNSIGNED_INT8:
-        case CL_UNSIGNED_INT16:
-        case CL_UNSIGNED_INT32:
-            return TYPE_UINT;
-        default:
-            return TYPE_FLOAT;
-        }
-    }
-};
-static PYOPENCL_INLINE image*
-new_image(cl_mem mem, void *buff, const cl_image_format *fmt)
-{
-    return pyopencl_convert_obj(image, clReleaseMemObject, mem, buff, fmt);
-}
-
-// {{{ image creation
-
-// #if PYOPENCL_CL_VERSION >= 0x1020
-
-//   PYOPENCL_INLINE
-//   image *create_image_from_desc(
-//       context const &ctx,
-//       cl_mem_flags flags,
-//       cl_image_format const &fmt,
-//       cl_image_desc &desc,
-//       py::object buffer)
-//   {
-//     if (buffer.ptr() != Py_None &&
-//         !(flags & (CL_MEM_USE_HOST_PTR | CL_MEM_COPY_HOST_PTR)))
-//       PyErr_Warn(PyExc_UserWarning, "'hostbuf' was passed, "
-//           "but no memory flags to make use of it.");
-
-//     void *buf = 0;
-//     PYOPENCL_BUFFER_SIZE_T len;
-//     py::object *retained_buf_obj = 0;
-
-//     if (buffer.ptr() != Py_None)
-//     {
-//       if (flags & CL_MEM_USE_HOST_PTR)
-//       {
-//         if (PyObject_AsWriteBuffer(buffer.ptr(), &buf, &len))
-//           throw py::error_already_set();
-//       }
-//       else
-//       {
-//         if (PyObject_AsReadBuffer(
-//               buffer.ptr(), const_cast<const void **>(&buf), &len))
-//           throw py::error_already_set();
-//       }
-
-//       if (flags & CL_MEM_USE_HOST_PTR)
-//         retained_buf_obj = &buffer;
-//     }
-
-//     PYOPENCL_PRINT_CALL_TRACE("clCreateImage");
-//     cl_int status_code;
-//     cl_mem mem = clCreateImage(ctx.data(), flags, &fmt, &desc, buf, &status_code);
-//     if (status_code != CL_SUCCESS)
-//       throw clerror("clCreateImage", status_code);
-
-//     try
-//     {
-//       return new image(mem, false, retained_buf_obj);
-//     }
-//     catch (...)
-//     {
-//       PYOPENCL_CALL_GUARDED(clReleaseMemObject, (mem));
-//       throw;
-//     }
-//   }
-
-// #endif
-
-// }}}
-
-// {{{ image transfers
-
-  //   PYOPENCL_INLINE
-  //   event *enqueue_copy_image_to_buffer(
-  //       command_queue &cq,
-  //       memory_object_holder &src,
-  //       memory_object_holder &dest,
-  //       py::object py_origin,
-  //       py::object py_region,
-  //       size_t offset,
-  //       py::object py_wait_for
-  //       )
-  //   {
-  //     PYOPENCL_PARSE_WAIT_FOR;
-  //     COPY_PY_COORD_TRIPLE(origin);
-  //     COPY_PY_REGION_TRIPLE(region);
-
-  //     cl_event evt;
-  //     PYOPENCL_RETRY_IF_MEM_ERROR(
-  //       PYOPENCL_CALL_GUARDED(clEnqueueCopyImageToBuffer, (
-  //             cq.data(), src.data(), dest.data(),
-  //             origin, region, offset,
-  //             PYOPENCL_WAITLIST_ARGS, &evt
-  //             ));
-  //       );
-  //     PYOPENCL_RETURN_NEW_EVENT(evt);
-  //   }
-
-  //   PYOPENCL_INLINE
-  //   event *enqueue_copy_buffer_to_image(
-  //       command_queue &cq,
-  //       memory_object_holder &src,
-  //       memory_object_holder &dest,
-  //       size_t offset,
-  //       py::object py_origin,
-  //       py::object py_region,
-  //       py::object py_wait_for
-  //       )
-  //   {
-  //     PYOPENCL_PARSE_WAIT_FOR;
-  //     COPY_PY_COORD_TRIPLE(origin);
-  //     COPY_PY_REGION_TRIPLE(region);
-
-  //     cl_event evt;
-  //     PYOPENCL_RETRY_IF_MEM_ERROR(
-  //       PYOPENCL_CALL_GUARDED(clEnqueueCopyBufferToImage, (
-  //             cq.data(), src.data(), dest.data(),
-  //             offset, origin, region,
-  //             PYOPENCL_WAITLIST_ARGS, &evt
-  //             ));
-  //       );
-  //     PYOPENCL_RETURN_NEW_EVENT(evt);
-  //   }
-
-  //   // }}}
-
-// }}}
-
-// {{{ gl interop
-
-#ifdef HAVE_GL
-
-#ifdef __APPLE__
-static PYOPENCL_INLINE cl_context_properties
-get_apple_cgl_share_group()
-{
-    CGLContextObj kCGLContext = CGLGetCurrentContext();
-    CGLShareGroupObj kCGLShareGroup = CGLGetShareGroup(kCGLContext);
-
-    return (cl_context_properties)kCGLShareGroup;
-}
-#endif /* __APPLE__ */
-
-class gl_buffer : public memory_object {
-public:
-    PYOPENCL_DEF_CL_CLASS(GL_BUFFER);
-    gl_buffer(cl_mem mem, bool retain, void *hostbuf=0)
-        : memory_object(mem, retain, hostbuf)
-    {}
-};
-
-class gl_renderbuffer : public memory_object {
-public:
-    PYOPENCL_DEF_CL_CLASS(GL_RENDERBUFFER);
-    gl_renderbuffer(cl_mem mem, bool retain, void *hostbuf=0)
-        : memory_object(mem, retain, hostbuf)
-    {}
-};
-
-class gl_texture : public image {
-  public:
-    gl_texture(cl_mem mem, bool retain, void *hostbuf=0)
-      : image(mem, retain, hostbuf)
-    {}
-    PYOPENCL_USE_RESULT generic_info
-    get_gl_texture_info(cl_gl_texture_info param_name) const
-    {
-        switch (param_name) {
-        case CL_GL_TEXTURE_TARGET:
-            return pyopencl_get_int_info(GLenum, GLTexture, this, param_name);
-        case CL_GL_MIPMAP_LEVEL:
-            return pyopencl_get_int_info(GLint, GLTexture, this, param_name);
-        default:
-            throw clerror("MemoryObject.get_gl_texture_info", CL_INVALID_VALUE);
-        }
-    }
-};
-
-PYOPENCL_USE_RESULT static gl_texture*
-create_from_gl_texture(context &ctx, cl_mem_flags flags, GLenum texture_target,
-                       GLint miplevel, GLuint texture, unsigned dims)
-{
-    if (dims == 2) {
-        cl_mem mem = pyopencl_call_guarded(clCreateFromGLTexture2D,
-                                           ctx, flags, texture_target,
-                                           miplevel, texture);
-        return pyopencl_convert_obj(gl_texture, clReleaseMemObject, mem);
-    } else if (dims == 3) {
-        cl_mem mem = pyopencl_call_guarded(clCreateFromGLTexture3D,
-                                           ctx, flags, texture_target,
-                                           miplevel, texture);
-        return pyopencl_convert_obj(gl_texture, clReleaseMemObject, mem);
-    } else {
-        throw clerror("Image", CL_INVALID_VALUE, "invalid dimension");
-    }
-}
-
-  // TODO:
-  // PYOPENCL_INLINE
-  // py::tuple get_gl_object_info(memory_object_holder const &mem)
-  // {
-  //   cl_gl_object_type otype;
-  //   GLuint gl_name;
-  //   PYOPENCL_CALL_GUARDED(clGetGLObjectInfo, (mem, &otype, &gl_name));
-  //   return py::make_tuple(otype, gl_name);
-  // }
-
-typedef cl_int (*clEnqueueGLObjectFunc)(cl_command_queue, cl_uint,
-                                        const cl_mem*, cl_uint,
-                                        const cl_event*, cl_event*);
-
-PYOPENCL_USE_RESULT static PYOPENCL_INLINE event*
-enqueue_gl_objects(clEnqueueGLObjectFunc func, const char *name,
-                   command_queue *cq, const clobj_t *mem_objects,
-                   uint32_t num_mem_objects, const clobj_t *wait_for,
-                   uint32_t num_wait_for)
-{
-    const auto _wait_for = buf_from_class<event>(wait_for, num_wait_for);
-    const auto _mem_objs = buf_from_class<memory_object_holder>(
-        mem_objects, num_mem_objects);
-    cl_event evt;
-    call_guarded(func, name, cq, _mem_objs, _wait_for, &evt);
-    return new_event(evt);
-}
-#define enqueue_gl_objects(what, args...)                       \
-    enqueue_gl_objects(clEnqueue##what##GLObjects,              \
-                       "clEnqueue" #what "GLObjects", args)
-
-#endif
-
-
-
-
-  // #if defined(cl_khr_gl_sharing) && (cl_khr_gl_sharing >= 1)
-  //   PYOPENCL_INLINE
-  //   py::object get_gl_context_info_khr(
-  //       py::object py_properties,
-  //       cl_gl_context_info param_name,
-  //       py::object py_platform
-  //       )
-  //   {
-  //     std::vector<cl_context_properties> props
-  //       = parse_context_properties(py_properties);
-
-  //     typedef CL_API_ENTRY cl_int (CL_API_CALL
-  //       *func_ptr_type)(const cl_context_properties * /* properties */,
-  //           cl_gl_context_info            /* param_name */,
-  //           size_t                        /* param_value_size */,
-  //           void *                        /* param_value */,
-  //           size_t *                      /* param_value_size_ret */) CL_API_SUFFIX__VERSION_1_0;
-
-  //     func_ptr_type func_ptr;
-
-  // #if PYOPENCL_CL_VERSION >= 0x1020
-  //     if (py_platform.ptr() != Py_None)
-  //     {
-  //       platform &plat = py::extract<platform &>(py_platform);
-
-  //       func_ptr = (func_ptr_type) clGetExtensionFunctionAddressForPlatform(
-  //             plat.data(), "clGetGLContextInfoKHR");
-  //     }
-  //     else
-  //     {
-  //       PYOPENCL_DEPRECATED("get_gl_context_info_khr with platform=None", "2013.1", );
-
-  //       func_ptr = (func_ptr_type) clGetExtensionFunctionAddress(
-  //             "clGetGLContextInfoKHR");
-  //     }
-  // #else
-  //     func_ptr = (func_ptr_type) clGetExtensionFunctionAddress(
-  //           "clGetGLContextInfoKHR");
-  // #endif
-
-
-  //     if (!func_ptr)
-  //       throw error("Context.get_info", CL_INVALID_PLATFORM,
-  //           "clGetGLContextInfoKHR extension function not present");
-
-  //     cl_context_properties *props_ptr
-  //       = props.empty( ) ? nullptr : &props.front();
-
-  //     switch (param_name)
-  //     {
-  //       case CL_CURRENT_DEVICE_FOR_GL_CONTEXT_KHR:
-  //         {
-  //           cl_device_id param_value;
-  //           PYOPENCL_CALL_GUARDED(func_ptr,
-  //               (props_ptr, param_name, sizeof(param_value), &param_value, 0));
-  //           return py::object(handle_from_new_ptr( new device(param_value, /*retain*/ true)));
-  //         }
-
-  //       case CL_DEVICES_FOR_GL_CONTEXT_KHR:
-  //         {
-  //           size_t size;
-  //           PYOPENCL_CALL_GUARDED(func_ptr,
-  //               (props_ptr, param_name, 0, 0, &size));
-
-  //           std::vector<cl_device_id> devices;
-
-  //           devices.resize(size / sizeof(devices.front()));
-
-  //           PYOPENCL_CALL_GUARDED(func_ptr,
-  //               (props_ptr, param_name, size,
-  //                devices.empty( ) ? nullptr : &devices.front(), &size));
-
-  //           py::list result;
-  //           BOOST_FOREACH(cl_device_id did, devices)
-  //             result.append(handle_from_new_ptr(
-  //                   new device(did)));
-
-  //           return result;
-  //         }
-
-  //       default:
-  //         throw error("get_gl_context_info_khr", CL_INVALID_VALUE);
-  //     }
-  //   }
-
-  // #endif
-
-  // }}}
-
-
-  // {{{ buffer
-
-class buffer;
-static PYOPENCL_INLINE buffer *new_buffer(cl_mem mem, void *buff=0);
-
-class buffer : public memory_object {
-public:
-    PYOPENCL_DEF_CL_CLASS(BUFFER);
-    buffer(cl_mem mem, bool retain, void *hostbuf=0)
-        : memory_object(mem, retain, hostbuf)
-    {}
-
-#if PYOPENCL_CL_VERSION >= 0x1010
-    PYOPENCL_USE_RESULT buffer*
-    get_sub_region(size_t origin, size_t size, cl_mem_flags flags) const
-    {
-        cl_buffer_region region = {origin, size};
-
-        auto mem = retry_mem_error<cl_mem>([&] {
-                return pyopencl_call_guarded(clCreateSubBuffer, this, flags,
-                                             CL_BUFFER_CREATE_TYPE_REGION,
-                                             &region);
-            });
-        return new_buffer(mem);
-    }
-
-    //       buffer *getitem(py::slice slc) const
-    //       {
-    //         PYOPENCL_BUFFER_SIZE_T start, end, stride, length;
-
-    //         size_t my_length;
-    //         PYOPENCL_CALL_GUARDED(clGetMemObjectInfo,
-    //             (this, CL_MEM_SIZE, sizeof(my_length), &my_length, 0));
-
-    // #if PY_VERSION_HEX >= 0x03020000
-    //         if (PySlice_GetIndicesEx(slc.ptr(),
-    // #else
-    //         if (PySlice_GetIndicesEx(reinterpret_cast<PySliceObject *>(slc.ptr()),
-    // #endif
-    //               my_length, &start, &end, &stride, &length) != 0)
-    //           throw py::error_already_set();
-
-    //         if (stride != 1)
-    //           throw clerror("Buffer.__getitem__", CL_INVALID_VALUE,
-    //               "Buffer slice must have stride 1");
-
-    //         cl_mem_flags my_flags;
-    //         PYOPENCL_CALL_GUARDED(clGetMemObjectInfo,
-    //             (this, CL_MEM_FLAGS, sizeof(my_flags), &my_flags, 0));
-
-    //         return get_sub_region(start, end, my_flags);
-    //       }
-#endif
-};
-PYOPENCL_USE_RESULT static PYOPENCL_INLINE buffer*
-new_buffer(cl_mem mem, void *buff)
-{
-    return pyopencl_convert_obj(buffer, clReleaseMemObject, mem, buff);
-}
-
-// }}}
-
-// {{{ memory_map
-
-class memory_map : public clobj<void*> {
-private:
-    mutable volatile std::atomic_bool m_valid;
-    command_queue m_queue;
-    memory_object m_mem;
-public:
-    memory_map(const command_queue *queue, const memory_object *mem, void *ptr)
-        : clobj(ptr), m_valid(true), m_queue(*queue), m_mem(*mem)
-    {}
-    ~memory_map()
-    {
-        if (!m_valid.exchange(false))
-            return;
-        pyopencl_call_guarded_cleanup(clEnqueueUnmapMemObject, m_queue,
-                                      m_mem, this, 0, nullptr, nullptr);
-    }
-    event*
-    release(const command_queue *queue, const clobj_t *_wait_for,
-            uint32_t num_wait_for) const
-    {
-        if (!m_valid.exchange(false)) {
-            throw clerror("MemoryMap.release", CL_INVALID_VALUE,
-                          "trying to double-unref mem map");
-        }
-        const auto wait_for = buf_from_class<event>(_wait_for, num_wait_for);
-        queue = queue ? queue : &m_queue;
-        cl_event evt;
-        pyopencl_call_guarded(clEnqueueUnmapMemObject, queue,
-                              m_mem, this, wait_for, &evt);
-        return new_event(evt);
-    }
-    generic_info
-    get_info(cl_uint) const
-    {
-        throw clerror("MemoryMap.get_info", CL_INVALID_VALUE);
-    }
-    intptr_t
-    intptr() const
-    {
-        return m_valid ? (intptr_t)data() : 0;
-    }
-};
-
-// }}}
-
-// {{{ sampler
-
-class sampler : public clobj<cl_sampler> {
-public:
-    PYOPENCL_DEF_CL_CLASS(SAMPLER);
-    sampler(const context *ctx, bool normalized_coordinates,
-            cl_addressing_mode am, cl_filter_mode fm)
-        : clobj(pyopencl_call_guarded(clCreateSampler, ctx,
-                                      normalized_coordinates, am, fm))
-    {}
-    sampler(cl_sampler samp, bool retain)
-        : clobj(samp)
-    {
-        if (retain) {
-            pyopencl_call_guarded(clRetainSampler, this);
-        }
-    }
-    ~sampler()
-    {
-        pyopencl_call_guarded_cleanup(clReleaseSampler, this);
-    }
-    generic_info
-    get_info(cl_uint param_name) const
-    {
-        switch ((cl_sampler_info)param_name) {
-        case CL_SAMPLER_REFERENCE_COUNT:
-            return pyopencl_get_int_info(cl_uint, Sampler,
-                                         this, param_name);
-        case CL_SAMPLER_CONTEXT:
-            return pyopencl_get_opaque_info(cl_context, context,
-                                            Sampler, this, param_name);
-        case CL_SAMPLER_ADDRESSING_MODE:
-            return pyopencl_get_int_info(cl_addressing_mode, Sampler,
-                                         this, param_name);
-        case CL_SAMPLER_FILTER_MODE:
-            return pyopencl_get_int_info(cl_filter_mode, Sampler,
-                                         this, param_name);
-        case CL_SAMPLER_NORMALIZED_COORDS:
-            return pyopencl_get_int_info(cl_bool, Sampler,
-                                         this, param_name);
-
-        default:
-            throw clerror("Sampler.get_info", CL_INVALID_VALUE);
-        }
-    }
-};
-
-// }}}
-
-
-// {{{ program
-
-class program : public clobj<cl_program> {
-private:
-    program_kind_type m_program_kind;
-
-public:
-    PYOPENCL_DEF_CL_CLASS(PROGRAM);
-    program(cl_program prog, bool retain,
-            program_kind_type progkind=KND_UNKNOWN)
-        : clobj(prog), m_program_kind(progkind)
-    {
-        if (retain) {
-            pyopencl_call_guarded(clRetainProgram, this);
-        }
-    }
-    ~program()
-    {
-        pyopencl_call_guarded_cleanup(clReleaseProgram, this);
-    }
-    program_kind_type
-    kind() const
-    {
-        return m_program_kind;
-    }
-    PYOPENCL_USE_RESULT pyopencl_buf<cl_device_id>
-    get_info__devices() const
-    {
-        return pyopencl_get_vec_info(cl_device_id, Program, this,
-                                     CL_PROGRAM_DEVICES);
-    }
-    generic_info
-    get_info(cl_uint param_name) const
-    {
-        switch ((cl_program_info)param_name) {
-        case CL_PROGRAM_CONTEXT:
-            return pyopencl_get_opaque_info(cl_context, context,
-                                            Program, this, param_name);
-        case CL_PROGRAM_REFERENCE_COUNT:
-        case CL_PROGRAM_NUM_DEVICES:
-            return pyopencl_get_int_info(cl_uint, Program, this, param_name);
-        case CL_PROGRAM_DEVICES:
-            return pyopencl_get_opaque_array_info(
-                cl_device_id, device, Program, this, param_name);
-        case CL_PROGRAM_SOURCE:
-            return pyopencl_get_str_info(Program, this, param_name);
-        case CL_PROGRAM_BINARY_SIZES:
-            return pyopencl_get_array_info(size_t, Program, this, param_name);
-        case CL_PROGRAM_BINARIES: {
-            auto sizes = pyopencl_get_vec_info(size_t, Program, this,
-                                               CL_PROGRAM_BINARY_SIZES);
-            pyopencl_buf<char*> result_ptrs(sizes.len());
-            for (size_t i  = 0;i < sizes.len();i++) {
-                result_ptrs[i] = (char*)malloc(sizes[i]);
-            }
-            try {
-                pyopencl_call_guarded(clGetProgramInfo, this,
-                                      CL_PROGRAM_BINARIES,
-                                      sizes.len() * sizeof(char*),
-                                      result_ptrs.get(), nullptr);
-            } catch (...) {
-                for (size_t i  = 0;i < sizes.len();i++) {
-                    free(result_ptrs[i]);
-                }
-            }
-            pyopencl_buf<generic_info> gis(sizes.len());
-            for (size_t i  = 0;i < sizes.len();i++) {
-                gis[i].value = result_ptrs[i];
-                gis[i].dontfree = 0;
-                gis[i].opaque_class = CLASS_NONE;
-                gis[i].type =  _copy_str(std::string("char[") +
-                                         tostring(sizes[i]) + "]");
-            }
-            return pyopencl_convert_array_info(generic_info, gis);
-        }
-
-#if PYOPENCL_CL_VERSION >= 0x1020
-        case CL_PROGRAM_NUM_KERNELS:
-            return pyopencl_get_int_info(size_t, Program, this, param_name);
-        case CL_PROGRAM_KERNEL_NAMES:
-            return pyopencl_get_str_info(Program, this, param_name);
-#endif
-        default:
-            throw clerror("Program.get_info", CL_INVALID_VALUE);
-        }
-    }
-    PYOPENCL_USE_RESULT generic_info
-    get_build_info(const device *dev, cl_program_build_info param_name) const
-    {
-        switch (param_name) {
-        case CL_PROGRAM_BUILD_STATUS:
-            return pyopencl_get_int_info(cl_build_status, ProgramBuild,
-                                         this, dev, param_name);
-        case CL_PROGRAM_BUILD_OPTIONS:
-        case CL_PROGRAM_BUILD_LOG:
-            return pyopencl_get_str_info(ProgramBuild, this,
-                                         dev, param_name);
-#if PYOPENCL_CL_VERSION >= 0x1020
-        case CL_PROGRAM_BINARY_TYPE:
-            return pyopencl_get_int_info(cl_program_binary_type, ProgramBuild,
-                                         this, dev, param_name);
-#endif
-        default:
-            throw clerror("Program.get_build_info", CL_INVALID_VALUE);
-        }
-    }
-    void
-    build(const char *options, cl_uint num_devices,
-          const clobj_t *_devices) const
-    {
-        const auto devices = buf_from_class<device>(_devices, num_devices);
-        pyopencl_call_guarded(clBuildProgram, this, devices, options,
-                              nullptr, nullptr);
-    }
-
-    // #if PYOPENCL_CL_VERSION >= 0x1020
-    //       void compile(std::string options, py::object py_devices,
-    //           py::object py_headers)
-    //       {
-    //         PYOPENCL_PARSE_PY_DEVICES;
-
-    //         // {{{ pick apart py_headers
-    //         // py_headers is a list of tuples *(name, program)*
-
-    //         std::vector<std::string> header_names;
-    //         std::vector<cl_program> programs;
-    //         PYTHON_FOREACH(name_hdr_tup, py_headers)
-    //         {
-    //           if (py::len(name_hdr_tup) != 2)
-    //             throw error("Program.compile", CL_INVALID_VALUE,
-    //                 "epxected (name, header) tuple in headers list");
-    //           std::string name = py::extract<std::string const &>(name_hdr_tup[0]);
-    //           program &prg = py::extract<program &>(name_hdr_tup[1]);
-
-    //           header_names.push_back(name);
-    //           programs.push_back(prg.data());
-    //         }
-
-    //         std::vector<const char *> header_name_ptrs;
-    //         BOOST_FOREACH(std::string const &name, header_names)
-    //           header_name_ptrs.push_back(name.c_str());
-
-    //         // }}}
-
-    //         PYOPENCL_CALL_GUARDED(clCompileProgram,
-    //             (this, num_devices, devices,
-    //              options.c_str(), header_names.size(),
-    //              programs.empty() ? nullptr : &programs.front(),
-    //              header_name_ptrs.empty() ? nullptr : &header_name_ptrs.front(),
-    //              0, 0));
-    //       }
-    // #endif
-};
-PYOPENCL_USE_RESULT static PYOPENCL_INLINE program*
-new_program(cl_program prog, program_kind_type progkind=KND_UNKNOWN)
-{
-    return pyopencl_convert_obj(program, clReleaseProgram, prog, progkind);
-}
-
-// }}}
-
-// {{{ kernel
-
-class kernel : public clobj<cl_kernel> {
-public:
-    PYOPENCL_DEF_CL_CLASS(KERNEL);
-    kernel(cl_kernel knl, bool retain)
-        : clobj(knl)
-    {
-        if (retain) {
-            pyopencl_call_guarded(clRetainKernel, this);
-        }
-    }
-    kernel(const program *prg, const char *kernel_name)
-        : clobj(pyopencl_call_guarded(clCreateKernel, prg, kernel_name))
-    {}
-    ~kernel()
-    {
-        pyopencl_call_guarded_cleanup(clReleaseKernel, this);
-    }
-    void
-    set_arg_null(cl_uint arg_index) const
-    {
-        const cl_mem m = 0;
-        pyopencl_call_guarded(clSetKernelArg, this, arg_index, make_sizearg(m));
-    }
-    void
-    set_arg_mem(cl_uint arg_index, const memory_object_holder *mem) const
-    {
-        pyopencl_call_guarded(clSetKernelArg, this, arg_index,
-                              make_sizearg(mem->data()));
-    }
-    void
-    set_arg_sampler(cl_uint arg_index, const sampler *smp) const
-    {
-        pyopencl_call_guarded(clSetKernelArg, this, arg_index,
-                              make_sizearg(smp->data()));
-    }
-    void
-    set_arg_buf(cl_uint arg_index, const void *buffer, size_t size) const
-    {
-        pyopencl_call_guarded(clSetKernelArg, this, arg_index, size, buffer);
-    }
-    generic_info
-    get_info(cl_uint param_name) const
-    {
-        switch ((cl_kernel_info)param_name) {
-        case CL_KERNEL_FUNCTION_NAME:
-            return pyopencl_get_str_info(Kernel, this, param_name);
-        case CL_KERNEL_NUM_ARGS:
-        case CL_KERNEL_REFERENCE_COUNT:
-            return pyopencl_get_int_info(cl_uint, Kernel, this, param_name);
-        case CL_KERNEL_CONTEXT:
-            return pyopencl_get_opaque_info(cl_context, context,
-                                            Kernel, this, param_name);
-        case CL_KERNEL_PROGRAM:
-            return pyopencl_get_opaque_info(cl_program, program,
-                                            Kernel, this, param_name);
-#if PYOPENCL_CL_VERSION >= 0x1020
-        case CL_KERNEL_ATTRIBUTES:
-            return pyopencl_get_str_info(Kernel, this, param_name);
-#endif
-        default:
-            throw clerror("Kernel.get_info", CL_INVALID_VALUE);
-        }
-    }
-    PYOPENCL_USE_RESULT generic_info
-    get_work_group_info(cl_kernel_work_group_info param_name,
-                        const device *dev) const
-    {
-        switch (param_name) {
-#if PYOPENCL_CL_VERSION >= 0x1010
-        case CL_KERNEL_PREFERRED_WORK_GROUP_SIZE_MULTIPLE:
-#endif
-        case CL_KERNEL_WORK_GROUP_SIZE:
-            return pyopencl_get_int_info(size_t, KernelWorkGroup,
-                                         this, dev, param_name);
-        case CL_KERNEL_COMPILE_WORK_GROUP_SIZE:
-            return pyopencl_get_array_info(size_t, KernelWorkGroup,
-                                           this, dev, param_name);
-        case CL_KERNEL_LOCAL_MEM_SIZE:
-#if PYOPENCL_CL_VERSION >= 0x1010
-        case CL_KERNEL_PRIVATE_MEM_SIZE:
-#endif
-            return pyopencl_get_int_info(cl_ulong, KernelWorkGroup,
-                                         this, dev, param_name);
-        default:
-            throw clerror("Kernel.get_work_group_info", CL_INVALID_VALUE);
-        }
-    }
-
-    // #if PYOPENCL_CL_VERSION >= 0x1020
-    //       py::object get_arg_info(
-    //           cl_uint arg_index,
-    //           cl_kernel_arg_info param_name
-    //           ) const
-    //       {
-    //         switch (param_name)
-    //         {
-    // #define PYOPENCL_FIRST_ARG data(), arg_index // hackety hack
-    //           case CL_KERNEL_ARG_ADDRESS_QUALIFIER:
-    //             PYOPENCL_GET_INTEGRAL_INFO(KernelArg,
-    //                 PYOPENCL_FIRST_ARG, param_name,
-    //                 cl_kernel_arg_address_qualifier);
-
-    //           case CL_KERNEL_ARG_ACCESS_QUALIFIER:
-    //             PYOPENCL_GET_INTEGRAL_INFO(KernelArg,
-    //                 PYOPENCL_FIRST_ARG, param_name,
-    //                 cl_kernel_arg_access_qualifier);
-
-    //           case CL_KERNEL_ARG_TYPE_NAME:
-    //           case CL_KERNEL_ARG_NAME:
-    //             PYOPENCL_GET_STR_INFO(KernelArg, PYOPENCL_FIRST_ARG, param_name);
-    // #undef PYOPENCL_FIRST_ARG
-    //           default:
-    //             throw error("Kernel.get_arg_info", CL_INVALID_VALUE);
-    //         }
-    //       }
-    // #endif
-};
-
-// }}}
-
-}
-
-
-// {{{ c wrapper
-
-// Import all the names in pyopencl namespace for c wrappers.
-using namespace pyopencl;
-
-// Generic functions
-int
-get_cl_version()
-{
-    return PYOPENCL_CL_VERSION;
-}
-
-void
-free_pointer(void *p)
-{
-    free(p);
-}
-
-void
-free_pointer_array(void **p, uint32_t size)
-{
-    for (uint32_t i = 0;i < size;i++) {
-        free(p[i]);
-    }
-}
-
-int
-have_gl()
-{
-#ifdef HAVE_GL
-    return 1;
-#else
-    return 0;
-#endif
-}
-
-
-// Platform
-error*
-get_platforms(clobj_t **_platforms, uint32_t *num_platforms)
-{
-    return c_handle_error([&] {
-            *num_platforms = 0;
-            pyopencl_call_guarded(clGetPlatformIDs, 0, nullptr,
-                                  make_argbuf(*num_platforms));
-            pyopencl_buf<cl_platform_id> platforms(*num_platforms);
-            pyopencl_call_guarded(clGetPlatformIDs, platforms,
-                                  make_argbuf(*num_platforms));
-            *_platforms = buf_to_base<platform>(platforms).release();
-        });
-}
-
-error*
-platform__get_devices(clobj_t _plat, clobj_t **_devices,
-                      uint32_t *num_devices, cl_device_type devtype)
-{
-    auto plat = static_cast<platform*>(_plat);
-    return c_handle_error([&] {
-            auto devices = plat->get_devices(devtype);
-            *num_devices = devices.len();
-            *_devices = buf_to_base<device>(devices).release();
-        });
-}
-
-
-// Context
-error*
-create_context(clobj_t *_ctx, const cl_context_properties *properties,
-               cl_uint num_devices, const clobj_t *_devices)
-{
-    return c_handle_error([&] {
-            const auto devices = buf_from_class<device>(_devices, num_devices);
-            *_ctx = new context(
-                pyopencl_call_guarded(
-                    clCreateContext,
-                    const_cast<cl_context_properties*>(properties),
-                    devices, nullptr, nullptr), false);
-        });
-}
-
-error*
-context__get_supported_image_formats(clobj_t _ctx, cl_mem_flags flags,
-                                     cl_mem_object_type image_type,
-                                     generic_info *out)
-{
-    auto ctx = static_cast<context*>(_ctx);
-    return c_handle_error([&] {
-            *out = ctx->get_supported_image_formats(flags, image_type);
-        });
-}
-
-
-// Command Queue
-error*
-create_command_queue(clobj_t *queue, clobj_t _ctx,
-                     clobj_t _dev, cl_command_queue_properties properties)
-{
-    auto ctx = static_cast<context*>(_ctx);
-    auto dev = static_cast<device*>(_dev);
-    return c_handle_error([&] {
-            *queue = new command_queue(ctx, dev, properties);
-        });
-}
-
-error*
-command_queue__finish(clobj_t queue)
-{
-    return c_handle_error([&] {
-            static_cast<command_queue*>(queue)->finish();
-        });
-}
-
-error*
-command_queue__flush(clobj_t queue)
-{
-    return c_handle_error([&] {
-            static_cast<command_queue*>(queue)->flush();
-        });
-}
-
-
-// Buffer
-error*
-create_buffer(clobj_t *buffer, clobj_t _ctx, cl_mem_flags flags,
-              size_t size, void *hostbuf)
-{
-    auto ctx = static_cast<context*>(_ctx);
-    return c_handle_error([&] {
-            auto mem = retry_mem_error<cl_mem>([&] {
-                    return pyopencl_call_guarded(clCreateBuffer, ctx,
-                                                 flags, size, hostbuf);
-                });
-            *buffer = new_buffer(mem, (flags & CL_MEM_USE_HOST_PTR ?
-                                       hostbuf : nullptr));
-        });
-}
-
-
-// Memory Object
-error*
-memory_object__release(clobj_t obj)
-{
-    return c_handle_error([&] {
-            static_cast<memory_object*>(obj)->release();
-        });
-}
-
-
-// Memory Map
-error*
-memory_map__release(clobj_t _map, clobj_t _queue, const clobj_t *_wait_for,
-                    uint32_t num_wait_for, clobj_t *evt)
-{
-    auto map = static_cast<memory_map*>(_map);
-    auto queue = static_cast<command_queue*>(_queue);
-    return c_handle_error([&] {
-            *evt = map->release(queue, _wait_for, num_wait_for);
-        });
-}
-
-void*
-memory_map__data(clobj_t _map)
-{
-    return static_cast<memory_map*>(_map)->data();
-}
-
-// Program
-error*
-create_program_with_source(clobj_t *prog, clobj_t _ctx, const char *src)
-{
-    auto ctx = static_cast<context*>(_ctx);
-    return c_handle_error([&] {
-            size_t length = strlen(src);
-            cl_program result = pyopencl_call_guarded(
-                clCreateProgramWithSource, ctx, 1, &src, &length);
-            *prog = new_program(result, KND_SOURCE);
-        });
-}
-
-error*
-create_program_with_binary(clobj_t *prog, clobj_t _ctx,
-                           cl_uint num_devices, const clobj_t *devices,
-                           char **binaries, size_t *binary_sizes)
-{
-    auto ctx = static_cast<context*>(_ctx);
-    const auto devs = buf_from_class<device>(devices, num_devices);
-    pyopencl_buf<cl_int> binary_statuses(num_devices);
-    return c_handle_error([&] {
-            cl_program result = pyopencl_call_guarded(
-                clCreateProgramWithBinary, ctx, devs,
-                binary_sizes, reinterpret_cast<const unsigned char**>(
-                    const_cast<const char**>(binaries)), binary_statuses.get());
-            // for (cl_uint i = 0; i < num_devices; ++i)
-            //   std::cout << i << ":" << binary_statuses[i] << std::endl;
-            *prog = new_program(result, KND_BINARY);
-        });
-}
-
-error*
-program__build(clobj_t _prog, const char *options,
-               cl_uint num_devices, const clobj_t *devices)
-{
-    auto prog = static_cast<program*>(_prog);
-    return c_handle_error([&] {
-            prog->build(options, num_devices, devices);
-        });
-}
-
-error*
-program__kind(clobj_t prog, int *kind)
-{
-    return c_handle_error([&] {
-            *kind = static_cast<program*>(prog)->kind();
-        });
-}
-
-error*
-program__get_build_info(clobj_t _prog, clobj_t _dev,
-                        cl_program_build_info param, generic_info *out)
-{
-    auto prog = static_cast<program*>(_prog);
-    auto dev = static_cast<device*>(_dev);
-    return c_handle_error([&] {
-            *out = prog->get_build_info(dev, param);
-        });
-}
-
-
-// Sampler
-error*
-create_sampler(clobj_t *samp, clobj_t _ctx, int norm_coords,
-               cl_addressing_mode am, cl_filter_mode fm)
-{
-    auto ctx = static_cast<context*>(_ctx);
-    return c_handle_error([&] {
-            *samp = new sampler(ctx, (bool)norm_coords, am, fm);
-        });
-}
-
-
-// Kernel
-error*
-create_kernel(clobj_t *knl, clobj_t _prog, const char *name)
-{
-    auto prog = static_cast<program*>(_prog);
-    return c_handle_error([&] {
-            *knl = new kernel(prog, name);
-        });
-}
-
-error*
-kernel__set_arg_null(clobj_t knl, cl_uint arg_index)
-{
-    return c_handle_error([&] {
-            static_cast<kernel*>(knl)->set_arg_null(arg_index);
-        });
-}
-
-error*
-kernel__set_arg_mem(clobj_t _knl, cl_uint arg_index, clobj_t _mem)
-{
-    auto knl = static_cast<kernel*>(_knl);
-    auto mem = static_cast<memory_object_holder*>(_mem);
-    return c_handle_error([&] {
-            knl->set_arg_mem(arg_index, mem);
-        });
-}
-
-error*
-kernel__set_arg_sampler(clobj_t _knl, cl_uint arg_index, clobj_t _samp)
-{
-    auto knl = static_cast<kernel*>(_knl);
-    auto samp = static_cast<sampler*>(_samp);
-    return c_handle_error([&] {
-            knl->set_arg_sampler(arg_index, samp);
-        });
-}
-
-error*
-kernel__set_arg_buf(clobj_t _knl, cl_uint arg_index,
-                    const void *buffer, size_t size)
-{
-    auto knl = static_cast<kernel*>(_knl);
-    return c_handle_error([&] {
-            knl->set_arg_buf(arg_index, buffer, size);
-        });
-}
-
-error*
-kernel__get_work_group_info(clobj_t _knl, cl_kernel_work_group_info param,
-                            clobj_t _dev, generic_info *out)
-{
-    auto knl = static_cast<kernel*>(_knl);
-    auto dev = static_cast<device*>(_dev);
-    return c_handle_error([&] {
-            *out = knl->get_work_group_info(param, dev);
-        });
-}
-
-
-// Image
-error*
-create_image_2d(clobj_t *img, clobj_t _ctx, cl_mem_flags flags,
-                cl_image_format *fmt, size_t width, size_t height,
-                size_t pitch, void *buffer)
-{
-    auto ctx = static_cast<context*>(_ctx);
-    return c_handle_error([&] {
-            auto mem = retry_mem_error<cl_mem>([&] {
-                    return pyopencl_call_guarded(
-                        clCreateImage2D, ctx, flags,
-                        fmt, width, height, pitch, buffer);
-                });
-            *img = new_image(mem, (flags & CL_MEM_USE_HOST_PTR ?
-                                   buffer : nullptr), fmt);
-        });
-}
-
-error*
-create_image_3d(clobj_t *img, clobj_t _ctx, cl_mem_flags flags,
-                cl_image_format *fmt, size_t width, size_t height,
-                size_t depth, size_t pitch_x, size_t pitch_y, void *buffer)
-{
-    auto ctx = static_cast<context*>(_ctx);
-    return c_handle_error([&] {
-            auto mem = retry_mem_error<cl_mem>([&] {
-                    return pyopencl_call_guarded(
-                        clCreateImage3D, ctx, flags, fmt, width,
-                        height, depth, pitch_x, pitch_y, buffer);
-                });
-            *img = new_image(mem, (flags & CL_MEM_USE_HOST_PTR ?
-                                   buffer : nullptr), fmt);
-        });
-}
-
-error*
-image__get_image_info(clobj_t img, cl_image_info param, generic_info *out)
-{
-    return c_handle_error([&] {
-            *out = static_cast<image*>(img)->get_image_info(param);
-        });
-}
-
-type_t
-image__get_fill_type(clobj_t img)
-{
-    return static_cast<image*>(img)->get_fill_type();
-}
-
-
-// Event
-error*
-event__get_profiling_info(clobj_t _evt, cl_profiling_info param,
-                          generic_info *out)
-{
-    auto evt = static_cast<event*>(_evt);
-    return c_handle_error([&] {
-            *out = evt->get_profiling_info(param);
-        });
-}
-
-error*
-event__wait(clobj_t evt)
-{
-    return c_handle_error([&] {
-            static_cast<event*>(evt)->wait();
-        });
-}
-
-#if PYOPENCL_CL_VERSION >= 0x1010
-error*
-event__set_callback(clobj_t _evt, cl_int type, void *pyobj)
-{
-    auto evt = static_cast<event*>(_evt);
-    return c_handle_error([&] {
-            evt->set_callback(type, [=] (cl_int status) {
-                    py::call(pyobj, status);
-                    py::deref(pyobj);
-                });
-            py::ref(pyobj);
-        });
-}
-#endif
-
-error*
-wait_for_events(const clobj_t *_wait_for, uint32_t num_wait_for)
-{
-    const auto wait_for = buf_from_class<event>(_wait_for, num_wait_for);
-    return c_handle_error([&] {
-            pyopencl_call_guarded(clWaitForEvents, wait_for);
-        });
-}
-
-// Nanny Event
-void*
-nanny_event__get_ward(clobj_t evt)
-{
-    return static_cast<nanny_event*>(evt)->get_ward();
-}
-
-
-// enqueue_*
-error*
-enqueue_nd_range_kernel(clobj_t *_evt, clobj_t _queue, clobj_t _knl,
-                        cl_uint work_dim, const size_t *global_work_offset,
-                        const size_t *global_work_size,
-                        const size_t *local_work_size,
-                        const clobj_t *_wait_for, uint32_t num_wait_for)
-{
-    auto queue = static_cast<command_queue*>(_queue);
-    auto knl = static_cast<kernel*>(_knl);
-    const auto wait_for = buf_from_class<event>(_wait_for, num_wait_for);
-    return c_handle_error([&] {
-            cl_event evt;
-            retry_mem_error<void>([&] {
-                    pyopencl_call_guarded(
-                        clEnqueueNDRangeKernel, queue, knl,
-                        work_dim, global_work_offset, global_work_size,
-                        local_work_size, wait_for, &evt);
-                });
-            *_evt = new_event(evt);
-        });
-}
-
-error*
-enqueue_task(clobj_t *_evt, clobj_t _queue, clobj_t _knl,
-             const clobj_t *_wait_for, uint32_t num_wait_for)
-{
-    auto queue = static_cast<command_queue*>(_queue);
-    auto knl = static_cast<kernel*>(_knl);
-    const auto wait_for = buf_from_class<event>(_wait_for, num_wait_for);
-    return c_handle_error([&] {
-            cl_event evt;
-            retry_mem_error<void>([&] {
-                    pyopencl_call_guarded(
-                        clEnqueueTask, queue, knl, wait_for, &evt);
-                });
-            *_evt = new_event(evt);
-        });
-}
-
-#if PYOPENCL_CL_VERSION >= 0x1020
-error*
-enqueue_marker_with_wait_list(clobj_t *_evt, clobj_t _queue,
-                              const clobj_t *_wait_for, uint32_t num_wait_for)
-{
-    auto queue = static_cast<command_queue*>(_queue);
-    const auto wait_for = buf_from_class<event>(_wait_for, num_wait_for);
-    return c_handle_error([&] {
-            cl_event evt;
-            pyopencl_call_guarded(clEnqueueMarkerWithWaitList, queue,
-                                  wait_for, &evt);
-            *_evt = new_event(evt);
-        });
-}
-
-error*
-enqueue_barrier_with_wait_list(clobj_t *_evt, clobj_t _queue,
-                               const clobj_t *_wait_for, uint32_t num_wait_for)
-{
-    auto queue = static_cast<command_queue*>(_queue);
-    const auto wait_for = buf_from_class<event>(_wait_for, num_wait_for);
-    return c_handle_error([&] {
-            cl_event evt;
-            pyopencl_call_guarded(clEnqueueBarrierWithWaitList, queue,
-                                  wait_for, &evt);
-            *_evt = new_event(evt);
-        });
-}
-#endif
+// {{{ c wrapper
 
-error*
-enqueue_wait_for_events(clobj_t _queue, const clobj_t *_wait_for,
-                        uint32_t num_wait_for)
-{
-    auto queue = static_cast<command_queue*>(_queue);
-    const auto wait_for = buf_from_class<event>(_wait_for, num_wait_for);
-    return c_handle_error([&] {
-            pyopencl_call_guarded(clEnqueueWaitForEvents, queue, wait_for);
-        });
-}
+// Import all the names in pyopencl namespace for c wrappers.
+using namespace pyopencl;
 
-error*
-enqueue_marker(clobj_t *_evt, clobj_t _queue)
+// Generic functions
+int
+get_cl_version()
 {
-    auto queue = static_cast<command_queue*>(_queue);
-    return c_handle_error([&] {
-            cl_event evt;
-            pyopencl_call_guarded(clEnqueueMarker, queue, &evt);
-            *_evt = new_event(evt);
-        });
+    return PYOPENCL_CL_VERSION;
 }
 
-error*
-enqueue_barrier(clobj_t _queue)
+void
+free_pointer(void *p)
 {
-    auto queue = static_cast<command_queue*>(_queue);
-    return c_handle_error([&] {
-            pyopencl_call_guarded(clEnqueueBarrier, queue);
-        });
+    free(p);
 }
 
-static memory_map*
-_convert_memory_map(clobj_t *_evt, cl_event evt, command_queue *queue,
-                    memory_object *buf, void *res)
+void
+free_pointer_array(void **p, uint32_t size)
 {
-    try {
-        *_evt = new event(evt, false);
-        evt = 0;
-        return new memory_map(queue, buf, res);
-    } catch (...) {
-        if (evt) {
-            pyopencl_call_guarded_cleanup(clReleaseEvent, evt);
-        }
-        pyopencl_call_guarded_cleanup(clEnqueueUnmapMemObject, queue,
-                                      buf, res, 0, nullptr, nullptr);
-        throw;
+    for (uint32_t i = 0;i < size;i++) {
+        free(p[i]);
     }
 }
 
-// {{{ enqueue_*_buffer*
-
-error*
-enqueue_read_buffer(clobj_t *_evt, clobj_t _queue, clobj_t _mem,
-                    void *buffer, size_t size, size_t device_offset,
-                    const clobj_t *_wait_for, uint32_t num_wait_for,
-                    int is_blocking, void *pyobj)
-{
-    const auto wait_for = buf_from_class<event>(_wait_for, num_wait_for);
-    auto queue = static_cast<command_queue*>(_queue);
-    auto mem = static_cast<memory_object_holder*>(_mem);
-    return c_handle_error([&] {
-            cl_event evt;
-            retry_mem_error<void>([&] {
-                    pyopencl_call_guarded(
-                        clEnqueueReadBuffer, queue, mem,
-                        cast_bool(is_blocking), device_offset, size,
-                        buffer, wait_for, &evt);
-                });
-            *_evt = new_nanny_event(evt, pyobj);
-        });
-}
-
-error*
-enqueue_write_buffer(clobj_t *_evt, clobj_t _queue, clobj_t _mem,
-                     const void *buffer, size_t size, size_t device_offset,
-                     const clobj_t *_wait_for, uint32_t num_wait_for,
-                     int is_blocking, void *pyobj)
-{
-    const auto wait_for = buf_from_class<event>(_wait_for, num_wait_for);
-    auto queue = static_cast<command_queue*>(_queue);
-    auto mem = static_cast<memory_object_holder*>(_mem);
-    return c_handle_error([&] {
-            cl_event evt;
-            retry_mem_error<void>([&] {
-                    pyopencl_call_guarded(
-                        clEnqueueWriteBuffer, queue, mem,
-                        cast_bool(is_blocking), device_offset, size, buffer,
-                        wait_for, &evt);
-                });
-            *_evt = new_nanny_event(evt, pyobj);
-        });
-}
-
-error*
-enqueue_copy_buffer(clobj_t *_evt, clobj_t _queue, clobj_t _src, clobj_t _dst,
-                    ptrdiff_t byte_count, size_t src_offset, size_t dst_offset,
-                    const clobj_t *_wait_for, uint32_t num_wait_for)
-{
-    auto queue = static_cast<command_queue*>(_queue);
-    auto src = static_cast<memory_object_holder*>(_src);
-    auto dst = static_cast<memory_object_holder*>(_dst);
-    return c_handle_error([&] {
-            if (byte_count < 0) {
-                size_t byte_count_src = 0;
-                size_t byte_count_dst = 0;
-                pyopencl_call_guarded(
-                    clGetMemObjectInfo, src, CL_MEM_SIZE,
-                    sizeof(byte_count), &byte_count_src, nullptr);
-                pyopencl_call_guarded(
-                    clGetMemObjectInfo, src, CL_MEM_SIZE,
-                    sizeof(byte_count), &byte_count_dst, nullptr);
-                byte_count = std::min(byte_count_src, byte_count_dst);
-            }
-            const auto wait_for = buf_from_class<event>(_wait_for,
-                                                        num_wait_for);
-            cl_event evt;
-            retry_mem_error<void>([&] {
-                    pyopencl_call_guarded(
-                        clEnqueueCopyBuffer, queue, src, dst, src_offset,
-                        dst_offset, byte_count, wait_for, &evt);
-                });
-            *_evt = new_event(evt);
-        });
-}
-
-error*
-enqueue_map_buffer(clobj_t *_evt, clobj_t *map, clobj_t _queue, clobj_t _mem,
-                   cl_map_flags flags, size_t offset, size_t size,
-                   const clobj_t *_wait_for, uint32_t num_wait_for,
-                   int block)
-{
-    auto queue = static_cast<command_queue*>(_queue);
-    auto buf = static_cast<buffer*>(_mem);
-    const auto wait_for = buf_from_class<event>(_wait_for, num_wait_for);
-    return c_handle_error([&] {
-            cl_event evt;
-            void *res = retry_mem_error<void*>([&] {
-                    return pyopencl_call_guarded(
-                        clEnqueueMapBuffer, queue, buf, cast_bool(block),
-                        flags, offset, size, wait_for, &evt);
-                });
-            *map = _convert_memory_map(_evt, evt, queue, buf, res);
-        });
-}
-
-error*
-enqueue_fill_buffer(clobj_t *_evt, clobj_t _queue, clobj_t _mem, void *pattern,
-                    size_t psize, size_t offset, size_t size,
-                    const clobj_t *_wait_for, uint32_t num_wait_for)
-{
-    const auto wait_for = buf_from_class<event>(_wait_for, num_wait_for);
-    auto queue = static_cast<command_queue*>(_queue);
-    auto mem = static_cast<memory_object_holder*>(_mem);
-    return c_handle_error([&] {
-            cl_event evt;
-            retry_mem_error<void>([&] {
-                    pyopencl_call_guarded(
-                        clEnqueueFillBuffer, queue, mem, pattern, psize,
-                        offset, size, wait_for, &evt);
-                });
-            *_evt = new_event(evt);
-        });
-}
-
-// }}}
-
-// {{{ enqueue_*_image*
-
-error*
-enqueue_read_image(clobj_t *_evt, clobj_t _queue, clobj_t _mem,
-                   const size_t *_origin, size_t origin_l,
-                   const size_t *_region, size_t region_l, void *buffer,
-                   size_t row_pitch, size_t slice_pitch,
-                   const clobj_t *_wait_for, uint32_t num_wait_for,
-                   int is_blocking, void *pyobj)
-{
-    const auto wait_for = buf_from_class<event>(_wait_for, num_wait_for);
-    auto queue = static_cast<command_queue*>(_queue);
-    auto img = static_cast<image*>(_mem);
-    ConstBuffer<size_t, 3> origin(_origin, origin_l);
-    ConstBuffer<size_t, 3> region(_region, region_l);
-    return c_handle_error([&] {
-            cl_event evt;
-            retry_mem_error<void>([&] {
-                    pyopencl_call_guarded(
-                        clEnqueueReadImage, queue, img,
-                        cast_bool(is_blocking), origin, region, row_pitch,
-                        slice_pitch, buffer, wait_for, &evt);
-                });
-            *_evt = new_nanny_event(evt, pyobj);
-        });
-}
-
-error*
-enqueue_copy_image(clobj_t *_evt, clobj_t _queue, clobj_t _src, clobj_t _dst,
-                   const size_t *_src_origin, size_t src_origin_l,
-                   const size_t *_dst_origin, size_t dst_origin_l,
-                   const size_t *_region, size_t region_l,
-                   const clobj_t *_wait_for, uint32_t num_wait_for)
-{
-    const auto wait_for = buf_from_class<event>(_wait_for, num_wait_for);
-    auto queue = static_cast<command_queue*>(_queue);
-    auto src = static_cast<image*>(_src);
-    auto dst = static_cast<image*>(_dst);
-    ConstBuffer<size_t, 3> src_origin(_src_origin, src_origin_l);
-    ConstBuffer<size_t, 3> dst_origin(_dst_origin, dst_origin_l);
-    ConstBuffer<size_t, 3> region(_region, region_l);
-    return c_handle_error([&] {
-            cl_event evt;
-            retry_mem_error<void>([&] {
-                    pyopencl_call_guarded(
-                        clEnqueueCopyImage, queue, src, dst, src_origin,
-                        dst_origin, region, wait_for, &evt);
-                });
-            *_evt = new_event(evt);
-        });
-}
-
-error*
-enqueue_write_image(clobj_t *_evt, clobj_t _queue, clobj_t _mem,
-                    const size_t *_origin, size_t origin_l,
-                    const size_t *_region, size_t region_l,
-                    const void *buffer, size_t row_pitch, size_t slice_pitch,
-                    const clobj_t *_wait_for, uint32_t num_wait_for,
-                    int is_blocking, void *pyobj)
-{
-    auto queue = static_cast<command_queue*>(_queue);
-    auto img = static_cast<image*>(_mem);
-    const auto wait_for = buf_from_class<event>(_wait_for, num_wait_for);
-    ConstBuffer<size_t, 3> origin(_origin, origin_l);
-    ConstBuffer<size_t, 3> region(_region, region_l);
-    return c_handle_error([&] {
-            cl_event evt;
-            retry_mem_error<void>([&] {
-                    pyopencl_call_guarded(
-                        clEnqueueWriteImage, queue, img,
-                        cast_bool(is_blocking), origin, region, row_pitch,
-                        slice_pitch, buffer, wait_for, &evt);
-                });
-            *_evt = new_nanny_event(evt, pyobj);
-        });
-}
-
-error*
-enqueue_map_image(clobj_t *_evt, clobj_t *map, clobj_t _queue, clobj_t _mem,
-                  cl_map_flags flags, const size_t *_origin, size_t origin_l,
-                  const size_t *_region, size_t region_l, size_t *row_pitch,
-                  size_t *slice_pitch, const clobj_t *_wait_for,
-                  uint32_t num_wait_for, int block)
-{
-    auto queue = static_cast<command_queue*>(_queue);
-    auto img = static_cast<image*>(_mem);
-    const auto wait_for = buf_from_class<event>(_wait_for, num_wait_for);
-    ConstBuffer<size_t, 3> origin(_origin, origin_l);
-    ConstBuffer<size_t, 3> region(_region, region_l);
-    return c_handle_error([&] {
-            cl_event evt;
-            void *res = retry_mem_error<void*>([&] {
-                    return pyopencl_call_guarded(
-                        clEnqueueMapImage, queue, img, cast_bool(block), flags,
-                        origin, region, row_pitch, slice_pitch, wait_for, &evt);
-                });
-            *map = _convert_memory_map(_evt, evt, queue, img, res);
-        });
-}
-
-#if PYOPENCL_CL_VERSION >= 0x1020
-  //   PYOPENCL_INLINE
-  //   event *enqueue_fill_image(
-  //       command_queue &cq,
-  //       memory_object_holder &mem,
-  //       py::object color,
-  //       py::object py_origin, py::object py_region,
-  //       py::object py_wait_for
-  //       )
-  //   {
-  //     PYOPENCL_PARSE_WAIT_FOR;
-
-  //     COPY_PY_COORD_TRIPLE(origin);
-  //     COPY_PY_REGION_TRIPLE(region);
-
-  //     const void *color_buf;
-  //     PYOPENCL_BUFFER_SIZE_T color_len;
-
-  //     if (PyObject_AsReadBuffer(color.ptr(), &color_buf, &color_len))
-  //       throw py::error_already_set();
-
-  //     cl_event evt;
-  //     PYOPENCL_RETRY_IF_MEM_ERROR(
-  //       PYOPENCL_CALL_GUARDED(clEnqueueFillImage, (
-  //             cq.data(),
-  //             mem.data(),
-  //             color_buf, origin, region,
-  //             PYOPENCL_WAITLIST_ARGS, &evt
-  //             ));
-  //       );
-  //     PYOPENCL_RETURN_NEW_EVENT(evt);
-  //   }
-#endif
-
-// }}}
 
 intptr_t
 clobj__int_ptr(clobj_t obj)
@@ -2594,7 +78,7 @@ _from_int_ptr(intptr_t ptr, class_t class_)
         return clobj_from_int_ptr<gl_renderbuffer>(ptr);
 #endif
     default:
-        throw pyopencl::clerror("unknown class", CL_INVALID_VALUE);
+        throw clerror("unknown class", CL_INVALID_VALUE);
   }
 }
 
@@ -2620,64 +104,6 @@ clobj__delete(clobj_t obj)
     delete obj;
 }
 
-// {{{ gl interop
-
-#ifdef HAVE_GL
-error*
-create_from_gl_buffer(clobj_t *ptr, clobj_t _ctx,
-                      cl_mem_flags flags, GLuint bufobj)
-{
-    auto ctx = static_cast<context*>(_ctx);
-    return c_handle_error([&] {
-            cl_mem mem = pyopencl_call_guarded(clCreateFromGLBuffer,
-                                               ctx, flags, bufobj);
-            *ptr = pyopencl_convert_obj(gl_buffer,
-                                        clReleaseMemObject, mem);
-        });
-}
-
-error*
-create_from_gl_renderbuffer(clobj_t *ptr, clobj_t _ctx,
-                            cl_mem_flags flags, GLuint bufobj)
-{
-    auto ctx = static_cast<context*>(_ctx);
-    return c_handle_error([&] {
-            cl_mem mem = pyopencl_call_guarded(clCreateFromGLRenderbuffer,
-                                               ctx, flags, bufobj);
-            *ptr = pyopencl_convert_obj(gl_renderbuffer,
-                                        clReleaseMemObject, mem);
-        });
-}
-
-error*
-enqueue_acquire_gl_objects(clobj_t *_evt, clobj_t queue,
-                           const clobj_t *mem_objects,
-                           uint32_t num_mem_objects,
-                           const clobj_t *wait_for, uint32_t num_wait_for)
-{
-    return c_handle_error([&] {
-            *_evt = enqueue_gl_objects(
-                Acquire, static_cast<command_queue*>(queue),
-                mem_objects, num_mem_objects, wait_for, num_wait_for);
-        });
-}
-
-error*
-enqueue_release_gl_objects(clobj_t *event, clobj_t queue,
-                           const clobj_t *mem_objects,
-                           uint32_t num_mem_objects,
-                           const clobj_t *wait_for, uint32_t num_wait_for)
-{
-    return c_handle_error([&] {
-            *event = enqueue_gl_objects(
-                Release, static_cast<command_queue*>(queue),
-                mem_objects, num_mem_objects, wait_for, num_wait_for);
-        });
-}
-#endif /* HAVE_GL */
-
-// }}}
-
 // }}}
 
 // vim: foldmethod=marker
-- 
GitLab