mirror of
https://github.com/juce-framework/JUCE.git
synced 2026-01-10 23:44:24 +00:00
Add simple Optional type
This commit is contained in:
parent
833c1e590f
commit
5d096b46d7
5 changed files with 1074 additions and 86 deletions
|
|
@ -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;
|
||||
|
|
|
|||
378
modules/juce_core/containers/juce_Optional.h
Normal file
378
modules/juce_core/containers/juce_Optional.h
Normal file
|
|
@ -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
|
||||
627
modules/juce_core/containers/juce_Optional_test.cpp
Normal file
627
modules/juce_core/containers/juce_Optional_test.cpp
Normal file
|
|
@ -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
|
||||
|
||||
//==============================================================================
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue