Arrays¶
Camp has its own array implementation very similar to C++ std::array. The array functionality is
included in the array.hpp file within include/camp/.
Some important notes:
Note
All Camp structures and classes are in the namespace camp.
Array Philosophy¶
In C++, an array is a constant-sized, statically allocated slice of contiguous memory. While C++ arrays are conceptually equivalent to C-style arrays–in that their size must be known at compile time–they have many quality-of-life features that make them easier and more convenient to use.
For example, in C and C++, one might create an array as follows:
#define N 100
double* arr[N];
The C++ standard library (STL) implemented an array class, which provides access functions,
iterators, size information that makes using an array much easier and more convenient.
constexpr size_t N = 100; // size must be known at compile time
std::array<double, N> arr;
The only drawback from the STL array implementation is that it can only be used on the host, not any device.
That’s where Camp comes in. The camp::array retains all of the features from the STL array, but can also be used in GPU device code.
In fact, the main difference is that the types are intentionally supported across host and device for both the array and tuple
Camp features. More information on Camp tuple can be found on the Tuples page.
The camp::array methods¶
The Camp array syntax is exactly the same as the C++ standard library syntax, the only
difference being that we use the camp namespace instead of std.
constexpr size_t N = 100;
camp::array<double, N> arr;
It’s that simple!
Important
While the Camp Array tries to emulate the STL array as closely as it can, there are some key differences
that users should be aware of:
no
__device__ "at"method. Theatmethod has the potential to throw exceptions, which is not GPU friendly.Calling
front()orback()is a compile time error whensize() == 0, as opposed to undefined behaviour set by the C++ standard.Camp arrays do not have reverse iterators implemented.
Camp arrays do not have the
swapmethod implemented.
Constructors¶
Camp arrays, like their STL counterparts, require the size and data type to be known at compile time. The array constructor is as follows:
camp::array<class T, std::size_t N>;
where T and N are provided by the user. In our previous example, we created an array of 100 doubles:
constexpr size_t N = 100;
camp::array<double, N> arr;
We can also initialize our array at compile time, if we know the values:
camp::array<int, 3> arr = {1, 2, 3};
Element Access¶
To access an element of an array, we can either use the operator[], or the at methods. The only real difference between these
two methods is that the at method performs bounds checking on the input, and will throw an exception if the index requested is out of
the bounds of the array.
camp::array<int, 3> arr = {1, 2, 3};
std::cout << arr[1] << std::endl; // prints 2
std::cout << arr.at(2) << std::endl; // prints 3
std::cout << arr.at(4) << std::endl; // throws std::out_of_range
std::cout << arr[4] << std::endl; // undefined behaviour; accesses illegal memory
The front() and back() methods can be used to obtain a reference (const or non-const) to the first and last element in the array, respectively:
camp::array<int, 3> arr = {1, 2, 3};
std::cout << "front is " << arr.front() << ", back is " << arr.back() <<std::endl;
// output: front is 1, back is 3
arr.front() = 4;
// arr is now {4, 2, 3}
A pointer to the underlying data can be obtained using the data() method. camp::array implements begin(), end(), cbegin(), and cend() iterator functions, which allow it to be used interchangeably in many C++ algorithms in the standard library, and beyond:
camp::array<int, 3> arr = {1, 2, 3};
for (const auto elem : arr) {
std::cout << elem << "\n";
}
Camp provides multiple get methods that can be used for constexpr element access and moving:
camp::array<int, 3> arr = {1, 2, 3};
return camp::get<1>(arr); // returns 2
get can return const and non-const lvalue references, and rvalue references.
camp::array<int, 3> arr = {1, 2, 3}
const camp::array<int, 3> arr_const = {3, 2, 1};
// lvalue reference
camp::get<0>(arr) = 5;
// const lvalue reference
camp::get<1>(arr_const) = 4; // Error! Can't assign to const reference;
// arr is {5, 2, 3};
// rvalue reference
int&& val = camp::get<0>(camp::move(arr));
// val is 5, arr is in a moved-from state, using it further would incur undefined behaviour
const int&& val2 = camp::get<1>(camp::move(arr_const));
// val2 is 2, arr_const is in a moved-from state, using it further would incur undefined behaviour
Camp also provides access to the underlying data stored in the array using the data() method. This method returns a pointer to the internal memory of the array.
This pointer is functionally equivalent to the C/C++ default array pointer demonstrated earlier.
camp::array<int, 3> arr;
int* data = arr.data();
data[0] = 2;
*(data + 1) = 4;
arr[3] = 5;
// arr = {2, 4, 5}
Size methods¶
The Camp array contains a size() method which can be used to find the number of elements contained in the array.
This is the same number that is passed into the array when it is constructed. The max_size() method does the same thing, as the
size of an array is necessarily the maximum size, since an array has constant size. The empty() method returns a bool indicating
whether the array has any elements. Since the array’s size is determined at compile time, the value of empty() will be true, unless
an array is constructed with size == 0, which would not be very useful.
The Camp array contains a fill method, which can be used to set all of the values of the array to one value:
camp::array<int, 3> arr = {1, 2, 3};
// array is {1, 2, 3}
arr.fill(0);
// array is {0, 0, 0}
Array Comparisons¶
The Camp array defines all of the standard comparison operators: ==, !=, <, <=, >, >=. Note that <, <=, >, >= use a
lexographical check to determine which one is greater or less than.