I have heard many complaints about the verbosity of the OpenCL API. This claim in not unwarranted.

The verbosity is due to the low level nature of OpenCL. It is written in the C programming language; the <a href="http://joelonsoftware.com/articles/CollegeAdvice.html">lingua franca</a> of programming languages. While this allows you to run an OpenCL program on virtually any platform, it has some disadvantages.

The simplest OpenCL program must perform:
<ol>
	<li>Query for the platform</li>
	<li>Get the device IDs from the platform</li>
	<li>Create a context from a set of device IDs</li>
	<li>Create a command queue from the context</li>
	<li>Create buffer objects for your data</li>
	<li>Transfer the data to the buffer</li>
	<li>Create and build a program from source</li>
	<li>Extract the kernels</li>
	<li>Launch the kernels</li>
	<li>Transfer the data to the host</li>
</ol>
Wow that was longer than I thought it would be. As you can imagine this can be a daunting task for anyone who is not familiar with the OpenCL API.

As a thought exercise I decided to create the smallest OpenCL program with a focus on simplicity and readability. I am going to make use of the excellent <a title="OpenCL C++ header" href="http://www.khronos.org/registry/cl/api/1.1/cl.hpp" target="_blank">C++ API</a> from the <a title="Khronos Website" href="http://www.khronos.org">Khronos</a> website.

Here are the results:

<pre>
#define __CL_ENABLE_EXCEPTIONS
#include "cl.hpp"
#include <vector>
#include <iostream>
#include <iterator>

using namespace cl;
using namespace std;

int main(int argc, char* argv[]) {
    clCreateContextFromType({0}, CL_DEVICE_TYPE_DEFAULT, nullptr, nullptr, nullptr);
    static const unsigned elements = 100;
    vector<float> data(elements, 5);
    Buffer a(begin(data), end(data), true, false);
    Buffer b(begin(data), end(data), true, false);
    Buffer c(CL_MEM_READ_WRITE, elements * sizeof(float));

    Program addProg(R"d(
        kernel
        void add(   global const float * restrict const a,
                    global const float * restrict const b,
                    global       float * restrict const c) {
            unsigned idx = get_global_id(0);
            c[idx] = a[idx] + b[idx];
        }
    )d", true);

    auto add = make_kernel<Buffer, Buffer, Buffer>(addProg, "add");
    add(EnqueueArgs(elements), a, b, c);

    vector<float> result(elements);
    copy(c, begin(result), end(result));

    copy(begin(result), end(result), ostream_iterator<float>(cout, ", "));
}
</pre>

Not bad eh? The biggest savings were realized by making use of the default platform, context, command queue objects in the C++ API.

 The C++ OpenCL API provides a much cleaner interface when compared to the C counterpart.