Suppose I had a program in openCL:
...
const unsigned char* binaries;
size_t binaries_size;
assign_binaries(&binaries, &binaries_size); // my assign function, assigns bin and bin_size
std::vector<std::vector<unsigned char>> Binaries;
cl::Program prog = cl::Program(context, device_list, Binaries, &binary_status, &err);
...
How would I go about pushing
binaries
and
binaries_size
into
Binaries
?
In the OpenCL docs, it says:
Binaries: A vector of pairs of a pointer to a binary object and its length.
However, this makes no sense to me, as there is an obvious type mismatch.
CodePudding user response:
How would I go about pushing
binaries
and
binaries_size
into
Binaries
?
There are many ways to accomplish this.
TL;DR: The most compact code to achieve OPs intention is probably:
cl::Program prog
= cl::Program(context, device_list,
{ std::vector<unsigned char>(binaries, binaries binaries_size) },
&binary_status, &err);
A longer story:
An MCVE to demonstrate two:
#include <iomanip>
#include <iostream>
#include <vector>
// the OpenCL typedef
using Binaries = std::vector<std::vector<unsigned char>>;
void print(const Binaries& binaries)
{
std::cout << "Binaries: [" << binaries.size() << "]: {\n";
for (const std::vector<unsigned char>& binary : binaries) {
std::cout << " [" << binary.size() << "]: {" ;
const char* sep = " ";
for (const unsigned char value : binary) {
std::cout << sep << "0x"
<< std::hex << std::setw(2) << std::setfill('0') << (unsigned)value;
sep = ", ";
}
std::cout << " }\n";
}
std::cout << "}\n";
}
int main()
{
// sample data
const unsigned char data[] = {
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08
};
// OPs initial exposure of data
const unsigned char *binaries = data;
const size_t binaries_size = std::size(data);
// filling a Binaries var.:
Binaries vecBinaries1;
vecBinaries1.push_back(
std::vector<unsigned char>(binaries, binaries binaries_size));
print(vecBinaries1);
// or
const Binaries vecBinaries2(1, std::vector<unsigned char>(binaries, binaries binaries_size));
print(vecBinaries2);
}
Output:
Binaries: [1]: {
[8]: { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08 }
}
Binaries: [1]: {
[8]: { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08 }
}
Please, note that data is prepared in binaries
, and then copied into the constructor std::vector<unsigned char>(binaries, binaries binaries_size)
of std::vector
.
std::vector
provides a variety of constructors. In this case, the constructor with first
and last
iterator is used:
template< class InputIt >
vector( InputIt first, InputIt last,
const Allocator& alloc = Allocator() );
(It's flavor (5)
in the linked doc.)
However, this might be an unnecessary copy-step. Providing the data in a std::vector<unsigned char>
from the beginning could be used to get rid of this.
The push_back() can be used with move semantic (which prevents a 2nd unnecessary deep-copy). (It's flavor (2)
in the linked doc.)
If the constructor of the inner vector is passed as argument directly, it results in an RValue which will trigger the move semantic automagically.
If there is an intermediate storage of the inner vector (e.g. in a local variable), the move semantic still can be forced with std::move()
:
std::vector<unsigned char> vecBinary3(binaries, binaries binaries_size);
Binaries vecBinaries3;
vecBinaries3.push_back(std::move(vecBinary3));
print(vecBinaries3);
Output:
Binaries: [1]: {
[8]: { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08 }
}
I also added brace initialization (which in this case is direct-list-initialization).
const Binaries vecBinaries4 {
std::vector<unsigned char>(binaries, binaries binaries_size)
};
print(vecBinaries4);
Output:
Binaries: [1]: {
[8]: { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08 }
}
(I must admit, I still consider brace init. a little bit scaring. Hence, I struggled to mention it at all but I should for completeness.)
In certain cases, e.g. if you have a vector of integrals, you have to be careful to distinguish the constructor with size n
and an initial fill value from the constructor with an initialization list of two values.
For both, you have to provide two integral values, but the kind and number of brackets decide which constructor is used. This can result in confusion easily.
Example:
#include <iostream>
#include <vector>
void print(const std::vector<int>& values)
{
std::cout << '{';
const char* sep = " ";
for (const int value : values) {
std::cout << sep << value;
sep = ", ";
}
std::cout << " }\n";
}
int main()
{
// sample 1:
print({ 1, 2 });
// sample 2:
print(std::vector<int>(1, 2));
// sample 3:
print(std::vector<int>{ 1, 2 });
// sample 4:
print(std::vector<int>({ 1, 2 }));
}
Output:
{ 1, 2 }
{ 2 }
{ 1, 2 }
{ 1, 2 }