-
-
Notifications
You must be signed in to change notification settings - Fork 1.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Beats: Use std::make_shared/shared_from_this() #4336
Changes from 5 commits
6cb1da2
1bbee5f
df574b1
beaf525
27be226
91b5d8c
90d362b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -46,6 +46,7 @@ class BeatGridIterator : public BeatIterator { | |
}; | ||
|
||
BeatGrid::BeatGrid( | ||
MakeSharedTag, | ||
audio::SampleRate sampleRate, | ||
const QString& subVersion, | ||
const mixxx::track::io::BeatGrid& grid, | ||
|
@@ -54,21 +55,20 @@ BeatGrid::BeatGrid( | |
m_sampleRate(sampleRate), | ||
m_grid(grid), | ||
m_beatLengthFrames(beatLengthFrames) { | ||
// BeatGrid should live in the same thread as the track it is associated | ||
// with. | ||
} | ||
|
||
BeatGrid::BeatGrid(const BeatGrid& other, | ||
BeatGrid::BeatGrid( | ||
MakeSharedTag, | ||
const BeatGrid& other, | ||
const mixxx::track::io::BeatGrid& grid, | ||
audio::FrameDiff_t beatLengthFrames) | ||
: m_subVersion(other.m_subVersion), | ||
m_sampleRate(other.m_sampleRate), | ||
m_grid(grid), | ||
m_beatLengthFrames(beatLengthFrames) { | ||
: BeatGrid({}, other.m_sampleRate, other.m_subVersion, grid, beatLengthFrames) { | ||
} | ||
|
||
BeatGrid::BeatGrid(const BeatGrid& other) | ||
: BeatGrid(other, other.m_grid, other.m_beatLengthFrames) { | ||
BeatGrid::BeatGrid( | ||
MakeSharedTag, | ||
const BeatGrid& other) | ||
: BeatGrid({}, other, other.m_grid, other.m_beatLengthFrames) { | ||
} | ||
|
||
// static | ||
|
@@ -98,7 +98,8 @@ BeatsPointer BeatGrid::makeBeatGrid( | |
// Calculate beat length as sample offsets | ||
const audio::FrameDiff_t beatLengthFrames = 60.0 * sampleRate / bpm.value(); | ||
|
||
return BeatsPointer(new BeatGrid(sampleRate, subVersion, grid, beatLengthFrames)); | ||
return std::make_shared<BeatGrid>( | ||
MakeSharedTag{}, sampleRate, subVersion, grid, beatLengthFrames); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why do you use here the explicit version, where above you use only the braced tantalizer? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Because std::make_shared() is not able to deduce the type and compilation fails. Try it. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think we should be explicit everywhere. |
||
} | ||
|
||
// static | ||
|
@@ -109,12 +110,16 @@ BeatsPointer BeatGrid::fromByteArray( | |
mixxx::track::io::BeatGrid grid; | ||
if (grid.ParseFromArray(byteArray.constData(), byteArray.length())) { | ||
const audio::FrameDiff_t beatLengthFrames = (60.0 * sampleRate / grid.bpm().bpm()); | ||
return BeatsPointer(new BeatGrid(sampleRate, subVersion, grid, beatLengthFrames)); | ||
return std::make_shared<BeatGrid>(MakeSharedTag{}, | ||
sampleRate, | ||
subVersion, | ||
grid, | ||
beatLengthFrames); | ||
} | ||
|
||
// Legacy fallback for BeatGrid-1.0 | ||
if (byteArray.size() != sizeof(BeatGridData)) { | ||
return BeatsPointer(new BeatGrid(sampleRate, QString(), grid, 0)); | ||
return std::make_shared<BeatGrid>(MakeSharedTag{}, sampleRate, QString(), grid, 0); | ||
} | ||
const BeatGridData* blob = reinterpret_cast<const BeatGridData*>(byteArray.constData()); | ||
const auto firstBeat = mixxx::audio::FramePos(blob->firstBeat); | ||
|
@@ -248,27 +253,28 @@ mixxx::Bpm BeatGrid::getBpm() const { | |
mixxx::Bpm BeatGrid::getBpmAroundPosition(audio::FramePos position, int n) const { | ||
Q_UNUSED(position); | ||
Q_UNUSED(n); | ||
|
||
if (!isValid()) { | ||
return {}; | ||
} | ||
return bpm(); | ||
return getBpm(); | ||
} | ||
|
||
BeatsPointer BeatGrid::translate(audio::FrameDiff_t offset) const { | ||
if (!isValid()) { | ||
return BeatsPointer(new BeatGrid(*this)); | ||
VERIFY_OR_DEBUG_ASSERT(isValid()) { | ||
return clone(); | ||
} | ||
|
||
mixxx::track::io::BeatGrid grid = m_grid; | ||
const audio::FramePos newFirstBeatPosition = firstBeatPosition() + offset; | ||
grid.mutable_first_beat()->set_frame_position( | ||
static_cast<google::protobuf::int32>( | ||
newFirstBeatPosition.toLowerFrameBoundary().value())); | ||
|
||
return BeatsPointer(new BeatGrid(*this, grid, m_beatLengthFrames)); | ||
return std::make_shared<BeatGrid>(MakeSharedTag{}, *this, grid, m_beatLengthFrames); | ||
} | ||
|
||
BeatsPointer BeatGrid::scale(BpmScale scale) const { | ||
VERIFY_OR_DEBUG_ASSERT(isValid()) { | ||
return clone(); | ||
} | ||
|
||
mixxx::track::io::BeatGrid grid = m_grid; | ||
auto bpm = mixxx::Bpm(grid.bpm().bpm()); | ||
|
||
|
@@ -293,27 +299,28 @@ BeatsPointer BeatGrid::scale(BpmScale scale) const { | |
break; | ||
default: | ||
DEBUG_ASSERT(!"scale value invalid"); | ||
return BeatsPointer(new BeatGrid(*this)); | ||
return clone(); | ||
} | ||
|
||
if (!bpm.isValid()) { | ||
return BeatsPointer(new BeatGrid(*this)); | ||
return clone(); | ||
} | ||
|
||
bpm = BeatUtils::roundBpmWithinRange(bpm - kBpmScaleRounding, bpm, bpm + kBpmScaleRounding); | ||
grid.mutable_bpm()->set_bpm(bpm.value()); | ||
const mixxx::audio::FrameDiff_t beatLengthFrames = (60.0 * m_sampleRate / bpm.value()); | ||
return BeatsPointer(new BeatGrid(*this, grid, beatLengthFrames)); | ||
return std::make_shared<BeatGrid>(MakeSharedTag{}, *this, grid, beatLengthFrames); | ||
} | ||
|
||
BeatsPointer BeatGrid::setBpm(mixxx::Bpm bpm) { | ||
BeatsPointer BeatGrid::setBpm(mixxx::Bpm bpm) const { | ||
VERIFY_OR_DEBUG_ASSERT(bpm.isValid()) { | ||
return nullptr; | ||
return clone(); | ||
} | ||
|
||
mixxx::track::io::BeatGrid grid = m_grid; | ||
grid.mutable_bpm()->set_bpm(bpm.value()); | ||
const mixxx::audio::FrameDiff_t beatLengthFrames = (60.0 * m_sampleRate / bpm.value()); | ||
return BeatsPointer(new BeatGrid(*this, grid, beatLengthFrames)); | ||
return std::make_shared<BeatGrid>(MakeSharedTag{}, *this, grid, beatLengthFrames); | ||
} | ||
|
||
} // namespace mixxx |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -15,7 +15,7 @@ | |
namespace mixxx { | ||
|
||
class Beats; | ||
typedef std::shared_ptr<Beats> BeatsPointer; | ||
typedef std::shared_ptr<const Beats> BeatsPointer; | ||
|
||
class BeatIterator { | ||
public: | ||
|
@@ -27,10 +27,17 @@ class BeatIterator { | |
/// Beats is the base class for BPM and beat management classes. It provides a | ||
/// specification of all methods a beat-manager class must provide, as well as | ||
/// a capability model for representing optional features. | ||
class Beats { | ||
/// | ||
/// All instances of this class are supposed to be managed by std::shared_ptr! | ||
class Beats : private std::enable_shared_from_this<Beats> { | ||
public: | ||
virtual ~Beats() = default; | ||
|
||
BeatsPointer clone() const { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The term clone() is misleading here, because the Beats object is not cloned in terms of making an identical copy. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Cloning an immutable object is trivial be sharing its reference. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Conceptually this is still a clone operation. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I was confused when reading it the first time. You can either add documentation that this does not really clone, or change the name. I would prefer the later. |
||
// All instances are immutable and can be shared safely | ||
return shared_from_this(); | ||
} | ||
|
||
static mixxx::BeatsPointer fromByteArray( | ||
mixxx::audio::SampleRate sampleRate, | ||
const QString& beatsVersion, | ||
|
@@ -166,10 +173,21 @@ class Beats { | |
virtual BeatsPointer scale(BpmScale scale) const = 0; | ||
|
||
/// Adjust the beats so the global average BPM matches `bpm`. | ||
virtual BeatsPointer setBpm(mixxx::Bpm bpm) = 0; | ||
virtual BeatsPointer setBpm(mixxx::Bpm bpm) const = 0; | ||
|
||
protected: | ||
/// Type tag for making public constructors of derived classes inaccessible. | ||
/// | ||
/// The constructors must be public for using std::make_shared(). | ||
struct MakeSharedTag {}; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is not only used by makeShared. It is more like a "CTorProtect" or "ProtectedTag" There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It is only needed for make_shared(). Otherwise the constructors could be hidden by making them private. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I disagree. Redundantly repeating the type name of the type tag class where it is not needed is useless. It must only specified explicitly when using std::make_shared(), the only purpose it was introduced for. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We hit often such discussion in our reviews. I think this is sourced by our different roles. A plain {} as a parameter can be everything, the reader needs to look up other references to understand it. This is the same for bool function parameter by the way. Whenever the function signature changes, a {} will still compile even though it might be wrong. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The plan was to use I don't see a reason to re-introduce the redundant type name in private forwards. |
||
|
||
Beats() = default; | ||
|
||
virtual bool isValid() const = 0; | ||
|
||
private: | ||
Beats(const Beats&) = delete; | ||
Beats(Beats&&) = delete; | ||
}; | ||
|
||
} // namespace mixxx |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why you don't pass the MakeSharedTag, but create a new one?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Because type tags are not supposed to carry any information and should be eliminated during compilation. Passing the instance is not desired in this special case. Just a hint for the compiler to ignore the unnamed parameter.