From f5abe981a0cd9b43137fac77e0af0d899da91a43 Mon Sep 17 00:00:00 2001 From: Dan Smith Date: Fri, 1 Dec 2023 10:37:18 -0500 Subject: [PATCH] Squashed 'externals/coda-oss/' changes from df4e0fc1f..28926b673 28926b673 provide our own (simple) mdspan implementation (#759) 6bb722454 make SpanRC API match std::mdspan (#758) git-subtree-dir: externals/coda-oss git-subtree-split: 28926b673931c3f148882ceca7d3de203accfa4e --- modules/c++/coda-oss.vcxproj | 3 + modules/c++/coda-oss.vcxproj.filters | 9 ++ .../c++/coda_oss/include/coda_oss/mdspan.h | 73 +++++++++ .../c++/coda_oss/include/coda_oss/mdspan_.h | 151 ++++++++++++++++++ .../c++/coda_oss/include/coda_oss/optional.h | 7 +- modules/c++/gsl/CMakeLists.txt | 2 - .../c++/hdf5.lite/include/hdf5/lite/SpanRC.h | 81 +--------- .../hdf5.lite/include/hdf5/lite/highfive.h | 6 +- .../c++/hdf5.lite/unittests/test_highfive.cpp | 22 +-- modules/c++/std/include/import/std.h | 1 + modules/c++/std/include/std/mdspan | 46 ++++++ modules/c++/types/include/types/RowCol.h | 8 +- 12 files changed, 311 insertions(+), 98 deletions(-) create mode 100644 modules/c++/coda_oss/include/coda_oss/mdspan.h create mode 100644 modules/c++/coda_oss/include/coda_oss/mdspan_.h create mode 100644 modules/c++/std/include/std/mdspan diff --git a/modules/c++/coda-oss.vcxproj b/modules/c++/coda-oss.vcxproj index 0f376dc0c5..6aacf1781a 100644 --- a/modules/c++/coda-oss.vcxproj +++ b/modules/c++/coda-oss.vcxproj @@ -19,6 +19,8 @@ + + @@ -509,6 +511,7 @@ + diff --git a/modules/c++/coda-oss.vcxproj.filters b/modules/c++/coda-oss.vcxproj.filters index 50db461753..a94b8aafd1 100644 --- a/modules/c++/coda-oss.vcxproj.filters +++ b/modules/c++/coda-oss.vcxproj.filters @@ -951,6 +951,12 @@ str + + coda_oss + + + coda_oss + @@ -1559,5 +1565,8 @@ sys + + std + \ No newline at end of file diff --git a/modules/c++/coda_oss/include/coda_oss/mdspan.h b/modules/c++/coda_oss/include/coda_oss/mdspan.h new file mode 100644 index 0000000000..3569440f41 --- /dev/null +++ b/modules/c++/coda_oss/include/coda_oss/mdspan.h @@ -0,0 +1,73 @@ +/* ========================================================================= + * This file is part of coda_oss-c++ + * ========================================================================= + * + * (C) Copyright 2004 - 2014, MDA Information Systems LLC + * © Copyright 2023, Maxar Technologies, Inc. + * + * coda_oss-c++ is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; If not, + * see . + * + */ +#pragma once + +#include "coda_oss/CPlusPlus.h" + +// This should always work ... it's in a `details` namespace +#include "coda_oss/mdspan_.h" + +// This logic needs to be here rather than so that `coda_oss::mdspan` will +// be the same as `std::mdspan`. +#ifndef CODA_OSS_HAVE_std_mdspan_ + #define CODA_OSS_HAVE_std_mdspan_ 0 // assume no +#endif +#ifndef CODA_OSS_HAVE_experimental_mdspan_ + #define CODA_OSS_HAVE_experimental_mdspan_ 0 // assume no std::experimental::mdspan +#endif +#if CODA_OSS_cpp17 // __has_include + #if __has_include() // not until C++23 + #include + #undef CODA_OSS_HAVE_std_mdspan_ + #define CODA_OSS_HAVE_std_mdspan_ 1 // provided by the implementation, probably C++23 + #endif + + #if CODA_OSS_cpp20 // Can't even #include this file with older C++14/17 compilers! :-( + // Put this in a __has_include so that it's optional. Our simple implemtnation works + // for our needs, and this brings along a lot of code that our older compilers don't + // like. By the time we need more functionality, maybe we'll be using C++23? + // + // Until then, having this available allows checking our implementation against + // something much more real. https://github.com/kokkos/mdspan + #if __has_include("coda_oss/experimental/mdspan") + #include "coda_oss/experimental/mdspan" + #undef CODA_OSS_HAVE_experimental_mdspan_ + #define CODA_OSS_HAVE_experimental_mdspan_ 1 // provided coda_oss/experimental/mdspan + #endif + #endif +#endif // CODA_OSS_cpp17 + +namespace coda_oss +{ + #if CODA_OSS_HAVE_std_mdspan_ + using std::mdspan; + using std::dextents; + #elif CODA_OSS_HAVE_experimental_mdspan_ + using std::experimental::mdspan; + using std::experimental::dextents; + #else + using details::mdspan; + using details::dextents; + #endif +} + diff --git a/modules/c++/coda_oss/include/coda_oss/mdspan_.h b/modules/c++/coda_oss/include/coda_oss/mdspan_.h new file mode 100644 index 0000000000..b678051d83 --- /dev/null +++ b/modules/c++/coda_oss/include/coda_oss/mdspan_.h @@ -0,0 +1,151 @@ +/* ========================================================================= + * This file is part of coda_oss-c++ + * ========================================================================= + * + * (C) Copyright 2004 - 2014, MDA Information Systems LLC + * © Copyright 2023, Maxar Technologies, Inc. + * + * coda_oss-c++ is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; If not, + * see . + * + */ +#pragma once + +#include + +#include + +#include "coda_oss/span.h" + +// This is a simple, partial, and incomplete implementation of `std::mdspan` (in C++23). +// https://en.cppreference.com/w/cpp/container/mdspan +// +// Why? Our (current) needs are much more limited than all the use-cases for `std::mdspan`: +// dynamic (not static) extents, rank of 2 (rows × cols), contiguous memory, ... +// By the time we really need more features, maybe we'll be using C++23? +namespace coda_oss +{ +namespace details +{ + // https://en.cppreference.com/w/cpp/container/mdspan/extents +template +struct dextents final // this is actually supposed to be an alias template with all dynamic extents +{ + static_assert(Rank == 2, "Rank must have a value of 2"); + using index_type = IndexType; + using size_type = index_type; + using rank_type = size_t; + + constexpr dextents() = default; + + // These are supposed to be templates, but we don't need that complication right now. + constexpr dextents(index_type i0, index_type i1) noexcept : exts_{i0, i1} + { + } + constexpr explicit dextents(const std::array& exts) noexcept : exts_(exts) + { + } + + dextents(const dextents&) = default; + dextents& operator=(const dextents&) = default; + dextents(dextents&&) = default; + dextents& operator=(dextents&&) = default; + + constexpr index_type extent(rank_type r) const noexcept + { + return exts_[r]; + } + + static constexpr auto rank() noexcept + { + return Rank; + } + +private: + std::array exts_; +}; + +template +class mdspan final +{ + coda_oss::span s_; // `span` instead of a raw pointer to get more range checking. + TExtents ext_; + + // c.f., `types::RowCol` + template + static size_t area(const dextents& exts) + { + return exts.extent(0) * exts.extent(1); + } + +public: + using extents_type = TExtents; + using size_type = typename extents_type::size_type; + using data_handle_type = T*; + using reference = T&; + + constexpr mdspan() = default; + + // Again, these are supposed to be templates ... + mdspan(data_handle_type p, const extents_type& ext) noexcept : s_(p, area(ext)), ext_(ext) + { + } + mdspan(data_handle_type p, const std::array& dims) noexcept : mdspan(p, extents_type(dims)) + { + } + + mdspan(const mdspan&) = default; + mdspan& operator=(const mdspan&) = default; + mdspan(mdspan&&) = default; + mdspan& operator=(mdspan&&) = default; + + constexpr data_handle_type data_handle() const noexcept + { + return s_.data(); + } + + /*constexpr*/ reference operator[](size_t idx) const noexcept + { + assert(idx < size()); // prevents "constexpr" in C++11 + return data_handle()[idx]; + } + /*constexpr*/ reference operator()(size_t r, size_t c) const noexcept + { + const auto offset = (r * extent(1)) + c; + return (*this)[offset]; + } + + constexpr size_t size() const noexcept + { + return s_.size(); + } + + constexpr bool empty() const noexcept + { + return s_.empty(); + } + + auto extent(size_type rank) const + { + return ext_.extent(rank); + } + + static constexpr auto rank() noexcept + { + return extents_type::rank(); + } +}; +} +} + diff --git a/modules/c++/coda_oss/include/coda_oss/optional.h b/modules/c++/coda_oss/include/coda_oss/optional.h index f1fab3a0b1..86298871cf 100644 --- a/modules/c++/coda_oss/include/coda_oss/optional.h +++ b/modules/c++/coda_oss/include/coda_oss/optional.h @@ -24,6 +24,9 @@ #include "coda_oss/CPlusPlus.h" +// always compile; it's in a details namespace +#include "coda_oss/optional_.h" + // This logic needs to be here rather than so that `coda_oss::optional` will // be the same as `std::optional`. #ifndef CODA_OSS_HAVE_std_optional_ @@ -37,10 +40,6 @@ #endif #endif // CODA_OSS_cpp17 -#if !CODA_OSS_HAVE_std_optional_ -#include "coda_oss/optional_.h" -#endif - namespace coda_oss { #if CODA_OSS_HAVE_std_optional_ diff --git a/modules/c++/gsl/CMakeLists.txt b/modules/c++/gsl/CMakeLists.txt index 6f17aa7797..10d694163b 100644 --- a/modules/c++/gsl/CMakeLists.txt +++ b/modules/c++/gsl/CMakeLists.txt @@ -4,5 +4,3 @@ coda_add_module( ${MODULE_NAME} VERSION 1.0 DEPS config-c++) - - diff --git a/modules/c++/hdf5.lite/include/hdf5/lite/SpanRC.h b/modules/c++/hdf5.lite/include/hdf5/lite/SpanRC.h index 9392890bb2..aea2d9c51b 100644 --- a/modules/c++/hdf5.lite/include/hdf5/lite/SpanRC.h +++ b/modules/c++/hdf5.lite/include/hdf5/lite/SpanRC.h @@ -20,22 +20,11 @@ * */ +#pragma once #ifndef CODA_OSS_hdf5_lite_SpanRC_h_INCLUDED_ #define CODA_OSS_hdf5_lite_SpanRC_h_INCLUDED_ -#pragma once -/*! - * \file SpanRC.h - * - * This is a super-simple version of C++23's mdspan. It's here because 1) don't want widespread use, and - * 2) CODA already has a View2D. - */ - -#include - -#include "config/Exports.h" -#include "coda_oss/span.h" -#include "types/RowCol.h" +#include "coda_oss/mdspan.h" namespace hdf5 { @@ -43,71 +32,7 @@ namespace lite { template -struct SpanRC final -{ - using size_type = types::RowCol; - using element_type = T; - using pointer = T*; - using reference = T&; - - SpanRC() = default; - SpanRC(pointer p, size_type rc) noexcept : s_(p, rc.area()), rc_(rc) - { - } - SpanRC(pointer p, size_t r, size_t c) noexcept : SpanRC(p, size_type(r, c)) - { - } - SpanRC(const SpanRC&) noexcept = default; - - constexpr pointer data() const noexcept - { - return s_.data(); - } - - /*constexpr*/ reference operator[](size_t idx) const noexcept - { - assert(idx < size()); // prevents "constexpr" in C++11 - return data()[idx]; - } - /*constexpr*/ reference operator()(size_t r, size_t c) const noexcept - { - const auto offset = (r * dims().col) + c; - return (*this)[offset]; - } - /*constexpr*/ reference operator[](size_type idx) const noexcept - { - return (*this)(idx.row, idx.col); - } - - constexpr size_t size() const noexcept - { - assert(s_.size() == rc_.area()); - return s_.size(); - } - constexpr size_t area() const noexcept - { - return size(); - } - - constexpr size_type size_bytes() const noexcept - { - return s_.size_bytes(); - } - - constexpr bool empty() const noexcept - { - return s_.empty(); - } - - const auto& dims() const - { - return rc_; - } - - private: - coda_oss::span s_; - types::RowCol rc_; -}; +using SpanRC = coda_oss::mdspan>; } } diff --git a/modules/c++/hdf5.lite/include/hdf5/lite/highfive.h b/modules/c++/hdf5.lite/include/hdf5/lite/highfive.h index fb276e681c..d5d91987bb 100644 --- a/modules/c++/hdf5.lite/include/hdf5/lite/highfive.h +++ b/modules/c++/hdf5.lite/include/hdf5/lite/highfive.h @@ -56,10 +56,10 @@ inline auto vv_load(const H5Easy::File& file, const std::string& dataset_name) template inline HighFive::DataSet writeDataSet(H5Easy::File& file, const std::string& dataset_name, SpanRC data /*, TODO ...*/) { - const std::vector dims{data.dims().row, data.dims().col}; + const std::vector dims{data.extent(0), data.extent(1)}; const HighFive::DataSpace dataspace{dims}; auto retval = file.createDataSet(dataset_name, dataspace); - retval.write_raw(data.data()); + retval.write_raw(data.data_handle()); return retval; } @@ -87,7 +87,7 @@ inline SpanRC readDataSet(const HighFive::DataSet& dataSet, std::vector& r result.resize(dims.area()); dataSet.read(result.data()); - return SpanRC(result.data(), dims); + return SpanRC(result.data(), std::array{dims.row, dims.col}); } template diff --git a/modules/c++/hdf5.lite/unittests/test_highfive.cpp b/modules/c++/hdf5.lite/unittests/test_highfive.cpp index f54526ab93..9fbfd76547 100644 --- a/modules/c++/hdf5.lite/unittests/test_highfive.cpp +++ b/modules/c++/hdf5.lite/unittests/test_highfive.cpp @@ -53,10 +53,11 @@ TEST_CASE(test_highfive_load) { std::vector lat; const auto rc = hdf5::lite::loadDataSet(file, "/g4/lat", lat); + static_assert(decltype(rc)::rank() == 2, "wrong rank()"); TEST_ASSERT_EQ(lat.size(), 19); - TEST_ASSERT_EQ(lat.size(), rc.area()); - TEST_ASSERT_EQ(rc.dims().row, 19); - TEST_ASSERT_EQ(rc.dims().col, 1); + TEST_ASSERT_EQ(lat.size(), rc.size()); + TEST_ASSERT_EQ(rc.extent(0), 19); + TEST_ASSERT_EQ(rc.extent(1), 1); TEST_ASSERT_ALMOST_EQ(lat[0], -90.0); TEST_ASSERT_ALMOST_EQ(lat[0], -lat[18]); } @@ -103,9 +104,9 @@ template static auto read_complex(const HighFive::DataSet& r, const HighFive::DataSet& i) { std::vector r_result; - hdf5::lite::readDataSet(r, r_result); + std::ignore = hdf5::lite::readDataSet(r, r_result); std::vector i_result; - hdf5::lite::readDataSet(i, i_result); + std::ignore = hdf5::lite::readDataSet(i, i_result); return std::make_pair(r, i); } template @@ -405,11 +406,11 @@ TEST_CASE(test_highfive_write) const types::RowCol dims{10, 20}; std::vector data_(dims.area()); - const hdf5::lite::SpanRC data(data_.data(), dims); + hdf5::lite::SpanRC data(data_.data(), std::array{dims.row, dims.col}); double d = 0.0; - for (size_t r = 0; r result; const auto rc = hdf5::lite::loadDataSet(file, "/DS1", result); - TEST_ASSERT(rc.dims() == dims); + TEST_ASSERT(rc.extent(0) == dims.row); + TEST_ASSERT(rc.extent(1) == dims.col); TEST_ASSERT_EQ(dims.area(), result.size()); for (size_t i = 0; i < result.size(); i++) { diff --git a/modules/c++/std/include/import/std.h b/modules/c++/std/include/import/std.h index 32702da30d..5c41ee61ba 100644 --- a/modules/c++/std/include/import/std.h +++ b/modules/c++/std/include/import/std.h @@ -102,6 +102,7 @@ CODA_OSS_disable_warning_system_header_push #include #include #include +#include CODA_OSS_disable_warning_pop diff --git a/modules/c++/std/include/std/mdspan b/modules/c++/std/include/std/mdspan new file mode 100644 index 0000000000..b25c5447d3 --- /dev/null +++ b/modules/c++/std/include/std/mdspan @@ -0,0 +1,46 @@ +/* ========================================================================= + * This file is part of std-c++ + * ========================================================================= + * + * © Copyright 2023, Maxar Technologies, Inc. + * + * std-c++ is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; If not, http://www.gnu.org/licenses/. + * + */ +#pragma once + +#include "coda_oss/mdspan.h" +#include "coda_oss/CPlusPlus.h" + +// Make it (too?) easy for clients to get our various std:: implementations +#ifndef CODA_OSS_NO_std_mdspan + #if CODA_OSS_cpp23 + #include + #define CODA_OSS_NO_std_mdspan 1 // part of C++23 + #else + #define CODA_OSS_NO_std_mdspan 0 // use our own + #endif +#endif + +#if !CODA_OSS_NO_std_mdspan +namespace std // This is slightly uncouth: we're not supposed to augment "std". +{ + using coda_oss::mdspan; + using coda_oss::dextents; +} +#ifndef __cpp_lib_mdspan +#define __cpp_lib_mdspan 202002L // https://en.cppreference.com/w/cpp/feature_test#cpp_lib_mdspan +#endif + +#endif // CODA_OSS_NO_std_mdspan diff --git a/modules/c++/types/include/types/RowCol.h b/modules/c++/types/include/types/RowCol.h index e13f125a2e..846280aaac 100644 --- a/modules/c++/types/include/types/RowCol.h +++ b/modules/c++/types/include/types/RowCol.h @@ -19,7 +19,7 @@ * see . * */ - + #pragma once #ifndef __TYPES_ROW_COL_H__ #define __TYPES_ROW_COL_H__ @@ -28,6 +28,7 @@ #include #include #include +#include #include "gsl/gsl.h" @@ -78,6 +79,11 @@ template class RowCol row = p.first; col = p.second; } + explicit RowCol(const std::array& a) noexcept + { + row = a[0]; + col = a[1]; + } template RowCol& operator=(const RowCol& p) noexcept {