diff --git a/lily/include/lily-guile.hh b/lily/include/lily-guile.hh index dbcaeb458a6b77d1a11f4aaaad07af65777fa77c..6f3a1823f9728151dc03b2dcf1ce39236ba48457 100644 --- a/lily/include/lily-guile.hh +++ b/lily/include/lily-guile.hh @@ -36,6 +36,7 @@ #include "lily-guile-macros.hh" #include <functional> +#include <type_traits> #include <utility> class Bezier; @@ -245,11 +246,93 @@ struct conv_scm_traits<SCM> static SCM to (const SCM &&); }; +// x_scm_t chooses the canonical type T for a to/from SCM conversion based on a +// function argument, e.g., `to_scm (value)`. It allows an optional explicit +// type to override the decision; this supports (e.g.) `to_scm<T> (value)`, +// which may be needed to resolve ambiguity in some situations. +template <typename Deduced, typename Explicit = void> +struct x_scm_canonicalizer +{ + using type = std::remove_const_t<Explicit>; +}; +template <typename Deduced> +struct x_scm_canonicalizer<Deduced> +{ + using type = std::remove_const_t<Deduced>; +}; +template <typename Deduced, typename Explicit = void> +using x_scm_t = typename x_scm_canonicalizer<Deduced, Explicit>::type; + +template <typename Deduced> +struct x_scm_canonicalizer<Deduced &> // remove deduced references +{ + using type = x_scm_t<Deduced>; +}; + +template <typename Deduced> +struct x_scm_canonicalizer<const Deduced *> // remove const but leave pointer +{ + using type = x_scm_t<Deduced *>; +}; + // since partial template specialisation is not available for // functions, we default to reflecting to a helper class for template // types like Drul_array template <typename T> -struct scm_conversions; +struct scm_conversions +{ +}; + +// robust_scm_conversions<T> is a scm_conversions<T> with a robust overload of +// from_scm(). It should not be customized. scm_conversions may be customized +// to provide a robust from_scm() itself, in which case robust_scm_conversions +// uses it rather than overriding it. +// +// This default robust_scm_conversions is used when the base scm_conversions +// does not have member functions. +template <typename T, typename = void> +struct robust_scm_conversions : public scm_conversions<T> +{ +}; + +// This specialization of robust_scm_conversions is used when the base +// scm_conversions<T> has member functions. For simplicity, we detect only +// is_scm() and expect other requirements to be met if that is present. +template <typename T> +class robust_scm_conversions< + T, std::void_t<decltype (scm_conversions<T>::is_scm (SCM_EOL))>> + : public scm_conversions<T> +{ +private: + using base_type = scm_conversions<T>; + + // is_base_robust<S, F>::value is true if the base provides from_scm(S, F). + // C++20: Replace this detector with a `requires` on from_scm(). + template <typename S, typename F, typename = void> + struct is_base_robust : public std::false_type + { + }; + template <typename S, typename F> + struct is_base_robust<S, F, + std::void_t<decltype (base_type::from_scm ( + std::declval<S> (), std::declval<F> ()))>> + : public std::true_type + { + }; + +public: + using base_type::from_scm; + using base_type::is_scm; + + // If the base does not provide from_scm(S, F)... + template <typename S, typename F, + typename = std::enable_if_t<!is_base_robust<S, F>::value>> + static decltype (auto) from_scm (S &&s, F &&fallback) + { + return base_type::is_scm (s) ? base_type::from_scm (std::forward<S> (s)) + : std::forward<F> (fallback); + } +}; template <typename T> inline bool @@ -279,25 +362,15 @@ from_scm (const SCM &&s) -> decltype (conv_scm_traits<T>::from (std::move (s))) } // "robust" variant with fallback -template <typename T> +template <typename ExplicitT = void, typename S, typename DeducedT, + typename Conv = robust_scm_conversions<x_scm_t<DeducedT, ExplicitT>>> inline auto -from_scm (const SCM &s, T fallback) -> decltype (conv_scm_traits<T>::from (s)) +from_scm (S &&s, DeducedT &&fallback) + -> decltype (Conv::from_scm (std::forward<S> (s), + std::forward<DeducedT> (fallback))) { - return scm_conversions<T>::from_scm (s, fallback); -} -template <typename T> -inline auto -from_scm (SCM &s, T fallback) -> decltype (conv_scm_traits<T>::from (s)) -{ - const auto &cs = s; - return ::from_scm<T> (cs, fallback); // defer to the const & overload -} -template <typename T> -inline auto -from_scm (const SCM &&s, T fallback) - -> decltype (conv_scm_traits<T>::from (std::move (s))) -{ - return ::from_scm<T> (s, fallback); // defer to the const & overload + return Conv::from_scm (std::forward<S> (s), + std::forward<DeducedT> (fallback)); } template <typename T> @@ -320,30 +393,7 @@ to_scm (const T &&v) -> decltype (conv_scm_traits<T>::to (std::move (v))) return ::to_scm (std::as_const (v)); // defer to the const & overload } -template <typename T> -struct scm_conversions -{ - // Add a default rule implementing robust_scm2T - // - // For better or worse, whenever we are specialising - // scm_conversions, we'll need to add this rule back in. - // - // An alternative would be to have a separate specialisation class - // just for the fallback - static T from_scm (SCM s, T fallback) - { - return ::is_scm<T> (s) ? ::from_scm<T> (s) : fallback; - } -}; - // These pass-through conversions for SCM are useful in generic code. -template <> -inline bool -is_scm<SCM> (SCM) -{ - return true; -} - template <> inline const SCM & from_scm<SCM> (const SCM &s) @@ -358,17 +408,14 @@ from_scm<SCM> (SCM &s) } template <> -inline const SCM & -from_scm<SCM> (const SCM &s, SCM) -{ - return s; -} -template <> -inline SCM & -from_scm<SCM> (SCM &s, SCM) +struct scm_conversions<SCM> { - return s; -} + static bool is_scm (SCM) { return true; } + + static SCM &from_scm (SCM &s, SCM) { return s; } + static const SCM &from_scm (const SCM &s, SCM) { return s; } + static SCM from_scm (const SCM &&s, SCM) { return s; } +}; template <> inline const SCM & @@ -384,164 +431,100 @@ to_scm<SCM> (SCM &s) } template <> -inline bool -is_scm<short> (SCM s) -{ - using limits = std::numeric_limits<short>; - return scm_is_signed_integer (s, limits::min (), limits::max ()); -} -template <> -inline short -from_scm<short> (const SCM &s) +struct scm_conversions<short> { - return scm_to_short (s); -} -template <> -inline SCM -to_scm<short> (const short &i) -{ - return scm_from_short (i); -} + static bool is_scm (SCM s) + { + using limits = std::numeric_limits<short>; + return scm_is_signed_integer (s, limits::min (), limits::max ()); + } + static short from_scm (SCM s) { return scm_to_short (s); } + static SCM to_scm (const short &i) { return scm_from_short (i); } +}; template <> -inline bool -is_scm<int> (SCM s) +struct scm_conversions<int> { - using limits = std::numeric_limits<int>; - return scm_is_signed_integer (s, limits::min (), limits::max ()); -} -template <> -inline int -from_scm<int> (const SCM &s) -{ - return scm_to_int (s); -} -template <> -inline SCM -to_scm<int> (const int &i) -{ - return scm_from_int (i); -} + static bool is_scm (SCM s) + { + using limits = std::numeric_limits<int>; + return scm_is_signed_integer (s, limits::min (), limits::max ()); + } + static int from_scm (SCM s) { return scm_to_int (s); } + static SCM to_scm (const int &i) { return scm_from_int (i); } +}; template <> -inline bool -is_scm<long> (SCM s) -{ - using limits = std::numeric_limits<long>; - return scm_is_signed_integer (s, limits::min (), limits::max ()); -} -template <> -inline long -from_scm<long> (const SCM &s) -{ - return scm_to_long (s); -} -template <> -inline SCM -to_scm<long> (const long &i) +struct scm_conversions<long> { - return scm_from_long (i); -} + static bool is_scm (SCM s) + { + using limits = std::numeric_limits<long>; + return scm_is_signed_integer (s, limits::min (), limits::max ()); + } + static long from_scm (SCM s) { return scm_to_long (s); } + static SCM to_scm (const long &i) { return scm_from_long (i); } +}; template <> -inline bool -is_scm<long long> (SCM s) +struct scm_conversions<long long> { - using limits = std::numeric_limits<long long>; - return scm_is_signed_integer (s, limits::min (), limits::max ()); -} -template <> -inline long long -from_scm<long long> (const SCM &s) -{ - return scm_to_long_long (s); -} -template <> -inline SCM -to_scm<long long> (const long long &i) -{ - return scm_from_long_long (i); -} + static bool is_scm (SCM s) + { + using limits = std::numeric_limits<long long>; + return scm_is_signed_integer (s, limits::min (), limits::max ()); + } + static long long from_scm (SCM s) { return scm_to_long_long (s); } + static SCM to_scm (const long long &i) { return scm_from_long_long (i); } +}; template <> -inline bool -is_scm<unsigned short> (SCM s) -{ - using limits = std::numeric_limits<unsigned short>; - return scm_is_unsigned_integer (s, limits::min (), limits::max ()); -} -template <> -inline unsigned short -from_scm<unsigned short> (const SCM &s) -{ - return scm_to_ushort (s); -} -template <> -inline SCM -to_scm<unsigned short> (const unsigned short &i) +struct scm_conversions<unsigned short> { - return scm_from_ushort (i); -} + static bool is_scm (SCM s) + { + using limits = std::numeric_limits<unsigned short>; + return scm_is_unsigned_integer (s, limits::min (), limits::max ()); + } + static unsigned short from_scm (SCM s) { return scm_to_ushort (s); } + static SCM to_scm (const unsigned short &i) { return scm_from_ushort (i); } +}; template <> -inline bool -is_scm<unsigned> (SCM s) -{ - using limits = std::numeric_limits<unsigned>; - return scm_is_unsigned_integer (s, limits::min (), limits::max ()); -} -template <> -inline unsigned -from_scm<unsigned> (const SCM &s) -{ - return scm_to_uint (s); -} -template <> -inline SCM -to_scm<unsigned> (const unsigned &i) +struct scm_conversions<unsigned> { - return scm_from_uint (i); -} + static bool is_scm (SCM s) + { + using limits = std::numeric_limits<unsigned>; + return scm_is_unsigned_integer (s, limits::min (), limits::max ()); + } + static unsigned from_scm (SCM s) { return scm_to_uint (s); } + static SCM to_scm (const unsigned &i) { return scm_from_uint (i); } +}; template <> -inline bool -is_scm<unsigned long> (SCM s) -{ - using limits = std::numeric_limits<unsigned long>; - return scm_is_unsigned_integer (s, limits::min (), limits::max ()); -} -template <> -inline unsigned long -from_scm<unsigned long> (const SCM &s) -{ - return scm_to_ulong (s); -} -template <> -inline SCM -to_scm<unsigned long> (const unsigned long &i) +struct scm_conversions<unsigned long> { - return scm_from_ulong (i); -} + static bool is_scm (SCM s) + { + using limits = std::numeric_limits<unsigned long>; + return scm_is_unsigned_integer (s, limits::min (), limits::max ()); + } + static unsigned long from_scm (SCM s) { return scm_to_ulong (s); } + static SCM to_scm (const unsigned long &i) { return scm_from_ulong (i); } +}; template <> -inline bool -is_scm<unsigned long long> (SCM s) -{ - using limits = std::numeric_limits<unsigned long long>; - return scm_is_unsigned_integer (s, limits::min (), limits::max ()); -} -template <> -inline unsigned long long -from_scm<unsigned long long> (const SCM &s) -{ - return scm_to_ulong_long (s); -} -template <> -inline SCM -to_scm<unsigned long long> (const unsigned long long &i) +struct scm_conversions<unsigned long long> { - return scm_from_ulong_long (i); -} + static bool is_scm (SCM s) + { + using limits = std::numeric_limits<unsigned long long>; + return scm_is_unsigned_integer (s, limits::min (), limits::max ()); + } + static unsigned long long from_scm (SCM s) { return scm_to_ulong_long (s); } + static SCM to_scm (unsigned long long i) { return scm_from_ulong_long (i); } +}; template <> struct scm_conversions<bool> @@ -558,42 +541,26 @@ struct scm_conversions<bool> }; template <> -inline bool -is_scm<double> (SCM s) -{ - return scm_is_real (s); -} -template <> -inline double -from_scm<double> (const SCM &s) -{ - return scm_to_double (s); -} -template <> -inline SCM -to_scm<double> (const double &i) +struct scm_conversions<double> { - return scm_from_double (i); -} + static bool is_scm (SCM s) { return scm_is_real (s); } + static double from_scm (SCM s) { return scm_to_double (s); } + static SCM to_scm (double i) { return scm_from_double (i); } +}; template <> -inline bool -is_scm<Axis> (SCM s) +struct scm_conversions<Axis> { - return scm_is_unsigned_integer (s, X_AXIS, Y_AXIS); -} -template <> -inline Axis -from_scm<Axis> (const SCM &s) -{ - return Axis (scm_to_unsigned_integer (s, X_AXIS, Y_AXIS)); -} -template <> -inline SCM -to_scm<Axis> (const Axis &d) -{ - return to_scm<int> (d); -} + static bool is_scm (SCM s) + { + return scm_is_unsigned_integer (s, X_AXIS, Y_AXIS); + } + static Axis from_scm (SCM s) + { + return Axis (scm_to_unsigned_integer (s, X_AXIS, Y_AXIS)); + } + static SCM to_scm (Axis d) { return ::to_scm<int> (d); } +}; template <> struct scm_conversions<Direction> @@ -611,11 +578,12 @@ struct scm_conversions<Direction> class Rational; template <> -bool is_scm<Rational> (SCM s); -template <> -Rational from_scm<Rational> (const SCM &s); -template <> -SCM to_scm<Rational> (const Rational &i); +struct scm_conversions<Rational> +{ + static bool is_scm (SCM); + static Rational from_scm (SCM); + static SCM to_scm (const Rational &); +}; template <typename T> inline bool @@ -632,30 +600,28 @@ is_scm_pair (SCM s) class Offset; template <> -inline bool -is_scm<Offset> (SCM s) +struct scm_conversions<Offset> { - return is_scm_pair<Real> (s); -} - -template <> -Offset from_scm<Offset> (const SCM &s); -template <> -SCM to_scm<Offset> (const Offset &i); + static bool is_scm (SCM s) { return is_scm_pair<Real> (s); } + static Offset from_scm (SCM); + static SCM to_scm (const Offset &); +}; template <> -bool is_scm<Bezier> (SCM s); -template <> -Bezier from_scm<Bezier> (const SCM &s); -template <> -SCM to_scm<Bezier> (const Bezier &b); +struct scm_conversions<Bezier> +{ + static bool is_scm (SCM); + static Bezier from_scm (SCM); + static SCM to_scm (const Bezier &); +}; template <> -bool is_scm<Skyline_pair> (SCM s); -template <> -Skyline_pair from_scm<Skyline_pair> (const SCM &s); -template <> -SCM to_scm<Skyline_pair> (const Skyline_pair &skyp); +struct scm_conversions<Skyline_pair> +{ + static bool is_scm (SCM); + static Skyline_pair from_scm (SCM); + static SCM to_scm (const Skyline_pair &); +}; template <typename T> struct scm_conversions<Drul_array<T>> @@ -669,10 +635,6 @@ struct scm_conversions<Drul_array<T>> { return {::from_scm<T> (scm_car (s)), ::from_scm<T> (scm_cdr (s))}; } - static Drul_array<T> from_scm (SCM s, Drul_array<T> fallback) - { - return is_scm (s) ? from_scm (s) : fallback; - } static SCM to_scm (const Drul_array<T> &s) { return scm_cons (::to_scm (s[LEFT]), ::to_scm (s[RIGHT])); @@ -692,10 +654,6 @@ struct scm_conversions<Interval_t<T>> return Interval_t<T> (::from_scm<T> (scm_car (s)), ::from_scm<T> (scm_cdr (s))); } - static Interval_t<T> from_scm (SCM s, Interval_t<T> fallback) - { - return is_scm (s) ? from_scm (s) : fallback; - } static SCM to_scm (const Interval_t<T> &s) { return scm_cons (::to_scm (s[LEFT]), ::to_scm (s[RIGHT])); diff --git a/lily/lily-guile-test.cc b/lily/lily-guile-test.cc index c4c23c7714b85bd7222f2e32b83690cc29fd50dc..f9f93c7b1e3c20184a4e8014f7cc89bcd72f6893 100644 --- a/lily/lily-guile-test.cc +++ b/lily/lily-guile-test.cc @@ -254,10 +254,9 @@ static_assert (std::is_same_v< // >); // `from_scm<reference_type> (scm, fallback)` shouldn't exist. -// TODO: Negate these tests and fix the problem. -static_assert (is_robust_from_scm_detected_v<SCM &, SCM &, SCM &>); +static_assert (!is_robust_from_scm_detected_v<SCM &, SCM &, SCM &>); static_assert ( - is_robust_from_scm_detected_v<const SCM &, const SCM &, const SCM &>); -static_assert (is_robust_from_scm_detected_v<SCM &&, SCM &&, SCM &&>); + !is_robust_from_scm_detected_v<const SCM &, const SCM &, const SCM &>); +static_assert (!is_robust_from_scm_detected_v<SCM &&, SCM &&, SCM &&>); static_assert ( - is_robust_from_scm_detected_v<const SCM &&, const SCM &&, const SCM &&>); + !is_robust_from_scm_detected_v<const SCM &&, const SCM &&, const SCM &&>); diff --git a/lily/lily-guile.cc b/lily/lily-guile.cc index 297afa6161a78154d35cc75be948c888ec97c3ad..a784ddb2504c72447cce694735ed1a5b718cd7ff 100644 --- a/lily/lily-guile.cc +++ b/lily/lily-guile.cc @@ -151,18 +151,17 @@ is_number_pair (SCM p) /* OFFSET */ -template <> Offset -from_scm<Offset> (const SCM &s) +scm_conversions<Offset>::from_scm (SCM s) { - return Offset (from_scm<Real> (scm_car (s)), from_scm<Real> (scm_cdr (s))); + return Offset (::from_scm<Real> (scm_car (s)), + ::from_scm<Real> (scm_cdr (s))); } -template <> SCM -to_scm<Offset> (const Offset &i) +scm_conversions<Offset>::to_scm (const Offset &i) { - return scm_cons (to_scm (i[X_AXIS]), to_scm (i[Y_AXIS])); + return scm_cons (::to_scm (i[X_AXIS]), ::to_scm (i[Y_AXIS])); } /* @@ -351,9 +350,8 @@ robust_scm2string (SCM k, const std::string &s) return s; } -template <> SCM -to_scm<Rational> (const Rational &r) +scm_conversions<Rational>::to_scm (const Rational &r) { if (isinf (r)) { @@ -363,12 +361,11 @@ to_scm<Rational> (const Rational &r) return scm_difference (scm_inf (), SCM_UNDEFINED); } - return scm_divide (to_scm (r.numerator ()), to_scm (r.denominator ())); + return scm_divide (::to_scm (r.numerator ()), ::to_scm (r.denominator ())); } -template <> Rational -from_scm<Rational> (const SCM &r) +scm_conversions<Rational>::from_scm (SCM r) { if (scm_is_true (scm_inf_p (r))) { @@ -386,59 +383,53 @@ from_scm<Rational> (const SCM &r) scm_to_int64 (scm_denominator (r))); } -template <> bool -is_scm<Rational> (SCM n) +scm_conversions<Rational>::is_scm (SCM n) { return (scm_is_real (n) && (scm_is_true (scm_exact_p (n)) || scm_is_true (scm_inf_p (n)))); } -template <> bool -is_scm<Bezier> (SCM s) +scm_conversions<Bezier>::is_scm (SCM s) { for (int i = 0; i < 4; i++) { if (!scm_is_pair (s)) return false; SCM next = scm_car (s); - if (!scm_is_pair (next) || !is_scm<Real> (scm_car (next)) - || !is_scm<Real> (scm_cdr (next))) + if (!scm_is_pair (next) || !::is_scm<Real> (scm_car (next)) + || !::is_scm<Real> (scm_cdr (next))) return false; s = scm_cdr (s); } return scm_is_null (s); } -template <> Bezier -from_scm<Bezier> (const SCM &s) +scm_conversions<Bezier>::from_scm (SCM s) { return Bezier (as_ly_scm_list (s)); } -template <> SCM -to_scm<Bezier> (const Bezier &b) +scm_conversions<Bezier>::to_scm (const Bezier &b) { SCM result = SCM_EOL; for (int i : {3, 2, 1, 0}) - result = scm_cons (to_scm (b.control_[i]), result); + result = scm_cons (::to_scm (b.control_[i]), result); return result; } -template <> bool -is_scm<Skyline_pair> (SCM s) +scm_conversions<Skyline_pair>::is_scm (SCM s) { return scm_is_pair (s) && unsmob<Skyline> (scm_car (s)) && unsmob<Skyline> (scm_cdr (s)); } -template <> Skyline_pair -from_scm<Skyline_pair> (const SCM &s) +scm_conversions<Skyline_pair>::from_scm (SCM s) { if (!scm_is_pair (s)) return Skyline_pair (); @@ -465,9 +456,8 @@ from_scm<Skyline_pair> (const SCM &s) return Skyline_pair (*left, *right); } -template <> SCM -to_scm<Skyline_pair> (const Skyline_pair &skyp) +scm_conversions<Skyline_pair>::to_scm (const Skyline_pair &skyp) { return scm_cons (skyp[LEFT].smobbed_copy (), skyp[RIGHT].smobbed_copy ()); } diff --git a/lily/open-type-font.cc b/lily/open-type-font.cc index d834f1b2e515ec6d0822410090b49275aa157850..8c42e7a22f71123bc2b92ac972a19e30a9dc03dd 100644 --- a/lily/open-type-font.cc +++ b/lily/open-type-font.cc @@ -323,7 +323,7 @@ Open_type_font::attachment_point (const std::string &glyph_name, return std::make_pair (o, false); // TODO: error out? SCM char_alist = entry; - SCM att_scm; + SCM att_scm = to_scm (Offset {}); bool rotate = false; if (d == DOWN) { diff --git a/lily/stream-event-scheme.cc b/lily/stream-event-scheme.cc index aa2226cb2fae508dbb47d0b6bb0bd4cee23931e4..c87e0405772790430b839c79c9cc26c02a0de288 100644 --- a/lily/stream-event-scheme.cc +++ b/lily/stream-event-scheme.cc @@ -106,5 +106,5 @@ effective length of the stream event when happening at @code{moment} is returned scm_remember_upto_here (event); - return to_scm<Moment> (len); + return to_scm (len); }