Skip to content

Commit

Permalink
Change operator& in FlagEnumClassOperations to return the flags
Browse files Browse the repository at this point in the history
This operator was defined to return a boolean because it is what is
actually needed in most situations. However, this behavior is very
unintuitive as well because `&` is supposed to be a binary and. So this was
changed and can be used by defining
`CPP_UTILITIES_FLAG_ENUM_CLASS_NO_LEGACY_AND`. Then `&&` needs to be used
if a boolean is needed.
  • Loading branch information
Martchus committed Nov 11, 2024
1 parent 16966ba commit 1f39bdc
Show file tree
Hide file tree
Showing 5 changed files with 41 additions and 21 deletions.
22 changes: 11 additions & 11 deletions chrono/datetime.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -534,40 +534,40 @@ std::string DateTimeExpression::toIsoString(char dateDelimiter, char timeDelimit
{
auto s = std::stringstream(std::stringstream::in | std::stringstream::out);
s << setfill('0');
if (parts & DateTimeParts::Year) {
if (parts && DateTimeParts::Year) {
s << setw(4) << value.year();
}
if (parts & DateTimeParts::Month) {
if (parts && DateTimeParts::Month) {
if (s.tellp()) {
s << dateDelimiter;
}
s << setw(2) << value.month();
}
if (parts & DateTimeParts::Day) {
if (parts && DateTimeParts::Day) {
if (s.tellp()) {
s << dateDelimiter;
}
s << setw(2) << value.day();
}
if (parts & DateTimeParts::Hour) {
if (parts && DateTimeParts::Hour) {
if (s.tellp()) {
s << 'T';
}
s << setw(2) << value.hour();
}
if (parts & DateTimeParts::Minute) {
if (parts && DateTimeParts::Minute) {
if (s.tellp()) {
s << timeDelimiter;
}
s << setw(2) << value.minute();
}
if (parts & DateTimeParts::Second) {
if (parts && DateTimeParts::Second) {
if (s.tellp()) {
s << timeDelimiter;
}
s << setw(2) << value.second();
}
if (parts & DateTimeParts::SubSecond) {
if (parts && DateTimeParts::SubSecond) {
const auto milli = value.millisecond();
const auto micro = value.microsecond();
const auto nano = value.nanosecond();
Expand All @@ -579,19 +579,19 @@ std::string DateTimeExpression::toIsoString(char dateDelimiter, char timeDelimit
}
}
}
if (parts & DateTimeParts::TimeZoneDelta) {
if (parts && DateTimeParts::TimeZoneDelta) {
auto d = delta;
if (d.isNegative()) {
s << '-';
d = TimeSpan(-d.totalTicks());
} else {
s << '+';
}
if (parts & DateTimeParts::DeltaHour) {
if (parts && DateTimeParts::DeltaHour) {
s << setw(2) << d.hours();
}
if (parts & DateTimeParts::DeltaMinute) {
if (parts & DateTimeParts::DeltaHour) {
if (parts && DateTimeParts::DeltaMinute) {
if (parts && DateTimeParts::DeltaHour) {
s << timeZoneDelimiter;
}
s << setw(2) << d.minutes();
Expand Down
6 changes: 3 additions & 3 deletions io/inifile.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -416,7 +416,7 @@ void AdvancedIniFile::make(ostream &outputStream, IniFileMakeOptions)
if (!section.precedingCommentBlock.empty()) {
outputStream << section.precedingCommentBlock;
}
if (!(section.flags & IniFileSectionFlags::Implicit)) {
if (!(section.flags && IniFileSectionFlags::Implicit)) {
outputStream << '[' << section.name << ']';
if (!section.followingInlineComment.empty()) {
outputStream << ' ' << section.followingInlineComment;
Expand All @@ -431,11 +431,11 @@ void AdvancedIniFile::make(ostream &outputStream, IniFileMakeOptions)
for (auto charsWritten = field.key.size(); charsWritten < field.paddedKeyLength; ++charsWritten) {
outputStream << ' ';
}
if (field.flags & IniFileFieldFlags::HasValue) {
if (field.flags && IniFileFieldFlags::HasValue) {
outputStream << '=' << ' ' << field.value;
}
if (!field.followingInlineComment.empty()) {
if (field.flags & IniFileFieldFlags::HasValue) {
if (field.flags && IniFileFieldFlags::HasValue) {
outputStream << ' ';
}
outputStream << field.followingInlineComment;
Expand Down
17 changes: 17 additions & 0 deletions misc/flagenumclass.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ template <typename T> struct IsFlagEnumClass : public Traits::Bool<false> {};
namespace Namespace { \
using CppUtilities::FlagEnumClassOperations::operator|; \
using CppUtilities::FlagEnumClassOperations::operator&; \
using CppUtilities::FlagEnumClassOperations::operator&&; \
using CppUtilities::FlagEnumClassOperations::operator|=; \
using CppUtilities::FlagEnumClassOperations::operator+=; \
using CppUtilities::FlagEnumClassOperations::operator-=; \
Expand All @@ -41,12 +42,28 @@ constexpr FlagEnumClass operator|(FlagEnumClass lhs, FlagEnumClass rhs)
static_cast<typename std::underlying_type<FlagEnumClass>::type>(lhs) | static_cast<typename std::underlying_type<FlagEnumClass>::type>(rhs));
}

#ifdef CPP_UTILITIES_FLAG_ENUM_CLASS_NO_LEGACY_AND
template <typename FlagEnumClass, Traits::EnableIf<IsFlagEnumClass<FlagEnumClass>> * = nullptr>
constexpr FlagEnumClass operator&(FlagEnumClass lhs, FlagEnumClass rhs)
{
return static_cast<FlagEnumClass>(
static_cast<typename std::underlying_type<FlagEnumClass>::type>(lhs) & static_cast<typename std::underlying_type<FlagEnumClass>::type>(rhs));
}
#else
template <typename FlagEnumClass, Traits::EnableIf<IsFlagEnumClass<FlagEnumClass>> * = nullptr>
constexpr bool operator&(FlagEnumClass lhs, FlagEnumClass rhs)
{
return static_cast<typename std::underlying_type<FlagEnumClass>::type>(lhs)
& static_cast<typename std::underlying_type<FlagEnumClass>::type>(rhs);
}
#endif

template <typename FlagEnumClass, Traits::EnableIf<IsFlagEnumClass<FlagEnumClass>> * = nullptr>
constexpr bool operator&&(FlagEnumClass lhs, FlagEnumClass rhs)
{
return static_cast<typename std::underlying_type<FlagEnumClass>::type>(lhs)
& static_cast<typename std::underlying_type<FlagEnumClass>::type>(rhs);
}

template <typename FlagEnumClass, Traits::EnableIf<IsFlagEnumClass<FlagEnumClass>> * = nullptr>
constexpr FlagEnumClass &operator|=(FlagEnumClass &lhs, FlagEnumClass rhs)
Expand Down
8 changes: 4 additions & 4 deletions tests/iotests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -453,23 +453,23 @@ void IoTests::testAdvancedIniFile()
CPPUNIT_ASSERT_EQUAL(7_st, options->fields.size());
CPPUNIT_ASSERT_EQUAL("HoldPkg"s, options->fields[0].key);
CPPUNIT_ASSERT_EQUAL("pacman glibc"s, options->fields[0].value);
CPPUNIT_ASSERT_MESSAGE("value present", options->fields[0].flags & IniFileFieldFlags::HasValue);
CPPUNIT_ASSERT_MESSAGE("value present", options->fields[0].flags && IniFileFieldFlags::HasValue);
#ifdef STD_REGEX_WORKS
TESTUTILS_ASSERT_LIKE_FLAGS("comment block between section header and first field",
"# The following paths are.*\n.*#HookDir = /etc/pacman\\.d/hooks/\n"s, std::regex::extended, options->fields[0].precedingCommentBlock);
#endif
CPPUNIT_ASSERT_EQUAL(""s, options->fields[0].followingInlineComment);
CPPUNIT_ASSERT_EQUAL("Foo"s, options->fields[1].key);
CPPUNIT_ASSERT_EQUAL("bar"s, options->fields[1].value);
CPPUNIT_ASSERT_MESSAGE("value present", options->fields[1].flags & IniFileFieldFlags::HasValue);
CPPUNIT_ASSERT_MESSAGE("value present", options->fields[1].flags && IniFileFieldFlags::HasValue);
#ifdef STD_REGEX_WORKS
TESTUTILS_ASSERT_LIKE_FLAGS("comment block between fields", "#XferCommand.*\n.*#CleanMethod = KeepInstalled\n"s, std::regex::extended,
options->fields[1].precedingCommentBlock);
#endif
CPPUNIT_ASSERT_EQUAL("# inline comment"s, options->fields[1].followingInlineComment);
CPPUNIT_ASSERT_EQUAL("CheckSpace"s, options->fields[3].key);
CPPUNIT_ASSERT_EQUAL(""s, options->fields[3].value);
CPPUNIT_ASSERT_MESSAGE("no value present", !(options->fields[3].flags & IniFileFieldFlags::HasValue));
CPPUNIT_ASSERT_MESSAGE("no value present", !(options->fields[3].flags && IniFileFieldFlags::HasValue));
#ifdef STD_REGEX_WORKS
TESTUTILS_ASSERT_LIKE_FLAGS("empty lines in comments preserved", "\n# Pacman.*\n.*\n\n#NoUpgrade =\n.*#TotalDownload\n"s, std::regex::extended,
options->fields[3].precedingCommentBlock);
Expand All @@ -480,7 +480,7 @@ void IoTests::testAdvancedIniFile()
CPPUNIT_ASSERT_EQUAL_MESSAGE("comment block which is only an empty line", "\n"s, extraScope->precedingCommentBlock);
CPPUNIT_ASSERT_EQUAL_MESSAGE("inline comment after scope", "# an inline comment after a scope name"s, extraScope->followingInlineComment);
CPPUNIT_ASSERT_EQUAL(1_st, extraScope->fields.size());
CPPUNIT_ASSERT(ini.sections.back().flags & IniFileSectionFlags::Implicit);
CPPUNIT_ASSERT(ini.sections.back().flags && IniFileSectionFlags::Implicit);
#ifdef STD_REGEX_WORKS
TESTUTILS_ASSERT_LIKE_FLAGS("comment block after last field present in implicitly added last scope", "\n# If you.*\n.*custompkgs\n"s,
std::regex::extended, ini.sections.back().precedingCommentBlock);
Expand Down
9 changes: 6 additions & 3 deletions tests/misctests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -187,9 +187,12 @@ constexpr bool testFlagEnumClass()
{
// test const operations
const auto testFlags = TestFlags::Foo | TestFlags::Baz;
static_assert(testFlags & TestFlags::Foo);
static_assert(!(testFlags & TestFlags::Bar));
static_assert(testFlags & TestFlags::Baz);
static_assert((testFlags & TestFlags::Foo) == TestFlags::Foo);
static_assert(testFlags && TestFlags::Foo);
static_assert(!(testFlags && TestFlags::Bar));
static_assert((testFlags & TestFlags::Bar) == TestFlags::None);
static_assert(testFlags && TestFlags::Baz);
static_assert((testFlags & TestFlags::Baz) == TestFlags::Baz);
static_assert(checkFlagEnum(testFlags, TestFlags::Foo | TestFlags::Baz));
static_assert(checkFlagEnum(testFlags, TestFlags::Foo));
static_assert(!checkFlagEnum(testFlags, TestFlags::Foo | TestFlags::Bar));
Expand Down

0 comments on commit 1f39bdc

Please sign in to comment.