diff --git a/doc/migration_guide.md b/doc/migration_guide.md index ee72e52c7..ac8048df6 100644 --- a/doc/migration_guide.md +++ b/doc/migration_guide.md @@ -233,6 +233,32 @@ multi-dimensional array isn't supported, because if we want to support array with runtime-defined rank, we can't deduce the correct shape, e.g. `[1]` vs. `[1, 1, 1]`, when read into an array. -# Removal of `Object*Props`. -To out knowledge these could not be used meaningfully. Please create an issue +## Change to `File::Truncate` and friends. +In `v2`, `File::{ReadOnly,Truncate,...}` was an anonymous member enum of +`File`. Effectively it's type was the same as an `int`. + +To improve type-safety, we converted it into an `enum class` called +`File::AccessMode`. In order to reduce the migration effort, we retained the +ability to write: `File::ReadOnly`. + +Functions that accept a file access mode should be modernized as follows: +``` +// old +HighFive::File open(std::string name, int mode) { + return HighFive::File(name, mode); +} + +// new +HighFive::File open(std::string name, HighFive::File::AccessMode mode) { + return HighFive::File(name, mode); +} +``` + +Note: There's a caveat, the short-hand notation `File::ReadOnly` doesn't have +an address. Meaning one can't take it's address or const-references of it +(results in a linker error about missing symbol `File::ReadOnly`). Use +`File::AccessMode::ReadOnly` instead. + +## Removal of `Object*Props`. +To our knowledge these could not be used meaningfully. Please create an issue if you relied on these. diff --git a/include/highfive/H5File.hpp b/include/highfive/H5File.hpp index b134aaa49..fd77042b0 100644 --- a/include/highfive/H5File.hpp +++ b/include/highfive/H5File.hpp @@ -9,6 +9,7 @@ #pragma once #include +#include #include "H5Object.hpp" #include "H5PropertyList.hpp" @@ -17,6 +18,7 @@ namespace HighFive { + /// /// \brief File class /// @@ -24,25 +26,35 @@ class File: public Object, public NodeTraits, public AnnotateTraits public: const static ObjectType type = ObjectType::File; - enum : unsigned { + enum class AccessMode { + None = 0x00u, /// Open flag: Read only access - ReadOnly = 0x00u, + ReadOnly = 0x01u, /// Open flag: Read Write access - ReadWrite = 0x01u, + ReadWrite = 0x02u, /// Open flag: Truncate a file if already existing - Truncate = 0x02u, + Truncate = 0x04u, /// Open flag: Open will fail if file already exist - Excl = 0x04u, + Excl = 0x08u, /// Open flag: Open in debug mode - Debug = 0x08u, + Debug = 0x10u, /// Open flag: Create non existing file - Create = 0x10u, + Create = 0x20u, /// Derived open flag: common write mode (=ReadWrite|Create|Truncate) Overwrite = Truncate, /// Derived open flag: Opens RW or exclusively creates OpenOrCreate = ReadWrite | Create }; + constexpr static AccessMode ReadOnly = AccessMode::ReadOnly; + constexpr static AccessMode ReadWrite = AccessMode::ReadWrite; + constexpr static AccessMode Truncate = AccessMode::Truncate; + constexpr static AccessMode Excl = AccessMode::Excl; + constexpr static AccessMode Debug = AccessMode::Debug; + constexpr static AccessMode Create = AccessMode::Create; + constexpr static AccessMode Overwrite = AccessMode::Overwrite; + constexpr static AccessMode OpenOrCreate = AccessMode::OpenOrCreate; + /// /// \brief File /// \param filename: filepath of the HDF5 file @@ -51,7 +63,7 @@ class File: public Object, public NodeTraits, public AnnotateTraits /// /// Open or create a new HDF5 file explicit File(const std::string& filename, - unsigned openFlags = ReadOnly, + AccessMode openFlags = ReadOnly, const FileAccessProps& fileAccessProps = FileAccessProps::Default()); /// @@ -63,7 +75,7 @@ class File: public Object, public NodeTraits, public AnnotateTraits /// /// Open or create a new HDF5 file File(const std::string& filename, - unsigned openFlags, + AccessMode openFlags, const FileCreateProps& fileCreateProps, const FileAccessProps& fileAccessProps = FileAccessProps::Default()); @@ -131,6 +143,46 @@ class File: public Object, public NodeTraits, public AnnotateTraits friend class PathTraits; }; +inline File::AccessMode operator|(File::AccessMode lhs, File::AccessMode rhs) { + using int_t = std::underlying_type::type; + return static_cast(static_cast(lhs) | static_cast(rhs)); +} + +inline File::AccessMode operator&(File::AccessMode lhs, File::AccessMode rhs) { + using int_t = std::underlying_type::type; + return static_cast(static_cast(lhs) & static_cast(rhs)); +} + +inline File::AccessMode operator^(File::AccessMode lhs, File::AccessMode rhs) { + using int_t = std::underlying_type::type; + return static_cast(static_cast(lhs) ^ static_cast(rhs)); +} + +inline File::AccessMode operator~(File::AccessMode mode) { + using int_t = std::underlying_type::type; + return static_cast(~static_cast(mode)); +} + +inline const File::AccessMode& operator|=(File::AccessMode& lhs, File::AccessMode rhs) { + lhs = lhs | rhs; + return lhs; +} + +inline File::AccessMode operator&=(File::AccessMode& lhs, File::AccessMode rhs) { + lhs = lhs & rhs; + return lhs; +} + +inline File::AccessMode operator^=(File::AccessMode& lhs, File::AccessMode rhs) { + lhs = lhs ^ rhs; + return lhs; +} + +inline bool any(File::AccessMode mode) { + return mode != File::AccessMode::None; +} + + } // namespace HighFive // H5File is the main user constructible -> bring in implementation headers diff --git a/include/highfive/bits/H5File_misc.hpp b/include/highfive/bits/H5File_misc.hpp index 6013953b1..0d8088da5 100644 --- a/include/highfive/bits/H5File_misc.hpp +++ b/include/highfive/bits/H5File_misc.hpp @@ -22,33 +22,33 @@ namespace { // unnamed // libhdf5 uses a preprocessor trick on their oflags // we can not declare them constant without a mapper -inline unsigned convert_open_flag(unsigned openFlags) { +inline unsigned convert_open_flag(File::AccessMode openFlags) { unsigned res_open = 0; - if (openFlags & File::ReadOnly) + if (any(openFlags & File::ReadOnly)) res_open |= H5F_ACC_RDONLY; - if (openFlags & File::ReadWrite) + if (any(openFlags & File::ReadWrite)) res_open |= H5F_ACC_RDWR; - if (openFlags & File::Create) + if (any(openFlags & File::Create)) res_open |= H5F_ACC_CREAT; - if (openFlags & File::Truncate) + if (any(openFlags & File::Truncate)) res_open |= H5F_ACC_TRUNC; - if (openFlags & File::Excl) + if (any(openFlags & File::Excl)) res_open |= H5F_ACC_EXCL; return res_open; } } // namespace inline File::File(const std::string& filename, - unsigned openFlags, + AccessMode openFlags, const FileAccessProps& fileAccessProps) : File(filename, openFlags, FileCreateProps::Default(), fileAccessProps) {} inline File::File(const std::string& filename, - unsigned openFlags, + AccessMode access_mode, const FileCreateProps& fileCreateProps, const FileAccessProps& fileAccessProps) { - openFlags = convert_open_flag(openFlags); + unsigned openFlags = convert_open_flag(access_mode); unsigned createMode = openFlags & (H5F_ACC_TRUNC | H5F_ACC_EXCL); unsigned openMode = openFlags & (H5F_ACC_RDWR | H5F_ACC_RDONLY); diff --git a/tests/unit/tests_high_five_base.cpp b/tests/unit/tests_high_five_base.cpp index 02ce7a04d..da6e024df 100644 --- a/tests/unit/tests_high_five_base.cpp +++ b/tests/unit/tests_high_five_base.cpp @@ -137,8 +137,27 @@ TEST_CASE("Test open modes in HighFive") { { File file(file_name, File::Truncate); } // Last but not least, defaults should be ok - { File file(file_name); } // ReadOnly - { File file(file_name, 0); } // force empty-flags, does open without flags + { File file(file_name); } // ReadOnly +} + +void check_access_mode(File::AccessMode mode) { + CHECK(any(mode)); + CHECK(((File::ReadOnly | mode) & File::ReadOnly) == File::AccessMode::ReadOnly); + CHECK(!any(File::ReadOnly & mode)); + CHECK(((File::ReadOnly | mode) ^ mode) == File::AccessMode::ReadOnly); + CHECK((File::ReadOnly & ~mode) == File::AccessMode::ReadOnly); + CHECK(!any(mode & ~mode)); +} + +TEST_CASE("File::AccessMode") { + CHECK(!any(File::AccessMode::None)); + CHECK(any(File::ReadOnly)); + + check_access_mode(File::ReadWrite); + check_access_mode(File::Truncate); + check_access_mode(File::Excl); + check_access_mode(File::Debug); + check_access_mode(File::Create); } TEST_CASE("Test file version bounds") {