1//
2// Copyright 2012 Francisco Jerez
3//
4// Permission is hereby granted, free of charge, to any person obtaining a
5// copy of this software and associated documentation files (the "Software"),
6// to deal in the Software without restriction, including without limitation
7// the rights to use, copy, modify, merge, publish, distribute, sublicense,
8// and/or sell copies of the Software, and to permit persons to whom the
9// Software is furnished to do so, subject to the following conditions:
10//
11// The above copyright notice and this permission notice shall be included in
12// all copies or substantial portions of the Software.
13//
14// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
17// THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
18// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
19// OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20// SOFTWARE.
21//
22
23#ifndef __CORE_BASE_HPP__
24#define __CORE_BASE_HPP__
25
26#include <stdexcept>
27#include <atomic>
28#include <cassert>
29#include <tuple>
30#include <vector>
31#include <functional>
32
33#include "CL/cl.h"
34
35///
36/// Main namespace of the CL state tracker.
37///
38namespace clover {
39   ///
40   /// Class that represents an error that can be converted to an
41   /// OpenCL status code.
42   ///
43   class error : public std::runtime_error {
44   public:
45      error(cl_int code, std::string what = "") :
46         std::runtime_error(what), code(code) {
47      }
48
49      cl_int get() const {
50         return code;
51      }
52
53   protected:
54      cl_int code;
55   };
56
57   ///
58   /// Base class for objects that support reference counting.
59   ///
60   class ref_counter {
61   public:
62      ref_counter() : __ref_count(1) {}
63
64      unsigned ref_count() {
65         return __ref_count;
66      }
67
68      void retain() {
69         __ref_count++;
70      }
71
72      bool release() {
73         return (--__ref_count) == 0;
74      }
75
76   private:
77      std::atomic<unsigned> __ref_count;
78   };
79
80   ///
81   /// Intrusive smart pointer for objects that implement the
82   /// clover::ref_counter interface.
83   ///
84   template<typename T>
85   class ref_ptr {
86   public:
87      ref_ptr(T *q = NULL) : p(NULL) {
88         reset(q);
89      }
90
91      ref_ptr(const ref_ptr<T> &ref) : p(NULL) {
92         reset(ref.p);
93      }
94
95      ~ref_ptr() {
96         reset(NULL);
97      }
98
99      void reset(T *q = NULL) {
100         if (q)
101            q->retain();
102         if (p && p->release())
103            delete p;
104         p = q;
105      }
106
107      ref_ptr &operator=(const ref_ptr &ref) {
108         reset(ref.p);
109         return *this;
110      }
111
112      T *operator*() const {
113         return p;
114      }
115
116      T *operator->() const {
117         return p;
118      }
119
120      operator bool() const {
121         return p;
122      }
123
124   private:
125      T *p;
126   };
127
128   ///
129   /// Transfer the caller's ownership of a reference-counted object
130   /// to a clover::ref_ptr smart pointer.
131   ///
132   template<typename T>
133   inline ref_ptr<T>
134   transfer(T *p) {
135      ref_ptr<T> ref { p };
136      p->release();
137      return ref;
138   }
139
140   template<typename T, typename S, int N>
141   struct __iter_helper {
142      template<typename F, typename Its, typename... Args>
143      static T
144      step(F op, S state, Its its, Args... args) {
145         return __iter_helper<T, S, N - 1>::step(
146            op, state, its, *(std::get<N>(its)++), args...);
147      }
148   };
149
150   template<typename T, typename S>
151   struct __iter_helper<T, S, 0> {
152      template<typename F, typename Its, typename... Args>
153      static T
154      step(F op, S state, Its its, Args... args) {
155         return op(state, *(std::get<0>(its)++), args...);
156      }
157   };
158
159   struct __empty {};
160
161   template<typename T>
162   struct __iter_helper<T, __empty, 0> {
163      template<typename F, typename Its, typename... Args>
164      static T
165      step(F op, __empty state, Its its, Args... args) {
166         return op(*(std::get<0>(its)++), args...);
167      }
168   };
169
170   template<typename F, typename... Its>
171   struct __result_helper {
172      typedef typename std::remove_const<
173         typename std::result_of<
174            F (typename std::iterator_traits<Its>::value_type...)
175            >::type
176         >::type type;
177   };
178
179   ///
180   /// Iterate \a op on the result of zipping all the specified
181   /// iterators together.
182   ///
183   /// Similar to std::for_each, but it accepts functions of an
184   /// arbitrary number of arguments.
185   ///
186   template<typename F, typename It0, typename... Its>
187   F
188   for_each(F op, It0 it0, It0 end0, Its... its) {
189      while (it0 != end0)
190         __iter_helper<void, __empty, sizeof...(Its)>::step(
191            op, {}, std::tie(it0, its...));
192
193      return op;
194   }
195
196   ///
197   /// Iterate \a op on the result of zipping all the specified
198   /// iterators together, storing return values in a new container.
199   ///
200   /// Similar to std::transform, but it accepts functions of an
201   /// arbitrary number of arguments and it doesn't have to be
202   /// provided with an output iterator.
203   ///
204   template<typename F, typename It0, typename... Its,
205            typename C = std::vector<
206               typename __result_helper<F, It0, Its...>::type>>
207   C
208   map(F op, It0 it0, It0 end0, Its... its) {
209      C c;
210
211      while (it0 != end0)
212         c.push_back(
213            __iter_helper<typename C::value_type, __empty, sizeof...(Its)>
214            ::step(op, {}, std::tie(it0, its...)));
215
216      return c;
217   }
218
219   ///
220   /// Reduce the result of zipping all the specified iterators
221   /// together, using iterative application of \a op from left to
222   /// right.
223   ///
224   /// Similar to std::accumulate, but it accepts functions of an
225   /// arbitrary number of arguments.
226   ///
227   template<typename F, typename T, typename It0, typename... Its>
228   T
229   fold(F op, T a, It0 it0, It0 end0, Its... its) {
230      while (it0 != end0)
231         a = __iter_helper<T, T, sizeof...(Its)>::step(
232            op, a, std::tie(it0, its...));
233
234      return a;
235   }
236
237   ///
238   /// Iterate \a op on the result of zipping the specified iterators
239   /// together, checking if any of the evaluations returns \a true.
240   ///
241   /// Similar to std::any_of, but it accepts functions of an
242   /// arbitrary number of arguments.
243   ///
244   template<typename F, typename It0, typename... Its>
245   bool
246   any_of(F op, It0 it0, It0 end0, Its... its) {
247      while (it0 != end0)
248         if (__iter_helper<bool, __empty, sizeof...(Its)>::step(
249                op, {}, std::tie(it0, its...)))
250            return true;
251
252      return false;
253   }
254
255   template<typename T, typename S>
256   T
257   keys(const std::pair<T, S> &ent) {
258      return ent.first;
259   }
260
261   template<typename T, typename S>
262   std::function<bool (const std::pair<T, S> &)>
263   key_equals(const T &x) {
264      return [=](const std::pair<T, S> &ent) {
265         return ent.first == x;
266      };
267   }
268
269   template<typename T, typename S>
270   S
271   values(const std::pair<T, S> &ent) {
272      return ent.second;
273   }
274
275   template<typename T>
276   std::function<bool (const T &)>
277   is_zero() {
278      return [](const T &x) {
279         return x == 0;
280      };
281   }
282}
283
284#endif
285