temporal: knarly template code to consolidate and rationalize duplicated methods

This commit is contained in:
Paul Davis 2021-04-06 10:43:55 -06:00
parent 7938d8de4f
commit f61adcd738
2 changed files with 125 additions and 113 deletions

View File

@ -1615,11 +1615,19 @@ TempoMap::dump (std::ostream& ostr) const
ostr << "------------\n\n\n";
}
template<typename T, typename T1> TempoMap::Points::const_iterator
TempoMap::_get_tempo_and_meter (TempoPoint const *& tp, MeterPoint const *& mp, T (Point::*method)() const, T arg, bool can_match, bool ret_iterator_after_not_at) const
template<class const_traits_t> typename const_traits_t::iterator_type
TempoMap::_get_tempo_and_meter (typename const_traits_t::tempo_point_type & tp,
typename const_traits_t::meter_point_type & mp,
typename const_traits_t::time_reference (Point::*method)() const,
typename const_traits_t::time_type arg,
typename const_traits_t::iterator_type begini,
typename const_traits_t::iterator_type endi,
typename const_traits_t::tempo_point_type tstart,
typename const_traits_t::meter_point_type mstart,
bool can_match, bool ret_iterator_after_not_at) const
{
Points::const_iterator p;
Points::const_iterator last_used = _points.end();
typename const_traits_t::iterator_type p;
typename const_traits_t::iterator_type last_used = endi;
bool tempo_done = false;
bool meter_done = false;
@ -1630,23 +1638,25 @@ TempoMap::_get_tempo_and_meter (TempoPoint const *& tp, MeterPoint const *& mp,
tp = 0;
mp = 0;
/* if the starting position is the beginning of the timeline (indicated
* by the default constructor value for T1, then we are always allowed
* to use the tempo & meter at that position. Without this, it would be
* necessary to special case "can_match" in the caller if the start is
* "zero". Instead we do that here, since common cases
* (e.g. ::get_grid()) will use can_match = false, but may pass in a
* zero start point.
/* If the starting position is the beginning of the timeline (indicated
* by the default constructor value for the time_type (superclock_t,
* Beats, BBT_Time), then we are always allowed to use the tempo &
* meter at that position.
*
* Without this, it would be necessary to special case "can_match" in
* the caller if the start is "zero". Instead we do that here, since
* common cases (e.g. ::get_grid()) will use can_match = false, but may
* pass in a zero start point.
*/
can_match = (can_match || arg == T1());
can_match = (can_match || arg == typename const_traits_t::time_type ());
for (tp = &_tempos.front(), mp = &_meters.front(), p = _points.begin(); p != _points.end(); ++p) {
for (tp = tstart, mp = mstart, p = begini; p != endi; ++p) {
TempoPoint const * tpp;
MeterPoint const * mpp;
typename const_traits_t::tempo_point_type tpp;
typename const_traits_t::meter_point_type mpp;
if (!tempo_done && (tpp = dynamic_cast<TempoPoint const *> (&(*p))) != 0) {
if (!tempo_done && (tpp = dynamic_cast<typename const_traits_t::tempo_point_type> (&(*p))) != 0) {
if ((can_match && (((*p).*method)() > arg)) || (!can_match && (((*p).*method)() >= arg))) {
tempo_done = true;
} else {
@ -1655,7 +1665,7 @@ TempoMap::_get_tempo_and_meter (TempoPoint const *& tp, MeterPoint const *& mp,
}
}
if (!meter_done && (mpp = dynamic_cast<MeterPoint const *> (&(*p))) != 0) {
if (!meter_done && (mpp = dynamic_cast<typename const_traits_t::meter_point_type> (&(*p))) != 0) {
if ((can_match && (((*p).*method)() > arg)) || (!can_match && (((*p).*method)() >= arg))) {
meter_done = true;
} else {
@ -1670,7 +1680,7 @@ TempoMap::_get_tempo_and_meter (TempoPoint const *& tp, MeterPoint const *& mp,
}
if (!tp || !mp) {
return _points.end();
return endi;
}
if (ret_iterator_after_not_at) {
@ -1678,9 +1688,9 @@ TempoMap::_get_tempo_and_meter (TempoPoint const *& tp, MeterPoint const *& mp,
p = last_used;
if (can_match) {
while (((*p).*method)() <= arg && p != _points.end()) ++p;
while (((*p).*method)() <= arg && p != endi) ++p;
} else {
while (((*p).*method)() < arg && p != _points.end()) ++p;
while (((*p).*method)() < arg && p != endi) ++p;
}
return p;
@ -1689,82 +1699,6 @@ TempoMap::_get_tempo_and_meter (TempoPoint const *& tp, MeterPoint const *& mp,
return last_used;
}
template<typename T, typename T1> TempoMap::Points::iterator
TempoMap::_get_tempo_and_meter (TempoPoint *& tp, MeterPoint *& mp, T (Point::*method)() const, T arg, bool can_match, bool ret_iterator_after_not_at)
{
Points::iterator p;
Points::iterator last_used = _points.end();
bool tempo_done = false;
bool meter_done = false;
assert (!_tempos.empty());
assert (!_meters.empty());
assert (!_points.empty());
tp = 0;
mp = 0;
/* if the starting position is the beginning of the timeline (indicated
* by the default constructor value for T1, then we are always allowed
* to use the tempo & meter at that position. Without this, it would be
* necessary to special case "can_match" in the caller if the start is
* "zero". Instead we do that here, since common cases
* (e.g. ::get_grid()) will use can_match = false, but may pass in a
* zero start point.
*/
can_match = (can_match || arg == T1());
for (tp = &_tempos.front(), mp = &_meters.front(), p = _points.begin(); p != _points.end(); ++p) {
TempoPoint * tpp;
MeterPoint * mpp;
if (!tempo_done && (tpp = dynamic_cast<TempoPoint*> (&(*p))) != 0) {
if ((can_match && (((*p).*method)() > arg)) || (!can_match && (((*p).*method)() >= arg))) {
tempo_done = true;
} else {
tp = tpp;
last_used = p;
}
}
if (!meter_done && (mpp = dynamic_cast<MeterPoint*> (&(*p))) != 0) {
if ((can_match && (((*p).*method)() > arg)) || (!can_match && (((*p).*method)() >= arg))) {
meter_done = true;
} else {
mp = mpp;
last_used = p;
}
}
if (meter_done && tempo_done) {
break;
}
}
if (!tp || !mp) {
return _points.end();
}
if (ret_iterator_after_not_at) {
p = last_used;
if (can_match) {
while (((*p).*method)() <= arg && p != _points.end()) ++p;
} else {
while (((*p).*method)() < arg && p != _points.end()) ++p;
}
return p;
}
return last_used;
}
void
TempoMap::get_grid (TempoMapPoints& ret, superclock_t start, superclock_t end, uint32_t bar_mod)
{

View File

@ -862,29 +862,107 @@ class LIBTEMPORAL_API TempoMap : public PBD::StatefulDestructible
BBT_Time bbt_at (Beats const &) const;
BBT_Time bbt_at (superclock_t sc) const;
/* fetch const tempo/meter pairs */
template<typename T, typename T1> struct const_traits {
typedef Points::const_iterator iterator_type;
typedef TempoPoint const * tempo_point_type;
typedef MeterPoint const * meter_point_type;
using time_reference = T;
using time_type = T1;
};
template<typename T, typename T1> Points::const_iterator _get_tempo_and_meter (TempoPoint const *& tp, MeterPoint const *& mp, T (Point::*method)() const, T arg, bool can_match, bool ret_iterator_after_not_at) const;
template<typename T, typename T1> struct non_const_traits {
typedef Points::iterator iterator_type;
typedef TempoPoint * tempo_point_type;
typedef MeterPoint * meter_point_type;
using time_reference = T;
using time_type = T1;
};
Points::const_iterator get_tempo_and_meter (TempoPoint const *& t, MeterPoint const *& m, BBT_Time const & bbt, bool can_match, bool ret_iterator_after_not_at) const {
return _get_tempo_and_meter<BBT_Time const &, BBT_Time> (t, m, &Point::bbt, bbt, can_match, ret_iterator_after_not_at);
}
Points::const_iterator get_tempo_and_meter (TempoPoint const *& t, MeterPoint const *& m, superclock_t sc, bool can_match, bool ret_iterator_after_not_at) const {
return _get_tempo_and_meter<superclock_t,superclock_t> (t, m, &Point::sclock, sc, can_match, ret_iterator_after_not_at);
}
Points::const_iterator get_tempo_and_meter (TempoPoint const *& t, MeterPoint const *& m, Beats const & b, bool can_match, bool ret_iterator_after_not_at) const {
return _get_tempo_and_meter<Beats const &, Beats> (t, m, &Point::beats, b, can_match, ret_iterator_after_not_at);
}
/* fetch non-const tempo/meter pairs. As of March 2020, we only need
* the superclock variant, but leave the templated design in place,
* just in case.
/* A somewhat complex method that sets a TempoPoint* and MeterPoint* to
* refer to the correct tempo and meter points for the given start
* time.
*
* It also returns an iterator which may point at the latter of the two
* points (tempo & meter; always the meter point if they are at the
* same time) OR may point at the iterator *after* the latter of the
* two, depending on whether or not @param ret_iterator_after_not_at is
* true or false.
*
* If @param can_match is true, the points used can be located at the
* given time. If false, they must be before it. Setting it to false is
* useful when you need to know the TempoMetric in effect at a given
* time if there was no tempo or meter point at that time.
*
* The templated structure here is to avoid code duplication in 2
* separate versions of this method, one that would be const, and one
* that would be non-const. This is a challenging problem in C++, and
* seems best solved by using a "traits" object as shown here.
*
* The begini, endi, tstart and mstart arguments are an additional
* complication. If we try to use e.g. _points.begin() inside the
* method, which is labelled const, we will always get the const
* version of the iterator. This const iterator type will conflict with
* the non-const iterator type defined by the "non_const_traits"
* type. The same happens with _tempos.front() etc. This problem is
* addressed by calling these methods in the caller method, which maybe
* const or non-const, and will provide appropriate versions based on that.
*/
template<typename T, typename T1> Points::iterator _get_tempo_and_meter (TempoPoint *& tp, MeterPoint *& mp, T (Point::*method)() const, T arg, bool can_match, bool ret_iterator_after_not_at);
template<class constness_traits_t> typename constness_traits_t::iterator_type
_get_tempo_and_meter (typename constness_traits_t::tempo_point_type &,
typename constness_traits_t::meter_point_type &,
typename constness_traits_t::time_reference (Point::*)() const,
typename constness_traits_t::time_type,
typename constness_traits_t::iterator_type begini,
typename constness_traits_t::iterator_type endi,
typename constness_traits_t::tempo_point_type tstart,
typename constness_traits_t::meter_point_type mstart,
bool can_match,
bool ret_iterator_after_not_at) const;
/* fetch non-const tempo/meter pairs and iterator (used in
* ::reset_starting_at() in which we will modify points.
*/
Points::iterator get_tempo_and_meter (TempoPoint *& t, MeterPoint *& m, superclock_t sc, bool can_match, bool ret_iterator_after_not_at) {
return _get_tempo_and_meter<superclock_t,superclock_t> (t, m, &Point::sclock, sc, can_match, ret_iterator_after_not_at);
/* because @param this is non-const (because the method is not
* marked const), the following:
_points.begin()
_points.end()
_tempos.front()
_meters.front()
will all be the non-const versions of these methods.
*/
return _get_tempo_and_meter<non_const_traits<superclock_t,superclock_t> > (t, m, &Point::sclock, sc, _points.begin(), _points.end(), &_tempos.front(), &_meters.front(), can_match, ret_iterator_after_not_at);
}
/* fetch const tempo/meter pairs and iterator (used in metric_at() and
* other similar call sites where we do not modify the map
*/
Points::const_iterator get_tempo_and_meter (TempoPoint const *& t, MeterPoint const *& m, BBT_Time const & bbt, bool can_match, bool ret_iterator_after_not_at) const {
/* because @param this is const (because the method is marked
* const), the following:
_points.begin()
_points.end()
_tempos.front()
_meters.front()
will all be the const versions of these methods.
*/
return _get_tempo_and_meter<const_traits<BBT_Time const &, BBT_Time> > (t, m, &Point::bbt, bbt, _points.begin(), _points.end(), &_tempos.front(), &_meters.front(), can_match, ret_iterator_after_not_at);
}
Points::const_iterator get_tempo_and_meter (TempoPoint const *& t, MeterPoint const *& m, superclock_t sc, bool can_match, bool ret_iterator_after_not_at) const {
return _get_tempo_and_meter<const_traits<superclock_t,superclock_t> > (t, m, &Point::sclock, sc, _points.begin(), _points.end(), &_tempos.front(), &_meters.front(), can_match, ret_iterator_after_not_at);
}
Points::const_iterator get_tempo_and_meter (TempoPoint const *& t, MeterPoint const *& m, Beats const & b, bool can_match, bool ret_iterator_after_not_at) const {
return _get_tempo_and_meter<const_traits<Beats const &, Beats> > (t, m, &Point::beats, b, _points.begin(), _points.end(), &_tempos.front(), &_meters.front(), can_match, ret_iterator_after_not_at);
}
/* parsing legacy tempo maps */