Skip to content

Commit

Permalink
Make File::AccessMode an enum class. (#1020)
Browse files Browse the repository at this point in the history
Make `File::AccessMode` an enum class and add compatibility `constexpr`
members, e.g. `File::ReadOnly`, `File::Truncate`, etc. to reduce migration
effort.

* Add Migration Guide.
  • Loading branch information
1uc authored Aug 20, 2024
1 parent a6e17bd commit 6837987
Show file tree
Hide file tree
Showing 4 changed files with 119 additions and 22 deletions.
30 changes: 28 additions & 2 deletions doc/migration_guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
70 changes: 61 additions & 9 deletions include/highfive/H5File.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#pragma once

#include <string>
#include <type_traits>

#include "H5Object.hpp"
#include "H5PropertyList.hpp"
Expand All @@ -17,32 +18,43 @@

namespace HighFive {


///
/// \brief File class
///
class File: public Object, public NodeTraits<File>, public AnnotateTraits<File> {
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
Expand All @@ -51,7 +63,7 @@ class File: public Object, public NodeTraits<File>, public AnnotateTraits<File>
///
/// Open or create a new HDF5 file
explicit File(const std::string& filename,
unsigned openFlags = ReadOnly,
AccessMode openFlags = ReadOnly,
const FileAccessProps& fileAccessProps = FileAccessProps::Default());

///
Expand All @@ -63,7 +75,7 @@ class File: public Object, public NodeTraits<File>, public AnnotateTraits<File>
///
/// Open or create a new HDF5 file
File(const std::string& filename,
unsigned openFlags,
AccessMode openFlags,
const FileCreateProps& fileCreateProps,
const FileAccessProps& fileAccessProps = FileAccessProps::Default());

Expand Down Expand Up @@ -131,6 +143,46 @@ class File: public Object, public NodeTraits<File>, public AnnotateTraits<File>
friend class PathTraits;
};

inline File::AccessMode operator|(File::AccessMode lhs, File::AccessMode rhs) {
using int_t = std::underlying_type<File::AccessMode>::type;
return static_cast<File::AccessMode>(static_cast<int_t>(lhs) | static_cast<int_t>(rhs));
}

inline File::AccessMode operator&(File::AccessMode lhs, File::AccessMode rhs) {
using int_t = std::underlying_type<File::AccessMode>::type;
return static_cast<File::AccessMode>(static_cast<int_t>(lhs) & static_cast<int_t>(rhs));
}

inline File::AccessMode operator^(File::AccessMode lhs, File::AccessMode rhs) {
using int_t = std::underlying_type<File::AccessMode>::type;
return static_cast<File::AccessMode>(static_cast<int_t>(lhs) ^ static_cast<int_t>(rhs));
}

inline File::AccessMode operator~(File::AccessMode mode) {
using int_t = std::underlying_type<File::AccessMode>::type;
return static_cast<File::AccessMode>(~static_cast<int_t>(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
Expand Down
18 changes: 9 additions & 9 deletions include/highfive/bits/H5File_misc.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
23 changes: 21 additions & 2 deletions tests/unit/tests_high_five_base.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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") {
Expand Down

0 comments on commit 6837987

Please sign in to comment.