1 //---------------------------------------------------------------------------//
2 // Copyright (c) 2013-2014 Kyle Lutz <kyle.r.lutz@gmail.com>
3 //
4 // Distributed under the Boost Software License, Version 1.0
5 // See accompanying file LICENSE_1_0.txt or copy at
6 // http://www.boost.org/LICENSE_1_0.txt
7 //
8 // See http://boostorg.github.com/compute for more information.
9 //---------------------------------------------------------------------------//
10 
11 #ifndef BOOST_COMPUTE_TYPES_STRUCT_HPP
12 #define BOOST_COMPUTE_TYPES_STRUCT_HPP
13 
14 #include <sstream>
15 
16 #include <boost/static_assert.hpp>
17 
18 #include <boost/preprocessor/expr_if.hpp>
19 #include <boost/preprocessor/stringize.hpp>
20 #include <boost/preprocessor/seq/fold_left.hpp>
21 #include <boost/preprocessor/seq/for_each.hpp>
22 #include <boost/preprocessor/seq/transform.hpp>
23 
24 #include <boost/compute/type_traits/type_definition.hpp>
25 #include <boost/compute/type_traits/type_name.hpp>
26 #include <boost/compute/detail/meta_kernel.hpp>
27 #include <boost/compute/detail/variadic_macros.hpp>
28 
29 namespace boost {
30 namespace compute {
31 namespace detail {
32 
33 template<class Struct, class T>
adapt_struct_insert_member(T Struct::*,const char * name)34 inline std::string adapt_struct_insert_member(T Struct::*, const char *name)
35 {
36     std::stringstream s;
37     s << "    " << type_name<T>() << " " << name << ";\n";
38     return s.str();
39 }
40 
41 
42 template<class Struct, class T, int N>
adapt_struct_insert_member(T (Struct::*)[N],const char * name)43 inline std::string adapt_struct_insert_member(T (Struct::*)[N], const char *name)
44 {
45     std::stringstream s;
46     s << "    " << type_name<T>() << " " << name << "[" << N << "]" << ";\n";
47     return s.str();
48 }
49 
50 } // end detail namespace
51 } // end compute namespace
52 } // end boost namespace
53 
54 /// \internal_
55 #define BOOST_COMPUTE_DETAIL_ADAPT_STRUCT_INSERT_MEMBER(r, type, member) \
56     << ::boost::compute::detail::adapt_struct_insert_member( \
57            &type::member, BOOST_PP_STRINGIZE(member) \
58        )
59 
60 /// \internal_
61 #define BOOST_COMPUTE_DETAIL_ADAPT_STRUCT_STREAM_MEMBER(r, data, i, elem) \
62     BOOST_PP_EXPR_IF(i, << ", ") << data.elem
63 
64 /// \internal_
65 #define BOOST_COMPUTE_DETAIL_STRUCT_MEMBER_SIZE(s, struct_, member_) \
66     sizeof(((struct_ *)0)->member_)
67 
68 /// \internal_
69 #define BOOST_COMPUTE_DETAIL_STRUCT_MEMBER_SIZE_ADD(s, x, y) (x+y)
70 
71 /// \internal_
72 #define BOOST_COMPUTE_DETAIL_STRUCT_MEMBER_SIZE_SUM(struct_, members_) \
73     BOOST_PP_SEQ_FOLD_LEFT( \
74         BOOST_COMPUTE_DETAIL_STRUCT_MEMBER_SIZE_ADD, \
75         0, \
76         BOOST_PP_SEQ_TRANSFORM( \
77             BOOST_COMPUTE_DETAIL_STRUCT_MEMBER_SIZE, struct_, members_ \
78         ) \
79     )
80 
81 /// \internal_
82 ///
83 /// Returns true if struct_ contains no internal padding bytes (i.e. it is
84 /// packed). members_ is a sequence of the names of the struct members.
85 #define BOOST_COMPUTE_DETAIL_STRUCT_IS_PACKED(struct_, members_) \
86     (sizeof(struct_) == BOOST_COMPUTE_DETAIL_STRUCT_MEMBER_SIZE_SUM(struct_, members_))
87 
88 /// The BOOST_COMPUTE_ADAPT_STRUCT() macro makes a C++ struct/class available
89 /// to OpenCL kernels.
90 ///
91 /// \param type The C++ type.
92 /// \param name The OpenCL name.
93 /// \param members A tuple of the struct's members.
94 ///
95 /// For example, to adapt a 2D particle struct with position (x, y) and
96 /// velocity (dx, dy):
97 /// \code
98 /// // c++ struct definition
99 /// struct Particle
100 /// {
101 ///     float x, y;
102 ///     float dx, dy;
103 /// };
104 ///
105 /// // adapt struct for OpenCL
106 /// BOOST_COMPUTE_ADAPT_STRUCT(Particle, Particle, (x, y, dx, dy))
107 /// \endcode
108 ///
109 /// After adapting the struct it can be used in Boost.Compute containers
110 /// and with Boost.Compute algorithms:
111 /// \code
112 /// // create vector of particles
113 /// boost::compute::vector<Particle> particles = ...
114 ///
115 /// // function to compare particles by their x-coordinate
116 /// BOOST_COMPUTE_FUNCTION(bool, sort_by_x, (Particle a, Particle b),
117 /// {
118 ///     return a.x < b.x;
119 /// });
120 ///
121 /// // sort particles by their x-coordinate
122 /// boost::compute::sort(
123 ///     particles.begin(), particles.end(), sort_by_x, queue
124 /// );
125 /// \endcode
126 ///
127 /// Due to differences in struct padding between the host compiler and the
128 /// device compiler, the \c BOOST_COMPUTE_ADAPT_STRUCT() macro requires that
129 /// the adapted struct is packed (i.e. no padding bytes between members).
130 ///
131 /// \see type_name()
132 #define BOOST_COMPUTE_ADAPT_STRUCT(type, name, members) \
133     BOOST_STATIC_ASSERT_MSG( \
134         BOOST_COMPUTE_DETAIL_STRUCT_IS_PACKED(type, BOOST_COMPUTE_PP_TUPLE_TO_SEQ(members)), \
135         "BOOST_COMPUTE_ADAPT_STRUCT() does not support structs with internal padding." \
136     ); \
137     BOOST_COMPUTE_TYPE_NAME(type, name) \
138     namespace boost { namespace compute { \
139     template<> \
140     inline std::string type_definition<type>() \
141     { \
142         std::stringstream declaration; \
143         declaration << "typedef struct __attribute__((packed)) {\n" \
144                     BOOST_PP_SEQ_FOR_EACH( \
145                         BOOST_COMPUTE_DETAIL_ADAPT_STRUCT_INSERT_MEMBER, \
146                         type, \
147                         BOOST_COMPUTE_PP_TUPLE_TO_SEQ(members) \
148                     ) \
149                     << "} " << type_name<type>() << ";\n"; \
150         return declaration.str(); \
151     } \
152     namespace detail { \
153     template<> \
154     struct inject_type_impl<type> \
155     { \
156         void operator()(meta_kernel &kernel) \
157         { \
158             kernel.add_type_declaration<type>(type_definition<type>()); \
159         } \
160     }; \
161     inline meta_kernel& operator<<(meta_kernel &k, type s) \
162     { \
163         return k << "(" << #name << "){" \
164                BOOST_PP_SEQ_FOR_EACH_I( \
165                    BOOST_COMPUTE_DETAIL_ADAPT_STRUCT_STREAM_MEMBER, \
166                    s, \
167                    BOOST_COMPUTE_PP_TUPLE_TO_SEQ(members) \
168                ) \
169                << "}"; \
170     } \
171     }}}
172 
173 #endif // BOOST_COMPUTE_TYPES_STRUCT_HPP
174