mirror of
https://github.com/microsoft/GSL.git
synced 2024-11-03 17:56:43 -05:00
649 lines
14 KiB
C++
649 lines
14 KiB
C++
///////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Copyright (c) 2015 Microsoft Corporation. All rights reserved.
|
|
//
|
|
// This code is licensed under the MIT License (MIT).
|
|
//
|
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
// THE SOFTWARE.
|
|
//
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
#include <UnitTest++/UnitTest++.h>
|
|
#include <array_view.h>
|
|
#include <numeric>
|
|
#include <array>
|
|
#include <string>
|
|
#include <vector>
|
|
#include <list>
|
|
#include <iostream>
|
|
#include <functional>
|
|
#include <algorithm>
|
|
|
|
|
|
using namespace std;
|
|
using namespace Guide;
|
|
|
|
namespace
|
|
{
|
|
void use(int&) {}
|
|
struct BaseClass {};
|
|
struct DerivedClass : BaseClass {};
|
|
}
|
|
|
|
SUITE(array_view_tests)
|
|
{
|
|
TEST(basics)
|
|
{
|
|
auto ptr = as_array_view(new int[10], 10);
|
|
fill(ptr.begin(), ptr.end(), 99);
|
|
for (int num : ptr)
|
|
{
|
|
CHECK(num == 99);
|
|
}
|
|
|
|
delete[] ptr.data();
|
|
|
|
|
|
static_bounds<size_t, 4, dynamic_range, 2> bounds{ 3 };
|
|
|
|
#ifdef CONFIRM_COMPILATION_ERRORS
|
|
array_view<int, 4, dynamic_range, 2> av(nullptr, bounds);
|
|
av.extent();
|
|
av.extent<2>();
|
|
av[8][4][3];
|
|
#endif
|
|
}
|
|
|
|
TEST (array_view_convertible)
|
|
{
|
|
#ifdef CONFIRM_COMPILATION_ERRORS
|
|
array_view<int, 7, 4, 2> av1(nullptr, b1);
|
|
#endif
|
|
|
|
auto f = [&]() { array_view<int, 7, 4, 2> av1(nullptr); };
|
|
CHECK_THROW(f(), fail_fast);
|
|
|
|
array_view<int, 7, dynamic_range, 2> av1(nullptr);
|
|
|
|
#ifdef CONFIRM_COMPILATION_ERRORS
|
|
static_bounds<size_t, 7, dynamic_range, 2> b12(b11);
|
|
b12 = b11;
|
|
b11 = b12;
|
|
|
|
array_view<int, dynamic_range> av1 = nullptr;
|
|
array_view<int, 7, dynamic_range, 2> av2(av1);
|
|
array_view<int, 7, 4, 2> av2(av1);
|
|
#endif
|
|
|
|
array_view<DerivedClass> avd;
|
|
#ifdef CONFIRM_COMPILATION_ERRORS
|
|
array_view<BaseClass> avb = avd;
|
|
#endif
|
|
array_view<const DerivedClass> avcd = avd;
|
|
}
|
|
|
|
TEST(boundary_checks)
|
|
{
|
|
int arr[10][2];
|
|
auto av = as_array_view(arr);
|
|
|
|
fill(begin(av), end(av), 0);
|
|
|
|
av[2][0] = 1;
|
|
av[1][1] = 3;
|
|
|
|
// out of bounds
|
|
CHECK_THROW(av[1][3] = 3, fail_fast);
|
|
CHECK_THROW((av[{1, 3}] = 3), fail_fast);
|
|
|
|
CHECK_THROW(av[10][2], fail_fast);
|
|
CHECK_THROW((av[{10,2}]), fail_fast);
|
|
}
|
|
|
|
void overloaded_func(array_view<const int, dynamic_range, 3, 5> exp, int expected_value) {
|
|
for (auto val : exp)
|
|
{
|
|
CHECK(val == expected_value);
|
|
}
|
|
}
|
|
|
|
void overloaded_func(array_view<const char, dynamic_range, 3, 5> exp, char expected_value) {
|
|
for (auto val : exp)
|
|
{
|
|
CHECK(val == expected_value);
|
|
}
|
|
}
|
|
|
|
void fixed_func(array_view<int, 3, 3, 5> exp, int expected_value) {
|
|
for (auto val : exp)
|
|
{
|
|
CHECK(val == expected_value);
|
|
}
|
|
}
|
|
|
|
TEST(array_view_parameter_test)
|
|
{
|
|
auto data = new int[4][3][5];
|
|
|
|
auto av = as_array_view(data, 4);
|
|
|
|
CHECK(av.size() == 60);
|
|
|
|
fill(av.begin(), av.end(), 34);
|
|
|
|
int count = 0;
|
|
for_each(av.rbegin(), av.rend(), [&](int val) { count += val; });
|
|
CHECK(count == 34 * 60);
|
|
overloaded_func(av, 34);
|
|
|
|
overloaded_func(av.as_array_view(dim<>(4), dim<>(3), dim<>(5)), 34);
|
|
|
|
//fixed_func(av, 34);
|
|
delete[] data;
|
|
}
|
|
|
|
|
|
TEST(md_access)
|
|
{
|
|
unsigned int width = 5, height = 20;
|
|
|
|
unsigned int imgSize = width * height;
|
|
auto image_ptr = new int[imgSize][3];
|
|
|
|
// size check will be done
|
|
auto image_view = as_array_view(image_ptr, imgSize).as_array_view(dim<>(height), dim<>(width), dim<3>());
|
|
|
|
iota(image_view.begin(), image_view.end(), 1);
|
|
|
|
int expected = 0;
|
|
for (unsigned int i = 0; i < height; i++)
|
|
{
|
|
for (unsigned int j = 0; j < width; j++)
|
|
{
|
|
CHECK(expected + 1 == image_view[i][j][0]);
|
|
CHECK(expected + 2 == image_view[i][j][1]);
|
|
CHECK(expected + 3 == image_view[i][j][2]);
|
|
|
|
auto val = image_view[{i, j, 0}];
|
|
CHECK(expected + 1 == val);
|
|
val = image_view[{i, j, 1}];
|
|
CHECK(expected + 2 == val);
|
|
val = image_view[{i, j, 2}];
|
|
CHECK(expected + 3 == val);
|
|
|
|
expected += 3;
|
|
}
|
|
}
|
|
}
|
|
|
|
TEST(array_view_factory_test)
|
|
{
|
|
{
|
|
int * arr = new int[150];
|
|
|
|
auto av = as_array_view(arr, dim<10>(), dim<>(3), dim<5>());
|
|
|
|
fill(av.begin(), av.end(), 24);
|
|
overloaded_func(av, 24);
|
|
|
|
delete[] arr;
|
|
|
|
|
|
array<int, 15> stdarr{ 0 };
|
|
auto av2 = as_array_view(stdarr);
|
|
overloaded_func(av2.as_array_view(dim<>(1), dim<3>(), dim<5>()), 0);
|
|
|
|
|
|
string str = "ttttttttttttttt"; // size = 15
|
|
auto t = str.data();
|
|
auto av3 = as_array_view(str);
|
|
overloaded_func(av3.as_array_view(dim<>(1), dim<3>(), dim<5>()), 't');
|
|
}
|
|
|
|
{
|
|
int a[3][4][5];
|
|
auto av = as_array_view(a);
|
|
const int (*b)[4][5];
|
|
b = a;
|
|
auto bv = as_array_view(b, 3);
|
|
|
|
CHECK(av == bv);
|
|
|
|
const std::array<double, 3> arr = {0.0, 0.0, 0.0};
|
|
auto cv = as_array_view(arr);
|
|
|
|
vector<float> vec(3);
|
|
auto dv = as_array_view(vec);
|
|
|
|
#ifdef CONFIRM_COMPILATION_ERRORS
|
|
auto dv2 = as_array_view(std::move(vec));
|
|
#endif
|
|
}
|
|
}
|
|
|
|
TEST (array_view_reshape_test)
|
|
{
|
|
int a[3][4][5];
|
|
auto av = as_array_view(a);
|
|
auto av2 = av.as_array_view(dim<60>());
|
|
auto av3 = av2.as_array_view(dim<3>(), dim<4>(), dim<5>());
|
|
auto av4 = av3.as_array_view(dim<4>(), dim<>(3), dim<5>());
|
|
auto av5 = av4.as_array_view(dim<3>(), dim<4>(), dim<5>());
|
|
auto av6 = av5.as_array_view(dim<12>(), dim<>(5));
|
|
|
|
fill(av6.begin(), av6.end(), 1);
|
|
|
|
auto av7 = av6.as_bytes();
|
|
|
|
auto av8 = av7.as_array_view<int>();
|
|
|
|
CHECK(av8.size() == av6.size());
|
|
for (size_t i = 0; i < av8.size(); i++)
|
|
{
|
|
CHECK(av8[i] == 1);
|
|
}
|
|
|
|
#ifdef CONFIRM_COMPILATION_ERRORS
|
|
struct Foo {char c[11];};
|
|
auto av9 = av7.as_array_view<Foo>();
|
|
#endif
|
|
}
|
|
|
|
|
|
TEST (array_view_section_test)
|
|
{
|
|
int a[30][4][5];
|
|
|
|
auto av = as_array_view(a);
|
|
auto sub = av.section({15, 0, 0}, Guide::index<3>{2, 2, 2});
|
|
auto subsub = sub.section({1, 0, 0}, Guide::index<3>{1, 1, 1});
|
|
}
|
|
|
|
|
|
TEST(constructors)
|
|
{
|
|
array_view<int, dynamic_range> av(nullptr);
|
|
CHECK(av.length() == 0);
|
|
|
|
array_view<int, dynamic_range> av2;
|
|
CHECK(av2.length() == 0);
|
|
|
|
array_view<int, dynamic_range> av3(nullptr, 0);
|
|
CHECK(av3.length() == 0);
|
|
|
|
// Constructing from a nullptr + length is specifically disallowed
|
|
auto f = [&]() {array_view<int, dynamic_range> av4(nullptr, 2);};
|
|
CHECK_THROW(f(), fail_fast);
|
|
|
|
int arr1[2][3];
|
|
array_view<int, 2, dynamic_range> av5(arr1);
|
|
|
|
array<int, 15> arr2;
|
|
array_view<int, 15> av6(arr2);
|
|
|
|
vector<int> vec1(19);
|
|
array_view<int> av7(vec1);
|
|
CHECK(av7.length() == 19);
|
|
|
|
|
|
array_view<int> av8;
|
|
CHECK(av8.length() == 0);
|
|
array_view<int> av9(arr2);
|
|
CHECK(av9.length() == 15);
|
|
|
|
|
|
#ifdef CONFIRM_COMPILATION_ERRORS
|
|
array_view<int, 4> av10;
|
|
DerivedClass *p = nullptr;
|
|
array_view<BaseClass> av11(p, 0);
|
|
#endif
|
|
|
|
|
|
}
|
|
|
|
TEST(copyandassignment)
|
|
{
|
|
array_view<int, dynamic_range> av1;
|
|
|
|
int arr[] = {3, 4, 5};
|
|
av1 = arr;
|
|
array_view<array_view_options<const int, unsigned char>, dynamic_range> av2;
|
|
av2 = av1;
|
|
}
|
|
|
|
TEST(array_view_first)
|
|
{
|
|
int arr[5] = { 1, 2, 3, 4, 5 };
|
|
|
|
{
|
|
array_view<int, 5> av = arr;
|
|
CHECK((av.first<2>().bounds() == static_bounds<size_t, 2>()));
|
|
CHECK(av.first<2>().length() == 2);
|
|
CHECK(av.first(2).length() == 2);
|
|
}
|
|
|
|
{
|
|
array_view<int, 5> av = arr;
|
|
CHECK((av.first<0>().bounds() == static_bounds<size_t, 0>()));
|
|
CHECK(av.first<0>().length() == 0);
|
|
CHECK(av.first(0).length() == 0);
|
|
}
|
|
|
|
{
|
|
array_view<int, 5> av = arr;
|
|
CHECK((av.first<5>().bounds() == static_bounds<size_t, 5>()));
|
|
CHECK(av.first<5>().length() == 5);
|
|
CHECK(av.first(5).length() == 5);
|
|
}
|
|
|
|
{
|
|
array_view<int, 5> av = arr;
|
|
#ifdef CONFIRM_COMPILATION_ERRORS
|
|
CHECK(av.first<6>().bounds() == static_bounds<size_t, 6>());
|
|
CHECK(av.first<6>().length() == 6);
|
|
#endif
|
|
CHECK_THROW(av.first(6).length(), fail_fast);
|
|
}
|
|
|
|
{
|
|
array_view<int, dynamic_range> av;
|
|
CHECK((av.first<0>().bounds() == static_bounds<size_t, 0>()));
|
|
CHECK(av.first<0>().length() == 0);
|
|
CHECK(av.first(0).length() == 0);
|
|
}
|
|
}
|
|
|
|
TEST(array_view_last)
|
|
{
|
|
int arr[5] = { 1, 2, 3, 4, 5 };
|
|
|
|
{
|
|
array_view<int, 5> av = arr;
|
|
CHECK((av.last<2>().bounds() == static_bounds<size_t, 2>()));
|
|
CHECK(av.last<2>().length() == 2);
|
|
CHECK(av.last(2).length() == 2);
|
|
}
|
|
|
|
{
|
|
array_view<int, 5> av = arr;
|
|
CHECK((av.last<0>().bounds() == static_bounds<size_t, 0>()));
|
|
CHECK(av.last<0>().length() == 0);
|
|
CHECK(av.last(0).length() == 0);
|
|
}
|
|
|
|
{
|
|
array_view<int, 5> av = arr;
|
|
CHECK((av.last<5>().bounds() == static_bounds<size_t, 5>()));
|
|
CHECK(av.last<5>().length() == 5);
|
|
CHECK(av.last(5).length() == 5);
|
|
}
|
|
|
|
|
|
{
|
|
array_view<int, 5> av = arr;
|
|
#ifdef CONFIRM_COMPILATION_ERRORS
|
|
CHECK((av.last<6>().bounds() == static_bounds<size_t, 6>()));
|
|
CHECK(av.last<6>().length() == 6);
|
|
#endif
|
|
CHECK_THROW(av.last(6).length(), fail_fast);
|
|
}
|
|
|
|
{
|
|
array_view<int, dynamic_range> av;
|
|
CHECK((av.last<0>().bounds() == static_bounds<size_t, 0>()));
|
|
CHECK(av.last<0>().length() == 0);
|
|
CHECK(av.last(0).length() == 0);
|
|
}
|
|
}
|
|
|
|
TEST(custmized_array_view_size)
|
|
{
|
|
double (*arr)[3][4] = new double[100][3][4];
|
|
array_view<array_view_options<double, char>, dynamic_range, 3, 4> av1(arr, (char)10);
|
|
|
|
struct EffectiveStructure
|
|
{
|
|
double* v1;
|
|
char v2;
|
|
};
|
|
CHECK(sizeof(av1) == sizeof(EffectiveStructure));
|
|
|
|
CHECK_THROW(av1[10][3][4], fail_fast);
|
|
|
|
array_view<const double, dynamic_range, 6, 4> av2 = av1.as_array_view(dim<>(5), dim<6>(), dim<4>());
|
|
|
|
}
|
|
|
|
TEST(array_view_sub)
|
|
{
|
|
int arr[5] = { 1, 2, 3, 4, 5 };
|
|
|
|
{
|
|
array_view<int, 5> av = arr;
|
|
CHECK((av.sub<2,2>().bounds() == static_bounds<size_t, 2>()));
|
|
CHECK((av.sub<2,2>().length() == 2));
|
|
CHECK(av.sub(2,2).length() == 2);
|
|
}
|
|
|
|
|
|
{
|
|
array_view<int, 5> av = arr;
|
|
CHECK((av.sub<0,0>().bounds() == static_bounds<size_t, 0>()));
|
|
CHECK((av.sub<0,0>().length() == 0));
|
|
CHECK(av.sub(0,0).length() == 0);
|
|
}
|
|
|
|
{
|
|
array_view<int, 5> av = arr;
|
|
CHECK((av.sub<0,5>().bounds() == static_bounds<size_t, 5>()));
|
|
CHECK((av.sub<0,5>().length() == 5));
|
|
CHECK(av.sub(0,5).length() == 5);
|
|
}
|
|
|
|
{
|
|
array_view<int, 5> av = arr;
|
|
#ifdef CONFIRM_COMPILATION_ERRORS
|
|
CHECK((av.sub<5,0>().bounds() == static_bounds<size_t, 0>()));
|
|
CHECK((av.sub<5,0>().length() == 0));
|
|
#endif
|
|
CHECK_THROW(av.sub(5,0).length(), fail_fast);
|
|
}
|
|
|
|
{
|
|
array_view<int, dynamic_range> av;
|
|
CHECK((av.sub<0,0>().bounds() == static_bounds<size_t, 0>()));
|
|
CHECK((av.sub<0,0>().length() == 0));
|
|
CHECK(av.sub(0,0).length() == 0);
|
|
}
|
|
}
|
|
|
|
void AssertNullEmptyProperties(array_view<int, dynamic_range>& av)
|
|
{
|
|
CHECK(av.length() == 0);
|
|
CHECK(av.data() == nullptr);
|
|
CHECK(!av);
|
|
}
|
|
|
|
template <class T, class U>
|
|
void AssertContentsMatch(T a1, U a2)
|
|
{
|
|
CHECK(a1.length() == a2.length());
|
|
for (size_t i = 0; i < a1.length(); ++i)
|
|
CHECK(a1[i] == a2[i]);
|
|
}
|
|
|
|
TEST(TestNullConstruction)
|
|
{
|
|
array_view<int, dynamic_range> av;
|
|
AssertNullEmptyProperties(av);
|
|
|
|
array_view<int, dynamic_range> av2(nullptr);
|
|
AssertNullEmptyProperties(av2);
|
|
}
|
|
|
|
TEST(ArrayConstruction)
|
|
{
|
|
int a[] = { 1, 2, 3, 4 };
|
|
|
|
array_view<int, dynamic_range> av = { &a[1], 3 };
|
|
CHECK(av.length() == 3);
|
|
|
|
array_view<int, dynamic_range> av3 = { a, 2 };
|
|
CHECK(av3.length() == 2);
|
|
|
|
array_view<int, dynamic_range> av2 = a;
|
|
CHECK(av2.length() == 4);
|
|
}
|
|
|
|
TEST(NonConstConstConversions)
|
|
{
|
|
int a[] = { 1, 2, 3, 4 };
|
|
|
|
#ifdef CONFIRM_COMPILATION_ERRORS
|
|
array_view<const int, dynamic_range> cav = a;
|
|
array_view<int, dynamic_range> av = cav;
|
|
#else
|
|
array_view<int, dynamic_range> av = a;
|
|
array_view<const int, dynamic_range> cav = av;
|
|
#endif
|
|
AssertContentsMatch(av, cav);
|
|
}
|
|
|
|
TEST(FixedSizeConversions)
|
|
{
|
|
int arr[] = { 1, 2, 3, 4 };
|
|
|
|
// converting to an array_view from an equal size array is ok
|
|
array_view<int, 4> av4 = arr;
|
|
CHECK(av4.length() == 4);
|
|
|
|
// converting to dynamic_range a_v is always ok
|
|
{
|
|
array_view<int, dynamic_range> av = av4;
|
|
}
|
|
{
|
|
array_view<int, dynamic_range> av = arr;
|
|
}
|
|
|
|
// initialization or assignment to static array_view that REDUCES size is NOT ok
|
|
#ifdef CONFIRM_COMPILATION_ERRORS
|
|
{
|
|
array_view<int, 2> av2 = arr;
|
|
}
|
|
{
|
|
array_view<int, 2> av2 = av4;
|
|
}
|
|
#endif
|
|
|
|
{
|
|
array_view<int, dynamic_range> av = arr;
|
|
array_view<int, 2> av2 = av;
|
|
}
|
|
|
|
#ifdef CONFIRM_COMPILATION_ERRORS
|
|
{
|
|
array_view<int, dynamic_range> av = arr;
|
|
array_view<int, 2, 1> av2 = av.as_array_view(dim<2>(), dim<2>());
|
|
}
|
|
#endif
|
|
|
|
{
|
|
array_view<int, dynamic_range> av = arr;
|
|
auto f = [&]() {array_view<int, 2, 1> av2 = av.as_array_view(dim<>(2), dim<>(2));};
|
|
CHECK_THROW(f(), fail_fast);
|
|
}
|
|
|
|
// but doing so explicitly is ok
|
|
|
|
// you can convert statically
|
|
{
|
|
array_view<int, 2> av2 = {arr, 2};
|
|
}
|
|
{
|
|
array_view<int, 1> av2 = av4.first<1>();
|
|
}
|
|
|
|
// ...or dynamically
|
|
{
|
|
// NB: implicit conversion to array_view<int,2> from array_view<int,dynamic_range>
|
|
array_view<int, 1> av2 = av4.first(1);
|
|
}
|
|
|
|
// initialization or assignment to static array_view that requires size INCREASE is not ok.
|
|
int arr2[2] = { 1, 2 };
|
|
|
|
#ifdef CONFIRM_COMPILATION_ERRORS
|
|
{
|
|
array_view<int, 4> av4 = arr2;
|
|
}
|
|
{
|
|
array_view<int, 2> av2 = arr2;
|
|
array_view<int, 4> av4 = av2;
|
|
}
|
|
#endif
|
|
{
|
|
auto f = [&]() {array_view<int, 4> av4 = {arr2, 2};};
|
|
CHECK_THROW(f(), fail_fast);
|
|
}
|
|
|
|
// this should fail - we are trying to assign a small dynamic a_v to a fixed_size larger one
|
|
array_view<int, dynamic_range> av = arr2;
|
|
auto f = [&](){ array_view<int, 4> av2 = av; };
|
|
CHECK_THROW(f(), fail_fast);
|
|
}
|
|
|
|
TEST(AsWriteableBytes)
|
|
{
|
|
int a[] = { 1, 2, 3, 4 };
|
|
|
|
{
|
|
#ifdef CONFIRM_COMPILATION_ERRORS
|
|
// you should not be able to get writeable bytes for const objects
|
|
array_view<const int, dynamic_range> av = a;
|
|
auto wav = av.as_writeable_bytes();
|
|
#endif
|
|
}
|
|
|
|
{
|
|
array_view<int, dynamic_range> av;
|
|
auto wav = av.as_writeable_bytes();
|
|
CHECK(wav.length() == av.length());
|
|
CHECK(wav.length() == 0);
|
|
CHECK(wav.bytes() == 0);
|
|
}
|
|
|
|
{
|
|
array_view<int, dynamic_range> av = a;
|
|
auto wav = av.as_writeable_bytes();
|
|
CHECK(wav.data() == (byte*)&a[0]);
|
|
CHECK(wav.length() == sizeof(a));
|
|
}
|
|
|
|
}
|
|
|
|
|
|
TEST(ArrayViewComparison)
|
|
{
|
|
int arr[10][2];
|
|
auto av1 = as_array_view(arr);
|
|
array_view<const int, dynamic_range, 2> av2 = av1;
|
|
|
|
CHECK(av1 == av2);
|
|
|
|
array_view<array_view_options<int, char>, 20> av3 = av1.as_array_view(dim<>(20));
|
|
CHECK(av3 == av2 && av3 == av1);
|
|
|
|
}
|
|
}
|
|
|
|
int main(int, const char *[])
|
|
{
|
|
return UnitTest::RunAllTests();
|
|
}
|