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 <initializer_list>
44#include <memory>
45#include <stdexcept>
46#include <string>
47#include <type_traits>
48#include <utility>
49#include <vector>
50
51namespace netCDF {
52
53template<int I>
54struct SystemType {};
55template<typename T>
56struct Type {
57 static constexpr bool is_atomic = false;
58};
59
60template<>
61struct Type<long long> {
62 static constexpr nc_type id = NC_INT64;
63 static constexpr bool is_atomic = true;
64};
65template<>
66struct Type<unsigned long long> {
67 static constexpr nc_type id = NC_UINT64;
68 static constexpr bool is_atomic = true;
69};
70
71#define NETCDFPP_IMPL_TYPE(internal, lib) \
72 template<> \
73 struct SystemType<lib> { \
74 using type = internal; \
75 }; \
76 template<> \
77 struct Type<internal> { \
78 static constexpr nc_type id = lib; \
79 static constexpr bool is_atomic = true; \
80 };
81
82NETCDFPP_IMPL_TYPE(char*, NC_STRING)
83NETCDFPP_IMPL_TYPE(char, NC_CHAR)
84NETCDFPP_IMPL_TYPE(double, NC_DOUBLE)
85NETCDFPP_IMPL_TYPE(float, NC_FLOAT)
86NETCDFPP_IMPL_TYPE(std::int16_t, NC_SHORT)
87NETCDFPP_IMPL_TYPE(std::int32_t, NC_INT)
88NETCDFPP_IMPL_TYPE(std::int64_t, NC_INT64)
89NETCDFPP_IMPL_TYPE(std::int8_t, NC_BYTE)
90NETCDFPP_IMPL_TYPE(std::uint16_t, NC_USHORT)
91NETCDFPP_IMPL_TYPE(std::uint32_t, NC_UINT)
92NETCDFPP_IMPL_TYPE(std::uint64_t, NC_UINT64)
93NETCDFPP_IMPL_TYPE(std::uint8_t, NC_UBYTE)
94
95template<typename T, typename Function>
99inline T for_type(nc_type t, Function&& f) {
100 switch (t) {
101 case NC_STRING: {
103 return f(type);
104 }
105 case NC_CHAR: {
107 return f(type);
108 }
109 case NC_DOUBLE: {
111 return f(type);
112 }
113 case NC_FLOAT: {
115 return f(type);
116 }
117 case NC_SHORT: {
119 return f(type);
120 }
121 case NC_INT: {
123 return f(type);
124 }
125 case NC_INT64: {
127 return f(type);
128 }
129 case NC_BYTE: {
131 return f(type);
132 }
133 case NC_USHORT: {
135 return f(type);
136 }
137 case NC_UINT: {
139 return f(type);
140 }
141 case NC_UINT64: {
143 return f(type);
144 }
145 case NC_UBYTE: {
147 return f(type);
148 }
149 default:
150 throw std::runtime_error("Unsupported type");
151 }
152}
153
158class Exception final : public std::runtime_error {
159 private:
160 int ret;
161
162 public:
164 explicit Exception(int r, std::string s) : std::runtime_error(std::move(s)), ret(r) {}
165
167 int return_code() const { return ret; }
168};
169
170class Attribute;
171class Dimension;
172class File;
173class Group;
174class UserType;
175class Variable;
176
177namespace testing {
178class TestUserType;
179}
180
181namespace detail {
182
183constexpr bool is_user_type(nc_type type) { return type >= NC_FIRSTUSERTYPEID; }
184
185template<typename T>
186inline T* data_or_null(std::vector<T>& v) {
187 return v.empty() ? nullptr : &v[0];
188}
189
190template<typename T>
191inline const T* data_or_null(const std::vector<T>& v) {
192 return v.empty() ? nullptr : &v[0];
193}
194
195inline std::vector<std::string> process_char_vector(std::vector<char*>& buf) {
196 std::vector<std::string> res;
197 res.reserve(buf.size());
198 std::copy(std::begin(buf), std::end(buf), std::back_inserter(res));
199 if (!buf.empty()) {
200 nc_free_string(buf.size(), detail::data_or_null(buf));
201 }
202 return res;
203}
204
205inline std::vector<std::string> process_char_vector(char** buf, std::size_t len) {
206 std::vector<std::string> res;
207 res.reserve(len);
208 std::copy(buf, buf + len, std::back_inserter(res));
209 nc_free_string(len, buf);
210 return res;
211}
212
213inline std::vector<const char*> process_string_vector(const std::vector<std::string>& v) {
214 std::vector<const char*> buf(v.size());
215 std::transform(std::begin(v), std::end(v), std::begin(buf), [](const std::string& s) { return s.c_str(); });
216 return buf;
217}
218
219struct Path {
220 std::string name;
221 int id;
223 std::shared_ptr<Path> parent;
224
225 std::string get_full_path() const {
226 std::string res = name;
227 const std::shared_ptr<Path>* current = &parent;
228 while (*current) {
229 res.insert(0, (*current)->name + ((*current)->parent ? "/" : ":"));
230 current = &(*current)->parent;
231 }
232 return res;
233 }
234};
235
236template<typename T>
237struct ClassName {};
238
239template<>
241 static constexpr const char* name = "Attribute";
242};
243
244template<>
246 static constexpr const char* name = "Dimension";
247};
248
249template<>
251 static constexpr const char* name = "File";
252};
253
254template<>
256 static constexpr const char* name = "Group";
257};
258
259template<>
261 static constexpr const char* name = "UserType";
262};
263
264template<>
266 static constexpr const char* name = "Variable";
267};
268
269class Object {
270 protected:
271 std::shared_ptr<Path> path;
272 explicit Object(std::shared_ptr<Path> path_p) : path(std::move(path_p)) {}
273
274 static inline std::string get_error_message(int ret) {
275 if (NC_ISSYSERR(ret)) {
276 return std::strerror(ret);
277 }
278 return nc_strerror(ret);
279 }
280
281 void raise_error(int ret) const { throw Exception(ret, get_error_message(ret) + ": " + path->get_full_path()); }
282
283 inline void check(int ret) const {
284 if (ret != NC_NOERR) {
285 raise_error(ret);
286 }
287 }
288
289 public:
290 const std::string& name() const { return path->name; }
291 int id() const { return path->id; }
292};
293
294} // namespace detail
295
296template<typename T>
302class Maybe final {
303 private:
304 std::shared_ptr<detail::Path> path;
305
306 void raise_error() const {
307 throw Exception(NC_ENOTFOUND, std::string(detail::ClassName<typename std::remove_const<T>::type>::name) + " not found: " + path->get_full_path());
308 }
309
310 public:
311 explicit Maybe(std::shared_ptr<detail::Path> path_p) : path(std::move(path_p)) {}
312
314 inline T require() const {
315 if (!valid()) {
316 raise_error();
317 }
318 return T(path);
319 }
320
322 inline bool valid() const { return path->id >= 0; }
323
325 inline operator bool() const { return valid(); }
326};
327
329class Attribute final : public detail::Object {
330 friend class Group;
331 friend class Maybe<Attribute>;
332 friend class Variable;
333
334 private:
335 explicit Attribute(std::shared_ptr<detail::Path> path_p) : detail::Object(std::move(path_p)) {}
336
337 int ncid() const {
338 if (path->parent->is_group) {
339 return path->parent->id;
340 } else {
341 return path->parent->parent->id;
342 }
343 }
344
345 int othid() const {
346 if (path->parent->is_group) {
347 return NC_GLOBAL;
348 } else {
349 return path->parent->id;
350 }
351 }
352
353 template<typename T>
354 int get_internal(int ncid_p, int othid_p, const char* name_p, T* v) const {
355 static_assert(!Type<T>::is_atomic, "Should only be used for user types");
356 return nc_get_att(ncid_p, othid_p, name_p, v);
357 }
358
359 template<typename T>
360 int set_internal(int ncid_p, int othid_p, const char* name_p, std::size_t len, const T* v);
361
362 public:
364 void copy_values(const Attribute& a);
365
366 template<typename T>
371 std::vector<T> get() const {
372 static_assert(!std::is_same<char*, T>::value && !std::is_same<const char*, T>::value, "Use get_string() for reading string attributes");
373 std::size_t len;
374 check(nc_inq_attlen(ncid(), othid(), path->name.c_str(), &len));
375 std::vector<T> res(len);
376 if (res.empty()) {
377 return res;
378 }
379 check(get_internal<T>(ncid(), othid(), path->name.c_str(), detail::data_or_null(res)));
380 return res;
381 }
382
384 std::string get_string() const {
385 std::size_t len;
386 check(nc_inq_attlen(ncid(), othid(), path->name.c_str(), &len));
387 std::vector<char> buf(len + 1);
388 buf[len] = '\0';
389 check(nc_get_att_text(ncid(), othid(), path->name.c_str(), &buf[0]));
390 return std::string(&buf[0]);
391 }
392
394 bool is_group_attribute() const { return path->parent->is_group; }
395
398 if (is_group_attribute()) {
399 return Maybe<Group>(path->parent);
400 }
401 return Maybe<Group>(std::make_shared<detail::Path>(detail::Path{"..", -1, true, path}));
402 }
403
406 if (!is_group_attribute()) {
407 return Maybe<Variable>(path->parent);
408 }
409 return Maybe<Variable>(std::make_shared<detail::Path>(detail::Path{"..", -1, true, path}));
410 }
411
413 void rename(std::string name) {
414 check(nc_rename_att(ncid(), othid(), path->name.c_str(), name.c_str()));
415 path->name = std::move(name);
416 }
417
418 template<typename T>
420 void set(const std::vector<T>& v) {
421 static_assert(Type<T>::is_atomic, "For user type attributes use Attribute::set(const std::vector<T>& v, const UserType& type)");
422 check(set_internal<T>(ncid(), othid(), path->name.c_str(), v.size(), detail::data_or_null(v)));
423 }
424
425 template<typename T>
427 void set(const std::vector<T>& v, const UserType& type);
428 template<typename T>
430 void set(const T& v, const UserType& type);
431
432 template<typename T>
434 typename std::enable_if<std::is_same<std::string, T>::value, void>::type set(T v) {
435 check(set_internal(ncid(), othid(), path->name.c_str(), v.length() + 1, v.c_str()));
436 }
437 template<typename T>
439 typename std::enable_if<std::is_same<const char*, T>::value || std::is_same<char*, T>::value, void>::type set(T v) {
440 check(set_internal(ncid(), othid(), path->name.c_str(), std::strlen(v) + 1, v));
441 }
442 template<typename T>
444 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(
445 T v) {
446 static_assert(Type<T>::is_atomic, "For user type attributes use Attribute::set(T v, const UserType& type)");
447 check(set_internal<T>(ncid(), othid(), path->name.c_str(), 1, &v));
448 }
449
451 std::size_t size() const {
452 std::size_t len;
453 check(nc_inq_attlen(ncid(), othid(), path->name.c_str(), &len));
454 return len;
455 }
456
458 nc_type type() const {
459 int res;
460 check(nc_inq_atttype(ncid(), othid(), path->name.c_str(), &res));
461 return res;
462 }
463
465 std::string type_name() const {
466 char name[NC_MAX_NAME + 1];
467 check(nc_inq_type(ncid(), type(), name, nullptr));
468 return name;
469 }
470
472 Attribute require_type(const std::string& name) const {
473 const auto name_l = type_name();
474 if (name_l != name) {
475 throw Exception(NC_EVARMETA, "Unexpected type '" + name_l + "': " + path->get_full_path());
476 }
477 return *this;
478 }
479
482};
483
485class Dimension final : public detail::Object {
486 friend class Group;
487 friend class Maybe<Dimension>;
488 friend class Variable;
489
490 private:
491 mutable std::size_t size_m = 0;
492 mutable bool size_read = false;
493
494 explicit Dimension(std::shared_ptr<detail::Path> path_p) : detail::Object(std::move(path_p)) {}
495
496 public:
498 std::size_t size() const {
499 if (!size_read) {
500 check(nc_inq_dimlen(path->parent->id, path->id, &size_m));
501 size_read = true;
502 }
503 return size_m;
504 }
505
507 bool is_unlimited() const {
508 int len;
509 check(nc_inq_unlimdims(path->parent->id, &len, nullptr));
510 if (len == 0) {
511 return false;
512 }
513 std::vector<int> ids(len);
514 check(nc_inq_unlimdims(path->parent->id, nullptr, detail::data_or_null(ids)));
515 return std::find(std::begin(ids), std::end(ids), path->id) != std::end(ids);
516 }
517
519 Group parent() const;
520
522 void rename(std::string name) {
523 check(nc_rename_dim(path->parent->id, path->id, name.c_str()));
524 path->name = std::move(name);
525 }
526};
527
529class Group : public detail::Object {
530 friend class Attribute;
531 friend class Dimension;
532 friend class Maybe<Group>;
533 friend class UserType;
534 friend class Variable;
535
536 protected:
537 explicit Group(std::shared_ptr<detail::Path> path_p) : detail::Object(std::move(path_p)) {}
538
539 public:
541 Attribute add_attribute(std::string name) { return Attribute(std::make_shared<detail::Path>(detail::Path{std::move(name), -1, false, path})); }
542
545 auto res = add_attribute(a.name());
546 res.copy_values(a);
547 return res;
548 }
549
551 Dimension add_dimension(std::string name) { return add_dimension(std::move(name), NC_UNLIMITED); }
552
554 Dimension add_dimension(std::string name, std::size_t len) {
555 int id;
556 check(nc_def_dim(path->id, name.c_str(), len, &id));
557 return Dimension(std::make_shared<detail::Path>(detail::Path{std::move(name), id, false, path}));
558 }
560 Dimension add_dimension(const Dimension& d) { return add_dimension(d.name(), d.is_unlimited() ? NC_UNLIMITED : d.size()); }
561
563 template<typename T>
566 template<typename T>
567 Variable add_dimension_variable(std::string name, std::size_t len);
569 template<typename T>
571
573 Group add_group(std::string name) {
574 int id;
575 check(nc_def_grp(path->id, name.c_str(), &id));
576 return Group(std::make_shared<detail::Path>(detail::Path{std::move(name), id, true, path}));
577 }
579 Group add_group(const Group& g, bool variable_values = false) {
580 auto res = add_group(g.name());
581 res.copy_from(g, variable_values);
582 return res;
583 }
584
586 UserType add_type_compound(std::string name, std::size_t bytes_size);
587 template<typename T>
589 UserType add_type_compound(std::string name);
590
592 UserType add_type_enum(std::string name, nc_type basetype);
593 template<typename T>
595 UserType add_type_enum(std::string name);
596
598 UserType add_type_vlen(std::string name, nc_type basetype);
599 template<typename T>
601 UserType add_type_vlen(std::string name);
602
604 UserType add_type_opaque(std::string name, std::size_t bytes_size);
605 template<typename T>
607 UserType add_type_opaque(std::string name);
608
611
613 Variable add_variable(std::string name, const UserType& type, const std::vector<int>& dims);
615 Variable add_variable(std::string name, const UserType& type, std::initializer_list<int> dims);
617 Variable add_variable(std::string name, const UserType& type, const std::vector<Dimension>& dims);
619 Variable add_variable(std::string name, const UserType& type, std::initializer_list<Dimension> dims);
621 Variable add_variable(std::string name, const UserType& type, const std::vector<std::string>& dims);
623 Variable add_variable(std::string name, const UserType& type, std::initializer_list<std::string> dims);
625 Variable add_variable(std::string name, nc_type type, const std::vector<int>& dims);
627 Variable add_variable(std::string name, nc_type type, std::initializer_list<int> dims);
629 Variable add_variable(std::string name, nc_type type, const std::vector<Dimension>& dims);
631 Variable add_variable(std::string name, nc_type type, std::initializer_list<Dimension> dims);
633 Variable add_variable(std::string name, nc_type type, const std::vector<std::string>& dims);
635 Variable add_variable(std::string name, nc_type type, std::initializer_list<std::string> dims);
636 template<typename T>
638 Variable add_variable(std::string name, const std::vector<int>& dims);
639 template<typename T>
641 Variable add_variable(std::string name, std::initializer_list<int> dims);
642 template<typename T>
644 Variable add_variable(std::string name, const std::vector<Dimension>& dims);
645 template<typename T>
647 Variable add_variable(std::string name, std::initializer_list<Dimension> dims);
648 template<typename T>
650 Variable add_variable(std::string name, const std::vector<std::string>& dims);
651 template<typename T>
653 Variable add_variable(std::string name, std::initializer_list<std::string> dims);
655 Variable add_variable(const Variable& v, bool with_values = false);
656
658 Maybe<Attribute> attribute(std::string name) const {
659 auto res = std::make_shared<detail::Path>(detail::Path{std::move(name), -1, false, path});
660 const auto ret = nc_inq_attid(path->id, NC_GLOBAL, res->name.c_str(), &res->id);
661 if (ret != NC_ENOTATT) {
662 check(ret);
663 }
664 return Maybe<Attribute>(std::move(res));
665 }
666
668 std::vector<Attribute> attributes() const {
669 int count;
670 check(nc_inq_natts(path->id, &count));
671 std::vector<Attribute> res;
672 res.reserve(count);
673 char name[NC_MAX_NAME + 1];
674 for (int id = 0; id < count; ++id) {
675 check(nc_inq_attname(path->id, NC_GLOBAL, id, name));
676 res.emplace_back(Attribute{std::make_shared<detail::Path>(detail::Path{std::string(name), id, false, path})});
677 }
678 return res;
679 }
680
682 void copy_attributes(const Group& g) {
683 for (const auto& it : g.attributes()) {
684 add_attribute(it);
685 }
686 }
688 void copy_dimensions(const Group& g) {
689 for (const auto& it : g.dimensions()) {
690 add_dimension(it);
691 }
692 }
694 void copy_from(const Group& g, bool variable_values = false) {
698 copy_variables(g, variable_values);
699 copy_groups(g, variable_values);
700 }
702 void copy_groups(const Group& g, bool variable_values = false) {
703 for (const auto& it : g.groups()) {
704 add_group(it, variable_values);
705 }
706 }
708 void copy_user_types(const Group& g);
709
711 void copy_variables(const Group& g, bool variable_values = false);
712
714 Maybe<Dimension> dimension(std::string name) const {
715 auto res = std::make_shared<detail::Path>(detail::Path{std::move(name), -1, false, path});
716 const auto ret = nc_inq_dimid(path->id, res->name.c_str(), &res->id);
717 if (ret != NC_EBADDIM) {
718 check(ret);
719 }
720 return Maybe<Dimension>(std::move(res));
721 }
722
724 std::vector<Dimension> dimensions() const {
725 int count;
726 check(nc_inq_ndims(path->id, &count));
727 if (count == 0) {
728 return {};
729 }
730
731 std::vector<int> ids(count);
732 check(nc_inq_dimids(path->id, &count, detail::data_or_null(ids), 0));
733
734 char name[NC_MAX_NAME + 1];
735 std::vector<Dimension> res;
736 res.reserve(count);
737 std::transform(std::begin(ids), std::end(ids), std::back_inserter(res), [&](int id) {
738 check(nc_inq_dimname(path->id, id, name));
739 return Dimension(std::make_shared<detail::Path>(detail::Path{name, id, false, path}));
740 });
741 return res;
742 }
743
745 Maybe<Group> group(std::string name) const {
746 auto res = std::make_shared<detail::Path>(detail::Path{std::move(name), -1, true, path});
747 const auto ret = nc_inq_grp_ncid(path->id, res->name.c_str(), &res->id);
748 if (ret != NC_ENOGRP) {
749 check(ret);
750 }
751 return Maybe<Group>(std::move(res));
752 }
753
755 std::vector<Group> groups() const {
756 int count;
757 check(nc_inq_grps(path->id, &count, nullptr));
758 if (count == 0) {
759 return {};
760 }
761
762 std::vector<int> ids(count);
763 check(nc_inq_grps(path->id, nullptr, detail::data_or_null(ids)));
764
765 char name[NC_MAX_NAME + 1];
766 std::vector<Group> res;
767 res.reserve(count);
768 std::transform(std::begin(ids), std::end(ids), std::back_inserter(res), [&](int id) {
769 check(nc_inq_grpname(id, name));
770 return Group(std::make_shared<detail::Path>(detail::Path{name, id, true, path}));
771 });
772 return res;
773 }
774
776 void rename(std::string name) {
777 check(nc_rename_grp(path->id, name.c_str()));
778 path->name = std::move(name);
779 }
780
783 if (path->parent) {
784 return Maybe<Group>(path->parent);
785 }
786 return Maybe<Group>(std::make_shared<detail::Path>(detail::Path{"..", -1, true, path}));
787 }
788
790 Maybe<UserType> user_type(std::string name) const {
791 auto res = std::make_shared<detail::Path>(detail::Path{std::move(name), -1, false, path});
792 const auto ret = nc_inq_typeid(path->id, res->name.c_str(), &res->id);
793 if (ret != NC_EBADTYPE) {
794 check(ret);
795 }
796 if (!detail::is_user_type(res->id)) {
797 res->id = -1;
798 }
799 return Maybe<UserType>(std::move(res));
800 }
801
803 std::vector<UserType> user_types() const;
804
806 Maybe<Variable> variable(std::string name) const {
807 auto res = std::make_shared<detail::Path>(detail::Path{std::move(name), -1, false, path});
808 const auto ret = nc_inq_varid(path->id, res->name.c_str(), &res->id);
809 if (ret != NC_ENOTVAR) {
810 check(ret);
811 }
812 return Maybe<Variable>(std::move(res));
813 }
814
816 std::vector<Variable> variables() const;
817};
818
822class File final : public Group {
823 public:
825 File() : Group(std::make_shared<detail::Path>(detail::Path{"", -1, true, nullptr})) {}
826
831 File(std::string filename, char mode) : File() { open(std::move(filename), mode); }
832
834 File(const char* filename, char mode) : File(std::string(filename), mode) {}
835
837 ~File() { close(); }
838
840 void open(std::string filename, char mode) {
841 close();
842 path->name = std::move(filename);
843 switch (mode) {
844 case 'a':
845 check(nc_open(path->name.c_str(), NC_WRITE, &path->id));
846 break;
847 case 'r':
848 check(nc_open(path->name.c_str(), NC_NOWRITE, &path->id));
849 break;
850 case 'w':
851 check(nc_create(path->name.c_str(), NC_NETCDF4 | NC_CLOBBER, &path->id));
852 break;
853 default:
854 throw std::runtime_error("Unknown file mode");
855 }
856 }
857
859 void close() {
860 if (is_open()) {
861 check(nc_close(path->id));
862 path->id = -1;
863 }
864 }
865
867 bool is_open() const { return path->id >= 0; }
868
870 void sync() const { check(nc_sync(path->id)); }
871};
872
874class UserType final : public detail::Object {
875 friend class Group;
876 friend class Maybe<UserType>;
878
879 public:
883 nc_type type;
885 std::string name;
887 std::size_t offset;
889 std::vector<int> dimensions;
890 };
891
892 private:
893 mutable bool fields_read = false;
894 mutable std::size_t size_m = 0;
895 mutable nc_type basetype_m = 0;
896 mutable std::size_t fieldscount_m = 0;
897 mutable int typeclass_m = 0;
898
899 explicit UserType(std::shared_ptr<detail::Path> path_p) : detail::Object(std::move(path_p)) {}
900
901 void read_fields() const {
902 check(nc_inq_user_type(path->parent->id, path->id, nullptr, &size_m, &basetype_m, &fieldscount_m, &typeclass_m));
903 fields_read = true;
904 }
905
906 public:
908 Group parent() const { return Group(path->parent); }
909
910 template<typename T>
912 UserType add_compound_field(const std::string& name, std::size_t offset) {
913 check(nc_insert_compound(path->parent->id, path->id, name.c_str(), offset, Type<T>::id));
914 return *this;
915 }
916
917 template<typename T>
919 UserType add_compound_field_array(const std::string& name, std::size_t offset, const std::vector<int>& dim_sizes) {
920 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(),
921 detail::data_or_null(dim_sizes)));
922 return *this;
923 }
924
925 template<typename T>
927 UserType add_enum_member(const std::string& name, T v) {
928 check(nc_insert_enum(path->parent->id, path->id, name.c_str(), &v));
929 return *this;
930 }
931
933 std::vector<CompoundField> compound_fields() const {
934 std::vector<CompoundField> res;
935 res.reserve(fieldscount());
936 int dims_count;
937 char name[NC_MAX_NAME + 1];
938 for (int i = 0; i < static_cast<int>(fieldscount()); ++i) {
939 check(nc_inq_compound_fieldndims(path->parent->id, path->id, i, &dims_count));
941 f.dimensions.resize(dims_count);
942 check(nc_inq_compound_field(path->parent->id, path->id, i, name, &f.offset, &f.type, nullptr, detail::data_or_null(f.dimensions)));
943 f.name = name;
944 res.emplace_back(std::move(f));
945 }
946 return res;
947 }
948
949 template<typename T>
951 std::vector<std::pair<std::string, T>> enum_members() const {
952 std::vector<std::pair<std::string, T>> res;
953 res.reserve(fieldscount());
954 char name[NC_MAX_NAME + 1];
955 T v;
956 for (std::size_t i = 0; i < fieldscount(); ++i) {
957 check(nc_inq_enum_member(path->parent->id, path->id, i, name, &v));
958 res.emplace_back(name, v);
959 }
960 return res;
961 }
962
964 nc_type basetype() const {
965 if (!fields_read) {
966 read_fields();
967 }
968 return basetype_m;
969 }
970
972 std::size_t fieldscount() const {
973 if (!fields_read) {
974 read_fields();
975 }
976 return fieldscount_m;
977 }
978
980 std::size_t memberscount() const {
981 if (!fields_read) {
982 read_fields();
983 }
984 return fieldscount_m;
985 }
986
988 std::size_t bytes_size() const {
989 if (!fields_read) {
990 read_fields();
991 }
992 return size_m;
993 }
994
996 int typeclass() const { // NC_VLEN, NC_OPAQUE, NC_ENUM, or NC_COMPOUND
997 if (!fields_read) {
998 read_fields();
999 }
1000 return typeclass_m;
1001 }
1002};
1003
1004template<typename T>
1008 const std::size_t size;
1010 const T* data;
1011
1012 VLenElement() : size(0), data(nullptr) {}
1013 ~VLenElement() { std::free(const_cast<T*>(data)); }
1014};
1015
1017class Variable final : public detail::Object {
1018 friend class Group;
1019 friend class Maybe<Variable>;
1020
1021 private:
1022 explicit Variable(std::shared_ptr<detail::Path> path_p) : detail::Object(std::move(path_p)) {}
1023
1024 std::vector<int> dimension_ids() const {
1025 const auto count = dimension_count();
1026 if (count == 0) {
1027 return {};
1028 }
1029 std::vector<int> ids(count);
1030 check(nc_inq_vardimid(path->parent->id, path->id, detail::data_or_null(ids)));
1031 return ids;
1032 }
1033
1034 static std::string type_name(int ncid_p, nc_type type_p) {
1035 char name[NC_MAX_NAME + 1];
1036 if (nc_inq_type(ncid_p, type_p, name, nullptr) != NC_NOERR) {
1037 return "";
1038 }
1039 return name;
1040 }
1041
1042 static bool same_type_for_copy(int this_ncid, nc_type this_type, int oth_ncid, nc_type oth_type) {
1043 const auto this_is_user = detail::is_user_type(this_type);
1044 const auto oth_is_user = detail::is_user_type(oth_type);
1045 if (this_is_user != oth_is_user) {
1046 return false;
1047 }
1048 if (!this_is_user) {
1049 return this_type == oth_type;
1050 }
1051 return type_name(this_ncid, this_type) == type_name(oth_ncid, oth_type);
1052 }
1053
1054 std::size_t size(const std::size_t* /* start */, const std::size_t* count) const {
1055 int dims_count;
1056 check(nc_inq_varndims(path->parent->id, path->id, &dims_count));
1057
1058 std::size_t res = 1;
1059 for (int i = 0; i < dims_count; ++i) {
1060 res *= count[i];
1061 }
1062 return res;
1063 }
1064
1065 public:
1067 Attribute add_attribute(std::string name) { return Attribute(std::make_shared<detail::Path>(detail::Path{std::move(name), -1, false, path})); }
1068
1071 auto res = add_attribute(a.name());
1072 res.copy_values(a);
1073 return res;
1074 }
1075
1077 Maybe<Attribute> attribute(std::string name) const {
1078 auto res = std::make_shared<detail::Path>(detail::Path{std::move(name), -1, false, path});
1079 const auto ret = nc_inq_attid(path->parent->id, path->id, res->name.c_str(), &res->id);
1080 if (ret != NC_ENOTATT) {
1081 check(ret);
1082 }
1083 return Maybe<Attribute>(std::move(res));
1084 }
1085
1087 std::vector<Attribute> attributes() const {
1088 int count;
1089 check(nc_inq_varnatts(path->parent->id, path->id, &count));
1090 std::vector<Attribute> res;
1091 res.reserve(count);
1092 char name[NC_MAX_NAME + 1];
1093 for (int id = 0; id < count; ++id) {
1094 check(nc_inq_attname(path->parent->id, path->id, id, name));
1095 res.emplace_back(Attribute{std::make_shared<detail::Path>(detail::Path{std::string(name), id, false, path})});
1096 }
1097 return res;
1098 }
1099
1101 bool check_dimensions(const std::vector<std::string>& names) const {
1102 const auto& dims = dimensions();
1103 if (dims.size() != names.size()) {
1104 return false;
1105 }
1106 for (std::size_t i = 0; i < names.size(); ++i) {
1107 if (dims[i].name() != names[i]) {
1108 return false;
1109 }
1110 }
1111 return true;
1112 }
1113
1115 Variable require_dimensions(const std::vector<std::string>& names) const {
1116 if (!check_dimensions(names)) {
1117 throw Exception(NC_EVARMETA, "Unexpected dimensions: " + path->get_full_path());
1118 }
1119 return *this;
1120 }
1121
1123 void copy_attributes(const Variable& v) {
1124 for (const auto& it : v.attributes()) {
1125 add_attribute(it);
1126 }
1127 }
1128
1130 void copy_values(const Variable& v) {
1131 const auto this_type = type();
1132 const auto oth_type = v.type();
1133 if (!same_type_for_copy(path->parent->id, this_type, v.path->parent->id, oth_type)) {
1134 throw Exception(NC_EVARMETA, "Variable types do not match: " + path->get_full_path() + " and " + v.path->get_full_path());
1135 }
1136
1137 std::size_t this_type_len;
1138 check(nc_inq_type(path->parent->id, this_type, nullptr, &this_type_len));
1139 std::size_t oth_type_len;
1140 check(nc_inq_type(v.path->parent->id, oth_type, nullptr, &oth_type_len));
1141
1142 if (this_type_len != oth_type_len) {
1143 throw Exception(NC_EVARMETA, "Variable type sizes do not match: " + path->get_full_path() + " and " + v.path->get_full_path());
1144 }
1145
1146 const auto this_sizes = sizes();
1147 const auto oth_sizes = v.sizes();
1148 if (this_sizes.size() != oth_sizes.size()) {
1149 throw Exception(NC_EVARMETA, "Variable dimension counts do not match: " + path->get_full_path() + " and " + v.path->get_full_path());
1150 }
1151 for (std::size_t i = 0; i < oth_sizes.size(); ++i) {
1152 if (this_sizes[i] != 0 // not unlimited dimension or already written to
1153 && this_sizes[i] != oth_sizes[i]) {
1154 throw Exception(NC_EVARMETA, "Variable sizes do not match: " + path->get_full_path() + " and " + v.path->get_full_path());
1155 }
1156 }
1157
1158 std::vector<std::size_t> index(this_sizes.size(), 0);
1159
1160 std::vector<char> buf(this_type_len * v.size());
1161 if (this_sizes.empty()) {
1162 check(nc_get_var(v.path->parent->id, v.path->id, detail::data_or_null(buf)));
1163 check(nc_put_var(path->parent->id, path->id, detail::data_or_null(buf)));
1164 } else {
1165 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)));
1166 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)));
1167 }
1168 }
1169
1171 std::size_t dimension_count() const {
1172 int count;
1173 check(nc_inq_varndims(path->parent->id, path->id, &count));
1174 return count;
1175 }
1176
1178 std::vector<Dimension> dimensions() const {
1179 const auto ids = dimension_ids();
1180 char name[NC_MAX_NAME + 1];
1181 std::vector<Dimension> res;
1182 res.reserve(ids.size());
1183 std::transform(std::begin(ids), std::end(ids), std::back_inserter(res), [&](int id) {
1184 check(nc_inq_dimname(path->parent->id, id, name));
1185 return Dimension(std::make_shared<detail::Path>(detail::Path{name, id, false, path->parent}));
1186 });
1187 return res;
1188 }
1189
1191 std::vector<std::size_t> get_chunking() const {
1192 int mode;
1193 std::vector<std::size_t> res(dimension_count());
1194 check(nc_inq_var_chunking(path->parent->id, path->id, &mode, detail::data_or_null(res)));
1195 if (mode == NC_CONTIGUOUS) {
1196 res.clear();
1197 }
1198 return res;
1199 }
1200
1202 void set_chunking(const std::vector<std::size_t>& chunks) {
1203 if (chunks.empty()) {
1204 check(nc_def_var_chunking(path->parent->id, path->id, NC_CONTIGUOUS, nullptr));
1205 } else {
1206 check(nc_def_var_chunking(path->parent->id, path->id, NC_CHUNKED, detail::data_or_null(chunks)));
1207 }
1208 }
1209
1211 void set_default_chunking() { check(nc_def_var_chunking(path->parent->id, path->id, NC_CHUNKED, nullptr)); }
1212
1214 std::pair<bool, int> get_compression() const {
1215 int shuffle_filter;
1216 int deflate_filter;
1217 int deflate_level;
1218 check(nc_inq_var_deflate(path->parent->id, path->id, &shuffle_filter, &deflate_filter, &deflate_level));
1219 return std::make_pair(shuffle_filter, deflate_filter ? deflate_level : -1);
1220 }
1221
1223 void set_compression(bool shuffle_filter, int deflate_level) {
1224 check(nc_def_var_deflate(path->parent->id, path->id, shuffle_filter, deflate_level < 0 ? 0 : 1, deflate_level));
1225 }
1226
1228 int get_endianness() const {
1229 int res;
1230 check(nc_inq_var_endian(path->parent->id, path->id, &res));
1231 return res;
1232 }
1233
1235 void set_endianness(int endianness) { check(nc_def_var_endian(path->parent->id, path->id, endianness)); }
1236
1239 int res;
1240 check(nc_inq_var_fletcher32(path->parent->id, path->id, &res));
1241 return res == NC_FLETCHER32;
1242 }
1243
1245 void set_checksum_enabled(bool v) { check(nc_def_var_fletcher32(path->parent->id, path->id, v ? NC_FLETCHER32 : NC_NOCHECKSUM)); }
1246
1247 template<typename T>
1249 std::pair<bool, T> get_fill() const {
1250 int no_fill;
1251 T res;
1252 check(nc_inq_var_fill(path->parent->id, path->id, &no_fill, &res));
1253 return std::make_pair(!no_fill, res);
1254 }
1255
1256 template<typename T>
1258 void set_fill(T v) {
1259 check(nc_def_var_fill(path->parent->id, path->id, 0, &v));
1260 }
1261
1263 void unset_fill() { check(nc_def_var_fill(path->parent->id, path->id, 1, nullptr)); }
1264
1266 Group parent() const { return Group(path->parent); }
1267
1269 void rename(std::string name) {
1270 check(nc_rename_var(path->parent->id, path->id, name.c_str()));
1271 path->name = std::move(name);
1272 }
1273
1275 std::vector<std::size_t> sizes() const {
1276 std::vector<std::size_t> res;
1277 const auto dims = dimension_ids();
1278 res.reserve(dims.size());
1279 std::transform(std::begin(dims), std::end(dims), std::back_inserter(res), [this](int id) {
1280 std::size_t tmp;
1281 check(nc_inq_dimlen(path->parent->id, id, &tmp));
1282 return tmp;
1283 });
1284 return res;
1285 }
1286
1288 std::size_t size() const {
1289 std::size_t res = 1;
1290 std::size_t tmp;
1291 for (const auto id : dimension_ids()) {
1292 check(nc_inq_dimlen(path->parent->id, id, &tmp));
1293 res *= tmp;
1294 }
1295 return res;
1296 }
1297
1298 template<std::size_t N>
1300 std::size_t size(const std::array<std::size_t, N>& start, const std::array<std::size_t, N>& count) const {
1301 return size(&start[0], &count[0]);
1302 }
1303
1305 Variable require_size(std::size_t size_p) const {
1306 if (size_p != size()) {
1307 throw Exception(NC_EVARMETA, "Unexpected variable size: " + path->get_full_path());
1308 }
1309 return *this;
1310 }
1311
1312 template<typename T>
1314 std::vector<T> get() const {
1315 std::vector<T> res(size());
1316 if (res.empty()) {
1317 return res;
1318 }
1320 return res;
1321 }
1322 template<typename T>
1324 T get(const std::size_t* index) const {
1325 T res;
1326 read(&res, index);
1327 return res;
1328 }
1329 template<typename T>
1331 std::vector<T> get(const std::size_t* start, const std::size_t* count) const {
1332 std::vector<T> res(size(start, count));
1333 if (res.empty()) {
1334 return res;
1335 }
1336 read(detail::data_or_null(res), start, count);
1337 return res;
1338 }
1339 template<typename T>
1341 std::vector<T> get(const std::size_t* start, const std::size_t* count, const std::ptrdiff_t* stride) const {
1342 std::vector<T> res(size(start, count));
1343 if (res.empty()) {
1344 return res;
1345 }
1346 read(detail::data_or_null(res), start, count, stride);
1347 return res;
1348 }
1349
1350 template<typename T, std::size_t N>
1352 T get(const std::array<std::size_t, N>& index) const {
1353 return get<T>(&index[0]);
1354 }
1355 template<typename T, std::size_t N>
1357 std::vector<T> get(const std::array<std::size_t, N>& start, const std::array<std::size_t, N>& count) const {
1358 return get<T>(&start[0], &count[0]);
1359 }
1360 template<typename T, std::size_t N>
1362 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 {
1363 return get<T>(&start[0], &count[0], &stride[0]);
1364 }
1365
1366 template<typename T>
1368 void read(T* v) const {
1369 static_assert(!Type<T>::is_atomic || std::is_same<void, T>::value, "Use void or one of the explicitly supported types");
1370 check(nc_get_var(path->parent->id, path->id, v));
1371 }
1372 template<typename T>
1374 void read(T* v, const std::size_t* index) const {
1375 static_assert(!Type<T>::is_atomic || std::is_same<void, T>::value, "Use void or one of the explicitly supported types");
1376 check(nc_get_var1(path->parent->id, path->id, index, v));
1377 }
1378 template<typename T>
1380 void read(T* v, const std::size_t* start, const std::size_t* count) const {
1381 static_assert(!Type<T>::is_atomic || std::is_same<void, T>::value, "Use void or one of the explicitly supported types");
1382 check(nc_get_vara(path->parent->id, path->id, start, count, v));
1383 }
1384 template<typename T>
1386 void read(T* v, const std::size_t* start, const std::size_t* count, const std::ptrdiff_t* stride) const {
1387 static_assert(!Type<T>::is_atomic || std::is_same<void, T>::value, "Use void or one of the explicitly supported types");
1388 check(nc_get_vars(path->parent->id, path->id, start, count, stride, v));
1389 }
1390 template<typename T>
1392 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 {
1393 static_assert(std::is_same<void, T>::value, "NetCDF supports mapped access only for atomic types");
1394 check(nc_get_varm(path->parent->id, path->id, start, count, stride, imap, v));
1395 }
1396
1397 template<typename T, std::size_t N>
1399 void read(T* v, const std::array<std::size_t, N>& index) const {
1400 read(v, &index[0]);
1401 }
1402 template<typename T, std::size_t N>
1404 void read(T* v, const std::array<std::size_t, N>& start, const std::array<std::size_t, N>& count) const {
1405 read(v, &start[0], &count[0]);
1406 }
1407 template<typename T, std::size_t N>
1409 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 {
1410 read(v, &start[0], &count[0], &stride[0]);
1411 }
1412 template<typename T, std::size_t N>
1414 void read(T* v,
1415 const std::array<std::size_t, N>& start,
1416 const std::array<std::size_t, N>& count,
1417 const std::array<std::ptrdiff_t, N>& stride,
1418 const std::array<std::ptrdiff_t, N>& imap) const {
1419 read(v, &start[0], &count[0], &stride[0], &imap[0]);
1420 }
1421
1422 template<typename T>
1424 void set(const std::vector<T>& v) {
1426 }
1427 template<typename T>
1429 typename std::enable_if<Type<T>::is_atomic || std::is_same<const char*, T>::value || std::is_same<char*, T>::value, void>::type set(
1430 T v, const std::size_t* index) {
1431 write(&v, index);
1432 }
1433 template<typename T>
1435 typename std::enable_if<!Type<T>::is_atomic && !std::is_same<const char*, T>::value && !std::is_same<char*, T>::value, void>::type set(
1436 const T& v, const std::size_t* index) {
1437 write(&v, index);
1438 }
1439 template<typename T>
1441 void set(const std::vector<T>& v, const std::size_t* start, const std::size_t* count) {
1442 write(detail::data_or_null(v), start, count);
1443 }
1444 template<typename T>
1446 void set(const std::vector<T>& v, const std::size_t* start, const std::size_t* count, const std::ptrdiff_t* stride) {
1447 write(detail::data_or_null(v), start, count, stride);
1448 }
1449 template<typename T>
1451 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) {
1452 write(detail::data_or_null(v), start, count, stride, imap);
1453 }
1454
1455 template<typename T, std::size_t N>
1457 void set(const T v, const std::array<std::size_t, N>& index) {
1458 set(v, &index[0]);
1459 }
1460 template<typename T, std::size_t N>
1462 void set(const std::vector<T>& v, const std::array<std::size_t, N>& start, const std::array<std::size_t, N>& count) {
1463 set(v, &start[0], &count[0]);
1464 }
1465 template<typename T, std::size_t N>
1467 void set(const std::vector<T>& v,
1468 const std::array<std::size_t, N>& start,
1469 const std::array<std::size_t, N>& count,
1470 const std::array<std::ptrdiff_t, N>& stride) {
1471 set(v, &start[0], &count[0], &stride[0]);
1472 }
1473 template<typename T, std::size_t N>
1475 void set(const std::vector<T>& v,
1476 const std::array<std::size_t, N>& start,
1477 const std::array<std::size_t, N>& count,
1478 const std::array<std::ptrdiff_t, N>& stride,
1479 const std::array<std::ptrdiff_t, N>& imap) {
1480 set(v, &start[0], &count[0], &stride[0], &imap[0]);
1481 }
1482
1483 template<typename T>
1485 void write(const T* v) {
1486 static_assert(!Type<T>::is_atomic || std::is_same<void, T>::value, "Use void or one of the explicitly supported types");
1487 check(nc_put_var(path->parent->id, path->id, v));
1488 }
1489 template<typename T>
1491 void write(const T* v, const std::size_t* index) {
1492 static_assert(!Type<T>::is_atomic || std::is_same<void, T>::value, "Use void or one of the explicitly supported types");
1493 check(nc_put_var1(path->parent->id, path->id, index, v));
1494 }
1495 template<typename T>
1497 void write(const T* v, const std::size_t* start, const std::size_t* count) {
1498 static_assert(!Type<T>::is_atomic || std::is_same<void, T>::value, "Use void or one of the explicitly supported types");
1499 check(nc_put_vara(path->parent->id, path->id, start, count, v));
1500 }
1501 template<typename T>
1503 void write(const T* v, const std::size_t* start, const std::size_t* count, const std::ptrdiff_t* stride) {
1504 static_assert(!Type<T>::is_atomic || std::is_same<void, T>::value, "Use void or one of the explicitly supported types");
1505 check(nc_put_vars(path->parent->id, path->id, start, count, stride, v));
1506 }
1507 template<typename T>
1509 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) {
1510 static_assert(std::is_same<void, T>::value, "NetCDF supports mapped access only for atomic types");
1511 check(nc_put_varm(path->parent->id, path->id, start, count, stride, imap, v));
1512 }
1513
1514 template<typename T, std::size_t N>
1516 void write(const T* v, const std::array<std::size_t, N>& index) {
1517 write(v, &index[0]);
1518 }
1519 template<typename T, std::size_t N>
1521 void write(const T* v, const std::array<std::size_t, N>& start, const std::array<std::size_t, N>& count) {
1522 write(v, &start[0], &count[0]);
1523 }
1524 template<typename T, std::size_t N>
1526 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) {
1527 write(v, &start[0], &count[0], &stride[0]);
1528 }
1529 template<typename T, std::size_t N>
1531 void write(const T* v,
1532 const std::array<std::size_t, N>& start,
1533 const std::array<std::size_t, N>& count,
1534 const std::array<std::ptrdiff_t, N>& stride,
1535 const std::array<std::ptrdiff_t, N>& imap) {
1536 write(v, &start[0], &count[0], &stride[0], &imap[0]);
1537 }
1538
1540 nc_type type() const {
1541 nc_type res;
1542 check(nc_inq_vartype(path->parent->id, path->id, &res));
1543 return res;
1544 }
1545
1547 std::string type_name() const {
1548 char name[NC_MAX_NAME + 1];
1549 check(nc_inq_type(path->parent->id, type(), name, nullptr));
1550 return name;
1551 }
1552
1554 Variable require_type(const std::string& name) const {
1555 const auto name_l = type_name();
1556 if (name_l != name) {
1557 throw Exception(NC_EVARMETA, "Unexpected type '" + name_l + "': " + path->get_full_path());
1558 }
1559 return *this;
1560 }
1561
1562 template<typename T>
1564 Variable require_compound(std::size_t fieldscount) const {
1565 const auto user_type_l = user_type().require();
1566 if (user_type_l.typeclass() != NC_COMPOUND) {
1567 throw Exception(NC_EVARMETA, "Type '" + user_type_l.name() + "' is not a compound: " + path->get_full_path());
1568 }
1569 if (user_type_l.bytes_size() != sizeof(T) || user_type_l.fieldscount() != fieldscount) {
1570 throw Exception(NC_EVARMETA, "Unexpected size for type '" + user_type_l.name() + "': " + path->get_full_path());
1571 }
1572 return *this;
1573 }
1574
1577 const auto id = type();
1578 char name[NC_MAX_NAME + 1];
1579 check(nc_inq_type(path->parent->id, id, name, nullptr));
1580 auto res = std::make_shared<detail::Path>(detail::Path{name, id, false, path->parent});
1581 if (!detail::is_user_type(res->id)) {
1582 res->id = -1;
1583 res->name = path->name + " not of user type";
1584 }
1585 return Maybe<UserType>(std::move(res));
1586 }
1587};
1588
1589inline void Attribute::copy_values(const Attribute& a) {
1590 auto type_l = a.type();
1591 std::size_t type_len;
1592 char name[NC_MAX_NAME + 1];
1593 check(nc_inq_type(a.ncid(), type_l, name, &type_len));
1594
1595 if (detail::is_user_type(type_l) && ncid() != a.ncid()) {
1596 const auto this_type = ([this, name]() {
1597 if (is_group_attribute()) {
1598 return parent_group().require().user_type(name).require();
1599 }
1600 return parent_variable().require().parent().user_type(name).require();
1601 })();
1602 type_l = this_type.id();
1603 if (this_type.bytes_size() != type_len) {
1604 throw Exception(NC_EATTMETA, "Attribute type sizes do not match: " + path->get_full_path() + " and " + a.path->get_full_path());
1605 }
1606 }
1607
1608 const auto oth_size = a.size();
1609 std::vector<char> buf(type_len * oth_size);
1610 if (!buf.empty()) {
1611 check(nc_get_att(a.ncid(), a.othid(), a.name().c_str(), detail::data_or_null(buf)));
1612 }
1613 check(nc_put_att(ncid(), othid(), path->name.c_str(), type_l, oth_size, detail::data_or_null(buf)));
1614}
1615
1616template<>
1617inline std::vector<std::string> Attribute::get() const {
1618 std::size_t len;
1619 check(nc_inq_attlen(ncid(), othid(), path->name.c_str(), &len));
1620 if (len == 0) {
1621 return {};
1622 }
1623 char** buf = static_cast<char**>(std::malloc(len * sizeof(char*)));
1624 check(nc_get_att_string(ncid(), othid(), path->name.c_str(), buf));
1625 return detail::process_char_vector(buf, len);
1626}
1627
1628template<>
1629inline void Attribute::set(const std::vector<std::string>& v) {
1630 auto buf = detail::process_string_vector(v);
1631 check(nc_put_att_string(ncid(), othid(), path->name.c_str(), buf.size(), detail::data_or_null(buf)));
1632}
1633
1634template<>
1635inline void Attribute::set(const std::vector<const char*>& v) {
1636 check(nc_put_att_string(ncid(), othid(), path->name.c_str(), v.size(), const_cast<const char**>(detail::data_or_null(v))));
1637}
1638
1639template<>
1640inline void Attribute::set(const std::vector<char*>& v) {
1641 check(nc_put_att_string(ncid(), othid(), path->name.c_str(), v.size(), const_cast<const char**>(detail::data_or_null(v))));
1642}
1643
1644template<typename T>
1645inline void Attribute::set(const std::vector<T>& v, const UserType& type) {
1646 static_assert(!Type<T>::is_atomic, "Should be of user type");
1647 check(nc_put_att(ncid(), othid(), path->name.c_str(), type.id(), v.size(), detail::data_or_null(v)));
1648}
1649template<typename T>
1650inline void Attribute::set(const T& v, const UserType& type) {
1651 static_assert(!Type<T>::is_atomic, "Should be of user type");
1652 check(nc_put_att(ncid(), othid(), path->name.c_str(), type.id(), 1, &v));
1653}
1654
1656 const auto id = type();
1657 char name[NC_MAX_NAME + 1];
1658 check(nc_inq_type(path->parent->id, id, name, nullptr));
1659 auto res = std::make_shared<detail::Path>(detail::Path{name, id, false, path->parent});
1660 if (!detail::is_user_type(res->id)) {
1661 res->id = -1;
1662 res->name = path->name + " not of user type";
1663 }
1664 return Maybe<UserType>(std::move(res));
1665}
1666
1667inline Group Dimension::parent() const { return Group(path->parent); }
1668
1669inline UserType Group::add_type_compound(std::string name, std::size_t bytes_size) {
1670 int id;
1671 check(nc_def_compound(path->id, bytes_size, name.c_str(), &id));
1672 return UserType(std::make_shared<detail::Path>(detail::Path{std::move(name), id, false, path}));
1673}
1674template<typename T>
1675inline UserType Group::add_type_compound(std::string name) {
1676 return add_type_compound(std::move(name), sizeof(T));
1677}
1678
1679template<typename T>
1680inline UserType Group::add_type_enum(std::string name) {
1681 static_assert(std::is_enum<T>::value, "should be an enum class");
1682 return add_type_enum(std::move(name), Type<typename std::underlying_type<T>::type>::id);
1683}
1684inline UserType Group::add_type_enum(std::string name, nc_type basetype) {
1685 int id;
1686 check(nc_def_enum(path->id, basetype, name.c_str(), &id));
1687 return UserType(std::make_shared<detail::Path>(detail::Path{std::move(name), id, false, path}));
1688}
1689
1690template<typename T>
1691inline UserType Group::add_type_opaque(std::string name) {
1692 return add_type_opaque(std::move(name), sizeof(T));
1693}
1694inline UserType Group::add_type_opaque(std::string name, std::size_t bytes_size) {
1695 int id;
1696 check(nc_def_opaque(path->id, bytes_size, name.c_str(), &id));
1697 return UserType(std::make_shared<detail::Path>(detail::Path{std::move(name), id, false, path}));
1698}
1699
1700template<typename T>
1701inline UserType Group::add_type_vlen(std::string name) {
1702 return add_type_vlen(std::move(name), Type<T>::id);
1703}
1704inline UserType Group::add_type_vlen(std::string name, nc_type basetype) {
1705 int id;
1706 check(nc_def_vlen(path->id, name.c_str(), basetype, &id));
1707 return UserType(std::make_shared<detail::Path>(detail::Path{std::move(name), id, false, path}));
1708}
1709
1711 switch (t.typeclass()) {
1712 case NC_VLEN:
1713 return add_type_vlen(t.name(), t.basetype());
1714 case NC_OPAQUE:
1715 return add_type_opaque(t.name(), t.bytes_size());
1716 case NC_ENUM: {
1717 auto res = add_type_enum(t.name(), t.basetype());
1718 std::vector<char> buf(t.bytes_size());
1719 char name[NC_MAX_NAME + 1];
1720 for (int i = 0; i < static_cast<int>(t.fieldscount()); ++i) {
1721 check(nc_inq_enum_member(t.path->parent->id, t.id(), i, name, &buf[0]));
1722 check(nc_insert_enum(path->id, res.id(), name, &buf[0]));
1723 }
1724 return res;
1725 }
1726 case NC_COMPOUND: {
1727 auto res = add_type_compound(t.name(), t.bytes_size());
1728 for (const auto& field : t.compound_fields()) {
1729 if (field.dimensions.empty()) {
1730 check(nc_insert_compound(path->id, res.id(), field.name.c_str(), field.offset, field.type));
1731 } else {
1732 check(nc_insert_array_compound(path->id, res.id(), field.name.c_str(), field.offset, field.type, static_cast<int>(field.dimensions.size()),
1733 detail::data_or_null(field.dimensions)));
1734 }
1735 }
1736 return res;
1737 }
1738 default:
1739 throw Exception(0, "Invalid user type class: " + t.path->get_full_path());
1740 }
1741}
1742
1743inline void Group::copy_user_types(const Group& g) {
1744 for (const auto& it : g.user_types()) {
1745 add_user_type(it);
1746 }
1747}
1748
1749inline void Group::copy_variables(const Group& g, bool variable_values) {
1750 for (const auto& it : g.variables()) {
1751 add_variable(it, variable_values);
1752 }
1753}
1754
1755inline Variable Group::add_variable(std::string name, const UserType& type, const std::vector<int>& dims) {
1756 return add_variable(std::move(name), type.id(), dims);
1757}
1758inline Variable Group::add_variable(std::string name, const UserType& type, const std::vector<Dimension>& dims) {
1759 return add_variable(std::move(name), type.id(), dims);
1760}
1761inline Variable Group::add_variable(std::string name, const UserType& type, const std::vector<std::string>& dims) {
1762 return add_variable(std::move(name), type.id(), dims);
1763}
1764
1765template<typename T>
1766inline Variable Group::add_dimension_variable(std::string name) {
1767 return add_dimension_variable<T>(add_dimension(std::move(name)));
1768}
1769template<typename T>
1770inline Variable Group::add_dimension_variable(std::string name, std::size_t len) {
1771 return add_dimension_variable<T>(add_dimension(std::move(name), len));
1772}
1773template<typename T>
1775 return add_variable<T>(d.name(), {d});
1776}
1777
1778inline Variable Group::add_variable(std::string name, nc_type type, const std::vector<int>& dims) {
1779 int id;
1780 check(nc_def_var(path->id, name.c_str(), type, static_cast<int>(dims.size()), detail::data_or_null(dims), &id));
1781 return Variable(std::make_shared<detail::Path>(detail::Path{std::move(name), id, false, path}));
1782}
1783inline Variable Group::add_variable(std::string name, nc_type type, const std::vector<Dimension>& dims) {
1784 std::vector<int> dimids(dims.size());
1785 std::transform(std::begin(dims), std::end(dims), std::begin(dimids), [](const Dimension& d) { return d.path->id; });
1786 return add_variable(std::move(name), type, dimids);
1787}
1788inline Variable Group::add_variable(std::string name, nc_type type, const std::vector<std::string>& dims) {
1789 std::vector<int> dimids(dims.size());
1790 std::transform(std::begin(dims), std::end(dims), std::begin(dimids), [this](const std::string& s) { return dimension(s).require().path->id; });
1791 return add_variable(std::move(name), type, dimids);
1792}
1793template<typename T>
1794inline Variable Group::add_variable(std::string name, const std::vector<int>& dims) {
1795 return add_variable(std::move(name), Type<T>::id, dims);
1796}
1797template<typename T>
1798inline Variable Group::add_variable(std::string name, const std::vector<Dimension>& dims) {
1799 return add_variable(std::move(name), Type<T>::id, dims);
1800}
1801template<typename T>
1802inline Variable Group::add_variable(std::string name, const std::vector<std::string>& dims) {
1803 return add_variable(std::move(name), Type<T>::id, dims);
1804}
1805template<>
1806inline Variable Group::add_variable<std::string>(std::string name, const std::vector<int>& dims) {
1807 return add_variable(std::move(name), Type<char*>::id, dims);
1808}
1809template<>
1810inline Variable Group::add_variable<std::string>(std::string name, const std::vector<Dimension>& dims) {
1811 return add_variable(std::move(name), Type<char*>::id, dims);
1812}
1813template<>
1814inline Variable Group::add_variable<std::string>(std::string name, const std::vector<std::string>& dims) {
1815 return add_variable(std::move(name), Type<char*>::id, dims);
1816}
1817inline Variable Group::add_variable(std::string name, const UserType& type, std::initializer_list<int> dims) {
1818 return add_variable(std::move(name), type, std::vector<int>(dims));
1819}
1820inline Variable Group::add_variable(std::string name, const UserType& type, std::initializer_list<Dimension> dims) {
1821 return add_variable(std::move(name), type, std::vector<Dimension>(dims));
1822}
1823inline Variable Group::add_variable(std::string name, const UserType& type, std::initializer_list<std::string> dims) {
1824 return add_variable(std::move(name), type, std::vector<std::string>(dims));
1825}
1826inline Variable Group::add_variable(std::string name, nc_type type, std::initializer_list<int> dims) {
1827 return add_variable(std::move(name), type, std::vector<int>(dims));
1828}
1829inline Variable Group::add_variable(std::string name, nc_type type, std::initializer_list<Dimension> dims) {
1830 return add_variable(std::move(name), type, std::vector<Dimension>(dims));
1831}
1832inline Variable Group::add_variable(std::string name, nc_type type, std::initializer_list<std::string> dims) {
1833 return add_variable(std::move(name), type, std::vector<std::string>(dims));
1834}
1835template<typename T>
1836inline Variable Group::add_variable(std::string name, std::initializer_list<int> dims) {
1837 return add_variable<T>(std::move(name), std::vector<int>(dims));
1838}
1839template<typename T>
1840inline Variable Group::add_variable(std::string name, std::initializer_list<Dimension> dims) {
1841 return add_variable<T>(std::move(name), std::vector<Dimension>(dims));
1842}
1843template<typename T>
1844inline Variable Group::add_variable(std::string name, std::initializer_list<std::string> dims) {
1845 return add_variable<T>(std::move(name), std::vector<std::string>(dims));
1846}
1847inline Variable Group::add_variable(const Variable& v, bool with_values) {
1848 const auto orig_dims = v.dimensions();
1849 std::vector<std::string> dims;
1850 dims.reserve(orig_dims.size());
1851 std::transform(std::begin(orig_dims), std::end(orig_dims), std::back_inserter(dims), [](const Dimension& d) { return d.name(); });
1852 auto type = v.type();
1853 if (detail::is_user_type(type) && v.path->parent->id != path->id) {
1854 type = user_type(v.user_type().require().name()).require().id();
1855 }
1856 auto res = add_variable(v.name(), type, dims);
1857 res.copy_attributes(v);
1858
1859 const auto comp = v.get_compression();
1860 res.set_compression(comp.first, comp.second);
1861
1862 res.set_chunking(v.get_chunking());
1863 if (v.get_checksum_enabled()) {
1864 res.set_checksum_enabled(true);
1865 } // otherwise chunking might get reset
1866
1867 if (!detail::is_user_type(type)) {
1868 // fill values are handled weirdly by NetCDF
1869 // _FillValue has been copied via copy_attributes already
1870 // _NoFill seems to only properly work for fundamental types
1871 int no_fill;
1872 check(nc_inq_var_fill(v.path->parent->id, v.path->id, &no_fill, nullptr));
1873 if (no_fill) {
1874 check(nc_def_var_fill(res.path->parent->id, res.path->id, 1, nullptr));
1875 }
1876 }
1877
1878 if (with_values) {
1879 res.copy_values(v);
1880 }
1881 return res;
1882}
1883
1884inline std::vector<UserType> Group::user_types() const {
1885 int count;
1886 check(nc_inq_typeids(path->id, &count, nullptr));
1887 if (count == 0) {
1888 return {};
1889 }
1890
1891 std::vector<int> ids(count);
1892 check(nc_inq_typeids(path->id, nullptr, detail::data_or_null(ids)));
1893
1894 char name[NC_MAX_NAME + 1];
1895 std::vector<UserType> res;
1896 for (const auto id : ids) {
1897 if (detail::is_user_type(id)) {
1898 check(nc_inq_type(path->id, id, name, nullptr));
1899 res.emplace_back(UserType(std::make_shared<detail::Path>(detail::Path{name, id, false, path})));
1900 }
1901 }
1902 return res;
1903}
1904
1905inline std::vector<Variable> Group::variables() const {
1906 int count;
1907 check(nc_inq_varids(path->id, &count, nullptr));
1908 if (count == 0) {
1909 return {};
1910 }
1911
1912 std::vector<int> ids(count);
1913 check(nc_inq_varids(path->id, nullptr, detail::data_or_null(ids)));
1914
1915 char name[NC_MAX_NAME + 1];
1916 std::vector<Variable> res;
1917 res.reserve(count);
1918 std::transform(std::begin(ids), std::end(ids), std::back_inserter(res), [&](int id) {
1919 check(nc_inq_varname(path->id, id, name));
1920 return Variable(std::make_shared<detail::Path>(detail::Path{name, id, false, path}));
1921 });
1922 return res;
1923}
1924
1925template<>
1926inline std::vector<std::string> Variable::get() const {
1927 std::vector<char*> buf(size());
1928 if (buf.empty()) {
1929 return {};
1930 }
1931 check(nc_get_var_string(path->parent->id, path->id, detail::data_or_null(buf)));
1932 return detail::process_char_vector(buf);
1933}
1934template<>
1935inline std::string Variable::get(const std::size_t* index) const {
1936 char* buf;
1937 check(nc_get_var1_string(path->parent->id, path->id, index, &buf));
1938 std::string res(buf);
1939 nc_free_string(1, &buf);
1940 return res;
1941}
1942template<>
1943inline std::vector<std::string> Variable::get(const std::size_t* start, const std::size_t* count) const {
1944 std::vector<char*> buf(size(start, count));
1945 if (buf.empty()) {
1946 return {};
1947 }
1948 check(nc_get_vara_string(path->parent->id, path->id, start, count, detail::data_or_null(buf)));
1949 return detail::process_char_vector(buf);
1950}
1951template<>
1952inline std::vector<std::string> Variable::get(const std::size_t* start, const std::size_t* count, const std::ptrdiff_t* stride) const {
1953 std::vector<char*> buf(size(start, count));
1954 if (buf.empty()) {
1955 return {};
1956 }
1957 check(nc_get_vars_string(path->parent->id, path->id, start, count, stride, detail::data_or_null(buf)));
1958 return detail::process_char_vector(buf);
1959}
1960
1961template<>
1962inline void Variable::set(const std::vector<std::string>& v) {
1963 auto buf = detail::process_string_vector(v);
1964 check(nc_put_var_string(path->parent->id, path->id, detail::data_or_null(buf)));
1965}
1966template<>
1967inline void Variable::set(const std::string& v, const std::size_t* index) {
1968 const char* t = v.c_str();
1969 check(nc_put_var1_string(path->parent->id, path->id, index, &t));
1970}
1971template<>
1972inline void Variable::set(const char* v, const std::size_t* index) {
1973 check(nc_put_var1_string(path->parent->id, path->id, index, &v));
1974}
1975template<>
1976inline void Variable::set(const std::vector<std::string>& v, const std::size_t* start, const std::size_t* count) {
1977 auto buf = detail::process_string_vector(v);
1978 check(nc_put_vara_string(path->parent->id, path->id, start, count, detail::data_or_null(buf)));
1979}
1980template<>
1981inline void Variable::set(const std::vector<std::string>& v, const std::size_t* start, const std::size_t* count, const std::ptrdiff_t* stride) {
1982 auto buf = detail::process_string_vector(v);
1983 check(nc_put_vars_string(path->parent->id, path->id, start, count, stride, detail::data_or_null(buf)));
1984}
1985template<>
1986inline void Variable::set(
1987 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) {
1988 auto buf = detail::process_string_vector(v);
1989 check(nc_put_varm_string(path->parent->id, path->id, start, count, stride, imap, detail::data_or_null(buf)));
1990}
1991
1992#define NETCDFPP_IMPL_ATTRIBUTE_GET(type, name) \
1993 template<> \
1994 inline int Attribute::get_internal(int ncid_p, int othid_p, const char* name_p, type* v) const { \
1995 return nc_get_att##name(ncid_p, othid_p, name_p, v); \
1996 }
1997
1998#define NETCDFPP_IMPL_ATTRIBUTE_SET(type, name) \
1999 template<> \
2000 inline int Attribute::set_internal(int ncid_p, int othid_p, const char* name_p, std::size_t len, const type* v) { \
2001 return nc_put_att##name(ncid_p, othid_p, name_p, Type<type>::id, len, v); \
2002 }
2003
2004#define NETCDFPP_IMPL_VARIABLE_READ(type, name) \
2005 template<> \
2006 inline void Variable::read(type* v) const { \
2007 check(nc_get_var##name(path->parent->id, path->id, v)); \
2008 } \
2009 template<> \
2010 inline void Variable::read(type* v, const std::size_t* index) const { \
2011 check(nc_get_var1##name(path->parent->id, path->id, index, v)); \
2012 } \
2013 template<> \
2014 inline void Variable::read(type* v, const std::size_t* start, const std::size_t* count) const { \
2015 check(nc_get_vara##name(path->parent->id, path->id, start, count, v)); \
2016 } \
2017 template<> \
2018 inline void Variable::read(type* v, const std::size_t* start, const std::size_t* count, const std::ptrdiff_t* stride) const { \
2019 check(nc_get_vars##name(path->parent->id, path->id, start, count, stride, v)); \
2020 } \
2021 template<> \
2022 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 { \
2023 check(nc_get_varm##name(path->parent->id, path->id, start, count, stride, imap, v)); \
2024 }
2025
2026#define NETCDFPP_IMPL_VARIABLE_WRITE(type, name) \
2027 template<> \
2028 inline void Variable::write(const type* v) { \
2029 check(nc_put_var##name(path->parent->id, path->id, v)); \
2030 } \
2031 template<> \
2032 inline void Variable::write(const type* v, const std::size_t* index) { \
2033 check(nc_put_var1##name(path->parent->id, path->id, index, v)); \
2034 } \
2035 template<> \
2036 inline void Variable::write(const type* v, const std::size_t* start, const std::size_t* count) { \
2037 check(nc_put_vara##name(path->parent->id, path->id, start, count, v)); \
2038 } \
2039 template<> \
2040 inline void Variable::write(const type* v, const std::size_t* start, const std::size_t* count, const std::ptrdiff_t* stride) { \
2041 check(nc_put_vars##name(path->parent->id, path->id, start, count, stride, v)); \
2042 } \
2043 template<> \
2044 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) { \
2045 check(nc_put_varm##name(path->parent->id, path->id, start, count, stride, imap, v)); \
2046 }
2047
2048#define NETCDFPP_IMPL_ALL(type, name) \
2049 NETCDFPP_IMPL_ATTRIBUTE_GET(type, name) \
2050 NETCDFPP_IMPL_ATTRIBUTE_SET(type, name) \
2051 NETCDFPP_IMPL_VARIABLE_READ(type, name) \
2052 NETCDFPP_IMPL_VARIABLE_WRITE(type, name)
2053
2054// the following maps each type to its corresponding nc_*var* (key is the type used as parameter type in the respective nc_*var*)
2055
2056// NETCDFPP_IMPL_ATTRIBUTE_GET(char, _text) handled by Attribute::get_string
2057template<>
2058inline int Attribute::set_internal(int ncid_p, int othid_p, const char* name_p, std::size_t len, const char* v) {
2059 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_*
2060}
2061NETCDFPP_IMPL_VARIABLE_READ(char, _text)
2063
2064NETCDFPP_IMPL_ALL(double, _double)
2065NETCDFPP_IMPL_ALL(float, _float)
2066NETCDFPP_IMPL_ALL(int, _int)
2067NETCDFPP_IMPL_ALL(long long, _longlong)
2068NETCDFPP_IMPL_ALL(long, _long)
2069NETCDFPP_IMPL_ALL(short, _short)
2070NETCDFPP_IMPL_ALL(signed char, _schar)
2071NETCDFPP_IMPL_ALL(unsigned char, _uchar)
2072NETCDFPP_IMPL_ALL(unsigned int, _uint)
2073NETCDFPP_IMPL_ALL(unsigned long long, _ulonglong)
2074NETCDFPP_IMPL_ALL(unsigned short, _ushort)
2075
2076} // namespace netCDF
2077
2078#endif
NetCDF attribute attached to a group or variable.
Definition netcdfpp.h:329
void set(const std::vector< T > &v)
Writes a vector of atomic values to the attribute.
Definition netcdfpp.h:420
std::vector< T > get() const
Reads the attribute as a vector of values.
Definition netcdfpp.h:371
void copy_values(const Attribute &a)
Copies the value and type metadata from another attribute.
Definition netcdfpp.h:1589
Attribute require_type(const std::string &name) const
Returns this attribute or throws if its NetCDF type name differs.
Definition netcdfpp.h:472
std::size_t size() const
Returns the number of values stored in the attribute.
Definition netcdfpp.h:451
Maybe< Variable > parent_variable() const
Returns the parent variable for variable attributes.
Definition netcdfpp.h:405
std::string type_name() const
Returns the NetCDF type name of the attribute.
Definition netcdfpp.h:465
Maybe< UserType > user_type() const
Returns the user-defined type for this attribute, if it has one.
Definition netcdfpp.h:1655
std::enable_if< std::is_same< std::string, T >::value, void >::type set(T v)
Writes a string attribute.
Definition netcdfpp.h:434
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:439
std::string get_string() const
Reads a classic NetCDF text attribute as a C++ string.
Definition netcdfpp.h:384
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:444
nc_type type() const
Returns the NetCDF type id of the attribute.
Definition netcdfpp.h:458
Maybe< Group > parent_group() const
Returns the parent group for group attributes.
Definition netcdfpp.h:397
void rename(std::string name)
Renames the attribute in place.
Definition netcdfpp.h:413
bool is_group_attribute() const
Returns true when this attribute is attached to a group rather than a variable.
Definition netcdfpp.h:394
NetCDF dimension.
Definition netcdfpp.h:485
friend class Group
Definition netcdfpp.h:486
std::size_t size() const
Returns the current dimension length.
Definition netcdfpp.h:498
void rename(std::string name)
Renames the dimension in place.
Definition netcdfpp.h:522
bool is_unlimited() const
Returns true when the dimension is unlimited.
Definition netcdfpp.h:507
Group parent() const
Returns the parent group.
Definition netcdfpp.h:1667
Exception thrown when a NetCDF-C call fails.
Definition netcdfpp.h:158
Exception(int r, std::string s)
Creates an exception with the NetCDF return code and a complete message.
Definition netcdfpp.h:164
int return_code() const
Returns the NetCDF-C return code that caused the exception.
Definition netcdfpp.h:167
NetCDF file handle and root group.
Definition netcdfpp.h:822
File(const char *filename, char mode)
Opens or creates a file.
Definition netcdfpp.h:834
File()
Creates a closed file handle.
Definition netcdfpp.h:825
void close()
Closes the file if it is open.
Definition netcdfpp.h:859
File(std::string filename, char mode)
Opens or creates a file.
Definition netcdfpp.h:831
void sync() const
Flushes buffered changes to disk.
Definition netcdfpp.h:870
bool is_open() const
Returns true when this object owns an open NetCDF file id.
Definition netcdfpp.h:867
~File()
Closes the file if it is open.
Definition netcdfpp.h:837
void open(std::string filename, char mode)
Opens or creates a file, closing any currently open file first.
Definition netcdfpp.h:840
NetCDF group, including the root group represented by File.
Definition netcdfpp.h:529
std::vector< Dimension > dimensions() const
Returns all dimensions visible in this group.
Definition netcdfpp.h:724
Variable add_dimension_variable(std::string name)
Defines an unlimited dimension and a variable with the same name.
Definition netcdfpp.h:1766
Attribute add_attribute(std::string name)
Defines an attribute handle. The attribute is created when a value is written.
Definition netcdfpp.h:541
Group(std::shared_ptr< detail::Path > path_p)
Definition netcdfpp.h:537
std::vector< Variable > variables() const
Returns all variables in this group.
Definition netcdfpp.h:1905
UserType add_user_type(const UserType &t)
Copies a user-defined type into this group.
Definition netcdfpp.h:1710
void copy_attributes(const Group &g)
Copies all group attributes from another group.
Definition netcdfpp.h:682
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:1755
Dimension add_dimension(std::string name, std::size_t len)
Defines a fixed-size dimension.
Definition netcdfpp.h:554
Attribute add_attribute(const Attribute &a)
Copies an attribute into this group.
Definition netcdfpp.h:544
UserType add_type_opaque(std::string name, std::size_t bytes_size)
Defines an opaque user type.
Definition netcdfpp.h:1694
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:1669
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:1704
UserType add_type_enum(std::string name, nc_type basetype)
Defines an enum user type with an explicit NetCDF base type.
Definition netcdfpp.h:1684
Maybe< Dimension > dimension(std::string name) const
Looks up a dimension by name.
Definition netcdfpp.h:714
std::vector< Group > groups() const
Returns all direct child groups.
Definition netcdfpp.h:755
void copy_dimensions(const Group &g)
Copies all dimensions from another group.
Definition netcdfpp.h:688
void copy_groups(const Group &g, bool variable_values=false)
Copies child groups from another group.
Definition netcdfpp.h:702
void copy_variables(const Group &g, bool variable_values=false)
Copies variables from another group.
Definition netcdfpp.h:1749
void copy_user_types(const Group &g)
Copies user-defined types from another group.
Definition netcdfpp.h:1743
Maybe< UserType > user_type(std::string name) const
Looks up a user-defined type by name.
Definition netcdfpp.h:790
void copy_from(const Group &g, bool variable_values=false)
Copies attributes, dimensions, user types, variables, and child groups.
Definition netcdfpp.h:694
void rename(std::string name)
Renames the group in place.
Definition netcdfpp.h:776
friend class Variable
Definition netcdfpp.h:534
Maybe< Group > parent() const
Returns the parent group, if this is not the root group.
Definition netcdfpp.h:782
Group add_group(const Group &g, bool variable_values=false)
Copies a group into this group.
Definition netcdfpp.h:579
friend class Dimension
Definition netcdfpp.h:531
friend class Attribute
Definition netcdfpp.h:530
Maybe< Variable > variable(std::string name) const
Looks up a variable by name.
Definition netcdfpp.h:806
Maybe< Group > group(std::string name) const
Looks up a child group by name.
Definition netcdfpp.h:745
Maybe< Attribute > attribute(std::string name) const
Looks up a group attribute by name.
Definition netcdfpp.h:658
friend class UserType
Definition netcdfpp.h:533
Dimension add_dimension(std::string name)
Defines an unlimited dimension.
Definition netcdfpp.h:551
Dimension add_dimension(const Dimension &d)
Copies a dimension into this group.
Definition netcdfpp.h:560
Group add_group(std::string name)
Defines a child group.
Definition netcdfpp.h:573
friend class Maybe< Group >
Definition netcdfpp.h:532
std::vector< Attribute > attributes() const
Returns all group attributes.
Definition netcdfpp.h:668
std::vector< UserType > user_types() const
Returns all user-defined types in this group.
Definition netcdfpp.h:1884
Optional result returned by object lookup functions.
Definition netcdfpp.h:302
bool valid() const
Returns true when the lookup found an object.
Definition netcdfpp.h:322
T require() const
Returns the object or throws netCDF::Exception if the lookup failed.
Definition netcdfpp.h:314
Maybe(std::shared_ptr< detail::Path > path_p)
Definition netcdfpp.h:311
NetCDF user-defined type.
Definition netcdfpp.h:874
Group parent() const
Returns the parent group.
Definition netcdfpp.h:908
std::size_t memberscount() const
Returns the number of members in an enum type.
Definition netcdfpp.h:980
nc_type basetype() const
Returns the base NetCDF type of enum and variable-length types.
Definition netcdfpp.h:964
friend class Group
Definition netcdfpp.h:875
UserType add_compound_field(const std::string &name, std::size_t offset)
Adds a scalar field to a compound type.
Definition netcdfpp.h:912
std::vector< CompoundField > compound_fields() const
Returns compound field metadata.
Definition netcdfpp.h:933
std::size_t bytes_size() const
Returns the byte size of the user-defined type.
Definition netcdfpp.h:988
int typeclass() const
Returns NC_VLEN, NC_OPAQUE, NC_ENUM, or NC_COMPOUND.
Definition netcdfpp.h:996
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:919
std::vector< std::pair< std::string, T > > enum_members() const
Returns enum members as name/value pairs.
Definition netcdfpp.h:951
std::size_t fieldscount() const
Returns the number of fields in a compound type.
Definition netcdfpp.h:972
UserType add_enum_member(const std::string &name, T v)
Adds an enum member.
Definition netcdfpp.h:927
friend class testing::TestUserType
Definition netcdfpp.h:877
NetCDF variable.
Definition netcdfpp.h:1017
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:1462
void unset_fill()
Disables fill for this variable.
Definition netcdfpp.h:1263
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:1380
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:1409
Attribute add_attribute(std::string name)
Defines an attribute handle. The attribute is created when a value is written.
Definition netcdfpp.h:1067
void set(const std::vector< T > &v)
Writes the whole variable from a vector.
Definition netcdfpp.h:1424
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:1331
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:1300
void set(const T v, const std::array< std::size_t, N > &index)
Writes one fixed-rank element.
Definition netcdfpp.h:1457
friend class Group
Definition netcdfpp.h:1018
void set_compression(bool shuffle_filter, int deflate_level)
Sets shuffle and deflate compression.
Definition netcdfpp.h:1223
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:1404
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:1521
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:1392
void write(const T *v)
Writes the whole variable from caller-provided storage.
Definition netcdfpp.h:1485
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:1362
void read(T *v, const std::size_t *index) const
Reads one element into caller-provided storage.
Definition netcdfpp.h:1374
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:1399
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:1341
void rename(std::string name)
Renames the variable in place.
Definition netcdfpp.h:1269
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:1446
std::string type_name() const
Returns the NetCDF type name of the variable.
Definition netcdfpp.h:1547
Group parent() const
Returns the parent group.
Definition netcdfpp.h:1266
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:1441
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:1451
Maybe< Attribute > attribute(std::string name) const
Looks up a variable attribute by name.
Definition netcdfpp.h:1077
nc_type type() const
Returns the NetCDF type id of the variable.
Definition netcdfpp.h:1540
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:1475
std::vector< std::size_t > get_chunking() const
Returns chunk sizes, or an empty vector for contiguous storage.
Definition netcdfpp.h:1191
void write(const T *v, const std::size_t *index)
Writes one element from caller-provided storage.
Definition netcdfpp.h:1491
std::vector< std::size_t > sizes() const
Returns the current size of each dimension.
Definition netcdfpp.h:1275
std::vector< T > get() const
Reads the whole variable as a vector.
Definition netcdfpp.h:1314
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:1564
void set_default_chunking()
Lets NetCDF choose default chunk sizes.
Definition netcdfpp.h:1211
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:1414
void read(T *v) const
Reads the whole variable into caller-provided storage.
Definition netcdfpp.h:1368
void set_chunking(const std::vector< std::size_t > &chunks)
Sets chunked storage, or contiguous storage when chunks is empty.
Definition netcdfpp.h:1202
bool get_checksum_enabled() const
Returns true when Fletcher32 checksums are enabled.
Definition netcdfpp.h:1238
std::vector< Attribute > attributes() const
Returns all variable attributes.
Definition netcdfpp.h:1087
Variable require_type(const std::string &name) const
Returns this variable or throws if its NetCDF type name differs.
Definition netcdfpp.h:1554
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:1429
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:1467
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:1509
void set_checksum_enabled(bool v)
Enables or disables Fletcher32 checksums.
Definition netcdfpp.h:1245
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:1531
T get(const std::array< std::size_t, N > &index) const
Reads one element by fixed-rank index.
Definition netcdfpp.h:1352
int get_endianness() const
Returns the NetCDF endianness setting.
Definition netcdfpp.h:1228
Variable require_dimensions(const std::vector< std::string > &names) const
Returns this variable or throws if its dimensions do not match.
Definition netcdfpp.h:1115
Variable require_size(std::size_t size_p) const
Returns this variable or throws if its total element count differs.
Definition netcdfpp.h:1305
std::vector< Dimension > dimensions() const
Returns the variable dimensions.
Definition netcdfpp.h:1178
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:1435
void copy_attributes(const Variable &v)
Copies all attributes from another variable.
Definition netcdfpp.h:1123
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:1497
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:1516
std::pair< bool, T > get_fill() const
Returns whether fill is enabled and the fill value.
Definition netcdfpp.h:1249
void copy_values(const Variable &v)
Copies raw values from another variable after checking shape and type size.
Definition netcdfpp.h:1130
void set_endianness(int endianness)
Sets NC_ENDIAN_NATIVE, NC_ENDIAN_LITTLE, or NC_ENDIAN_BIG.
Definition netcdfpp.h:1235
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:1526
std::pair< bool, int > get_compression() const
Returns shuffle status and deflate level, or -1 when deflate is disabled.
Definition netcdfpp.h:1214
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:1503
Maybe< UserType > user_type() const
Returns the user-defined type for this variable, if it has one.
Definition netcdfpp.h:1576
std::size_t dimension_count() const
Returns the number of dimensions.
Definition netcdfpp.h:1171
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:1357
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:1386
T get(const std::size_t *index) const
Reads one element by raw NetCDF index pointer.
Definition netcdfpp.h:1324
bool check_dimensions(const std::vector< std::string > &names) const
Returns true when the variable dimensions match the supplied names exactly.
Definition netcdfpp.h:1101
Attribute add_attribute(const Attribute &a)
Copies an attribute onto this variable.
Definition netcdfpp.h:1070
std::size_t size() const
Returns the total number of elements.
Definition netcdfpp.h:1288
void set_fill(T v)
Enables fill and sets the fill value.
Definition netcdfpp.h:1258
Definition netcdfpp.h:269
const std::string & name() const
Definition netcdfpp.h:290
void raise_error(int ret) const
Definition netcdfpp.h:281
int id() const
Definition netcdfpp.h:291
std::shared_ptr< Path > path
Definition netcdfpp.h:271
static std::string get_error_message(int ret)
Definition netcdfpp.h:274
void check(int ret) const
Definition netcdfpp.h:283
Object(std::shared_ptr< Path > path_p)
Definition netcdfpp.h:272
T * data_or_null(std::vector< T > &v)
Definition netcdfpp.h:186
std::vector< std::string > process_char_vector(std::vector< char * > &buf)
Definition netcdfpp.h:195
constexpr bool is_user_type(nc_type type)
Definition netcdfpp.h:183
std::vector< const char * > process_string_vector(const std::vector< std::string > &v)
Definition netcdfpp.h:213
Definition netcdfpp.h:51
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:99
#define NETCDFPP_IMPL_TYPE(internal, lib)
Definition netcdfpp.h:71
#define NETCDFPP_IMPL_VARIABLE_WRITE(type, name)
Definition netcdfpp.h:2026
#define NETCDFPP_IMPL_ALL(type, name)
Definition netcdfpp.h:2048
#define NETCDFPP_IMPL_VARIABLE_READ(type, name)
Definition netcdfpp.h:2004
Definition netcdfpp.h:54
Definition netcdfpp.h:56
static constexpr bool is_atomic
Definition netcdfpp.h:57
Compound field metadata.
Definition netcdfpp.h:881
nc_type type
NetCDF type id of the field.
Definition netcdfpp.h:883
std::vector< int > dimensions
Array dimensions for array fields; empty for scalar fields.
Definition netcdfpp.h:889
std::size_t offset
Byte offset of the field in the compound type.
Definition netcdfpp.h:887
std::string name
Field name.
Definition netcdfpp.h:885
RAII wrapper for one NetCDF variable-length value returned by the C API.
Definition netcdfpp.h:1006
const T * data
Pointer to the allocated element data.
Definition netcdfpp.h:1010
~VLenElement()
Definition netcdfpp.h:1013
const std::size_t size
Number of elements.
Definition netcdfpp.h:1008
VLenElement()
Definition netcdfpp.h:1012
Definition netcdfpp.h:237
Definition netcdfpp.h:219
std::string name
Definition netcdfpp.h:220
int id
Definition netcdfpp.h:221
bool is_group
Definition netcdfpp.h:222
std::shared_ptr< Path > parent
Definition netcdfpp.h:223
std::string get_full_path() const
Definition netcdfpp.h:225