1 //---------------------------------------------------------------------------//
2 // Copyright (c) 2013 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_PROGRAM_HPP
12 #define BOOST_COMPUTE_PROGRAM_HPP
13 
14 #include <string>
15 #include <vector>
16 #include <fstream>
17 #include <streambuf>
18 
19 #ifdef BOOST_COMPUTE_DEBUG_KERNEL_COMPILATION
20 #include <iostream>
21 #endif
22 
23 #include <boost/compute/config.hpp>
24 #include <boost/compute/context.hpp>
25 #include <boost/compute/exception.hpp>
26 #include <boost/compute/exception/program_build_failure.hpp>
27 #include <boost/compute/detail/assert_cl_success.hpp>
28 
29 #ifdef BOOST_COMPUTE_USE_OFFLINE_CACHE
30 #include <sstream>
31 #include <boost/optional.hpp>
32 #include <boost/compute/platform.hpp>
33 #include <boost/compute/detail/getenv.hpp>
34 #include <boost/compute/detail/path.hpp>
35 #include <boost/compute/detail/sha1.hpp>
36 #endif
37 
38 namespace boost {
39 namespace compute {
40 
41 class kernel;
42 
43 /// \class program
44 /// \brief A compute program.
45 ///
46 /// The program class represents an OpenCL program.
47 ///
48 /// Program objects are created with one of the static \c create_with_*
49 /// functions. For example, to create a program from a source string:
50 ///
51 /// \snippet test/test_program.cpp create_with_source
52 ///
53 /// And to create a program from a source file:
54 /// \code
55 /// boost::compute::program bar_program =
56 ///     boost::compute::program::create_with_source_file("/path/to/bar.cl", context);
57 /// \endcode
58 ///
59 /// Once a program object has been successfully created, it can be compiled
60 /// using the \c build() method:
61 /// \code
62 /// // build the program
63 /// foo_program.build();
64 /// \endcode
65 ///
66 /// Once the program is built, \ref kernel objects can be created using the
67 /// \c create_kernel() method by passing their name:
68 /// \code
69 /// // create a kernel from the compiled program
70 /// boost::compute::kernel foo_kernel = foo_program.create_kernel("foo");
71 /// \endcode
72 ///
73 /// \see kernel
74 class program
75 {
76 public:
77     /// Creates a null program object.
program()78     program()
79         : m_program(0)
80     {
81     }
82 
83     /// Creates a program object for \p program. If \p retain is \c true,
84     /// the reference count for \p program will be incremented.
program(cl_program program,bool retain=true)85     explicit program(cl_program program, bool retain = true)
86         : m_program(program)
87     {
88         if(m_program && retain){
89             clRetainProgram(m_program);
90         }
91     }
92 
93     /// Creates a new program object as a copy of \p other.
program(const program & other)94     program(const program &other)
95         : m_program(other.m_program)
96     {
97         if(m_program){
98             clRetainProgram(m_program);
99         }
100     }
101 
102     /// Copies the program object from \p other to \c *this.
operator =(const program & other)103     program& operator=(const program &other)
104     {
105         if(this != &other){
106             if(m_program){
107                 clReleaseProgram(m_program);
108             }
109 
110             m_program = other.m_program;
111 
112             if(m_program){
113                 clRetainProgram(m_program);
114             }
115         }
116 
117         return *this;
118     }
119 
120     #ifndef BOOST_COMPUTE_NO_RVALUE_REFERENCES
121     /// Move-constructs a new program object from \p other.
program(program && other)122     program(program&& other) BOOST_NOEXCEPT
123         : m_program(other.m_program)
124     {
125         other.m_program = 0;
126     }
127 
128     /// Move-assigns the program from \p other to \c *this.
operator =(program && other)129     program& operator=(program&& other) BOOST_NOEXCEPT
130     {
131         if(m_program){
132             clReleaseProgram(m_program);
133         }
134 
135         m_program = other.m_program;
136         other.m_program = 0;
137 
138         return *this;
139     }
140     #endif // BOOST_COMPUTE_NO_RVALUE_REFERENCES
141 
142     /// Destroys the program object.
~program()143     ~program()
144     {
145         if(m_program){
146             BOOST_COMPUTE_ASSERT_CL_SUCCESS(
147                 clReleaseProgram(m_program)
148             );
149         }
150     }
151 
152     /// Returns the underlying OpenCL program.
get() const153     cl_program& get() const
154     {
155         return const_cast<cl_program &>(m_program);
156     }
157 
158     /// Returns the source code for the program.
source() const159     std::string source() const
160     {
161         return get_info<std::string>(CL_PROGRAM_SOURCE);
162     }
163 
164     /// Returns the binary for the program.
binary() const165     std::vector<unsigned char> binary() const
166     {
167         size_t binary_size = get_info<size_t>(CL_PROGRAM_BINARY_SIZES);
168         std::vector<unsigned char> binary(binary_size);
169 
170         unsigned char *binary_ptr = &binary[0];
171         cl_int error = clGetProgramInfo(m_program,
172                                         CL_PROGRAM_BINARIES,
173                                         sizeof(unsigned char **),
174                                         &binary_ptr,
175                                         0);
176         if(error != CL_SUCCESS){
177             BOOST_THROW_EXCEPTION(opencl_error(error));
178         }
179 
180         return binary;
181     }
182 
183     #if defined(BOOST_COMPUTE_CL_VERSION_2_1) || defined(BOOST_COMPUTE_DOXYGEN_INVOKED)
184     /// Returns the SPIR-V binary for the program.
il_binary() const185     std::vector<unsigned char> il_binary() const
186     {
187         return get_info<std::vector<unsigned char> >(CL_PROGRAM_IL);
188     }
189     #endif // BOOST_COMPUTE_CL_VERSION_2_1
190 
get_devices() const191     std::vector<device> get_devices() const
192     {
193         std::vector<cl_device_id> device_ids =
194             get_info<std::vector<cl_device_id> >(CL_PROGRAM_DEVICES);
195 
196         std::vector<device> devices;
197         for(size_t i = 0; i < device_ids.size(); i++){
198             devices.push_back(device(device_ids[i]));
199         }
200 
201         return devices;
202     }
203 
204     /// Returns the context for the program.
get_context() const205     context get_context() const
206     {
207         return context(get_info<cl_context>(CL_PROGRAM_CONTEXT));
208     }
209 
210     /// Returns information about the program.
211     ///
212     /// \see_opencl_ref{clGetProgramInfo}
213     template<class T>
get_info(cl_program_info info) const214     T get_info(cl_program_info info) const
215     {
216         return detail::get_object_info<T>(clGetProgramInfo, m_program, info);
217     }
218 
219     /// \overload
220     template<int Enum>
221     typename detail::get_object_info_type<program, Enum>::type
222     get_info() const;
223 
224     /// Returns build information about the program.
225     ///
226     /// For example, this function can be used to retreive the options used
227     /// to build the program:
228     /// \code
229     /// std::string build_options =
230     ///     program.get_build_info<std::string>(CL_PROGRAM_BUILD_OPTIONS);
231     /// \endcode
232     ///
233     /// \see_opencl_ref{clGetProgramInfo}
234     template<class T>
get_build_info(cl_program_build_info info,const device & device) const235     T get_build_info(cl_program_build_info info, const device &device) const
236     {
237         return detail::get_object_info<T>(clGetProgramBuildInfo, m_program, info, device.id());
238     }
239 
240     /// Builds the program with \p options.
241     ///
242     /// If the program fails to compile, this function will throw an
243     /// opencl_error exception.
244     /// \code
245     /// try {
246     ///     // attempt to compile to program
247     ///     program.build();
248     /// }
249     /// catch(boost::compute::opencl_error &e){
250     ///     // program failed to compile, print out the build log
251     ///     std::cout << program.build_log() << std::endl;
252     /// }
253     /// \endcode
254     ///
255     /// \see_opencl_ref{clBuildProgram}
build(const std::string & options=std::string ())256     void build(const std::string &options = std::string())
257     {
258         const char *options_string = 0;
259 
260         if(!options.empty()){
261             options_string = options.c_str();
262         }
263 
264         cl_int ret = clBuildProgram(m_program, 0, 0, options_string, 0, 0);
265 
266         #ifdef BOOST_COMPUTE_DEBUG_KERNEL_COMPILATION
267         if(ret != CL_SUCCESS){
268             // print the error, source code and build log
269             std::cerr << "Boost.Compute: "
270                       << "kernel compilation failed (" << ret << ")\n"
271                       << "--- source ---\n"
272                       << source()
273                       << "\n--- build log ---\n"
274                       << build_log()
275                       << std::endl;
276         }
277         #endif
278 
279         if(ret != CL_SUCCESS){
280             BOOST_THROW_EXCEPTION(program_build_failure(ret, build_log()));
281         }
282     }
283 
284     #if defined(BOOST_COMPUTE_CL_VERSION_1_2) || defined(BOOST_COMPUTE_DOXYGEN_INVOKED)
285     /// Compiles the program with \p options.
286     ///
287     /// \opencl_version_warning{1,2}
288     ///
289     /// \see_opencl_ref{clCompileProgram}
290     void compile(const std::string &options = std::string(),
291                  const std::vector<std::pair<std::string, program> > &headers =
292                     std::vector<std::pair<std::string, program> >())
293     {
294         const char *options_string = 0;
295 
296         if(!options.empty()){
297             options_string = options.c_str();
298         }
299 
300         cl_int ret;
301         if (headers.empty())
302         {
303             ret = clCompileProgram(
304                 m_program, 0, 0, options_string, 0, 0, 0, 0, 0
305             );
306         }
307         else
308         {
309             std::vector<const char*> header_names(headers.size());
310             std::vector<cl_program> header_programs(headers.size());
311             for (size_t i = 0; i < headers.size(); ++i)
312             {
313                 header_names[i] = headers[i].first.c_str();
314                 header_programs[i] = headers[i].second.m_program;
315             }
316 
317             ret = clCompileProgram(
318                 m_program,
319                 0,
320                 0,
321                 options_string,
322                 static_cast<cl_uint>(headers.size()),
323                 header_programs.data(),
324                 header_names.data(),
325                 0,
326                 0
327             );
328         }
329 
330         if(ret != CL_SUCCESS){
331             BOOST_THROW_EXCEPTION(opencl_error(ret));
332         }
333     }
334 
335     /// Links the programs in \p programs with \p options in \p context.
336     ///
337     /// \opencl_version_warning{1,2}
338     ///
339     /// \see_opencl_ref{clLinkProgram}
link(const std::vector<program> & programs,const context & context,const std::string & options=std::string ())340     static program link(const std::vector<program> &programs,
341                         const context &context,
342                         const std::string &options = std::string())
343     {
344         const char *options_string = 0;
345 
346         if(!options.empty()){
347             options_string = options.c_str();
348         }
349 
350         cl_int ret;
351         cl_program program_ = clLinkProgram(
352             context.get(),
353             0,
354             0,
355             options_string,
356             static_cast<uint_>(programs.size()),
357             reinterpret_cast<const cl_program*>(&programs[0]),
358             0,
359             0,
360             &ret
361         );
362 
363         if(!program_){
364             BOOST_THROW_EXCEPTION(opencl_error(ret));
365         }
366 
367         return program(program_, false);
368     }
369     #endif // BOOST_COMPUTE_CL_VERSION_1_2
370 
371     /// Returns the build log.
build_log() const372     std::string build_log() const
373     {
374         return get_build_info<std::string>(CL_PROGRAM_BUILD_LOG, get_devices().front());
375     }
376 
377     /// Creates and returns a new kernel object for \p name.
378     ///
379     /// For example, to create the \c "foo" kernel (after the program has been
380     /// created and built):
381     /// \code
382     /// boost::compute::kernel foo_kernel = foo_program.create_kernel("foo");
383     /// \endcode
384     kernel create_kernel(const std::string &name) const;
385 
386     /// Returns \c true if the program is the same at \p other.
operator ==(const program & other) const387     bool operator==(const program &other) const
388     {
389         return m_program == other.m_program;
390     }
391 
392     /// Returns \c true if the program is different from \p other.
operator !=(const program & other) const393     bool operator!=(const program &other) const
394     {
395         return m_program != other.m_program;
396     }
397 
398     /// \internal_
operator cl_program() const399     operator cl_program() const
400     {
401         return m_program;
402     }
403 
404     /// Creates a new program with \p source in \p context.
405     ///
406     /// \see_opencl_ref{clCreateProgramWithSource}
create_with_source(const std::string & source,const context & context)407     static program create_with_source(const std::string &source,
408                                       const context &context)
409     {
410         const char *source_string = source.c_str();
411 
412         cl_int error = 0;
413         cl_program program_ = clCreateProgramWithSource(context,
414                                                         uint_(1),
415                                                         &source_string,
416                                                         0,
417                                                         &error);
418         if(!program_){
419             BOOST_THROW_EXCEPTION(opencl_error(error));
420         }
421 
422         return program(program_, false);
423     }
424 
425     /// Creates a new program with \p sources in \p context.
426     ///
427     /// \see_opencl_ref{clCreateProgramWithSource}
create_with_source(const std::vector<std::string> & sources,const context & context)428     static program create_with_source(const std::vector<std::string> &sources,
429                                       const context &context)
430     {
431         std::vector<const char*> source_strings(sources.size());
432         for(size_t i = 0; i < sources.size(); i++){
433             source_strings[i] = sources[i].c_str();
434         }
435 
436         cl_int error = 0;
437         cl_program program_ = clCreateProgramWithSource(context,
438                                                         uint_(sources.size()),
439                                                         &source_strings[0],
440                                                         0,
441                                                         &error);
442         if(!program_){
443             BOOST_THROW_EXCEPTION(opencl_error(error));
444         }
445 
446         return program(program_, false);
447     }
448 
449     /// Creates a new program with \p file in \p context.
450     ///
451     /// \see_opencl_ref{clCreateProgramWithSource}
create_with_source_file(const std::string & file,const context & context)452     static program create_with_source_file(const std::string &file,
453                                            const context &context)
454     {
455         // create program
456         return create_with_source(read_source_file(file), context);
457     }
458 
459     /// Creates a new program with \p files in \p context.
460     ///
461     /// \see_opencl_ref{clCreateProgramWithSource}
create_with_source_file(const std::vector<std::string> & files,const context & context)462     static program create_with_source_file(const std::vector<std::string> &files,
463                                            const context &context)
464     {
465         std::vector<std::string> sources(files.size());
466 
467         for(size_t i = 0; i < files.size(); ++i) {
468             // open file stream
469             std::ifstream stream(files[i].c_str());
470 
471             if(stream.fail()){
472                 BOOST_THROW_EXCEPTION(std::ios_base::failure("failed to create stream."));
473             }
474 
475             // read source
476             sources[i] = std::string(
477                     (std::istreambuf_iterator<char>(stream)),
478                     std::istreambuf_iterator<char>()
479             );
480         }
481 
482         // create program
483         return create_with_source(sources, context);
484     }
485 
486     /// Creates a new program with \p binary of \p binary_size in
487     /// \p context.
488     ///
489     /// \see_opencl_ref{clCreateProgramWithBinary}
create_with_binary(const unsigned char * binary,size_t binary_size,const context & context)490     static program create_with_binary(const unsigned char *binary,
491                                       size_t binary_size,
492                                       const context &context)
493     {
494         const cl_device_id device = context.get_device().id();
495 
496         cl_int error = 0;
497         cl_int binary_status = 0;
498         cl_program program_ = clCreateProgramWithBinary(context,
499                                                         uint_(1),
500                                                         &device,
501                                                         &binary_size,
502                                                         &binary,
503                                                         &binary_status,
504                                                         &error);
505         if(!program_){
506             BOOST_THROW_EXCEPTION(opencl_error(error));
507         }
508         if(binary_status != CL_SUCCESS){
509             BOOST_THROW_EXCEPTION(opencl_error(binary_status));
510         }
511 
512         return program(program_, false);
513     }
514 
515     /// Creates a new program with \p binary in \p context.
516     ///
517     /// \see_opencl_ref{clCreateProgramWithBinary}
create_with_binary(const std::vector<unsigned char> & binary,const context & context)518     static program create_with_binary(const std::vector<unsigned char> &binary,
519                                       const context &context)
520     {
521         return create_with_binary(&binary[0], binary.size(), context);
522     }
523 
524     /// Creates a new program with \p file in \p context.
525     ///
526     /// \see_opencl_ref{clCreateProgramWithBinary}
create_with_binary_file(const std::string & file,const context & context)527     static program create_with_binary_file(const std::string &file,
528                                            const context &context)
529     {
530         // open file stream
531         std::ifstream stream(file.c_str(), std::ios::in | std::ios::binary);
532 
533         // read binary
534         std::vector<unsigned char> binary(
535             (std::istreambuf_iterator<char>(stream)),
536             std::istreambuf_iterator<char>()
537         );
538 
539         // create program
540         return create_with_binary(&binary[0], binary.size(), context);
541     }
542 
543     #if defined(BOOST_COMPUTE_CL_VERSION_1_2) || defined(BOOST_COMPUTE_DOXYGEN_INVOKED)
544     /// Creates a new program with the built-in kernels listed in
545     /// \p kernel_names for \p devices in \p context.
546     ///
547     /// \opencl_version_warning{1,2}
548     ///
549     /// \see_opencl_ref{clCreateProgramWithBuiltInKernels}
create_with_builtin_kernels(const context & context,const std::vector<device> & devices,const std::string & kernel_names)550     static program create_with_builtin_kernels(const context &context,
551                                                const std::vector<device> &devices,
552                                                const std::string &kernel_names)
553     {
554         cl_int error = 0;
555 
556         cl_program program_ = clCreateProgramWithBuiltInKernels(
557             context.get(),
558             static_cast<uint_>(devices.size()),
559             reinterpret_cast<const cl_device_id *>(&devices[0]),
560             kernel_names.c_str(),
561             &error
562         );
563 
564         if(!program_){
565             BOOST_THROW_EXCEPTION(opencl_error(error));
566         }
567 
568         return program(program_, false);
569     }
570     #endif // BOOST_COMPUTE_CL_VERSION_1_2
571 
572     #if defined(BOOST_COMPUTE_CL_VERSION_2_1) || defined(BOOST_COMPUTE_DOXYGEN_INVOKED)
573     /// Creates a new program with \p il_binary (SPIR-V binary)
574     /// of \p il_size size in \p context.
575     ///
576     /// \opencl_version_warning{2,1}
577     ///
578     /// \see_opencl21_ref{clCreateProgramWithIL}
create_with_il(const void * il_binary,const size_t il_size,const context & context)579     static program create_with_il(const void * il_binary,
580                                   const size_t il_size,
581                                   const context &context)
582     {
583         cl_int error = 0;
584 
585         cl_program program_ = clCreateProgramWithIL(
586             context.get(), il_binary, il_size, &error
587         );
588 
589         if(!program_){
590             BOOST_THROW_EXCEPTION(opencl_error(error));
591         }
592 
593         return program(program_, false);
594     }
595 
596     /// Creates a new program with \p il_binary (SPIR-V binary)
597     /// in \p context.
598     ///
599     /// \opencl_version_warning{2,1}
600     ///
601     /// \see_opencl_ref{clCreateProgramWithIL}
create_with_il(const std::vector<unsigned char> & il_binary,const context & context)602     static program create_with_il(const std::vector<unsigned char> &il_binary,
603                                   const context &context)
604     {
605         return create_with_il(&il_binary[0], il_binary.size(), context);
606     }
607 
608     /// Creates a new program in \p context using SPIR-V
609     /// binary \p file.
610     ///
611     /// \opencl_version_warning{2,1}
612     ///
613     /// \see_opencl_ref{clCreateProgramWithIL}
create_with_il_file(const std::string & file,const context & context)614     static program create_with_il_file(const std::string &file,
615                                        const context &context)
616     {
617         // open file stream
618         std::ifstream stream(file.c_str(), std::ios::in | std::ios::binary);
619 
620         // read binary
621         std::vector<unsigned char> il(
622             (std::istreambuf_iterator<char>(stream)),
623             std::istreambuf_iterator<char>()
624         );
625 
626         // create program
627         return create_with_il(&il[0], il.size(), context);
628     }
629     #endif // BOOST_COMPUTE_CL_VERSION_2_1
630 
631     /// Create a new program with \p source in \p context and builds it with \p options.
632     /**
633      * In case BOOST_COMPUTE_USE_OFFLINE_CACHE macro is defined,
634      * the compiled binary is stored for reuse in the offline cache located in
635      * $HOME/.boost_compute on UNIX-like systems and in %APPDATA%/boost_compute
636      * on Windows.
637      */
build_with_source(const std::string & source,const context & context,const std::string & options=std::string ())638     static program build_with_source(
639             const std::string &source,
640             const context     &context,
641             const std::string &options = std::string()
642             )
643     {
644 #ifdef BOOST_COMPUTE_USE_OFFLINE_CACHE
645         // Get hash string for the kernel.
646         device   d = context.get_device();
647         platform p = d.platform();
648 
649         detail::sha1 hash;
650         hash.process( p.name()    )
651             .process( p.version() )
652             .process( d.name()    )
653             .process( options     )
654             .process( source      )
655             ;
656         std::string hash_string = hash;
657 
658         // Try to get cached program binaries:
659         try {
660             boost::optional<program> prog = load_program_binary(hash_string, context);
661 
662             if (prog) {
663                 prog->build(options);
664                 return *prog;
665             }
666         } catch (...) {
667             // Something bad happened. Fallback to normal compilation.
668         }
669 
670         // Cache is apparently not available. Just compile the sources.
671 #endif
672         const char *source_string = source.c_str();
673 
674         cl_int error = 0;
675         cl_program program_ = clCreateProgramWithSource(context,
676                                                         uint_(1),
677                                                         &source_string,
678                                                         0,
679                                                         &error);
680         if(!program_){
681             BOOST_THROW_EXCEPTION(opencl_error(error));
682         }
683 
684         program prog(program_, false);
685         prog.build(options);
686 
687 #ifdef BOOST_COMPUTE_USE_OFFLINE_CACHE
688         // Save program binaries for future reuse.
689         save_program_binary(hash_string, prog);
690 #endif
691 
692         return prog;
693     }
694 
695     /// Create a new program with \p file in \p context and builds it with \p options.
696     /**
697      * In case BOOST_COMPUTE_USE_OFFLINE_CACHE macro is defined,
698      * the compiled binary is stored for reuse in the offline cache located in
699      * $HOME/.boost_compute on UNIX-like systems and in %APPDATA%/boost_compute
700      * on Windows.
701      */
build_with_source_file(const std::string & file,const context & context,const std::string & options=std::string ())702     static program build_with_source_file(
703             const std::string &file,
704             const context     &context,
705             const std::string &options = std::string()
706             )
707     {
708         return build_with_source(read_source_file(file), context, options);
709     }
710 
711 private:
712 #ifdef BOOST_COMPUTE_USE_OFFLINE_CACHE
713     // Saves program binaries for future reuse.
save_program_binary(const std::string & hash,const program & prog)714     static void save_program_binary(const std::string &hash, const program &prog)
715     {
716         std::string fname = detail::program_binary_path(hash, true) + "kernel";
717         std::ofstream bfile(fname.c_str(), std::ios::binary);
718         if (!bfile) return;
719 
720         std::vector<unsigned char> binary = prog.binary();
721 
722         size_t binary_size = binary.size();
723         bfile.write((char*)&binary_size, sizeof(size_t));
724         bfile.write((char*)binary.data(), binary_size);
725     }
726 
727     // Tries to read program binaries from file cache.
load_program_binary(const std::string & hash,const context & ctx)728     static boost::optional<program> load_program_binary(
729             const std::string &hash, const context &ctx
730             )
731     {
732         std::string fname = detail::program_binary_path(hash) + "kernel";
733         std::ifstream bfile(fname.c_str(), std::ios::binary);
734         if (!bfile) return boost::optional<program>();
735 
736         size_t binary_size;
737         std::vector<unsigned char> binary;
738 
739         bfile.read((char*)&binary_size, sizeof(size_t));
740 
741         binary.resize(binary_size);
742         bfile.read((char*)binary.data(), binary_size);
743 
744         return boost::optional<program>(
745                 program::create_with_binary(
746                     binary.data(), binary_size, ctx
747                     )
748                 );
749     }
750 #endif // BOOST_COMPUTE_USE_OFFLINE_CACHE
751 
read_source_file(const std::string & file)752     static std::string read_source_file(const std::string &file)
753     {
754         // open file stream
755         std::ifstream stream(file.c_str());
756 
757         if(stream.fail()){
758           BOOST_THROW_EXCEPTION(std::ios_base::failure("failed to create stream."));
759         }
760 
761         // read source
762         return std::string(
763             (std::istreambuf_iterator<char>(stream)),
764             std::istreambuf_iterator<char>()
765         );
766     }
767 
768 private:
769     cl_program m_program;
770 };
771 
772 /// \internal_ define get_info() specializations for program
773 BOOST_COMPUTE_DETAIL_DEFINE_GET_INFO_SPECIALIZATIONS(program,
774     ((cl_uint, CL_PROGRAM_REFERENCE_COUNT))
775     ((cl_context, CL_PROGRAM_CONTEXT))
776     ((cl_uint, CL_PROGRAM_NUM_DEVICES))
777     ((std::vector<cl_device_id>, CL_PROGRAM_DEVICES))
778     ((std::string, CL_PROGRAM_SOURCE))
779     ((std::vector<size_t>, CL_PROGRAM_BINARY_SIZES))
780     ((std::vector<unsigned char *>, CL_PROGRAM_BINARIES))
781 )
782 
783 #ifdef BOOST_COMPUTE_CL_VERSION_1_2
784 BOOST_COMPUTE_DETAIL_DEFINE_GET_INFO_SPECIALIZATIONS(program,
785     ((size_t, CL_PROGRAM_NUM_KERNELS))
786     ((std::string, CL_PROGRAM_KERNEL_NAMES))
787 )
788 #endif // BOOST_COMPUTE_CL_VERSION_1_2
789 
790 #ifdef BOOST_COMPUTE_CL_VERSION_2_1
791 BOOST_COMPUTE_DETAIL_DEFINE_GET_INFO_SPECIALIZATIONS(program,
792     ((std::vector<unsigned char>, CL_PROGRAM_IL))
793 )
794 #endif // BOOST_COMPUTE_CL_VERSION_2_1
795 
796 } // end compute namespace
797 } // end boost namespace
798 
799 #endif // BOOST_COMPUTE_PROGRAM_HPP
800