| @@ -46,18 +46,6 @@ namespace MidiFileHelpers | |||
| } | |||
| } | |||
| template <typename Value> | |||
| struct Optional | |||
| { | |||
| Optional() = default; | |||
| Optional (const Value& v) | |||
| : value (v), valid (true) {} | |||
| Value value = Value(); | |||
| bool valid = false; | |||
| }; | |||
| template <typename Integral> | |||
| struct ReadTrait; | |||
| @@ -100,23 +88,23 @@ namespace MidiFileHelpers | |||
| auto ch = tryRead<uint32> (data, remaining); | |||
| if (! ch.valid) | |||
| if (! ch.hasValue()) | |||
| return {}; | |||
| if (ch.value != ByteOrder::bigEndianInt ("MThd")) | |||
| if (*ch != ByteOrder::bigEndianInt ("MThd")) | |||
| { | |||
| auto ok = false; | |||
| if (ch.value == ByteOrder::bigEndianInt ("RIFF")) | |||
| if (*ch == ByteOrder::bigEndianInt ("RIFF")) | |||
| { | |||
| for (int i = 0; i < 8; ++i) | |||
| { | |||
| ch = tryRead<uint32> (data, remaining); | |||
| if (! ch.valid) | |||
| if (! ch.hasValue()) | |||
| return {}; | |||
| if (ch.value == ByteOrder::bigEndianInt ("MThd")) | |||
| if (*ch == ByteOrder::bigEndianInt ("MThd")) | |||
| { | |||
| ok = true; | |||
| break; | |||
| @@ -130,29 +118,29 @@ namespace MidiFileHelpers | |||
| const auto bytesRemaining = tryRead<uint32> (data, remaining); | |||
| if (! bytesRemaining.valid || bytesRemaining.value > remaining) | |||
| if (! bytesRemaining.hasValue() || *bytesRemaining > remaining) | |||
| return {}; | |||
| const auto optFileType = tryRead<uint16> (data, remaining); | |||
| if (! optFileType.valid || 2 < optFileType.value) | |||
| if (! optFileType.hasValue() || 2 < *optFileType) | |||
| return {}; | |||
| const auto optNumTracks = tryRead<uint16> (data, remaining); | |||
| if (! optNumTracks.valid || (optFileType.value == 0 && optNumTracks.value != 1)) | |||
| if (! optNumTracks.hasValue() || (*optFileType == 0 && *optNumTracks != 1)) | |||
| return {}; | |||
| const auto optTimeFormat = tryRead<uint16> (data, remaining); | |||
| if (! optTimeFormat.valid) | |||
| if (! optTimeFormat.hasValue()) | |||
| return {}; | |||
| HeaderDetails result; | |||
| result.fileType = (short) optFileType.value; | |||
| result.timeFormat = (short) optTimeFormat.value; | |||
| result.numberOfTracks = (short) optNumTracks.value; | |||
| result.fileType = (short) *optFileType; | |||
| result.timeFormat = (short) *optTimeFormat; | |||
| result.numberOfTracks = (short) *optNumTracks; | |||
| result.bytesRead = maxSize - remaining; | |||
| return { result }; | |||
| @@ -373,10 +361,10 @@ bool MidiFile::readFrom (InputStream& sourceStream, | |||
| const auto optHeader = MidiFileHelpers::parseMidiHeader (d, size); | |||
| if (! optHeader.valid) | |||
| if (! optHeader.hasValue()) | |||
| return false; | |||
| const auto header = optHeader.value; | |||
| const auto header = *optHeader; | |||
| timeFormat = header.timeFormat; | |||
| d += header.bytesRead; | |||
| @@ -386,20 +374,20 @@ bool MidiFile::readFrom (InputStream& sourceStream, | |||
| { | |||
| const auto optChunkType = MidiFileHelpers::tryRead<uint32> (d, size); | |||
| if (! optChunkType.valid) | |||
| if (! optChunkType.hasValue()) | |||
| return false; | |||
| const auto optChunkSize = MidiFileHelpers::tryRead<uint32> (d, size); | |||
| if (! optChunkSize.valid) | |||
| if (! optChunkSize.hasValue()) | |||
| return false; | |||
| const auto chunkSize = optChunkSize.value; | |||
| const auto chunkSize = *optChunkSize; | |||
| if (size < chunkSize) | |||
| return false; | |||
| if (optChunkType.value == ByteOrder::bigEndianInt ("MTrk")) | |||
| if (*optChunkType == ByteOrder::bigEndianInt ("MTrk")) | |||
| readNextTrack (d, (int) chunkSize, createMatchingNoteOffs); | |||
| size -= chunkSize; | |||
| @@ -610,7 +598,7 @@ struct MidiFileTest : public UnitTest | |||
| { | |||
| // No data | |||
| const auto header = parseHeader ([] (OutputStream&) {}); | |||
| expect (! header.valid); | |||
| expect (! header.hasValue()); | |||
| } | |||
| { | |||
| @@ -620,7 +608,7 @@ struct MidiFileTest : public UnitTest | |||
| writeBytes (os, { 0xff }); | |||
| }); | |||
| expect (! header.valid); | |||
| expect (! header.hasValue()); | |||
| } | |||
| { | |||
| @@ -630,7 +618,7 @@ struct MidiFileTest : public UnitTest | |||
| writeBytes (os, { 'M', 'T', 'h', 'd' }); | |||
| }); | |||
| expect (! header.valid); | |||
| expect (! header.hasValue()); | |||
| } | |||
| { | |||
| @@ -640,7 +628,7 @@ struct MidiFileTest : public UnitTest | |||
| writeBytes (os, { 'M', 'T', 'h', 'd', 0, 0, 0, 6, 0, 0, 0, 16, 0, 1 }); | |||
| }); | |||
| expect (! header.valid); | |||
| expect (! header.hasValue()); | |||
| } | |||
| { | |||
| @@ -650,7 +638,7 @@ struct MidiFileTest : public UnitTest | |||
| writeBytes (os, { 'M', 'T', 'h', 'd', 0, 0, 0, 6, 0, 5, 0, 16, 0, 1 }); | |||
| }); | |||
| expect (! header.valid); | |||
| expect (! header.hasValue()); | |||
| } | |||
| { | |||
| @@ -660,12 +648,12 @@ struct MidiFileTest : public UnitTest | |||
| writeBytes (os, { 'M', 'T', 'h', 'd', 0, 0, 0, 6, 0, 1, 0, 16, 0, 1 }); | |||
| }); | |||
| expect (header.valid); | |||
| expect (header.hasValue()); | |||
| expectEquals (header.value.fileType, (short) 1); | |||
| expectEquals (header.value.numberOfTracks, (short) 16); | |||
| expectEquals (header.value.timeFormat, (short) 1); | |||
| expectEquals ((int) header.value.bytesRead, 14); | |||
| expectEquals (header->fileType, (short) 1); | |||
| expectEquals (header->numberOfTracks, (short) 16); | |||
| expectEquals (header->timeFormat, (short) 1); | |||
| expectEquals ((int) header->bytesRead, 14); | |||
| } | |||
| } | |||
| @@ -674,7 +662,7 @@ struct MidiFileTest : public UnitTest | |||
| { | |||
| // Empty input | |||
| const auto file = parseFile ([] (OutputStream&) {}); | |||
| expect (! file.valid); | |||
| expect (! file.hasValue()); | |||
| } | |||
| { | |||
| @@ -684,7 +672,7 @@ struct MidiFileTest : public UnitTest | |||
| writeBytes (os, { 'M', 'T', 'h', 'd' }); | |||
| }); | |||
| expect (! file.valid); | |||
| expect (! file.hasValue()); | |||
| } | |||
| { | |||
| @@ -694,8 +682,8 @@ struct MidiFileTest : public UnitTest | |||
| writeBytes (os, { 'M', 'T', 'h', 'd', 0, 0, 0, 6, 0, 1, 0, 0, 0, 1 }); | |||
| }); | |||
| expect (file.valid); | |||
| expectEquals (file.value.getNumTracks(), 0); | |||
| expect (file.hasValue()); | |||
| expectEquals (file->getNumTracks(), 0); | |||
| } | |||
| { | |||
| @@ -706,7 +694,7 @@ struct MidiFileTest : public UnitTest | |||
| writeBytes (os, { 'M', 'T', 'r', '?' }); | |||
| }); | |||
| expect (! file.valid); | |||
| expect (! file.hasValue()); | |||
| } | |||
| { | |||
| @@ -717,9 +705,9 @@ struct MidiFileTest : public UnitTest | |||
| writeBytes (os, { 'M', 'T', 'r', 'k', 0, 0, 0, 1, 0xff }); | |||
| }); | |||
| expect (file.valid); | |||
| expectEquals (file.value.getNumTracks(), 1); | |||
| expectEquals (file.value.getTrack (0)->getNumEvents(), 0); | |||
| expect (file.hasValue()); | |||
| expectEquals (file->getNumTracks(), 1); | |||
| expectEquals (file->getTrack (0)->getNumEvents(), 0); | |||
| } | |||
| { | |||
| @@ -730,7 +718,7 @@ struct MidiFileTest : public UnitTest | |||
| writeBytes (os, { 'M', 'T', 'r', 'k', 0x0f, 0, 0, 0, 0xff }); | |||
| }); | |||
| expect (! file.valid); | |||
| expect (! file.hasValue()); | |||
| } | |||
| { | |||
| @@ -744,10 +732,10 @@ struct MidiFileTest : public UnitTest | |||
| writeBytes (os, { 0x80, 0x00, 0x00 }); | |||
| }); | |||
| expect (file.valid); | |||
| expectEquals (file.value.getNumTracks(), 1); | |||
| expect (file.hasValue()); | |||
| expectEquals (file->getNumTracks(), 1); | |||
| auto& track = *file.value.getTrack (0); | |||
| auto& track = *file->getTrack (0); | |||
| expectEquals (track.getNumEvents(), 1); | |||
| expect (track.getEventPointer (0)->message.isNoteOff()); | |||
| expectEquals (track.getEventPointer (0)->message.getTimeStamp(), (double) 0x0f); | |||
| @@ -766,7 +754,7 @@ struct MidiFileTest : public UnitTest | |||
| } | |||
| template <typename Fn> | |||
| static MidiFileHelpers::Optional<MidiFileHelpers::HeaderDetails> parseHeader (Fn&& fn) | |||
| static Optional<MidiFileHelpers::HeaderDetails> parseHeader (Fn&& fn) | |||
| { | |||
| MemoryOutputStream os; | |||
| fn (os); | |||
| @@ -776,7 +764,7 @@ struct MidiFileTest : public UnitTest | |||
| } | |||
| template <typename Fn> | |||
| static MidiFileHelpers::Optional<MidiFile> parseFile (Fn&& fn) | |||
| static Optional<MidiFile> parseFile (Fn&& fn) | |||
| { | |||
| MemoryOutputStream os; | |||
| fn (os); | |||
| @@ -306,63 +306,56 @@ void MidiMessageSequence::deleteSysExMessages() | |||
| //============================================================================== | |||
| class OptionalPitchWheel | |||
| { | |||
| int value = 0; | |||
| bool valid = false; | |||
| Optional<int> value; | |||
| public: | |||
| void emit (int channel, Array<MidiMessage>& out) const | |||
| { | |||
| if (valid) | |||
| out.add (MidiMessage::pitchWheel (channel, value)); | |||
| if (value.hasValue()) | |||
| out.add (MidiMessage::pitchWheel (channel, *value)); | |||
| } | |||
| void set (int v) | |||
| { | |||
| value = v; | |||
| valid = true; | |||
| } | |||
| }; | |||
| class OptionalControllerValues | |||
| { | |||
| int values[128]; | |||
| Optional<char> values[128]; | |||
| public: | |||
| OptionalControllerValues() | |||
| { | |||
| std::fill (std::begin (values), std::end (values), -1); | |||
| } | |||
| void emit (int channel, Array<MidiMessage>& out) const | |||
| { | |||
| for (auto it = std::begin (values); it != std::end (values); ++it) | |||
| if (*it != -1) | |||
| out.add (MidiMessage::controllerEvent (channel, (int) std::distance (std::begin (values), it), *it)); | |||
| if (it->hasValue()) | |||
| out.add (MidiMessage::controllerEvent (channel, (int) std::distance (std::begin (values), it), **it)); | |||
| } | |||
| void set (int controller, int value) | |||
| { | |||
| values[controller] = value; | |||
| values[controller] = (char) value; | |||
| } | |||
| }; | |||
| class OptionalProgramChange | |||
| { | |||
| int value = -1, bankLSB = -1, bankMSB = -1; | |||
| Optional<char> value, bankLSB, bankMSB; | |||
| public: | |||
| void emit (int channel, double time, Array<MidiMessage>& out) const | |||
| { | |||
| if (value == -1) | |||
| if (! value.hasValue()) | |||
| return; | |||
| if (bankLSB != -1 && bankMSB != -1) | |||
| if (bankLSB.hasValue() && bankMSB.hasValue()) | |||
| { | |||
| out.add (MidiMessage::controllerEvent (channel, 0x00, bankMSB).withTimeStamp (time)); | |||
| out.add (MidiMessage::controllerEvent (channel, 0x20, bankLSB).withTimeStamp (time)); | |||
| out.add (MidiMessage::controllerEvent (channel, 0x00, *bankMSB).withTimeStamp (time)); | |||
| out.add (MidiMessage::controllerEvent (channel, 0x20, *bankLSB).withTimeStamp (time)); | |||
| } | |||
| out.add (MidiMessage::programChange (channel, value).withTimeStamp (time)); | |||
| out.add (MidiMessage::programChange (channel, *value).withTimeStamp (time)); | |||
| } | |||
| // Returns true if this is a bank number change, and false otherwise. | |||
| @@ -370,22 +363,21 @@ public: | |||
| { | |||
| switch (controller) | |||
| { | |||
| case 0x00: bankMSB = v; return true; | |||
| case 0x20: bankLSB = v; return true; | |||
| case 0x00: bankMSB = (char) v; return true; | |||
| case 0x20: bankLSB = (char) v; return true; | |||
| } | |||
| return false; | |||
| } | |||
| void setProgram (int v) { value = v; } | |||
| void setProgram (int v) { value = (char) v; } | |||
| }; | |||
| class ParameterNumberState | |||
| { | |||
| enum class Kind { rpn, nrpn }; | |||
| int newestRpnLsb = -1, newestRpnMsb = -1, newestNrpnLsb = -1, newestNrpnMsb = -1; | |||
| int lastSentLsb = -1, lastSentMsb = -1; | |||
| Optional<char> newestRpnLsb, newestRpnMsb, newestNrpnLsb, newestNrpnMsb, lastSentLsb, lastSentMsb; | |||
| Kind lastSentKind = Kind::rpn, newestKind = Kind::rpn; | |||
| public: | |||
| @@ -401,11 +393,11 @@ public: | |||
| auto lastSent = std::tie (lastSentKind, lastSentMsb, lastSentLsb); | |||
| const auto newest = std::tie (newestKind, newestMsb, newestLsb); | |||
| if (lastSent == newest || newestMsb == -1 || newestLsb == -1) | |||
| if (lastSent == newest || ! newestMsb.hasValue() || ! newestLsb.hasValue()) | |||
| return; | |||
| out.add (MidiMessage::controllerEvent (channel, newestKind == Kind::rpn ? 0x65 : 0x63, newestMsb).withTimeStamp (time)); | |||
| out.add (MidiMessage::controllerEvent (channel, newestKind == Kind::rpn ? 0x64 : 0x62, newestLsb).withTimeStamp (time)); | |||
| out.add (MidiMessage::controllerEvent (channel, newestKind == Kind::rpn ? 0x65 : 0x63, *newestMsb).withTimeStamp (time)); | |||
| out.add (MidiMessage::controllerEvent (channel, newestKind == Kind::rpn ? 0x64 : 0x62, *newestLsb).withTimeStamp (time)); | |||
| lastSent = newest; | |||
| } | |||
| @@ -415,10 +407,10 @@ public: | |||
| { | |||
| switch (controller) | |||
| { | |||
| case 0x65: newestRpnMsb = value; newestKind = Kind::rpn; return true; | |||
| case 0x64: newestRpnLsb = value; newestKind = Kind::rpn; return true; | |||
| case 0x63: newestNrpnMsb = value; newestKind = Kind::nrpn; return true; | |||
| case 0x62: newestNrpnLsb = value; newestKind = Kind::nrpn; return true; | |||
| case 0x65: newestRpnMsb = (char) value; newestKind = Kind::rpn; return true; | |||
| case 0x64: newestRpnLsb = (char) value; newestKind = Kind::rpn; return true; | |||
| case 0x63: newestNrpnMsb = (char) value; newestKind = Kind::nrpn; return true; | |||
| case 0x62: newestNrpnLsb = (char) value; newestKind = Kind::nrpn; return true; | |||
| } | |||
| return false; | |||
| @@ -0,0 +1,378 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the JUCE library. | |||
| Copyright (c) 2022 - Raw Material Software Limited | |||
| JUCE is an open source library subject to commercial or open-source | |||
| licensing. | |||
| The code included in this file is provided under the terms of the ISC license | |||
| http://www.isc.org/downloads/software-support-policy/isc-license. Permission | |||
| To use, copy, modify, and/or distribute this software for any purpose with or | |||
| without fee is hereby granted provided that the above copyright notice and | |||
| this permission notice appear in all copies. | |||
| JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER | |||
| EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE | |||
| DISCLAIMED. | |||
| ============================================================================== | |||
| */ | |||
| #pragma once | |||
| #include <utility> | |||
| namespace juce | |||
| { | |||
| namespace detail | |||
| { | |||
| namespace adlSwap | |||
| { | |||
| using std::swap; | |||
| template <typename T> | |||
| constexpr auto isNothrowSwappable = noexcept (swap (std::declval<T&>(), std::declval<T&>())); | |||
| } // namespace adlSwap | |||
| } // namespace detail | |||
| struct Nullopt {}; | |||
| constexpr Nullopt nullopt; | |||
| // Without this, our tests can emit "unreachable code" warnings during | |||
| // link time code generation. | |||
| JUCE_BEGIN_IGNORE_WARNINGS_MSVC (4702) | |||
| /* For internal use only! | |||
| A simple optional type. | |||
| Has similar (not necessarily identical!) semantics to std::optional. | |||
| This isn't really intended to be used by JUCE clients. Instead, it's to be | |||
| used internally in JUCE code, with an API close-enough to std::optional | |||
| that the types can be swapped with fairly minor disruption at some point in | |||
| the future, but *without breaking any public APIs*. | |||
| */ | |||
| template <typename Value> | |||
| class Optional | |||
| { | |||
| template <typename T, typename U> | |||
| struct NotConstructibleFromSimilarType | |||
| { | |||
| static constexpr auto value = ! std::is_constructible<T, Optional<U>&>::value | |||
| && ! std::is_constructible<T, const Optional<U>&>::value | |||
| && ! std::is_constructible<T, Optional<U>&&>::value | |||
| && ! std::is_constructible<T, const Optional<U>&&>::value | |||
| && ! std::is_convertible<Optional<U>&, T>::value | |||
| && ! std::is_convertible<const Optional<U>&, T>::value | |||
| && ! std::is_convertible<Optional<U>&&, T>::value | |||
| && ! std::is_convertible<const Optional<U>&&, T>::value; | |||
| }; | |||
| template <typename T, typename U> | |||
| using OptionalCopyConstructorEnabled = std::enable_if_t<std::is_constructible<T, const U&>::value && NotConstructibleFromSimilarType<T, U>::value>; | |||
| template <typename T, typename U> | |||
| using OptionalMoveConstructorEnabled = std::enable_if_t<std::is_constructible<T, U&&>::value && NotConstructibleFromSimilarType<T, U>::value>; | |||
| template <typename T, typename U> | |||
| static auto notAssignableFromSimilarType = NotConstructibleFromSimilarType<T, U>::value | |||
| && ! std::is_assignable<T&, Optional<U>&>::value | |||
| && ! std::is_assignable<T&, const Optional<U>&>::value | |||
| && ! std::is_assignable<T&, Optional<U>&&>::value | |||
| && ! std::is_assignable<T&, const Optional<U>&&>::value; | |||
| template <typename T, typename U> | |||
| using OptionalCopyAssignmentEnabled = std::enable_if_t<std::is_constructible<T, const U&>::value | |||
| && std::is_assignable<T&, const U&>::value | |||
| && NotConstructibleFromSimilarType<T, U>::value>; | |||
| template <typename T, typename U> | |||
| using OptionalMoveAssignmentEnabled = std::enable_if_t<std::is_constructible<T, U>::value | |||
| && std::is_nothrow_assignable<T&, U>::value | |||
| && NotConstructibleFromSimilarType<T, U>::value>; | |||
| public: | |||
| Optional() = default; | |||
| Optional (Nullopt) noexcept {} | |||
| template <typename U = Value, | |||
| typename = std::enable_if_t<std::is_constructible<Value, U&&>::value | |||
| && ! std::is_same<std::decay_t<U>, Optional>::value>> | |||
| Optional (U&& value) noexcept (noexcept (Value (std::forward<U> (value)))) | |||
| : valid (true) | |||
| { | |||
| new (&storage) Value (std::forward<U> (value)); | |||
| } | |||
| Optional (Optional&& other) noexcept (noexcept (std::declval<Optional>().constructFrom (other))) | |||
| { | |||
| constructFrom (other); | |||
| } | |||
| Optional (const Optional& other) | |||
| : valid (other.valid) | |||
| { | |||
| if (valid) | |||
| new (&storage) Value (*other); | |||
| } | |||
| template <typename Other, typename = OptionalMoveConstructorEnabled<Value, Other>> | |||
| Optional (Optional<Other>&& other) noexcept (noexcept (std::declval<Optional>().constructFrom (other))) | |||
| { | |||
| constructFrom (other); | |||
| } | |||
| template <typename Other, typename = OptionalCopyConstructorEnabled<Value, Other>> | |||
| Optional (const Optional<Other>& other) | |||
| : valid (other.hasValue()) | |||
| { | |||
| if (valid) | |||
| new (&storage) Value (*other); | |||
| } | |||
| Optional& operator= (Nullopt) noexcept | |||
| { | |||
| reset(); | |||
| return *this; | |||
| } | |||
| template <typename U = Value, | |||
| typename = std::enable_if_t<std::is_nothrow_move_constructible<U>::value | |||
| && std::is_nothrow_move_assignable<U>::value>> | |||
| Optional& operator= (Optional&& other) noexcept (noexcept (std::declval<Optional>().assign (std::declval<Optional&>()))) | |||
| { | |||
| assign (other); | |||
| return *this; | |||
| } | |||
| template <typename U = Value, | |||
| typename = std::enable_if_t<! std::is_same<std::decay_t<U>, Optional>::value | |||
| && std::is_constructible<Value, U>::value | |||
| && std::is_assignable<Value&, U>::value | |||
| && (! std::is_scalar<Value>::value || ! std::is_same<std::decay_t<U>, Value>::value)>> | |||
| Optional& operator= (U&& value) | |||
| { | |||
| if (valid) | |||
| **this = std::forward<U> (value); | |||
| else | |||
| new (&storage) Value (std::forward<U> (value)); | |||
| valid = true; | |||
| return *this; | |||
| } | |||
| /* Maintains the strong exception safety guarantee. */ | |||
| Optional& operator= (const Optional& other) | |||
| { | |||
| auto copy = other; | |||
| assign (copy); | |||
| return *this; | |||
| } | |||
| template <typename Other, typename = OptionalMoveAssignmentEnabled<Value, Other>> | |||
| Optional& operator= (Optional<Other>&& other) noexcept (noexcept (std::declval<Optional>().assign (other))) | |||
| { | |||
| assign (other); | |||
| return *this; | |||
| } | |||
| /* Maintains the strong exception safety guarantee. */ | |||
| template <typename Other, typename = OptionalCopyAssignmentEnabled<Value, Other>> | |||
| Optional& operator= (const Optional<Other>& other) | |||
| { | |||
| auto copy = other; | |||
| assign (copy); | |||
| return *this; | |||
| } | |||
| ~Optional() noexcept | |||
| { | |||
| reset(); | |||
| } | |||
| Value* operator->() noexcept { return reinterpret_cast< Value*> (&storage); } | |||
| const Value* operator->() const noexcept { return reinterpret_cast<const Value*> (&storage); } | |||
| Value& operator*() noexcept { return *operator->(); } | |||
| const Value& operator*() const noexcept { return *operator->(); } | |||
| explicit operator bool() const noexcept { return valid; } | |||
| bool hasValue() const noexcept { return valid; } | |||
| void reset() | |||
| { | |||
| if (std::exchange (valid, false)) | |||
| operator*().~Value(); | |||
| } | |||
| /* Like std::optional::value_or */ | |||
| template <typename U> | |||
| Value orFallback (U&& fallback) const { return *this ? **this : std::forward<U> (fallback); } | |||
| template <typename... Args> | |||
| Value& emplace (Args&&... args) | |||
| { | |||
| reset(); | |||
| new (&storage) Value (std::forward<Args> (args)...); | |||
| valid = true; | |||
| return **this; | |||
| } | |||
| void swap (Optional& other) noexcept (std::is_nothrow_move_constructible<Value>::value | |||
| && detail::adlSwap::isNothrowSwappable<Value>) | |||
| { | |||
| if (hasValue() && other.hasValue()) | |||
| { | |||
| using std::swap; | |||
| swap (**this, *other); | |||
| } | |||
| else if (hasValue() || other.hasValue()) | |||
| { | |||
| (hasValue() ? other : *this).constructFrom (hasValue() ? *this : other); | |||
| } | |||
| } | |||
| private: | |||
| template <typename Other> | |||
| void constructFrom (Optional<Other>& other) noexcept (noexcept (Value (std::move (*other)))) | |||
| { | |||
| if (! other.hasValue()) | |||
| return; | |||
| new (&storage) Value (std::move (*other)); | |||
| valid = true; | |||
| other.reset(); | |||
| } | |||
| template <typename Other> | |||
| void assign (Optional<Other>& other) noexcept (noexcept (std::declval<Value&>() = std::move (*other)) && noexcept (std::declval<Optional>().constructFrom (other))) | |||
| { | |||
| if (valid) | |||
| { | |||
| if (other.hasValue()) | |||
| { | |||
| **this = std::move (*other); | |||
| other.reset(); | |||
| } | |||
| else | |||
| { | |||
| reset(); | |||
| } | |||
| } | |||
| else | |||
| { | |||
| constructFrom (other); | |||
| } | |||
| } | |||
| std::aligned_storage_t<sizeof (Value), alignof (Value)> storage; | |||
| bool valid = false; | |||
| }; | |||
| JUCE_END_IGNORE_WARNINGS_MSVC | |||
| template <class T, class U> | |||
| bool operator== (const Optional<T>& lhs, const Optional<U>& rhs) | |||
| { | |||
| if (lhs.hasValue() != rhs.hasValue()) return false; | |||
| if (! lhs.hasValue()) return true; | |||
| return *lhs == *rhs; | |||
| } | |||
| template <class T, class U> | |||
| bool operator!= (const Optional<T>& lhs, const Optional<U>& rhs) | |||
| { | |||
| if (lhs.hasValue() != rhs.hasValue()) return true; | |||
| if (! lhs.hasValue()) return false; | |||
| return *lhs != *rhs; | |||
| } | |||
| template <class T, class U> | |||
| bool operator< (const Optional<T>& lhs, const Optional<U>& rhs) | |||
| { | |||
| if (! rhs.hasValue()) return false; | |||
| if (! lhs.hasValue()) return true; | |||
| return *lhs < *rhs; | |||
| } | |||
| template <class T, class U> | |||
| bool operator<= (const Optional<T>& lhs, const Optional<U>& rhs) | |||
| { | |||
| if (! lhs.hasValue()) return true; | |||
| if (! rhs.hasValue()) return false; | |||
| return *lhs <= *rhs; | |||
| } | |||
| template <class T, class U> | |||
| bool operator> (const Optional<T>& lhs, const Optional<U>& rhs) | |||
| { | |||
| if (! lhs.hasValue()) return false; | |||
| if (! rhs.hasValue()) return true; | |||
| return *lhs > *rhs; | |||
| } | |||
| template <class T, class U> | |||
| bool operator>= (const Optional<T>& lhs, const Optional<U>& rhs) | |||
| { | |||
| if (! rhs.hasValue()) return true; | |||
| if (! lhs.hasValue()) return false; | |||
| return *lhs >= *rhs; | |||
| } | |||
| template <class T> | |||
| bool operator== (const Optional<T>& opt, Nullopt) noexcept { return ! opt.hasValue(); } | |||
| template <class T> | |||
| bool operator== (Nullopt, const Optional<T>& opt) noexcept { return ! opt.hasValue(); } | |||
| template <class T> | |||
| bool operator!= (const Optional<T>& opt, Nullopt) noexcept { return opt.hasValue(); } | |||
| template <class T> | |||
| bool operator!= (Nullopt, const Optional<T>& opt) noexcept { return opt.hasValue(); } | |||
| template <class T> | |||
| bool operator< (const Optional<T>&, Nullopt) noexcept { return false; } | |||
| template <class T> | |||
| bool operator< (Nullopt, const Optional<T>& opt) noexcept { return opt.hasValue(); } | |||
| template <class T> | |||
| bool operator<= (const Optional<T>& opt, Nullopt) noexcept { return ! opt.hasValue(); } | |||
| template <class T> | |||
| bool operator<= (Nullopt, const Optional<T>&) noexcept { return true; } | |||
| template <class T> | |||
| bool operator> (const Optional<T>& opt, Nullopt) noexcept { return opt.hasValue(); } | |||
| template <class T> | |||
| bool operator> (Nullopt, const Optional<T>&) noexcept { return false; } | |||
| template <class T> | |||
| bool operator>= (const Optional<T>&, Nullopt) noexcept { return true; } | |||
| template <class T> | |||
| bool operator>= (Nullopt, const Optional<T>& opt) noexcept { return ! opt.hasValue(); } | |||
| template <class T, class U> | |||
| bool operator== (const Optional<T>& opt, const U& value) { return opt.hasValue() ? *opt == value : false; } | |||
| template <class T, class U> | |||
| bool operator== (const T& value, const Optional<U>& opt) { return opt.hasValue() ? value == *opt : false; } | |||
| template <class T, class U> | |||
| bool operator!= (const Optional<T>& opt, const U& value) { return opt.hasValue() ? *opt != value : true; } | |||
| template <class T, class U> | |||
| bool operator!= (const T& value, const Optional<U>& opt) { return opt.hasValue() ? value != *opt : true; } | |||
| template <class T, class U> | |||
| bool operator< (const Optional<T>& opt, const U& value) { return opt.hasValue() ? *opt < value : true; } | |||
| template <class T, class U> | |||
| bool operator< (const T& value, const Optional<U>& opt) { return opt.hasValue() ? value < *opt : false; } | |||
| template <class T, class U> | |||
| bool operator<= (const Optional<T>& opt, const U& value) { return opt.hasValue() ? *opt <= value : true; } | |||
| template <class T, class U> | |||
| bool operator<= (const T& value, const Optional<U>& opt) { return opt.hasValue() ? value <= *opt : false; } | |||
| template <class T, class U> | |||
| bool operator> (const Optional<T>& opt, const U& value) { return opt.hasValue() ? *opt > value : false; } | |||
| template <class T, class U> | |||
| bool operator> (const T& value, const Optional<U>& opt) { return opt.hasValue() ? value > *opt : true; } | |||
| template <class T, class U> | |||
| bool operator>= (const Optional<T>& opt, const U& value) { return opt.hasValue() ? *opt >= value : false; } | |||
| template <class T, class U> | |||
| bool operator>= (const T& value, const Optional<U>& opt) { return opt.hasValue() ? value >= *opt : true; } | |||
| } // namespace juce | |||
| @@ -0,0 +1,627 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the JUCE library. | |||
| Copyright (c) 2022 - Raw Material Software Limited | |||
| JUCE is an open source library subject to commercial or open-source | |||
| licensing. | |||
| The code included in this file is provided under the terms of the ISC license | |||
| http://www.isc.org/downloads/software-support-policy/isc-license. Permission | |||
| To use, copy, modify, and/or distribute this software for any purpose with or | |||
| without fee is hereby granted provided that the above copyright notice and | |||
| this permission notice appear in all copies. | |||
| JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER | |||
| EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE | |||
| DISCLAIMED. | |||
| ============================================================================== | |||
| */ | |||
| namespace juce | |||
| { | |||
| /* Not nested, so that ADL works for the swap function. */ | |||
| struct ThrowOnMoveOrSwap | |||
| { | |||
| ThrowOnMoveOrSwap() = default; | |||
| ThrowOnMoveOrSwap (ThrowOnMoveOrSwap&&) { throw std::bad_alloc{}; } | |||
| }; | |||
| static void swap (ThrowOnMoveOrSwap&, ThrowOnMoveOrSwap&) { throw std::bad_alloc{}; } | |||
| class OptionalUnitTest : public UnitTest | |||
| { | |||
| public: | |||
| OptionalUnitTest() : UnitTest ("Optional", UnitTestCategories::containers) {} | |||
| void runTest() override | |||
| { | |||
| beginTest ("Default-constructed optional is invalid"); | |||
| { | |||
| Optional<int> o; | |||
| expect (! o.hasValue()); | |||
| } | |||
| beginTest ("Constructing from Nullopt is invalid"); | |||
| { | |||
| Optional<int> o (nullopt); | |||
| expect (! o.hasValue()); | |||
| } | |||
| beginTest ("Optional constructed from value is valid"); | |||
| { | |||
| Optional<int> o = 5; | |||
| expect (o.hasValue()); | |||
| expectEquals (*o, 5); | |||
| } | |||
| using Ptr = std::shared_ptr<int>; | |||
| const auto makePtr = [] { return std::make_shared<int>(); }; | |||
| beginTest ("Constructing from a moved optional calls appropriate member functions"); | |||
| { | |||
| auto ptr = makePtr(); | |||
| Optional<Ptr> original (ptr); | |||
| expect (ptr.use_count() == 2); | |||
| auto other = std::move (original); | |||
| expect (! original.hasValue()); | |||
| expect (other.hasValue()); | |||
| expect (ptr.use_count() == 2); | |||
| } | |||
| beginTest ("Moving an empty optional to a populated one destroys the instance"); | |||
| { | |||
| auto ptr = makePtr(); | |||
| Optional<Ptr> original (ptr); | |||
| expect (ptr.use_count() == 2); | |||
| original = Optional<Ptr>(); | |||
| expect (ptr.use_count() == 1); | |||
| } | |||
| beginTest ("Copying an empty optional to a populated one destroys the instance"); | |||
| { | |||
| auto ptr = makePtr(); | |||
| Optional<Ptr> original (ptr); | |||
| expect (ptr.use_count() == 2); | |||
| Optional<Ptr> empty; | |||
| original = empty; | |||
| expect (ptr.use_count() == 1); | |||
| } | |||
| beginTest ("Moving a populated optional calls appropriate member functions"); | |||
| { | |||
| auto a = makePtr(); | |||
| auto b = makePtr(); | |||
| Optional<Ptr> aOpt (a); | |||
| Optional<Ptr> bOpt (b); | |||
| expect (a.use_count() == 2); | |||
| expect (b.use_count() == 2); | |||
| aOpt = std::move (bOpt); | |||
| expect (aOpt.hasValue()); | |||
| expect (! bOpt.hasValue()); | |||
| expect (a.use_count() == 1); | |||
| expect (b.use_count() == 2); | |||
| } | |||
| beginTest ("Copying a populated optional calls appropriate member functions"); | |||
| { | |||
| auto a = makePtr(); | |||
| auto b = makePtr(); | |||
| Optional<Ptr> aOpt (a); | |||
| Optional<Ptr> bOpt (b); | |||
| expect (a.use_count() == 2); | |||
| expect (b.use_count() == 2); | |||
| aOpt = bOpt; | |||
| expect (aOpt.hasValue()); | |||
| expect (bOpt.hasValue()); | |||
| expect (a.use_count() == 1); | |||
| expect (b.use_count() == 3); | |||
| } | |||
| beginTest ("Moving an empty optional to an empty one does nothing"); | |||
| { | |||
| Optional<Ptr> original; | |||
| original = Optional<Ptr>(); | |||
| expect (! original.hasValue()); | |||
| } | |||
| beginTest ("Copying an empty optional to an empty one does nothing"); | |||
| { | |||
| Optional<Ptr> original; | |||
| Optional<Ptr> empty; | |||
| original = empty; | |||
| expect (! original.hasValue()); | |||
| expect (! empty.hasValue()); | |||
| } | |||
| beginTest ("Moving a populated optional calls appropriate member functions"); | |||
| { | |||
| auto a = makePtr(); | |||
| Optional<Ptr> aOpt (a); | |||
| Optional<Ptr> empty; | |||
| expect (a.use_count() == 2); | |||
| empty = std::move (aOpt); | |||
| expect (empty.hasValue()); | |||
| expect (! aOpt.hasValue()); | |||
| expect (a.use_count() == 2); | |||
| } | |||
| beginTest ("Copying a populated optional calls appropriate member functions"); | |||
| { | |||
| auto a = makePtr(); | |||
| Optional<Ptr> aOpt (a); | |||
| Optional<Ptr> empty; | |||
| expect (a.use_count() == 2); | |||
| empty = aOpt; | |||
| expect (aOpt.hasValue()); | |||
| expect (empty.hasValue()); | |||
| expect (a.use_count() == 3); | |||
| } | |||
| struct ThrowOnCopy | |||
| { | |||
| ThrowOnCopy() = default; | |||
| // Put into an invalid state and throw | |||
| ThrowOnCopy (const ThrowOnCopy&) | |||
| { | |||
| value = -100; | |||
| throw std::bad_alloc{}; | |||
| } | |||
| // Put into an invalid state and throw | |||
| ThrowOnCopy& operator= (const ThrowOnCopy&) | |||
| { | |||
| value = -100; | |||
| throw std::bad_alloc{}; | |||
| } | |||
| ThrowOnCopy (ThrowOnCopy&&) noexcept = default; | |||
| ThrowOnCopy& operator= (ThrowOnCopy&&) noexcept = default; | |||
| ~ThrowOnCopy() = default; | |||
| int value = 0; | |||
| }; | |||
| beginTest ("Strong exception safety is maintained when forwarding over empty object"); | |||
| { | |||
| bool threw = false; | |||
| Optional<ThrowOnCopy> a; | |||
| try | |||
| { | |||
| ThrowOnCopy t; | |||
| a = t; | |||
| } | |||
| catch (const std::bad_alloc&) | |||
| { | |||
| threw = true; | |||
| } | |||
| expect (threw); | |||
| expect (! a.hasValue()); // If construction failed, this object should still be well-formed but empty | |||
| } | |||
| beginTest ("Weak exception safety is maintained when forwarding over populated object"); | |||
| { | |||
| bool threw = false; | |||
| Optional<ThrowOnCopy> a = ThrowOnCopy(); | |||
| a->value = 5; | |||
| try | |||
| { | |||
| ThrowOnCopy t; | |||
| a = t; | |||
| } | |||
| catch (const std::bad_alloc&) | |||
| { | |||
| threw = true; | |||
| } | |||
| expect (threw); | |||
| expect (a.hasValue()); | |||
| expect (a->value == -100); // If we assign to an extant object, it's up to that object to provide an exception guarantee | |||
| } | |||
| beginTest ("Strong exception safety is maintained when copying over empty object"); | |||
| { | |||
| bool threw = false; | |||
| Optional<ThrowOnCopy> a; | |||
| try | |||
| { | |||
| Optional<ThrowOnCopy> t = ThrowOnCopy{}; | |||
| a = t; | |||
| } | |||
| catch (const std::bad_alloc&) | |||
| { | |||
| threw = true; | |||
| } | |||
| expect (threw); | |||
| expect (! a.hasValue()); | |||
| } | |||
| beginTest ("Strong exception safety is maintained when copying over populated object"); | |||
| { | |||
| bool threw = false; | |||
| Optional<ThrowOnCopy> a = ThrowOnCopy(); | |||
| a->value = 5; | |||
| try | |||
| { | |||
| Optional<ThrowOnCopy> t = ThrowOnCopy{}; | |||
| a = t; | |||
| } | |||
| catch (const std::bad_alloc&) | |||
| { | |||
| threw = true; | |||
| } | |||
| expect (threw); | |||
| expect (a.hasValue()); | |||
| expect (a->value == 5); | |||
| } | |||
| beginTest ("Assigning from nullopt clears the instance"); | |||
| { | |||
| auto ptr = makePtr(); | |||
| Optional<Ptr> a (ptr); | |||
| expect (ptr.use_count() == 2); | |||
| a = nullopt; | |||
| expect (ptr.use_count() == 1); | |||
| } | |||
| struct Foo {}; | |||
| struct Bar : Foo {}; | |||
| beginTest ("Can be constructed from compatible type"); | |||
| { | |||
| Optional<std::shared_ptr<Foo>> opt { std::make_shared<Bar>() }; | |||
| } | |||
| beginTest ("Can be assigned from compatible type"); | |||
| { | |||
| Optional<std::shared_ptr<Foo>> opt; | |||
| opt = std::make_shared<Bar>(); | |||
| } | |||
| beginTest ("Can copy from compatible type"); | |||
| { | |||
| auto ptr = std::make_shared<Bar>(); | |||
| Optional<std::shared_ptr<Bar>> bar (ptr); | |||
| Optional<std::shared_ptr<Foo>> foo (bar); | |||
| expect (ptr.use_count() == 3); | |||
| } | |||
| beginTest ("Can move from compatible type"); | |||
| { | |||
| auto ptr = std::make_shared<Bar>(); | |||
| Optional<std::shared_ptr<Foo>> foo (Optional<std::shared_ptr<Bar>> { ptr }); | |||
| expect (ptr.use_count() == 2); | |||
| } | |||
| beginTest ("Can copy assign from compatible type"); | |||
| { | |||
| auto ptr = std::make_shared<Bar>(); | |||
| Optional<std::shared_ptr<Bar>> bar (ptr); | |||
| Optional<std::shared_ptr<Foo>> foo; | |||
| foo = bar; | |||
| expect (ptr.use_count() == 3); | |||
| } | |||
| beginTest ("Can move assign from compatible type"); | |||
| { | |||
| auto ptr = std::make_shared<Bar>(); | |||
| Optional<std::shared_ptr<Foo>> foo; | |||
| foo = Optional<std::shared_ptr<Bar>> (ptr); | |||
| expect (ptr.use_count() == 2); | |||
| } | |||
| beginTest ("An exception thrown during emplace leaves the optional without a value"); | |||
| { | |||
| Optional<ThrowOnCopy> opt { ThrowOnCopy{} }; | |||
| bool threw = false; | |||
| try | |||
| { | |||
| ThrowOnCopy t; | |||
| opt.emplace (t); | |||
| } | |||
| catch (const std::bad_alloc&) | |||
| { | |||
| threw = true; | |||
| } | |||
| expect (threw); | |||
| expect (! opt.hasValue()); | |||
| } | |||
| beginTest ("Swap does nothing to two empty optionals"); | |||
| { | |||
| Optional<Ptr> a, b; | |||
| expect (! a.hasValue()); | |||
| expect (! b.hasValue()); | |||
| a.swap (b); | |||
| expect (! a.hasValue()); | |||
| expect (! b.hasValue()); | |||
| } | |||
| beginTest ("Swap transfers ownership if one optional contains a value"); | |||
| { | |||
| { | |||
| Ptr ptr = makePtr(); | |||
| Optional<Ptr> a, b = ptr; | |||
| expect (! a.hasValue()); | |||
| expect (b.hasValue()); | |||
| expect (ptr.use_count() == 2); | |||
| a.swap (b); | |||
| expect (a.hasValue()); | |||
| expect (! b.hasValue()); | |||
| expect (ptr.use_count() == 2); | |||
| } | |||
| { | |||
| auto ptr = makePtr(); | |||
| Optional<Ptr> a = ptr, b; | |||
| expect (a.hasValue()); | |||
| expect (! b.hasValue()); | |||
| expect (ptr.use_count() == 2); | |||
| a.swap (b); | |||
| expect (! a.hasValue()); | |||
| expect (b.hasValue()); | |||
| expect (ptr.use_count() == 2); | |||
| } | |||
| } | |||
| beginTest ("Swap calls std::swap to swap two populated optionals"); | |||
| { | |||
| auto x = makePtr(), y = makePtr(); | |||
| Optional<Ptr> a = x, b = y; | |||
| expect (a.hasValue()); | |||
| expect (b.hasValue()); | |||
| expect (x.use_count() == 2); | |||
| expect (y.use_count() == 2); | |||
| expect (*a == x); | |||
| expect (*b == y); | |||
| a.swap (b); | |||
| expect (a.hasValue()); | |||
| expect (b.hasValue()); | |||
| expect (x.use_count() == 2); | |||
| expect (y.use_count() == 2); | |||
| expect (*a == y); | |||
| expect (*b == x); | |||
| } | |||
| beginTest ("An exception thrown during a swap leaves both objects in the previous populated state"); | |||
| { | |||
| { | |||
| Optional<ThrowOnMoveOrSwap> a, b; | |||
| a.emplace(); | |||
| expect (a.hasValue()); | |||
| expect (! b.hasValue()); | |||
| bool threw = false; | |||
| try | |||
| { | |||
| a.swap (b); | |||
| } | |||
| catch (const std::bad_alloc&) | |||
| { | |||
| threw = true; | |||
| } | |||
| expect (threw); | |||
| expect (a.hasValue()); | |||
| expect (! b.hasValue()); | |||
| } | |||
| { | |||
| Optional<ThrowOnMoveOrSwap> a, b; | |||
| b.emplace(); | |||
| expect (! a.hasValue()); | |||
| expect (b.hasValue()); | |||
| bool threw = false; | |||
| try | |||
| { | |||
| a.swap (b); | |||
| } | |||
| catch (const std::bad_alloc&) | |||
| { | |||
| threw = true; | |||
| } | |||
| expect (threw); | |||
| expect (! a.hasValue()); | |||
| expect (b.hasValue()); | |||
| } | |||
| { | |||
| Optional<ThrowOnMoveOrSwap> a, b; | |||
| a.emplace(); | |||
| b.emplace(); | |||
| expect (a.hasValue()); | |||
| expect (b.hasValue()); | |||
| bool threw = false; | |||
| try | |||
| { | |||
| a.swap (b); | |||
| } | |||
| catch (const std::bad_alloc&) | |||
| { | |||
| threw = true; | |||
| } | |||
| expect (threw); | |||
| expect (a.hasValue()); | |||
| expect (b.hasValue()); | |||
| } | |||
| } | |||
| beginTest ("Relational tests"); | |||
| { | |||
| expect (Optional<int> (1) == Optional<int> (1)); | |||
| expect (Optional<int>() == Optional<int>()); | |||
| expect (! (Optional<int> (1) == Optional<int>())); | |||
| expect (! (Optional<int>() == Optional<int> (1))); | |||
| expect (! (Optional<int> (1) == Optional<int> (2))); | |||
| expect (Optional<int> (1) != Optional<int> (2)); | |||
| expect (! (Optional<int>() != Optional<int>())); | |||
| expect (Optional<int> (1) != Optional<int>()); | |||
| expect (Optional<int>() != Optional<int> (1)); | |||
| expect (! (Optional<int> (1) != Optional<int> (1))); | |||
| expect (Optional<int>() < Optional<int> (1)); | |||
| expect (! (Optional<int> (1) < Optional<int>())); | |||
| expect (! (Optional<int>() < Optional<int>())); | |||
| expect (Optional<int> (1) < Optional<int> (2)); | |||
| expect (Optional<int>() <= Optional<int> (1)); | |||
| expect (! (Optional<int> (1) <= Optional<int>())); | |||
| expect (Optional<int>() <= Optional<int>()); | |||
| expect (Optional<int> (1) <= Optional<int> (2)); | |||
| expect (! (Optional<int>() > Optional<int> (1))); | |||
| expect (Optional<int> (1) > Optional<int>()); | |||
| expect (! (Optional<int>() > Optional<int>())); | |||
| expect (! (Optional<int> (1) > Optional<int> (2))); | |||
| expect (! (Optional<int>() >= Optional<int> (1))); | |||
| expect (Optional<int> (1) >= Optional<int>()); | |||
| expect (Optional<int>() >= Optional<int>()); | |||
| expect (! (Optional<int> (1) >= Optional<int> (2))); | |||
| expect (Optional<int>() == nullopt); | |||
| expect (! (Optional<int> (1) == nullopt)); | |||
| expect (nullopt == Optional<int>()); | |||
| expect (! (nullopt == Optional<int> (1))); | |||
| expect (! (Optional<int>() != nullopt)); | |||
| expect (Optional<int> (1) != nullopt); | |||
| expect (! (nullopt != Optional<int>())); | |||
| expect (nullopt != Optional<int> (1)); | |||
| expect (! (Optional<int>() < nullopt)); | |||
| expect (! (Optional<int> (1) < nullopt)); | |||
| expect (! (nullopt < Optional<int>())); | |||
| expect (nullopt < Optional<int> (1)); | |||
| expect (Optional<int>() <= nullopt); | |||
| expect (! (Optional<int> (1) <= nullopt)); | |||
| expect (nullopt <= Optional<int>()); | |||
| expect (nullopt <= Optional<int> (1)); | |||
| expect (! (Optional<int>() > nullopt)); | |||
| expect (Optional<int> (1) > nullopt); | |||
| expect (! (nullopt > Optional<int>())); | |||
| expect (! (nullopt > Optional<int> (1))); | |||
| expect (Optional<int>() >= nullopt); | |||
| expect (Optional<int> (1) >= nullopt); | |||
| expect (nullopt >= Optional<int>()); | |||
| expect (! (nullopt >= Optional<int> (1))); | |||
| expect (! (Optional<int>() == 5)); | |||
| expect (! (Optional<int> (1) == 5)); | |||
| expect (Optional<int> (1) == 1); | |||
| expect (! (5 == Optional<int>())); | |||
| expect (! (5 == Optional<int> (1))); | |||
| expect (1 == Optional<int> (1)); | |||
| expect (Optional<int>() != 5); | |||
| expect (Optional<int> (1) != 5); | |||
| expect (! (Optional<int> (1) != 1)); | |||
| expect (5 != Optional<int>()); | |||
| expect (5 != Optional<int> (1)); | |||
| expect (! (1 != Optional<int> (1))); | |||
| expect (Optional<int>() < 5); | |||
| expect (Optional<int> (1) < 5); | |||
| expect (! (Optional<int> (1) < 1)); | |||
| expect (! (Optional<int> (1) < 0)); | |||
| expect (! (5 < Optional<int>())); | |||
| expect (! (5 < Optional<int> (1))); | |||
| expect (! (1 < Optional<int> (1))); | |||
| expect (0 < Optional<int> (1)); | |||
| expect (Optional<int>() <= 5); | |||
| expect (Optional<int> (1) <= 5); | |||
| expect (Optional<int> (1) <= 1); | |||
| expect (! (Optional<int> (1) <= 0)); | |||
| expect (! (5 <= Optional<int>())); | |||
| expect (! (5 <= Optional<int> (1))); | |||
| expect (1 <= Optional<int> (1)); | |||
| expect (0 <= Optional<int> (1)); | |||
| expect (! (Optional<int>() > 5)); | |||
| expect (! (Optional<int> (1) > 5)); | |||
| expect (! (Optional<int> (1) > 1)); | |||
| expect (Optional<int> (1) > 0); | |||
| expect (5 > Optional<int>()); | |||
| expect (5 > Optional<int> (1)); | |||
| expect (! (1 > Optional<int> (1))); | |||
| expect (! (0 > Optional<int> (1))); | |||
| expect (! (Optional<int>() >= 5)); | |||
| expect (! (Optional<int> (1) >= 5)); | |||
| expect (Optional<int> (1) >= 1); | |||
| expect (Optional<int> (1) >= 0); | |||
| expect (5 >= Optional<int>()); | |||
| expect (5 >= Optional<int> (1)); | |||
| expect (1 >= Optional<int> (1)); | |||
| expect (! (0 >= Optional<int> (1))); | |||
| } | |||
| } | |||
| }; | |||
| static OptionalUnitTest optionalUnitTest; | |||
| } // namespace juce | |||
| @@ -261,6 +261,9 @@ | |||
| //============================================================================== | |||
| #if JUCE_UNIT_TESTS | |||
| #include "containers/juce_HashMap_test.cpp" | |||
| #include "containers/juce_Optional.h" | |||
| #include "containers/juce_Optional_test.cpp" | |||
| #endif | |||
| //============================================================================== | |||