netcdfpp
Header-only NetCDF C++ wrapper
Loading...
Searching...
No Matches
netcdfpp.h
Go to the documentation of this file.
1/*
2 Copyright (c) 2020 Sven Willner <sven.willner@yfx.de>
3
4 Permission is hereby granted, free of charge, to any person
5 obtaining a copy of this software and associated documentation files
6 (the "Software"), to deal in the Software without restriction,
7 including without limitation the rights to use, copy, modify, merge,
8 publish, distribute, sublicense, and/or sell copies of the Software,
9 and to permit persons to whom the Software is furnished to do so,
10 subject to the following conditions:
11
12 The above copyright notice and this permission notice shall be
13 included in all copies or substantial portions of the Software.
14
15 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
19 BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
20 ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
21 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 SOFTWARE.
23*/
24
30// Full repository: https://github.com/swillner/netcdfpp
31
32#ifndef NETCDFPP_H
33#define NETCDFPP_H
34
35#include <netcdf.h>
36
37#include <algorithm>
38#include <array>
39#include <cstddef>
40#include <cstdint>
41#include <cstdlib>
42#include <cstring>
43#include <memory>
44#include <stdexcept>
45#include <string>
46#include <type_traits>
47#include <utility>
48#include <vector>
49
50namespace netCDF {
51
52template<int I>
53struct SystemType {};
54template<typename T>
55struct Type {
56 static constexpr bool is_atomic = false;
57};
58
59template<>
60struct Type<long long> {
61 static constexpr nc_type id = NC_INT64;
62 static constexpr bool is_atomic = true;
63};
64template<>
65struct Type<unsigned long long> {
66 static constexpr nc_type id = NC_UINT64;
67 static constexpr bool is_atomic = true;
68};
69
70#define NETCDFPP_IMPL_TYPE(internal, lib) \
71 template<> \
72 struct SystemType<lib> { \
73 using type = internal; \
74 }; \
75 template<> \
76 struct Type<internal> { \
77 static constexpr nc_type id = lib; \
78 static constexpr bool is_atomic = true; \
79 };
80
81NETCDFPP_IMPL_TYPE(char*, NC_STRING)
82NETCDFPP_IMPL_TYPE(char, NC_CHAR)
83NETCDFPP_IMPL_TYPE(double, NC_DOUBLE)
84NETCDFPP_IMPL_TYPE(float, NC_FLOAT)
85NETCDFPP_IMPL_TYPE(std::int16_t, NC_SHORT)
86NETCDFPP_IMPL_TYPE(std::int32_t, NC_INT)
87NETCDFPP_IMPL_TYPE(std::int64_t, NC_INT64)
88NETCDFPP_IMPL_TYPE(std::int8_t, NC_BYTE)
89NETCDFPP_IMPL_TYPE(std::uint16_t, NC_USHORT)
90NETCDFPP_IMPL_TYPE(std::uint32_t, NC_UINT)
91NETCDFPP_IMPL_TYPE(std::uint64_t, NC_UINT64)
92NETCDFPP_IMPL_TYPE(std::uint8_t, NC_UBYTE)
93
94template<typename T, typename Function>
98inline T for_type(nc_type t, Function&& f) {
99 switch (t) {
100 case NC_STRING: {
102 return f(type);
103 }
104 case NC_CHAR: {
106 return f(type);
107 }
108 case NC_DOUBLE: {
110 return f(type);
111 }
112 case NC_FLOAT: {
114 return f(type);
115 }
116 case NC_SHORT: {
118 return f(type);
119 }
120 case NC_INT: {
122 return f(type);
123 }
124 case NC_INT64: {
126 return f(type);
127 }
128 case NC_BYTE: {
130 return f(type);
131 }
132 case NC_USHORT: {
134 return f(type);
135 }
136 case NC_UINT: {
138 return f(type);
139 }
140 case NC_UINT64: {
142 return f(type);
143 }
144 case NC_UBYTE: {
146 return f(type);
147 }
148 default:
149 throw std::runtime_error("Unsupported type");
150 }
151}
152
157class Exception final : public std::runtime_error {
158 private:
159 int ret;
160
161 public:
163 explicit Exception(int r, std::string s) : std::runtime_error(std::move(s)), ret(r) {}
164
166 int return_code() const { return ret; }
167};
168
169class Attribute;
170class Dimension;
171class File;
172class Group;
173class UserType;
174class Variable;
175
176namespace testing {
177class TestUserType;
178}
179
180namespace detail {
181
182constexpr bool is_user_type(nc_type type) { return type >= NC_FIRSTUSERTYPEID; }
183
184template<typename T>
185inline T* data_or_null(std::vector<T>& v) {
186 return v.empty() ? nullptr : &v[0];
187}
188
189template<typename T>
190inline const T* data_or_null(const std::vector<T>& v) {
191 return v.empty() ? nullptr : &v[0];
192}
193
194inline std::vector<std::string> process_char_vector(std::vector<char*>& buf) {
195 std::vector<std::string> res;
196 res.reserve(buf.size());
197 std::copy(std::begin(buf), std::end(buf), std::back_inserter(res));
198 if (!buf.empty()) {
199 nc_free_string(buf.size(), detail::data_or_null(buf));
200 }
201 return res;
202}
203
204inline std::vector<std::string> process_char_vector(char** buf, std::size_t len) {
205 std::vector<std::string> res;
206 res.reserve(len);
207 std::copy(buf, buf + len, std::back_inserter(res));
208 nc_free_string(len, buf);
209 return res;
210}
211
212inline std::vector<const char*> process_string_vector(const std::vector<std::string>& v) {
213 std::vector<const char*> buf(v.size());
214 std::transform(std::begin(v), std::end(v), std::begin(buf), [](const std::string& s) { return s.c_str(); });
215 return buf;
216}
217
218struct Path {
219 std::string name;
220 int id;
222 std::shared_ptr<Path> parent;
223
224 std::string get_full_path() const {
225 std::string res = name;
226 const std::shared_ptr<Path>* current = &parent;
227 while (*current) {
228 res.insert(0, (*current)->name + ((*current)->parent ? "/" : ":"));
229 current = &(*current)->parent;
230 }
231 return res;
232 }
233};
234
235template<typename T>
236struct ClassName {};
237
238template<>
240 static constexpr const char* name = "Attribute";
241};
242
243template<>
245 static constexpr const char* name = "Dimension";
246};
247
248template<>
250 static constexpr const char* name = "File";
251};
252
253template<>
255 static constexpr const char* name = "Group";
256};
257
258template<>
260 static constexpr const char* name = "UserType";
261};
262
263template<>
265 static constexpr const char* name = "Variable";
266};
267
268class Object {
269 protected:
270 std::shared_ptr<Path> path;
271 explicit Object(std::shared_ptr<Path> path_p) : path(std::move(path_p)) {}
272
273 static inline std::string get_error_message(int ret) {
274 if (NC_ISSYSERR(ret)) {
275 return std::strerror(ret);
276 }
277 return nc_strerror(ret);
278 }
279
280 void raise_error(int ret) const { throw Exception(ret, get_error_message(ret) + ": " + path->get_full_path()); }
281
282 inline void check(int ret) const {
283 if (ret != NC_NOERR) {
284 raise_error(ret);
285 }
286 }
287
288 public:
289 const std::string& name() const { return path->name; }
290 int id() const { return path->id; }
291};
292
293} // namespace detail
294
295template<typename T>
301class Maybe final {
302 private:
303 std::shared_ptr<detail::Path> path;
304
305 void raise_error() const {
306 throw Exception(NC_ENOTFOUND, std::string(detail::ClassName<typename std::remove_const<T>::type>::name) + " not found: " + path->get_full_path());
307 }
308
309 public:
310 explicit Maybe(std::shared_ptr<detail::Path> path_p) : path(std::move(path_p)) {}
311
313 inline T require() const {
314 if (!valid()) {
315 raise_error();
316 }
317 return T(path);
318 }
319
321 inline bool valid() const { return path->id >= 0; }
322
324 inline operator bool() const { return valid(); }
325};
326
328class Attribute final : public detail::Object {
329 friend class Group;
330 friend class Maybe<Attribute>;
331 friend class Variable;
332
333 private:
334 explicit Attribute(std::shared_ptr<detail::Path> path_p) : detail::Object(std::move(path_p)) {}
335
336 int ncid() const {
337 if (path->parent->is_group) {
338 return path->parent->id;
339 } else {
340 return path->parent->parent->id;
341 }
342 }
343
344 int othid() const {
345 if (path->parent->is_group) {
346 return NC_GLOBAL;
347 } else {
348 return path->parent->id;
349 }
350 }
351
352 template<typename T>
353 int get_internal(int ncid_p, int othid_p, const char* name_p, T* v) const {
354 static_assert(!Type<T>::is_atomic, "Should only be used for user types");
355 return nc_get_att(ncid_p, othid_p, name_p, v);
356 }
357
358 template<typename T>
359 int set_internal(int ncid_p, int othid_p, const char* name_p, std::size_t len, const T* v);
360
361 public:
363 void copy_values(const Attribute& a);
364
365 template<typename T>
370 std::vector<T> get() const {
371 static_assert(!std::is_same<char*, T>::value && !std::is_same<const char*, T>::value, "Use get_string() for reading string attributes");
372 std::size_t len;
373 check(nc_inq_attlen(ncid(), othid(), path->name.c_str(), &len));
374 std::vector<T> res(len);
375 if (res.empty()) {
376 return res;
377 }
378 check(get_internal<T>(ncid(), othid(), path->name.c_str(), detail::data_or_null(res)));
379 return res;
380 }
381
383 std::string get_string() const {
384 std::size_t len;
385 check(nc_inq_attlen(ncid(), othid(), path->name.c_str(), &len));
386 std::vector<char> buf(len + 1);
387 buf[len] = '\0';
388 check(nc_get_att_text(ncid(), othid(), path->name.c_str(), &buf[0]));
389 return std::string(&buf[0]);
390 }
391
393 bool is_group_attribute() const { return path->parent->is_group; }
394
397 if (is_group_attribute()) {
398 return Maybe<Group>(path->parent);
399 }
400 return Maybe<Group>(std::make_shared<detail::Path>(detail::Path{"..", -1, true, path}));
401 }
402
405 if (!is_group_attribute()) {
406 return Maybe<Variable>(path->parent);
407 }
408 return Maybe<Variable>(std::make_shared<detail::Path>(detail::Path{"..", -1, true, path}));
409 }
410
412 void rename(std::string name) {
413 check(nc_rename_att(ncid(), othid(), path->name.c_str(), name.c_str()));
414 path->name = std::move(name);
415 }
416
417 template<typename T>
419 void set(const std::vector<T>& v) {
420 static_assert(Type<T>::is_atomic, "For user type attributes use Attribute::set(const std::vector<T>& v, const UserType& type)");
421 check(set_internal<T>(ncid(), othid(), path->name.c_str(), v.size(), detail::data_or_null(v)));
422 }
423
424 template<typename T>
426 void set(const std::vector<T>& v, const UserType& type);
427 template<typename T>
429 void set(const T& v, const UserType& type);
430
431 template<typename T>
433 typename std::enable_if<std::is_same<std::string, T>::value, void>::type set(T v) {
434 check(set_internal(ncid(), othid(), path->name.c_str(), v.length() + 1, v.c_str()));
435 }
436 template<typename T>
438 typename std::enable_if<std::is_same<const char*, T>::value || std::is_same<char*, T>::value, void>::type set(T v) {
439 check(set_internal(ncid(), othid(), path->name.c_str(), std::strlen(v) + 1, v));
440 }
441 template<typename T>
443 typename std::enable_if<!std::is_same<std::string, T>::value && !std::is_same<const char*, T>::value && !std::is_same<char*, T>::value, void>::type set(
444 T v) {
445 static_assert(Type<T>::is_atomic, "For user type attributes use Attribute::set(T v, const UserType& type)");
446 check(set_internal<T>(ncid(), othid(), path->name.c_str(), 1, &v));
447 }
448
450 std::size_t size() const {
451 std::size_t len;
452 check(nc_inq_attlen(ncid(), othid(), path->name.c_str(), &len));
453 return len;
454 }
455
457 nc_type type() const {
458 int res;
459 check(nc_inq_atttype(ncid(), othid(), path->name.c_str(), &res));
460 return res;
461 }
462
464 std::string type_name() const {
465 char name[NC_MAX_NAME + 1];
466 check(nc_inq_type(ncid(), type(), name, nullptr));
467 return name;
468 }
469
471 Attribute require_type(const std::string& name) const {
472 const auto name_l = type_name();
473 if (name_l != name) {
474 throw Exception(NC_EVARMETA, "Unexpected type '" + name_l + "': " + path->get_full_path());
475 }
476 return *this;
477 }
478
481};
482
484class Dimension final : public detail::Object {
485 friend class Group;
486 friend class Maybe<Dimension>;
487 friend class Variable;
488
489 private:
490 mutable std::size_t size_m = 0;
491 mutable bool size_read = false;
492
493 explicit Dimension(std::shared_ptr<detail::Path> path_p) : detail::Object(std::move(path_p)) {}
494
495 public:
497 std::size_t size() const {
498 if (!size_read) {
499 check(nc_inq_dimlen(path->parent->id, path->id, &size_m));
500 size_read = true;
501 }
502 return size_m;
503 }
504
506 bool is_unlimited() const {
507 int len;
508 check(nc_inq_unlimdims(path->parent->id, &len, nullptr));
509 if (len == 0) {
510 return false;
511 }
512 std::vector<int> ids(len);
513 check(nc_inq_unlimdims(path->parent->id, nullptr, detail::data_or_null(ids)));
514 return std::find(std::begin(ids), std::end(ids), path->id) != std::end(ids);
515 }
516
518 Group parent() const;
519
521 void rename(std::string name) {
522 check(nc_rename_dim(path->parent->id, path->id, name.c_str()));
523 path->name = std::move(name);
524 }
525};
526
528class Group : public detail::Object {
529 friend class Attribute;
530 friend class Dimension;
531 friend class Maybe<Group>;
532 friend class UserType;
533 friend class Variable;
534
535 protected:
536 explicit Group(std::shared_ptr<detail::Path> path_p) : detail::Object(std::move(path_p)) {}
537
538 public:
540 Attribute add_attribute(std::string name) { return Attribute(std::make_shared<detail::Path>(detail::Path{std::move(name), -1, false, path})); }
541
544 auto res = add_attribute(a.name());
545 res.copy_values(a);
546 return res;
547 }
548
550 Dimension add_dimension(std::string name) { return add_dimension(std::move(name), NC_UNLIMITED); }
551
553 Dimension add_dimension(std::string name, std::size_t len) {
554 int id;
555 check(nc_def_dim(path->id, name.c_str(), len, &id));
556 return Dimension(std::make_shared<detail::Path>(detail::Path{std::move(name), id, false, path}));
557 }
559 Dimension add_dimension(const Dimension& d) { return add_dimension(d.name(), d.is_unlimited() ? NC_UNLIMITED : d.size()); }
560
562 template<typename T>
565 template<typename T>
566 Variable add_dimension_variable(std::string name, std::size_t len);
568 template<typename T>
570
572 Group add_group(std::string name) {
573 int id;
574 check(nc_def_grp(path->id, name.c_str(), &id));
575 return Group(std::make_shared<detail::Path>(detail::Path{std::move(name), id, true, path}));
576 }
578 Group add_group(const Group& g, bool variable_values = false) {
579 auto res = add_group(g.name());
580 res.copy_from(g, variable_values);
581 return res;
582 }
583
585 UserType add_type_compound(std::string name, std::size_t bytes_size);
586 template<typename T>
588 UserType add_type_compound(std::string name);
589
591 UserType add_type_enum(std::string name, nc_type basetype);
592 template<typename T>
594 UserType add_type_enum(std::string name);
595
597 UserType add_type_vlen(std::string name, nc_type basetype);
598 template<typename T>
600 UserType add_type_vlen(std::string name);
601
603 UserType add_type_opaque(std::string name, std::size_t bytes_size);
604
607
609 Variable add_variable(std::string name, const UserType& type, const std::vector<int>& dims);
611 Variable add_variable(std::string name, const UserType& type, const std::vector<Dimension>& dims);
613 Variable add_variable(std::string name, const UserType& type, const std::vector<std::string>& dims);
615 Variable add_variable(std::string name, nc_type type, const std::vector<int>& dims);
617 Variable add_variable(std::string name, nc_type type, const std::vector<Dimension>& dims);
619 Variable add_variable(std::string name, nc_type type, const std::vector<std::string>& dims);
620 template<typename T>
622 Variable add_variable(std::string name, const std::vector<int>& dims);
623 template<typename T>
625 Variable add_variable(std::string name, const std::vector<Dimension>& dims);
626 template<typename T>
628 Variable add_variable(std::string name, const std::vector<std::string>& dims);
630 Variable add_variable(const Variable& v, bool with_values = false);
631
633 Maybe<Attribute> attribute(std::string name) const {
634 auto res = std::make_shared<detail::Path>(detail::Path{std::move(name), -1, false, path});
635 const auto ret = nc_inq_attid(path->id, NC_GLOBAL, res->name.c_str(), &res->id);
636 if (ret != NC_ENOTATT) {
637 check(ret);
638 }
639 return Maybe<Attribute>(std::move(res));
640 }
641
643 std::vector<Attribute> attributes() const {
644 int count;
645 check(nc_inq_natts(path->id, &count));
646 std::vector<Attribute> res;
647 res.reserve(count);
648 char name[NC_MAX_NAME + 1];
649 for (int id = 0; id < count; ++id) {
650 check(nc_inq_attname(path->id, NC_GLOBAL, id, name));
651 res.emplace_back(Attribute{std::make_shared<detail::Path>(detail::Path{std::string(name), id, false, path})});
652 }
653 return res;
654 }
655
657 void copy_attributes(const Group& g) {
658 for (const auto& it : g.attributes()) {
659 add_attribute(it);
660 }
661 }
663 void copy_dimensions(const Group& g) {
664 for (const auto& it : g.dimensions()) {
665 add_dimension(it);
666 }
667 }
669 void copy_from(const Group& g, bool variable_values = false) {
673 copy_variables(g, variable_values);
674 copy_groups(g, variable_values);
675 }
677 void copy_groups(const Group& g, bool variable_values = false) {
678 for (const auto& it : g.groups()) {
679 add_group(it, variable_values);
680 }
681 }
683 void copy_user_types(const Group& g);
684
686 void copy_variables(const Group& g, bool variable_values = false);
687
689 Maybe<Dimension> dimension(std::string name) const {
690 auto res = std::make_shared<detail::Path>(detail::Path{std::move(name), -1, false, path});
691 const auto ret = nc_inq_dimid(path->id, res->name.c_str(), &res->id);
692 if (ret != NC_EBADDIM) {
693 check(ret);
694 }
695 return Maybe<Dimension>(std::move(res));
696 }
697
699 std::vector<Dimension> dimensions() const {
700 int count;
701 check(nc_inq_ndims(path->id, &count));
702 if (count == 0) {
703 return {};
704 }
705
706 std::vector<int> ids(count);
707 check(nc_inq_dimids(path->id, &count, detail::data_or_null(ids), 0));
708
709 char name[NC_MAX_NAME + 1];
710 std::vector<Dimension> res;
711 res.reserve(count);
712 std::transform(std::begin(ids), std::end(ids), std::back_inserter(res), [&](int id) {
713 check(nc_inq_dimname(path->id, id, name));
714 return Dimension(std::make_shared<detail::Path>(detail::Path{name, id, false, path}));
715 });
716 return res;
717 }
718
720 Maybe<Group> group(std::string name) const {
721 auto res = std::make_shared<detail::Path>(detail::Path{std::move(name), -1, true, path});
722 const auto ret = nc_inq_grp_ncid(path->id, res->name.c_str(), &res->id);
723 if (ret != NC_ENOGRP) {
724 check(ret);
725 }
726 return Maybe<Group>(std::move(res));
727 }
728
730 std::vector<Group> groups() const {
731 int count;
732 check(nc_inq_grps(path->id, &count, nullptr));
733 if (count == 0) {
734 return {};
735 }
736
737 std::vector<int> ids(count);
738 check(nc_inq_grps(path->id, nullptr, detail::data_or_null(ids)));
739
740 char name[NC_MAX_NAME + 1];
741 std::vector<Group> res;
742 res.reserve(count);
743 std::transform(std::begin(ids), std::end(ids), std::back_inserter(res), [&](int id) {
744 check(nc_inq_grpname(id, name));
745 return Group(std::make_shared<detail::Path>(detail::Path{name, id, true, path}));
746 });
747 return res;
748 }
749
751 void rename(std::string name) {
752 check(nc_rename_grp(path->id, name.c_str()));
753 path->name = std::move(name);
754 }
755
758 if (path->parent) {
759 return Maybe<Group>(path->parent);
760 }
761 return Maybe<Group>(std::make_shared<detail::Path>(detail::Path{"..", -1, true, path}));
762 }
763
765 Maybe<UserType> user_type(std::string name) const {
766 auto res = std::make_shared<detail::Path>(detail::Path{std::move(name), -1, false, path});
767 const auto ret = nc_inq_typeid(path->id, res->name.c_str(), &res->id);
768 if (ret != NC_EBADTYPE) {
769 check(ret);
770 }
771 if (!detail::is_user_type(res->id)) {
772 res->id = -1;
773 }
774 return Maybe<UserType>(std::move(res));
775 }
776
778 std::vector<UserType> user_types() const;
779
781 Maybe<Variable> variable(std::string name) const {
782 auto res = std::make_shared<detail::Path>(detail::Path{std::move(name), -1, false, path});
783 const auto ret = nc_inq_varid(path->id, res->name.c_str(), &res->id);
784 if (ret != NC_ENOTVAR) {
785 check(ret);
786 }
787 return Maybe<Variable>(std::move(res));
788 }
789
791 std::vector<Variable> variables() const;
792};
793
797class File final : public Group {
798 public:
800 File() : Group(std::make_shared<detail::Path>(detail::Path{"", -1, true, nullptr})) {}
801
806 File(std::string filename, char mode) : File() { open(std::move(filename), mode); }
807
809 File(const char* filename, char mode) : File(std::string(filename), mode) {}
810
812 ~File() { close(); }
813
815 void open(std::string filename, char mode) {
816 close();
817 path->name = std::move(filename);
818 switch (mode) {
819 case 'a':
820 check(nc_open(path->name.c_str(), NC_WRITE, &path->id));
821 break;
822 case 'r':
823 check(nc_open(path->name.c_str(), NC_NOWRITE, &path->id));
824 break;
825 case 'w':
826 check(nc_create(path->name.c_str(), NC_NETCDF4 | NC_CLOBBER, &path->id));
827 break;
828 default:
829 throw std::runtime_error("Unknown file mode");
830 }
831 }
832
834 void close() {
835 if (is_open()) {
836 check(nc_close(path->id));
837 path->id = -1;
838 }
839 }
840
842 bool is_open() const { return path->id >= 0; }
843
845 void sync() const { check(nc_sync(path->id)); }
846};
847
849class UserType final : public detail::Object {
850 friend class Group;
851 friend class Maybe<UserType>;
853
854 public:
858 nc_type type;
860 std::string name;
862 std::size_t offset;
864 std::vector<int> dimensions;
865 };
866
867 private:
868 mutable bool fields_read = false;
869 mutable std::size_t size_m = 0;
870 mutable nc_type basetype_m = 0;
871 mutable std::size_t fieldscount_m = 0;
872 mutable int typeclass_m = 0;
873
874 explicit UserType(std::shared_ptr<detail::Path> path_p) : detail::Object(std::move(path_p)) {}
875
876 void read_fields() const {
877 check(nc_inq_user_type(path->parent->id, path->id, nullptr, &size_m, &basetype_m, &fieldscount_m, &typeclass_m));
878 fields_read = true;
879 }
880
881 public:
883 Group parent() const { return Group(path->parent); }
884
885 template<typename T>
887 UserType add_compound_field(const std::string& name, std::size_t offset) {
888 check(nc_insert_compound(path->parent->id, path->id, name.c_str(), offset, Type<T>::id));
889 return *this;
890 }
891
892 template<typename T>
894 UserType add_compound_field_array(const std::string& name, std::size_t offset, const std::vector<int>& dim_sizes) {
895 check(nc_insert_array_compound(path->parent->id, path->id, name.c_str(), offset, Type<typename std::remove_all_extents<T>::type>::id, dim_sizes.size(),
896 detail::data_or_null(dim_sizes)));
897 return *this;
898 }
899
900 template<typename T>
902 UserType add_enum_member(const std::string& name, T v) {
903 check(nc_insert_enum(path->parent->id, path->id, name.c_str(), &v));
904 return *this;
905 }
906
908 std::vector<CompoundField> compound_fields() const {
909 std::vector<CompoundField> res;
910 res.reserve(fieldscount());
911 int dims_count;
912 char name[NC_MAX_NAME + 1];
913 for (int i = 0; i < static_cast<int>(fieldscount()); ++i) {
914 check(nc_inq_compound_fieldndims(path->parent->id, path->id, i, &dims_count));
916 f.dimensions.resize(dims_count);
917 check(nc_inq_compound_field(path->parent->id, path->id, i, name, &f.offset, &f.type, nullptr, detail::data_or_null(f.dimensions)));
918 f.name = name;
919 res.emplace_back(std::move(f));
920 }
921 return res;
922 }
923
924 template<typename T>
926 std::vector<std::pair<std::string, T>> enum_members() const {
927 std::vector<std::pair<std::string, T>> res;
928 res.reserve(fieldscount());
929 char name[NC_MAX_NAME + 1];
930 T v;
931 for (std::size_t i = 0; i < fieldscount(); ++i) {
932 check(nc_inq_enum_member(path->parent->id, path->id, i, name, &v));
933 res.emplace_back(name, v);
934 }
935 return res;
936 }
937
939 nc_type basetype() const {
940 if (!fields_read) {
941 read_fields();
942 }
943 return basetype_m;
944 }
945
947 std::size_t fieldscount() const {
948 if (!fields_read) {
949 read_fields();
950 }
951 return fieldscount_m;
952 }
953
955 std::size_t memberscount() const {
956 if (!fields_read) {
957 read_fields();
958 }
959 return fieldscount_m;
960 }
961
963 std::size_t bytes_size() const {
964 if (!fields_read) {
965 read_fields();
966 }
967 return size_m;
968 }
969
971 int typeclass() const { // NC_VLEN, NC_OPAQUE, NC_ENUM, or NC_COMPOUND
972 if (!fields_read) {
973 read_fields();
974 }
975 return typeclass_m;
976 }
977};
978
979template<typename T>
983 const std::size_t size;
985 const T* data;
986
987 VLenElement() : size(0), data(nullptr) {}
988 ~VLenElement() { std::free(const_cast<T*>(data)); }
989};
990
992class Variable final : public detail::Object {
993 friend class Group;
994 friend class Maybe<Variable>;
995
996 private:
997 explicit Variable(std::shared_ptr<detail::Path> path_p) : detail::Object(std::move(path_p)) {}
998
999 std::vector<int> dimension_ids() const {
1000 const auto count = dimension_count();
1001 if (count == 0) {
1002 return {};
1003 }
1004 std::vector<int> ids(count);
1005 check(nc_inq_vardimid(path->parent->id, path->id, detail::data_or_null(ids)));
1006 return ids;
1007 }
1008
1009 static std::string type_name(int ncid_p, nc_type type_p) {
1010 char name[NC_MAX_NAME + 1];
1011 if (nc_inq_type(ncid_p, type_p, name, nullptr) != NC_NOERR) {
1012 return "";
1013 }
1014 return name;
1015 }
1016
1017 static bool same_type_for_copy(int this_ncid, nc_type this_type, int oth_ncid, nc_type oth_type) {
1018 const auto this_is_user = detail::is_user_type(this_type);
1019 const auto oth_is_user = detail::is_user_type(oth_type);
1020 if (this_is_user != oth_is_user) {
1021 return false;
1022 }
1023 if (!this_is_user) {
1024 return this_type == oth_type;
1025 }
1026 return type_name(this_ncid, this_type) == type_name(oth_ncid, oth_type);
1027 }
1028
1029 std::size_t size(const std::size_t* /* start */, const std::size_t* count) const {
1030 int dims_count;
1031 check(nc_inq_varndims(path->parent->id, path->id, &dims_count));
1032
1033 std::size_t res = 1;
1034 for (int i = 0; i < dims_count; ++i) {
1035 res *= count[i];
1036 }
1037 return res;
1038 }
1039
1040 public:
1042 Attribute add_attribute(std::string name) { return Attribute(std::make_shared<detail::Path>(detail::Path{std::move(name), -1, false, path})); }
1043
1046 auto res = add_attribute(a.name());
1047 res.copy_values(a);
1048 return res;
1049 }
1050
1052 Maybe<Attribute> attribute(std::string name) const {
1053 auto res = std::make_shared<detail::Path>(detail::Path{std::move(name), -1, false, path});
1054 const auto ret = nc_inq_attid(path->parent->id, path->id, res->name.c_str(), &res->id);
1055 if (ret != NC_ENOTATT) {
1056 check(ret);
1057 }
1058 return Maybe<Attribute>(std::move(res));
1059 }
1060
1062 std::vector<Attribute> attributes() const {
1063 int count;
1064 check(nc_inq_varnatts(path->parent->id, path->id, &count));
1065 std::vector<Attribute> res;
1066 res.reserve(count);
1067 char name[NC_MAX_NAME + 1];
1068 for (int id = 0; id < count; ++id) {
1069 check(nc_inq_attname(path->parent->id, path->id, id, name));
1070 res.emplace_back(Attribute{std::make_shared<detail::Path>(detail::Path{std::string(name), id, false, path})});
1071 }
1072 return res;
1073 }
1074
1076 bool check_dimensions(const std::vector<std::string>& names) const {
1077 const auto& dims = dimensions();
1078 if (dims.size() != names.size()) {
1079 return false;
1080 }
1081 for (std::size_t i = 0; i < names.size(); ++i) {
1082 if (dims[i].name() != names[i]) {
1083 return false;
1084 }
1085 }
1086 return true;
1087 }
1088
1090 Variable require_dimensions(const std::vector<std::string>& names) const {
1091 if (!check_dimensions(names)) {
1092 throw Exception(NC_EVARMETA, "Unexpected dimensions: " + path->get_full_path());
1093 }
1094 return *this;
1095 }
1096
1098 void copy_attributes(const Variable& v) {
1099 for (const auto& it : v.attributes()) {
1100 add_attribute(it);
1101 }
1102 }
1103
1105 void copy_values(const Variable& v) {
1106 const auto this_type = type();
1107 const auto oth_type = v.type();
1108 if (!same_type_for_copy(path->parent->id, this_type, v.path->parent->id, oth_type)) {
1109 throw Exception(NC_EVARMETA, "Variable types do not match: " + path->get_full_path() + " and " + v.path->get_full_path());
1110 }
1111
1112 std::size_t this_type_len;
1113 check(nc_inq_type(path->parent->id, this_type, nullptr, &this_type_len));
1114 std::size_t oth_type_len;
1115 check(nc_inq_type(v.path->parent->id, oth_type, nullptr, &oth_type_len));
1116
1117 if (this_type_len != oth_type_len) {
1118 throw Exception(NC_EVARMETA, "Variable type sizes do not match: " + path->get_full_path() + " and " + v.path->get_full_path());
1119 }
1120
1121 const auto this_sizes = sizes();
1122 const auto oth_sizes = v.sizes();
1123 if (this_sizes.size() != oth_sizes.size()) {
1124 throw Exception(NC_EVARMETA, "Variable dimension counts do not match: " + path->get_full_path() + " and " + v.path->get_full_path());
1125 }
1126 for (std::size_t i = 0; i < oth_sizes.size(); ++i) {
1127 if (this_sizes[i] != 0 // not unlimited dimension or already written to
1128 && this_sizes[i] != oth_sizes[i]) {
1129 throw Exception(NC_EVARMETA, "Variable sizes do not match: " + path->get_full_path() + " and " + v.path->get_full_path());
1130 }
1131 }
1132
1133 std::vector<std::size_t> index(this_sizes.size(), 0);
1134
1135 std::vector<char> buf(this_type_len * v.size());
1136 if (this_sizes.empty()) {
1137 check(nc_get_var(v.path->parent->id, v.path->id, detail::data_or_null(buf)));
1138 check(nc_put_var(path->parent->id, path->id, detail::data_or_null(buf)));
1139 } else {
1140 check(nc_get_vara(v.path->parent->id, v.path->id, detail::data_or_null(index), detail::data_or_null(oth_sizes), detail::data_or_null(buf)));
1141 check(nc_put_vara(path->parent->id, path->id, detail::data_or_null(index), detail::data_or_null(oth_sizes), detail::data_or_null(buf)));
1142 }
1143 }
1144
1146 std::size_t dimension_count() const {
1147 int count;
1148 check(nc_inq_varndims(path->parent->id, path->id, &count));
1149 return count;
1150 }
1151
1153 std::vector<Dimension> dimensions() const {
1154 const auto ids = dimension_ids();
1155 char name[NC_MAX_NAME + 1];
1156 std::vector<Dimension> res;
1157 res.reserve(ids.size());
1158 std::transform(std::begin(ids), std::end(ids), std::back_inserter(res), [&](int id) {
1159 check(nc_inq_dimname(path->parent->id, id, name));
1160 return Dimension(std::make_shared<detail::Path>(detail::Path{name, id, false, path->parent}));
1161 });
1162 return res;
1163 }
1164
1166 std::vector<std::size_t> get_chunking() const {
1167 int mode;
1168 std::vector<std::size_t> res(dimension_count());
1169 check(nc_inq_var_chunking(path->parent->id, path->id, &mode, detail::data_or_null(res)));
1170 if (mode == NC_CONTIGUOUS) {
1171 res.clear();
1172 }
1173 return res;
1174 }
1175
1177 void set_chunking(const std::vector<std::size_t>& chunks) {
1178 if (chunks.empty()) {
1179 check(nc_def_var_chunking(path->parent->id, path->id, NC_CONTIGUOUS, nullptr));
1180 } else {
1181 check(nc_def_var_chunking(path->parent->id, path->id, NC_CHUNKED, detail::data_or_null(chunks)));
1182 }
1183 }
1184
1186 void set_default_chunking() { check(nc_def_var_chunking(path->parent->id, path->id, NC_CHUNKED, nullptr)); }
1187
1189 std::pair<bool, int> get_compression() const {
1190 int shuffle_filter;
1191 int deflate_filter;
1192 int deflate_level;
1193 check(nc_inq_var_deflate(path->parent->id, path->id, &shuffle_filter, &deflate_filter, &deflate_level));
1194 return std::make_pair(shuffle_filter, deflate_filter ? deflate_level : -1);
1195 }
1196
1198 void set_compression(bool shuffle_filter, int deflate_level) {
1199 check(nc_def_var_deflate(path->parent->id, path->id, shuffle_filter, deflate_level < 0 ? 0 : 1, deflate_level));
1200 }
1201
1203 int get_endianness() const {
1204 int res;
1205 check(nc_inq_var_endian(path->parent->id, path->id, &res));
1206 return res;
1207 }
1208
1210 void set_endianness(int endianness) { check(nc_def_var_endian(path->parent->id, path->id, endianness)); }
1211
1214 int res;
1215 check(nc_inq_var_fletcher32(path->parent->id, path->id, &res));
1216 return res == NC_FLETCHER32;
1217 }
1218
1220 void set_checksum_enabled(bool v) { check(nc_def_var_fletcher32(path->parent->id, path->id, v ? NC_FLETCHER32 : NC_NOCHECKSUM)); }
1221
1222 template<typename T>
1224 std::pair<bool, T> get_fill() const {
1225 int no_fill;
1226 T res;
1227 check(nc_inq_var_fill(path->parent->id, path->id, &no_fill, &res));
1228 return std::make_pair(!no_fill, res);
1229 }
1230
1231 template<typename T>
1233 void set_fill(T v) {
1234 check(nc_def_var_fill(path->parent->id, path->id, 0, &v));
1235 }
1236
1238 void unset_fill() { check(nc_def_var_fill(path->parent->id, path->id, 1, nullptr)); }
1239
1241 Group parent() const { return Group(path->parent); }
1242
1244 void rename(std::string name) {
1245 check(nc_rename_var(path->parent->id, path->id, name.c_str()));
1246 path->name = std::move(name);
1247 }
1248
1250 std::vector<std::size_t> sizes() const {
1251 std::vector<std::size_t> res;
1252 const auto dims = dimension_ids();
1253 res.reserve(dims.size());
1254 std::transform(std::begin(dims), std::end(dims), std::back_inserter(res), [this](int id) {
1255 std::size_t tmp;
1256 check(nc_inq_dimlen(path->parent->id, id, &tmp));
1257 return tmp;
1258 });
1259 return res;
1260 }
1261
1263 std::size_t size() const {
1264 std::size_t res = 1;
1265 std::size_t tmp;
1266 for (const auto id : dimension_ids()) {
1267 check(nc_inq_dimlen(path->parent->id, id, &tmp));
1268 res *= tmp;
1269 }
1270 return res;
1271 }
1272
1273 template<int N>
1275 std::size_t size(const std::array<std::size_t, N>& start, const std::array<std::size_t, N>& count) const {
1276 return size(&start[0], &count[0]);
1277 }
1278
1280 Variable require_size(std::size_t size_p) const {
1281 if (size_p != size()) {
1282 throw Exception(NC_EVARMETA, "Unexpected variable size: " + path->get_full_path());
1283 }
1284 return *this;
1285 }
1286
1287 template<typename T>
1289 std::vector<T> get() const {
1290 std::vector<T> res(size());
1291 if (res.empty()) {
1292 return res;
1293 }
1295 return res;
1296 }
1297 template<typename T>
1299 T get(const std::size_t* index) const {
1300 T res;
1301 read(&res, index);
1302 return res;
1303 }
1304 template<typename T>
1306 std::vector<T> get(const std::size_t* start, const std::size_t* count) const {
1307 std::vector<T> res(size(start, count));
1308 if (res.empty()) {
1309 return res;
1310 }
1311 read(detail::data_or_null(res), start, count);
1312 return res;
1313 }
1314 template<typename T>
1316 std::vector<T> get(const std::size_t* start, const std::size_t* count, const std::ptrdiff_t* stride) const {
1317 std::vector<T> res(size(start, count));
1318 if (res.empty()) {
1319 return res;
1320 }
1321 read(detail::data_or_null(res), start, count, stride);
1322 return res;
1323 }
1324
1325 template<typename T, int N>
1327 T get(const std::array<std::size_t, N>& index) const {
1328 return get<T>(&index[0]);
1329 }
1330 template<typename T, int N>
1332 std::vector<T> get(const std::array<std::size_t, N>& start, const std::array<std::size_t, N>& count) const {
1333 return get<T>(&start[0], &count[0]);
1334 }
1335 template<typename T, int N>
1337 std::vector<T> get(const std::array<std::size_t, N>& start, const std::array<std::size_t, N>& count, const std::array<std::ptrdiff_t, N>& stride) const {
1338 return get<T>(&start[0], &count[0], &stride[0]);
1339 }
1340
1341 template<typename T>
1343 void read(T* v) const {
1344 static_assert(!Type<T>::is_atomic || std::is_same<void, T>::value, "Use void or one of the explicitly supported types");
1345 check(nc_get_var(path->parent->id, path->id, v));
1346 }
1347 template<typename T>
1349 void read(T* v, const std::size_t* index) const {
1350 static_assert(!Type<T>::is_atomic || std::is_same<void, T>::value, "Use void or one of the explicitly supported types");
1351 check(nc_get_var1(path->parent->id, path->id, index, v));
1352 }
1353 template<typename T>
1355 void read(T* v, const std::size_t* start, const std::size_t* count) const {
1356 static_assert(!Type<T>::is_atomic || std::is_same<void, T>::value, "Use void or one of the explicitly supported types");
1357 check(nc_get_vara(path->parent->id, path->id, start, count, v));
1358 }
1359 template<typename T>
1361 void read(T* v, const std::size_t* start, const std::size_t* count, const std::ptrdiff_t* stride) const {
1362 static_assert(!Type<T>::is_atomic || std::is_same<void, T>::value, "Use void or one of the explicitly supported types");
1363 check(nc_get_vars(path->parent->id, path->id, start, count, stride, v));
1364 }
1365 template<typename T>
1367 void read(T* v, const std::size_t* start, const std::size_t* count, const std::ptrdiff_t* stride, const std::ptrdiff_t* imap) const {
1368 static_assert(std::is_same<void, T>::value, "NetCDF supports mapped access only for atomic types");
1369 check(nc_get_varm(path->parent->id, path->id, start, count, stride, imap, v));
1370 }
1371
1372 template<typename T, int N>
1374 void read(T* v, const std::array<std::size_t, N>& index) const {
1375 read(v, &index[0]);
1376 }
1377 template<typename T, int N>
1379 void read(T* v, const std::array<std::size_t, N>& start, const std::array<std::size_t, N>& count) const {
1380 read(v, &start[0], &count[0]);
1381 }
1382 template<typename T, int N>
1384 void read(T* v, const std::array<std::size_t, N>& start, const std::array<std::size_t, N>& count, const std::array<std::ptrdiff_t, N>& stride) const {
1385 read(v, &start[0], &count[0], &stride[0]);
1386 }
1387 template<typename T, int N>
1389 void read(T* v,
1390 const std::array<std::size_t, N>& start,
1391 const std::array<std::size_t, N>& count,
1392 const std::array<std::ptrdiff_t, N>& stride,
1393 const std::array<std::ptrdiff_t, N>& imap) const {
1394 read(v, &start[0], &count[0], &stride[0], &imap[0]);
1395 }
1396
1397 template<typename T>
1399 void set(const std::vector<T>& v) {
1401 }
1402 template<typename T>
1404 typename std::enable_if<Type<T>::is_atomic || std::is_same<const char*, T>::value || std::is_same<char*, T>::value, void>::type set(
1405 T v, const std::size_t* index) {
1406 write(&v, index);
1407 }
1408 template<typename T>
1410 typename std::enable_if<!Type<T>::is_atomic && !std::is_same<const char*, T>::value && !std::is_same<char*, T>::value, void>::type set(
1411 const T& v, const std::size_t* index) {
1412 write(&v, index);
1413 }
1414 template<typename T>
1416 void set(const std::vector<T>& v, const std::size_t* start, const std::size_t* count) {
1417 write(detail::data_or_null(v), start, count);
1418 }
1419 template<typename T>
1421 void set(const std::vector<T>& v, const std::size_t* start, const std::size_t* count, const std::ptrdiff_t* stride) {
1422 write(detail::data_or_null(v), start, count, stride);
1423 }
1424 template<typename T>
1426 void set(const std::vector<T>& v, const std::size_t* start, const std::size_t* count, const std::ptrdiff_t* stride, const std::ptrdiff_t* imap) {
1427 write(detail::data_or_null(v), start, count, stride, imap);
1428 }
1429
1430 template<typename T, int N>
1432 void set(const T v, const std::array<std::size_t, N>& index) {
1433 set(v, &index[0]);
1434 }
1435 template<typename T, int N>
1437 void set(const std::vector<T>& v, const std::array<std::size_t, N>& start, const std::array<std::size_t, N>& count) {
1438 set(v, &start[0], &count[0]);
1439 }
1440 template<typename T, int N>
1442 void set(const std::vector<T>& v,
1443 const std::array<std::size_t, N>& start,
1444 const std::array<std::size_t, N>& count,
1445 const std::array<std::ptrdiff_t, N>& stride) {
1446 set(v, &start[0], &count[0], &stride[0]);
1447 }
1448 template<typename T, int N>
1450 void set(const std::vector<T>& v,
1451 const std::array<std::size_t, N>& start,
1452 const std::array<std::size_t, N>& count,
1453 const std::array<std::ptrdiff_t, N>& stride,
1454 const std::array<std::ptrdiff_t, N>& imap) {
1455 set(v, &start[0], &count[0], &stride[0], &imap[0]);
1456 }
1457
1458 template<typename T>
1460 void write(const T* v) {
1461 static_assert(!Type<T>::is_atomic || std::is_same<void, T>::value, "Use void or one of the explicitly supported types");
1462 check(nc_put_var(path->parent->id, path->id, v));
1463 }
1464 template<typename T>
1466 void write(const T* v, const std::size_t* index) {
1467 static_assert(!Type<T>::is_atomic || std::is_same<void, T>::value, "Use void or one of the explicitly supported types");
1468 check(nc_put_var1(path->parent->id, path->id, index, v));
1469 }
1470 template<typename T>
1472 void write(const T* v, const std::size_t* start, const std::size_t* count) {
1473 static_assert(!Type<T>::is_atomic || std::is_same<void, T>::value, "Use void or one of the explicitly supported types");
1474 check(nc_put_vara(path->parent->id, path->id, start, count, v));
1475 }
1476 template<typename T>
1478 void write(const T* v, const std::size_t* start, const std::size_t* count, const std::ptrdiff_t* stride) {
1479 static_assert(!Type<T>::is_atomic || std::is_same<void, T>::value, "Use void or one of the explicitly supported types");
1480 check(nc_put_vars(path->parent->id, path->id, start, count, stride, v));
1481 }
1482 template<typename T>
1484 void write(const T* v, const std::size_t* start, const std::size_t* count, const std::ptrdiff_t* stride, const std::ptrdiff_t* imap) {
1485 static_assert(std::is_same<void, T>::value, "NetCDF supports mapped access only for atomic types");
1486 check(nc_put_varm(path->parent->id, path->id, start, count, stride, imap, v));
1487 }
1488
1489 template<typename T, int N>
1491 void write(const T* v, const std::array<std::size_t, N>& index) {
1492 write(v, &index[0]);
1493 }
1494 template<typename T, int N>
1496 void write(const T* v, const std::array<std::size_t, N>& start, const std::array<std::size_t, N>& count) {
1497 write(v, &start[0], &count[0]);
1498 }
1499 template<typename T, int N>
1501 void write(const T* v, const std::array<std::size_t, N>& start, const std::array<std::size_t, N>& count, const std::array<std::ptrdiff_t, N>& stride) {
1502 write(v, &start[0], &count[0], &stride[0]);
1503 }
1504 template<typename T, int N>
1506 void write(const T* v,
1507 const std::array<std::size_t, N>& start,
1508 const std::array<std::size_t, N>& count,
1509 const std::array<std::ptrdiff_t, N>& stride,
1510 const std::array<std::ptrdiff_t, N>& imap) {
1511 write(v, &start[0], &count[0], &stride[0], &imap[0]);
1512 }
1513
1515 nc_type type() const {
1516 nc_type res;
1517 check(nc_inq_vartype(path->parent->id, path->id, &res));
1518 return res;
1519 }
1520
1522 std::string type_name() const {
1523 char name[NC_MAX_NAME + 1];
1524 check(nc_inq_type(path->parent->id, type(), name, nullptr));
1525 return name;
1526 }
1527
1529 Variable require_type(const std::string& name) const {
1530 const auto name_l = type_name();
1531 if (name_l != name) {
1532 throw Exception(NC_EVARMETA, "Unexpected type '" + name_l + "': " + path->get_full_path());
1533 }
1534 return *this;
1535 }
1536
1537 template<typename T>
1539 Variable require_compound(std::size_t fieldscount) const {
1540 const auto user_type_l = user_type().require();
1541 if (user_type_l.typeclass() != NC_COMPOUND) {
1542 throw Exception(NC_EVARMETA, "Type '" + user_type_l.name() + "' is not a compound: " + path->get_full_path());
1543 }
1544 if (user_type_l.bytes_size() != sizeof(T) || user_type_l.fieldscount() != fieldscount) {
1545 throw Exception(NC_EVARMETA, "Unexpected size for type '" + user_type_l.name() + "': " + path->get_full_path());
1546 }
1547 return *this;
1548 }
1549
1552 const auto id = type();
1553 char name[NC_MAX_NAME + 1];
1554 check(nc_inq_type(path->parent->id, id, name, nullptr));
1555 auto res = std::make_shared<detail::Path>(detail::Path{name, id, false, path->parent});
1556 if (!detail::is_user_type(res->id)) {
1557 res->id = -1;
1558 res->name = path->name + " not of user type";
1559 }
1560 return Maybe<UserType>(std::move(res));
1561 }
1562};
1563
1564inline void Attribute::copy_values(const Attribute& a) {
1565 auto type_l = a.type();
1566 std::size_t type_len;
1567 char name[NC_MAX_NAME + 1];
1568 check(nc_inq_type(a.ncid(), type_l, name, &type_len));
1569
1570 if (detail::is_user_type(type_l) && ncid() != a.ncid()) {
1571 const auto this_type = ([this, name]() {
1572 if (is_group_attribute()) {
1573 return parent_group().require().user_type(name).require();
1574 }
1575 return parent_variable().require().parent().user_type(name).require();
1576 })();
1577 type_l = this_type.id();
1578 if (this_type.bytes_size() != type_len) {
1579 throw Exception(NC_EATTMETA, "Attribute type sizes do not match: " + path->get_full_path() + " and " + a.path->get_full_path());
1580 }
1581 }
1582
1583 const auto oth_size = a.size();
1584 std::vector<char> buf(type_len * oth_size);
1585 if (!buf.empty()) {
1586 check(nc_get_att(a.ncid(), a.othid(), a.name().c_str(), detail::data_or_null(buf)));
1587 }
1588 check(nc_put_att(ncid(), othid(), path->name.c_str(), type_l, oth_size, detail::data_or_null(buf)));
1589}
1590
1591template<>
1592inline std::vector<std::string> Attribute::get() const {
1593 std::size_t len;
1594 check(nc_inq_attlen(ncid(), othid(), path->name.c_str(), &len));
1595 if (len == 0) {
1596 return {};
1597 }
1598 char** buf = static_cast<char**>(std::malloc(len * sizeof(char*)));
1599 check(nc_get_att_string(ncid(), othid(), path->name.c_str(), buf));
1600 return detail::process_char_vector(buf, len);
1601}
1602
1603template<>
1604inline void Attribute::set(const std::vector<std::string>& v) {
1605 auto buf = detail::process_string_vector(v);
1606 check(nc_put_att_string(ncid(), othid(), path->name.c_str(), buf.size(), detail::data_or_null(buf)));
1607}
1608
1609template<>
1610inline void Attribute::set(const std::vector<const char*>& v) {
1611 check(nc_put_att_string(ncid(), othid(), path->name.c_str(), v.size(), const_cast<const char**>(detail::data_or_null(v))));
1612}
1613
1614template<>
1615inline void Attribute::set(const std::vector<char*>& v) {
1616 check(nc_put_att_string(ncid(), othid(), path->name.c_str(), v.size(), const_cast<const char**>(detail::data_or_null(v))));
1617}
1618
1619template<typename T>
1620inline void Attribute::set(const std::vector<T>& v, const UserType& type) {
1621 static_assert(!Type<T>::is_atomic, "Should be of user type");
1622 check(nc_put_att(ncid(), othid(), path->name.c_str(), type.id(), v.size(), detail::data_or_null(v)));
1623}
1624template<typename T>
1625inline void Attribute::set(const T& v, const UserType& type) {
1626 static_assert(!Type<T>::is_atomic, "Should be of user type");
1627 check(nc_put_att(ncid(), othid(), path->name.c_str(), type.id(), 1, &v));
1628}
1629
1631 const auto id = type();
1632 char name[NC_MAX_NAME + 1];
1633 check(nc_inq_type(path->parent->id, id, name, nullptr));
1634 auto res = std::make_shared<detail::Path>(detail::Path{name, id, false, path->parent});
1635 if (!detail::is_user_type(res->id)) {
1636 res->id = -1;
1637 res->name = path->name + " not of user type";
1638 }
1639 return Maybe<UserType>(std::move(res));
1640}
1641
1642inline Group Dimension::parent() const { return Group(path->parent); }
1643
1644inline UserType Group::add_type_compound(std::string name, std::size_t bytes_size) {
1645 int id;
1646 check(nc_def_compound(path->id, bytes_size, name.c_str(), &id));
1647 return UserType(std::make_shared<detail::Path>(detail::Path{std::move(name), id, false, path}));
1648}
1649template<typename T>
1650inline UserType Group::add_type_compound(std::string name) {
1651 return add_type_compound(std::move(name), sizeof(T));
1652}
1653
1654template<typename T>
1655inline UserType Group::add_type_enum(std::string name) {
1656 static_assert(std::is_enum<T>::value, "should be an enum class");
1657 return add_type_enum(std::move(name), Type<typename std::underlying_type<T>::type>::id);
1658}
1659inline UserType Group::add_type_enum(std::string name, nc_type basetype) {
1660 int id;
1661 check(nc_def_enum(path->id, basetype, name.c_str(), &id));
1662 return UserType(std::make_shared<detail::Path>(detail::Path{std::move(name), id, false, path}));
1663}
1664
1665inline UserType Group::add_type_opaque(std::string name, std::size_t bytes_size) {
1666 int id;
1667 check(nc_def_opaque(path->id, bytes_size, name.c_str(), &id));
1668 return UserType(std::make_shared<detail::Path>(detail::Path{std::move(name), id, false, path}));
1669}
1670
1671template<typename T>
1672inline UserType Group::add_type_vlen(std::string name) {
1673 return add_type_vlen(std::move(name), Type<T>::id);
1674}
1675inline UserType Group::add_type_vlen(std::string name, nc_type basetype) {
1676 int id;
1677 check(nc_def_vlen(path->id, name.c_str(), basetype, &id));
1678 return UserType(std::make_shared<detail::Path>(detail::Path{std::move(name), id, false, path}));
1679}
1680
1682 switch (t.typeclass()) {
1683 case NC_VLEN:
1684 return add_type_vlen(t.name(), t.basetype());
1685 case NC_OPAQUE:
1686 return add_type_opaque(t.name(), t.bytes_size());
1687 case NC_ENUM: {
1688 auto res = add_type_enum(t.name(), t.basetype());
1689 std::vector<char> buf(t.bytes_size());
1690 char name[NC_MAX_NAME + 1];
1691 for (int i = 0; i < static_cast<int>(t.fieldscount()); ++i) {
1692 check(nc_inq_enum_member(t.path->parent->id, t.id(), i, name, &buf[0]));
1693 check(nc_insert_enum(path->id, res.id(), name, &buf[0]));
1694 }
1695 return res;
1696 }
1697 case NC_COMPOUND: {
1698 auto res = add_type_compound(t.name(), t.bytes_size());
1699 for (const auto& field : t.compound_fields()) {
1700 if (field.dimensions.empty()) {
1701 check(nc_insert_compound(path->id, res.id(), field.name.c_str(), field.offset, field.type));
1702 } else {
1703 check(nc_insert_array_compound(path->id, res.id(), field.name.c_str(), field.offset, field.type, static_cast<int>(field.dimensions.size()),
1704 detail::data_or_null(field.dimensions)));
1705 }
1706 }
1707 return res;
1708 }
1709 default:
1710 throw Exception(0, "Invalid user type class: " + t.path->get_full_path());
1711 }
1712}
1713
1714inline void Group::copy_user_types(const Group& g) {
1715 for (const auto& it : g.user_types()) {
1716 add_user_type(it);
1717 }
1718}
1719
1720inline void Group::copy_variables(const Group& g, bool variable_values) {
1721 for (const auto& it : g.variables()) {
1722 add_variable(it, variable_values);
1723 }
1724}
1725
1726inline Variable Group::add_variable(std::string name, const UserType& type, const std::vector<int>& dims) {
1727 return add_variable(std::move(name), type.id(), dims);
1728}
1729inline Variable Group::add_variable(std::string name, const UserType& type, const std::vector<Dimension>& dims) {
1730 return add_variable(std::move(name), type.id(), dims);
1731}
1732inline Variable Group::add_variable(std::string name, const UserType& type, const std::vector<std::string>& dims) {
1733 return add_variable(std::move(name), type.id(), dims);
1734}
1735
1736template<typename T>
1737inline Variable Group::add_dimension_variable(std::string name) {
1738 return add_dimension_variable<T>(add_dimension(std::move(name)));
1739}
1740template<typename T>
1741inline Variable Group::add_dimension_variable(std::string name, std::size_t len) {
1742 return add_dimension_variable<T>(add_dimension(std::move(name), len));
1743}
1744template<typename T>
1746 return add_variable<T>(d.name(), {d});
1747}
1748
1749inline Variable Group::add_variable(std::string name, nc_type type, const std::vector<int>& dims) {
1750 int id;
1751 check(nc_def_var(path->id, name.c_str(), type, static_cast<int>(dims.size()), detail::data_or_null(dims), &id));
1752 return Variable(std::make_shared<detail::Path>(detail::Path{std::move(name), id, false, path}));
1753}
1754inline Variable Group::add_variable(std::string name, nc_type type, const std::vector<Dimension>& dims) {
1755 std::vector<int> dimids(dims.size());
1756 std::transform(std::begin(dims), std::end(dims), std::begin(dimids), [](const Dimension& d) { return d.path->id; });
1757 return add_variable(std::move(name), type, dimids);
1758}
1759inline Variable Group::add_variable(std::string name, nc_type type, const std::vector<std::string>& dims) {
1760 std::vector<int> dimids(dims.size());
1761 std::transform(std::begin(dims), std::end(dims), std::begin(dimids), [this](const std::string& s) { return dimension(s).require().path->id; });
1762 return add_variable(std::move(name), type, dimids);
1763}
1764template<typename T>
1765inline Variable Group::add_variable(std::string name, const std::vector<int>& dims) {
1766 return add_variable(std::move(name), Type<T>::id, dims);
1767}
1768template<typename T>
1769inline Variable Group::add_variable(std::string name, const std::vector<Dimension>& dims) {
1770 return add_variable(std::move(name), Type<T>::id, dims);
1771}
1772template<typename T>
1773inline Variable Group::add_variable(std::string name, const std::vector<std::string>& dims) {
1774 return add_variable(std::move(name), Type<T>::id, dims);
1775}
1776template<>
1777inline Variable Group::add_variable<std::string>(std::string name, const std::vector<int>& dims) {
1778 return add_variable(std::move(name), Type<char*>::id, dims);
1779}
1780template<>
1781inline Variable Group::add_variable<std::string>(std::string name, const std::vector<Dimension>& dims) {
1782 return add_variable(std::move(name), Type<char*>::id, dims);
1783}
1784template<>
1785inline Variable Group::add_variable<std::string>(std::string name, const std::vector<std::string>& dims) {
1786 return add_variable(std::move(name), Type<char*>::id, dims);
1787}
1788inline Variable Group::add_variable(const Variable& v, bool with_values) {
1789 const auto orig_dims = v.dimensions();
1790 std::vector<std::string> dims;
1791 dims.reserve(orig_dims.size());
1792 std::transform(std::begin(orig_dims), std::end(orig_dims), std::back_inserter(dims), [](const Dimension& d) { return d.name(); });
1793 auto type = v.type();
1794 if (detail::is_user_type(type) && v.path->parent->id != path->id) {
1795 type = user_type(v.user_type().require().name()).require().id();
1796 }
1797 auto res = add_variable(v.name(), type, dims);
1798 res.copy_attributes(v);
1799
1800 const auto comp = v.get_compression();
1801 res.set_compression(comp.first, comp.second);
1802
1803 res.set_chunking(v.get_chunking());
1804 if (v.get_checksum_enabled()) {
1805 res.set_checksum_enabled(true);
1806 } // otherwise chunking might get reset
1807
1808 if (!detail::is_user_type(type)) {
1809 // fill values are handled weirdly by NetCDF
1810 // _FillValue has been copied via copy_attributes already
1811 // _NoFill seems to only properly work for fundamental types
1812 int no_fill;
1813 check(nc_inq_var_fill(v.path->parent->id, v.path->id, &no_fill, nullptr));
1814 if (no_fill) {
1815 check(nc_def_var_fill(res.path->parent->id, res.path->id, 1, nullptr));
1816 }
1817 }
1818
1819 if (with_values) {
1820 res.copy_values(v);
1821 }
1822 return res;
1823}
1824
1825inline std::vector<UserType> Group::user_types() const {
1826 int count;
1827 check(nc_inq_typeids(path->id, &count, nullptr));
1828 if (count == 0) {
1829 return {};
1830 }
1831
1832 std::vector<int> ids(count);
1833 check(nc_inq_typeids(path->id, nullptr, detail::data_or_null(ids)));
1834
1835 char name[NC_MAX_NAME + 1];
1836 std::vector<UserType> res;
1837 for (const auto id : ids) {
1838 if (detail::is_user_type(id)) {
1839 check(nc_inq_type(path->id, id, name, nullptr));
1840 res.emplace_back(UserType(std::make_shared<detail::Path>(detail::Path{name, id, false, path})));
1841 }
1842 }
1843 return res;
1844}
1845
1846inline std::vector<Variable> Group::variables() const {
1847 int count;
1848 check(nc_inq_varids(path->id, &count, nullptr));
1849 if (count == 0) {
1850 return {};
1851 }
1852
1853 std::vector<int> ids(count);
1854 check(nc_inq_varids(path->id, nullptr, detail::data_or_null(ids)));
1855
1856 char name[NC_MAX_NAME + 1];
1857 std::vector<Variable> res;
1858 res.reserve(count);
1859 std::transform(std::begin(ids), std::end(ids), std::back_inserter(res), [&](int id) {
1860 check(nc_inq_varname(path->id, id, name));
1861 return Variable(std::make_shared<detail::Path>(detail::Path{name, id, false, path}));
1862 });
1863 return res;
1864}
1865
1866template<>
1867inline std::vector<std::string> Variable::get() const {
1868 std::vector<char*> buf(size());
1869 if (buf.empty()) {
1870 return {};
1871 }
1872 check(nc_get_var_string(path->parent->id, path->id, detail::data_or_null(buf)));
1873 return detail::process_char_vector(buf);
1874}
1875template<>
1876inline std::string Variable::get(const std::size_t* index) const {
1877 char* buf;
1878 check(nc_get_var1_string(path->parent->id, path->id, index, &buf));
1879 std::string res(buf);
1880 nc_free_string(1, &buf);
1881 return res;
1882}
1883template<>
1884inline std::vector<std::string> Variable::get(const std::size_t* start, const std::size_t* count) const {
1885 std::vector<char*> buf(size(start, count));
1886 if (buf.empty()) {
1887 return {};
1888 }
1889 check(nc_get_vara_string(path->parent->id, path->id, start, count, detail::data_or_null(buf)));
1890 return detail::process_char_vector(buf);
1891}
1892template<>
1893inline std::vector<std::string> Variable::get(const std::size_t* start, const std::size_t* count, const std::ptrdiff_t* stride) const {
1894 std::vector<char*> buf(size(start, count));
1895 if (buf.empty()) {
1896 return {};
1897 }
1898 check(nc_get_vars_string(path->parent->id, path->id, start, count, stride, detail::data_or_null(buf)));
1899 return detail::process_char_vector(buf);
1900}
1901
1902template<>
1903inline void Variable::set(const std::vector<std::string>& v) {
1904 auto buf = detail::process_string_vector(v);
1905 check(nc_put_var_string(path->parent->id, path->id, detail::data_or_null(buf)));
1906}
1907template<>
1908inline void Variable::set(const std::string& v, const std::size_t* index) {
1909 const char* t = v.c_str();
1910 check(nc_put_var1_string(path->parent->id, path->id, index, &t));
1911}
1912template<>
1913inline void Variable::set(const char* v, const std::size_t* index) {
1914 check(nc_put_var1_string(path->parent->id, path->id, index, &v));
1915}
1916template<>
1917inline void Variable::set(const std::vector<std::string>& v, const std::size_t* start, const std::size_t* count) {
1918 auto buf = detail::process_string_vector(v);
1919 check(nc_put_vara_string(path->parent->id, path->id, start, count, detail::data_or_null(buf)));
1920}
1921template<>
1922inline void Variable::set(const std::vector<std::string>& v, const std::size_t* start, const std::size_t* count, const std::ptrdiff_t* stride) {
1923 auto buf = detail::process_string_vector(v);
1924 check(nc_put_vars_string(path->parent->id, path->id, start, count, stride, detail::data_or_null(buf)));
1925}
1926template<>
1927inline void Variable::set(
1928 const std::vector<std::string>& v, const std::size_t* start, const std::size_t* count, const std::ptrdiff_t* stride, const std::ptrdiff_t* imap) {
1929 auto buf = detail::process_string_vector(v);
1930 check(nc_put_varm_string(path->parent->id, path->id, start, count, stride, imap, detail::data_or_null(buf)));
1931}
1932
1933#define NETCDFPP_IMPL_ATTRIBUTE_GET(type, name) \
1934 template<> \
1935 inline int Attribute::get_internal(int ncid_p, int othid_p, const char* name_p, type* v) const { \
1936 return nc_get_att##name(ncid_p, othid_p, name_p, v); \
1937 }
1938
1939#define NETCDFPP_IMPL_ATTRIBUTE_SET(type, name) \
1940 template<> \
1941 inline int Attribute::set_internal(int ncid_p, int othid_p, const char* name_p, std::size_t len, const type* v) { \
1942 return nc_put_att##name(ncid_p, othid_p, name_p, Type<type>::id, len, v); \
1943 }
1944
1945#define NETCDFPP_IMPL_VARIABLE_READ(type, name) \
1946 template<> \
1947 inline void Variable::read(type* v) const { \
1948 check(nc_get_var##name(path->parent->id, path->id, v)); \
1949 } \
1950 template<> \
1951 inline void Variable::read(type* v, const std::size_t* index) const { \
1952 check(nc_get_var1##name(path->parent->id, path->id, index, v)); \
1953 } \
1954 template<> \
1955 inline void Variable::read(type* v, const std::size_t* start, const std::size_t* count) const { \
1956 check(nc_get_vara##name(path->parent->id, path->id, start, count, v)); \
1957 } \
1958 template<> \
1959 inline void Variable::read(type* v, const std::size_t* start, const std::size_t* count, const std::ptrdiff_t* stride) const { \
1960 check(nc_get_vars##name(path->parent->id, path->id, start, count, stride, v)); \
1961 } \
1962 template<> \
1963 inline void Variable::read(type* v, const std::size_t* start, const std::size_t* count, const std::ptrdiff_t* stride, const std::ptrdiff_t* imap) const { \
1964 check(nc_get_varm##name(path->parent->id, path->id, start, count, stride, imap, v)); \
1965 }
1966
1967#define NETCDFPP_IMPL_VARIABLE_WRITE(type, name) \
1968 template<> \
1969 inline void Variable::write(const type* v) { \
1970 check(nc_put_var##name(path->parent->id, path->id, v)); \
1971 } \
1972 template<> \
1973 inline void Variable::write(const type* v, const std::size_t* index) { \
1974 check(nc_put_var1##name(path->parent->id, path->id, index, v)); \
1975 } \
1976 template<> \
1977 inline void Variable::write(const type* v, const std::size_t* start, const std::size_t* count) { \
1978 check(nc_put_vara##name(path->parent->id, path->id, start, count, v)); \
1979 } \
1980 template<> \
1981 inline void Variable::write(const type* v, const std::size_t* start, const std::size_t* count, const std::ptrdiff_t* stride) { \
1982 check(nc_put_vars##name(path->parent->id, path->id, start, count, stride, v)); \
1983 } \
1984 template<> \
1985 inline void Variable::write(const type* v, const std::size_t* start, const std::size_t* count, const std::ptrdiff_t* stride, const std::ptrdiff_t* imap) { \
1986 check(nc_put_varm##name(path->parent->id, path->id, start, count, stride, imap, v)); \
1987 }
1988
1989#define NETCDFPP_IMPL_ALL(type, name) \
1990 NETCDFPP_IMPL_ATTRIBUTE_GET(type, name) \
1991 NETCDFPP_IMPL_ATTRIBUTE_SET(type, name) \
1992 NETCDFPP_IMPL_VARIABLE_READ(type, name) \
1993 NETCDFPP_IMPL_VARIABLE_WRITE(type, name)
1994
1995// the following maps each type to its corresponding nc_*var* (key is the type used as parameter type in the respective nc_*var*)
1996
1997// NETCDFPP_IMPL_ATTRIBUTE_GET(char, _text) handled by Attribute::get_string
1998template<>
1999inline int Attribute::set_internal(int ncid_p, int othid_p, const char* name_p, std::size_t len, const char* v) {
2000 return nc_put_att_text(ncid_p, othid_p, name_p, len, v); // unfortunately this has a different signature than the other nc_put_att_*
2001}
2002NETCDFPP_IMPL_VARIABLE_READ(char, _text)
2004
2005NETCDFPP_IMPL_ALL(double, _double)
2006NETCDFPP_IMPL_ALL(float, _float)
2007NETCDFPP_IMPL_ALL(int, _int)
2008NETCDFPP_IMPL_ALL(long long, _longlong)
2009NETCDFPP_IMPL_ALL(long, _long)
2010NETCDFPP_IMPL_ALL(short, _short)
2011NETCDFPP_IMPL_ALL(signed char, _schar)
2012NETCDFPP_IMPL_ALL(unsigned char, _uchar)
2013NETCDFPP_IMPL_ALL(unsigned int, _uint)
2014NETCDFPP_IMPL_ALL(unsigned long long, _ulonglong)
2015NETCDFPP_IMPL_ALL(unsigned short, _ushort)
2016
2017} // namespace netCDF
2018
2019#endif
NetCDF attribute attached to a group or variable.
Definition netcdfpp.h:328
void set(const std::vector< T > &v)
Writes a vector of atomic values to the attribute.
Definition netcdfpp.h:419
std::vector< T > get() const
Reads the attribute as a vector of values.
Definition netcdfpp.h:370
void copy_values(const Attribute &a)
Copies the value and type metadata from another attribute.
Definition netcdfpp.h:1564
Attribute require_type(const std::string &name) const
Returns this attribute or throws if its NetCDF type name differs.
Definition netcdfpp.h:471
std::size_t size() const
Returns the number of values stored in the attribute.
Definition netcdfpp.h:450
Maybe< Variable > parent_variable() const
Returns the parent variable for variable attributes.
Definition netcdfpp.h:404
std::string type_name() const
Returns the NetCDF type name of the attribute.
Definition netcdfpp.h:464
Maybe< UserType > user_type() const
Returns the user-defined type for this attribute, if it has one.
Definition netcdfpp.h:1630
std::enable_if< std::is_same< std::string, T >::value, void >::type set(T v)
Writes a string attribute.
Definition netcdfpp.h:433
std::enable_if< std::is_same< constchar *, T >::value||std::is_same< char *, T >::value, void >::type set(T v)
Writes a string attribute from a C string.
Definition netcdfpp.h:438
std::string get_string() const
Reads a classic NetCDF text attribute as a C++ string.
Definition netcdfpp.h:383
std::enable_if<!std::is_same< std::string, T >::value &&!std::is_same< constchar *, T >::value &&!std::is_same< char *, T >::value, void >::type set(T v)
Writes one atomic value to the attribute.
Definition netcdfpp.h:443
nc_type type() const
Returns the NetCDF type id of the attribute.
Definition netcdfpp.h:457
Maybe< Group > parent_group() const
Returns the parent group for group attributes.
Definition netcdfpp.h:396
void rename(std::string name)
Renames the attribute in place.
Definition netcdfpp.h:412
bool is_group_attribute() const
Returns true when this attribute is attached to a group rather than a variable.
Definition netcdfpp.h:393
NetCDF dimension.
Definition netcdfpp.h:484
friend class Group
Definition netcdfpp.h:485
std::size_t size() const
Returns the current dimension length.
Definition netcdfpp.h:497
void rename(std::string name)
Renames the dimension in place.
Definition netcdfpp.h:521
bool is_unlimited() const
Returns true when the dimension is unlimited.
Definition netcdfpp.h:506
Group parent() const
Returns the parent group.
Definition netcdfpp.h:1642
Exception thrown when a NetCDF-C call fails.
Definition netcdfpp.h:157
Exception(int r, std::string s)
Creates an exception with the NetCDF return code and a complete message.
Definition netcdfpp.h:163
int return_code() const
Returns the NetCDF-C return code that caused the exception.
Definition netcdfpp.h:166
NetCDF file handle and root group.
Definition netcdfpp.h:797
File(const char *filename, char mode)
Opens or creates a file.
Definition netcdfpp.h:809
File()
Creates a closed file handle.
Definition netcdfpp.h:800
void close()
Closes the file if it is open.
Definition netcdfpp.h:834
File(std::string filename, char mode)
Opens or creates a file.
Definition netcdfpp.h:806
void sync() const
Flushes buffered changes to disk.
Definition netcdfpp.h:845
bool is_open() const
Returns true when this object owns an open NetCDF file id.
Definition netcdfpp.h:842
~File()
Closes the file if it is open.
Definition netcdfpp.h:812
void open(std::string filename, char mode)
Opens or creates a file, closing any currently open file first.
Definition netcdfpp.h:815
NetCDF group, including the root group represented by File.
Definition netcdfpp.h:528
std::vector< Dimension > dimensions() const
Returns all dimensions visible in this group.
Definition netcdfpp.h:699
Variable add_dimension_variable(std::string name)
Defines an unlimited dimension and a variable with the same name.
Definition netcdfpp.h:1737
Attribute add_attribute(std::string name)
Defines an attribute handle. The attribute is created when a value is written.
Definition netcdfpp.h:540
Group(std::shared_ptr< detail::Path > path_p)
Definition netcdfpp.h:536
std::vector< Variable > variables() const
Returns all variables in this group.
Definition netcdfpp.h:1846
UserType add_user_type(const UserType &t)
Copies a user-defined type into this group.
Definition netcdfpp.h:1681
void copy_attributes(const Group &g)
Copies all group attributes from another group.
Definition netcdfpp.h:657
Variable add_variable(std::string name, const UserType &type, const std::vector< int > &dims)
Defines a variable using a user-defined type and dimension ids.
Definition netcdfpp.h:1726
Dimension add_dimension(std::string name, std::size_t len)
Defines a fixed-size dimension.
Definition netcdfpp.h:553
Attribute add_attribute(const Attribute &a)
Copies an attribute into this group.
Definition netcdfpp.h:543
UserType add_type_opaque(std::string name, std::size_t bytes_size)
Defines an opaque user type.
Definition netcdfpp.h:1665
UserType add_type_compound(std::string name, std::size_t bytes_size)
Defines a compound user type with an explicit byte size.
Definition netcdfpp.h:1644
UserType add_type_vlen(std::string name, nc_type basetype)
Defines a variable-length user type with an explicit NetCDF base type.
Definition netcdfpp.h:1675
UserType add_type_enum(std::string name, nc_type basetype)
Defines an enum user type with an explicit NetCDF base type.
Definition netcdfpp.h:1659
Maybe< Dimension > dimension(std::string name) const
Looks up a dimension by name.
Definition netcdfpp.h:689
std::vector< Group > groups() const
Returns all direct child groups.
Definition netcdfpp.h:730
void copy_dimensions(const Group &g)
Copies all dimensions from another group.
Definition netcdfpp.h:663
void copy_groups(const Group &g, bool variable_values=false)
Copies child groups from another group.
Definition netcdfpp.h:677
void copy_variables(const Group &g, bool variable_values=false)
Copies variables from another group.
Definition netcdfpp.h:1720
void copy_user_types(const Group &g)
Copies user-defined types from another group.
Definition netcdfpp.h:1714
Maybe< UserType > user_type(std::string name) const
Looks up a user-defined type by name.
Definition netcdfpp.h:765
void copy_from(const Group &g, bool variable_values=false)
Copies attributes, dimensions, user types, variables, and child groups.
Definition netcdfpp.h:669
void rename(std::string name)
Renames the group in place.
Definition netcdfpp.h:751
friend class Variable
Definition netcdfpp.h:533
Maybe< Group > parent() const
Returns the parent group, if this is not the root group.
Definition netcdfpp.h:757
Group add_group(const Group &g, bool variable_values=false)
Copies a group into this group.
Definition netcdfpp.h:578
friend class Dimension
Definition netcdfpp.h:530
friend class Attribute
Definition netcdfpp.h:529
Maybe< Variable > variable(std::string name) const
Looks up a variable by name.
Definition netcdfpp.h:781
Maybe< Group > group(std::string name) const
Looks up a child group by name.
Definition netcdfpp.h:720
Maybe< Attribute > attribute(std::string name) const
Looks up a group attribute by name.
Definition netcdfpp.h:633
friend class UserType
Definition netcdfpp.h:532
Dimension add_dimension(std::string name)
Defines an unlimited dimension.
Definition netcdfpp.h:550
Dimension add_dimension(const Dimension &d)
Copies a dimension into this group.
Definition netcdfpp.h:559
Group add_group(std::string name)
Defines a child group.
Definition netcdfpp.h:572
friend class Maybe< Group >
Definition netcdfpp.h:531
std::vector< Attribute > attributes() const
Returns all group attributes.
Definition netcdfpp.h:643
std::vector< UserType > user_types() const
Returns all user-defined types in this group.
Definition netcdfpp.h:1825
Optional result returned by object lookup functions.
Definition netcdfpp.h:301
bool valid() const
Returns true when the lookup found an object.
Definition netcdfpp.h:321
T require() const
Returns the object or throws netCDF::Exception if the lookup failed.
Definition netcdfpp.h:313
Maybe(std::shared_ptr< detail::Path > path_p)
Definition netcdfpp.h:310
NetCDF user-defined type.
Definition netcdfpp.h:849
Group parent() const
Returns the parent group.
Definition netcdfpp.h:883
std::size_t memberscount() const
Returns the number of members in an enum type.
Definition netcdfpp.h:955
nc_type basetype() const
Returns the base NetCDF type of enum and variable-length types.
Definition netcdfpp.h:939
friend class Group
Definition netcdfpp.h:850
UserType add_compound_field(const std::string &name, std::size_t offset)
Adds a scalar field to a compound type.
Definition netcdfpp.h:887
std::vector< CompoundField > compound_fields() const
Returns compound field metadata.
Definition netcdfpp.h:908
std::size_t bytes_size() const
Returns the byte size of the user-defined type.
Definition netcdfpp.h:963
int typeclass() const
Returns NC_VLEN, NC_OPAQUE, NC_ENUM, or NC_COMPOUND.
Definition netcdfpp.h:971
UserType add_compound_field_array(const std::string &name, std::size_t offset, const std::vector< int > &dim_sizes)
Adds an array field to a compound type.
Definition netcdfpp.h:894
std::vector< std::pair< std::string, T > > enum_members() const
Returns enum members as name/value pairs.
Definition netcdfpp.h:926
std::size_t fieldscount() const
Returns the number of fields in a compound type.
Definition netcdfpp.h:947
UserType add_enum_member(const std::string &name, T v)
Adds an enum member.
Definition netcdfpp.h:902
friend class testing::TestUserType
Definition netcdfpp.h:852
NetCDF variable.
Definition netcdfpp.h:992
void set(const std::vector< T > &v, const std::array< std::size_t, N > &start, const std::array< std::size_t, N > &count)
Writes a fixed-rank hyperslab from a vector.
Definition netcdfpp.h:1437
void unset_fill()
Disables fill for this variable.
Definition netcdfpp.h:1238
void read(T *v, const std::size_t *start, const std::size_t *count) const
Reads a hyperslab into caller-provided storage.
Definition netcdfpp.h:1355
void read(T *v, const std::array< std::size_t, N > &start, const std::array< std::size_t, N > &count, const std::array< std::ptrdiff_t, N > &stride) const
Reads a fixed-rank strided hyperslab into caller-provided storage.
Definition netcdfpp.h:1384
Attribute add_attribute(std::string name)
Defines an attribute handle. The attribute is created when a value is written.
Definition netcdfpp.h:1042
void set(const std::vector< T > &v)
Writes the whole variable from a vector.
Definition netcdfpp.h:1399
std::vector< T > get(const std::size_t *start, const std::size_t *count) const
Reads a hyperslab by raw NetCDF start/count pointers.
Definition netcdfpp.h:1306
std::size_t size(const std::array< std::size_t, N > &start, const std::array< std::size_t, N > &count) const
Returns the number of elements selected by a hyperslab count.
Definition netcdfpp.h:1275
void set(const T v, const std::array< std::size_t, N > &index)
Writes one fixed-rank element.
Definition netcdfpp.h:1432
friend class Group
Definition netcdfpp.h:993
void set_compression(bool shuffle_filter, int deflate_level)
Sets shuffle and deflate compression.
Definition netcdfpp.h:1198
void read(T *v, const std::array< std::size_t, N > &start, const std::array< std::size_t, N > &count) const
Reads a fixed-rank hyperslab into caller-provided storage.
Definition netcdfpp.h:1379
void write(const T *v, const std::array< std::size_t, N > &start, const std::array< std::size_t, N > &count)
Writes a fixed-rank hyperslab from caller-provided storage.
Definition netcdfpp.h:1496
void read(T *v, const std::size_t *start, const std::size_t *count, const std::ptrdiff_t *stride, const std::ptrdiff_t *imap) const
Reads mapped data into caller-provided storage.
Definition netcdfpp.h:1367
void write(const T *v)
Writes the whole variable from caller-provided storage.
Definition netcdfpp.h:1460
std::vector< T > get(const std::array< std::size_t, N > &start, const std::array< std::size_t, N > &count, const std::array< std::ptrdiff_t, N > &stride) const
Reads a fixed-rank strided hyperslab.
Definition netcdfpp.h:1337
void read(T *v, const std::size_t *index) const
Reads one element into caller-provided storage.
Definition netcdfpp.h:1349
void read(T *v, const std::array< std::size_t, N > &index) const
Reads one fixed-rank element into caller-provided storage.
Definition netcdfpp.h:1374
std::vector< T > get(const std::size_t *start, const std::size_t *count, const std::ptrdiff_t *stride) const
Reads a strided hyperslab by raw NetCDF pointers.
Definition netcdfpp.h:1316
void rename(std::string name)
Renames the variable in place.
Definition netcdfpp.h:1244
void set(const std::vector< T > &v, const std::size_t *start, const std::size_t *count, const std::ptrdiff_t *stride)
Writes a strided hyperslab from a vector.
Definition netcdfpp.h:1421
std::string type_name() const
Returns the NetCDF type name of the variable.
Definition netcdfpp.h:1522
Group parent() const
Returns the parent group.
Definition netcdfpp.h:1241
void set(const std::vector< T > &v, const std::size_t *start, const std::size_t *count)
Writes a hyperslab from a vector.
Definition netcdfpp.h:1416
void set(const std::vector< T > &v, const std::size_t *start, const std::size_t *count, const std::ptrdiff_t *stride, const std::ptrdiff_t *imap)
Writes mapped data from a vector.
Definition netcdfpp.h:1426
Maybe< Attribute > attribute(std::string name) const
Looks up a variable attribute by name.
Definition netcdfpp.h:1052
nc_type type() const
Returns the NetCDF type id of the variable.
Definition netcdfpp.h:1515
void set(const std::vector< T > &v, const std::array< std::size_t, N > &start, const std::array< std::size_t, N > &count, const std::array< std::ptrdiff_t, N > &stride, const std::array< std::ptrdiff_t, N > &imap)
Writes fixed-rank mapped data from a vector.
Definition netcdfpp.h:1450
std::vector< std::size_t > get_chunking() const
Returns chunk sizes, or an empty vector for contiguous storage.
Definition netcdfpp.h:1166
void write(const T *v, const std::size_t *index)
Writes one element from caller-provided storage.
Definition netcdfpp.h:1466
std::vector< std::size_t > sizes() const
Returns the current size of each dimension.
Definition netcdfpp.h:1250
std::vector< T > get() const
Reads the whole variable as a vector.
Definition netcdfpp.h:1289
Variable require_compound(std::size_t fieldscount) const
Returns this variable or throws if its user-defined type is not a matching compound type.
Definition netcdfpp.h:1539
void set_default_chunking()
Lets NetCDF choose default chunk sizes.
Definition netcdfpp.h:1186
void read(T *v, const std::array< std::size_t, N > &start, const std::array< std::size_t, N > &count, const std::array< std::ptrdiff_t, N > &stride, const std::array< std::ptrdiff_t, N > &imap) const
Reads fixed-rank mapped data into caller-provided storage.
Definition netcdfpp.h:1389
void read(T *v) const
Reads the whole variable into caller-provided storage.
Definition netcdfpp.h:1343
void set_chunking(const std::vector< std::size_t > &chunks)
Sets chunked storage, or contiguous storage when chunks is empty.
Definition netcdfpp.h:1177
bool get_checksum_enabled() const
Returns true when Fletcher32 checksums are enabled.
Definition netcdfpp.h:1213
std::vector< Attribute > attributes() const
Returns all variable attributes.
Definition netcdfpp.h:1062
Variable require_type(const std::string &name) const
Returns this variable or throws if its NetCDF type name differs.
Definition netcdfpp.h:1529
std::enable_if< Type< T >::is_atomic||std::is_same< constchar *, T >::value||std::is_same< char *, T >::value, void >::type set(T v, const std::size_t *index)
Writes one value by raw NetCDF index pointer.
Definition netcdfpp.h:1404
void set(const std::vector< T > &v, const std::array< std::size_t, N > &start, const std::array< std::size_t, N > &count, const std::array< std::ptrdiff_t, N > &stride)
Writes a fixed-rank strided hyperslab from a vector.
Definition netcdfpp.h:1442
void write(const T *v, const std::size_t *start, const std::size_t *count, const std::ptrdiff_t *stride, const std::ptrdiff_t *imap)
Writes mapped data from caller-provided storage.
Definition netcdfpp.h:1484
void set_checksum_enabled(bool v)
Enables or disables Fletcher32 checksums.
Definition netcdfpp.h:1220
void write(const T *v, const std::array< std::size_t, N > &start, const std::array< std::size_t, N > &count, const std::array< std::ptrdiff_t, N > &stride, const std::array< std::ptrdiff_t, N > &imap)
Writes fixed-rank mapped data from caller-provided storage.
Definition netcdfpp.h:1506
T get(const std::array< std::size_t, N > &index) const
Reads one element by fixed-rank index.
Definition netcdfpp.h:1327
int get_endianness() const
Returns the NetCDF endianness setting.
Definition netcdfpp.h:1203
Variable require_dimensions(const std::vector< std::string > &names) const
Returns this variable or throws if its dimensions do not match.
Definition netcdfpp.h:1090
Variable require_size(std::size_t size_p) const
Returns this variable or throws if its total element count differs.
Definition netcdfpp.h:1280
std::vector< Dimension > dimensions() const
Returns the variable dimensions.
Definition netcdfpp.h:1153
std::enable_if<!Type< T >::is_atomic &&!std::is_same< constchar *, T >::value &&!std::is_same< char *, T >::value, void >::type set(const T &v, const std::size_t *index)
Writes one non-atomic value by raw NetCDF index pointer.
Definition netcdfpp.h:1410
void copy_attributes(const Variable &v)
Copies all attributes from another variable.
Definition netcdfpp.h:1098
void write(const T *v, const std::size_t *start, const std::size_t *count)
Writes a hyperslab from caller-provided storage.
Definition netcdfpp.h:1472
void write(const T *v, const std::array< std::size_t, N > &index)
Writes one fixed-rank element from caller-provided storage.
Definition netcdfpp.h:1491
std::pair< bool, T > get_fill() const
Returns whether fill is enabled and the fill value.
Definition netcdfpp.h:1224
void copy_values(const Variable &v)
Copies raw values from another variable after checking shape and type size.
Definition netcdfpp.h:1105
void set_endianness(int endianness)
Sets NC_ENDIAN_NATIVE, NC_ENDIAN_LITTLE, or NC_ENDIAN_BIG.
Definition netcdfpp.h:1210
void write(const T *v, const std::array< std::size_t, N > &start, const std::array< std::size_t, N > &count, const std::array< std::ptrdiff_t, N > &stride)
Writes a fixed-rank strided hyperslab from caller-provided storage.
Definition netcdfpp.h:1501
std::pair< bool, int > get_compression() const
Returns shuffle status and deflate level, or -1 when deflate is disabled.
Definition netcdfpp.h:1189
void write(const T *v, const std::size_t *start, const std::size_t *count, const std::ptrdiff_t *stride)
Writes a strided hyperslab from caller-provided storage.
Definition netcdfpp.h:1478
Maybe< UserType > user_type() const
Returns the user-defined type for this variable, if it has one.
Definition netcdfpp.h:1551
std::size_t dimension_count() const
Returns the number of dimensions.
Definition netcdfpp.h:1146
std::vector< T > get(const std::array< std::size_t, N > &start, const std::array< std::size_t, N > &count) const
Reads a fixed-rank hyperslab.
Definition netcdfpp.h:1332
void read(T *v, const std::size_t *start, const std::size_t *count, const std::ptrdiff_t *stride) const
Reads a strided hyperslab into caller-provided storage.
Definition netcdfpp.h:1361
T get(const std::size_t *index) const
Reads one element by raw NetCDF index pointer.
Definition netcdfpp.h:1299
bool check_dimensions(const std::vector< std::string > &names) const
Returns true when the variable dimensions match the supplied names exactly.
Definition netcdfpp.h:1076
Attribute add_attribute(const Attribute &a)
Copies an attribute onto this variable.
Definition netcdfpp.h:1045
std::size_t size() const
Returns the total number of elements.
Definition netcdfpp.h:1263
void set_fill(T v)
Enables fill and sets the fill value.
Definition netcdfpp.h:1233
Definition netcdfpp.h:268
const std::string & name() const
Definition netcdfpp.h:289
void raise_error(int ret) const
Definition netcdfpp.h:280
int id() const
Definition netcdfpp.h:290
std::shared_ptr< Path > path
Definition netcdfpp.h:270
static std::string get_error_message(int ret)
Definition netcdfpp.h:273
void check(int ret) const
Definition netcdfpp.h:282
Object(std::shared_ptr< Path > path_p)
Definition netcdfpp.h:271
T * data_or_null(std::vector< T > &v)
Definition netcdfpp.h:185
std::vector< std::string > process_char_vector(std::vector< char * > &buf)
Definition netcdfpp.h:194
constexpr bool is_user_type(nc_type type)
Definition netcdfpp.h:182
std::vector< const char * > process_string_vector(const std::vector< std::string > &v)
Definition netcdfpp.h:212
Definition netcdfpp.h:50
T for_type(nc_type t, Function &&f)
Calls a callable with a default value of the C++ type matching a NetCDF atomic type.
Definition netcdfpp.h:98
#define NETCDFPP_IMPL_TYPE(internal, lib)
Definition netcdfpp.h:70
#define NETCDFPP_IMPL_VARIABLE_WRITE(type, name)
Definition netcdfpp.h:1967
#define NETCDFPP_IMPL_ALL(type, name)
Definition netcdfpp.h:1989
#define NETCDFPP_IMPL_VARIABLE_READ(type, name)
Definition netcdfpp.h:1945
Definition netcdfpp.h:53
Definition netcdfpp.h:55
static constexpr bool is_atomic
Definition netcdfpp.h:56
Compound field metadata.
Definition netcdfpp.h:856
nc_type type
NetCDF type id of the field.
Definition netcdfpp.h:858
std::vector< int > dimensions
Array dimensions for array fields; empty for scalar fields.
Definition netcdfpp.h:864
std::size_t offset
Byte offset of the field in the compound type.
Definition netcdfpp.h:862
std::string name
Field name.
Definition netcdfpp.h:860
RAII wrapper for one NetCDF variable-length value returned by the C API.
Definition netcdfpp.h:981
const T * data
Pointer to the allocated element data.
Definition netcdfpp.h:985
~VLenElement()
Definition netcdfpp.h:988
const std::size_t size
Number of elements.
Definition netcdfpp.h:983
VLenElement()
Definition netcdfpp.h:987
Definition netcdfpp.h:236
Definition netcdfpp.h:218
std::string name
Definition netcdfpp.h:219
int id
Definition netcdfpp.h:220
bool is_group
Definition netcdfpp.h:221
std::shared_ptr< Path > parent
Definition netcdfpp.h:222
std::string get_full_path() const
Definition netcdfpp.h:224