Введите условные выражения в шаблонах C ++? - PullRequest
1 голос
/ 23 июня 2010

У меня есть метод в C # следующим образом (который заключает число в диапазон, скажем, от 0 до 360 ... если вы передаете 0-359, вы получаете то же значение, если вы передаете 360, вы получаете 0, 361 получает 1 и т. д.):

    /// <summary>
    /// Wraps the value across the specified boundary range.
    /// 
    /// If the value is in the range <paramref name="min"/> (inclusive) to <paramref name="max"/> (exclusive),
    /// <paramref name="value"/> will be returned. If <paramref name="value"/> is equal to <paramref name="max"/>,
    /// <paramref name="min"/> will be returned. The method essentially creates a loop between <paramref name="min"/>
    /// and <paramref name="max"/>.
    /// </summary>
    /// <param name="value">The value to wrap.</param>
    /// <param name="min">The minimum value of the boundary range, inclusive.</param>
    /// <param name="max">The maximum value of the boundary range, exclusive.</param>
    /// <returns>The value wrapped across the specified range.</returns>
    public static T Wrap<T>(T value, T min, T max) where T : IComparable<T>
    {
        // If it's positive or negative infinity, we just return the minimum, which is the "origin"
        bool infinityDouble = typeof(T) == typeof(double) && (double.IsPositiveInfinity(Convert.ToDouble(value)) || double.IsNegativeInfinity(Convert.ToDouble(value)));
        bool infinityFloat = typeof(T) == typeof(float) && (float.IsPositiveInfinity(Convert.ToSingle(value)) || float.IsNegativeInfinity(Convert.ToSingle(value)));
        if (infinityDouble || infinityFloat)
        {
            return min;
        }

        // If the value is between the origin (inclusive) and the maximum value (exclusive), just return the value
        if (value.CompareTo(min) >= 0 && value.CompareTo(max) < 0)
        {
            return value;
        }

        // The range of the wrapping function
        var range = (dynamic)max - (dynamic)min;

        return ((((value % range) + range) - min) % range) + min;
    }

Мне также понадобился этот метод в C ++, который я определил следующим образом:

/*!
    Wraps the value across the specified boundary range.

    If the value is in the range \a min (inclusive) to \a max (exclusive), \a value will be returned.
    If \a value is equal to \a max, \a min will be returned. The method essentially creates a loop between
    \a min and \a max.

    \param value The value to wrap.
    \param min The minimum value of the boundary range, inclusive.
    \param max The maximum value of the boundary range, exclusive.
    \return The value wrapped across the specified range.
 */
template <typename T> const T& MathHelper::wrap(const T &value, const T &min, const T &max)
{
    // If it's positive or negative infinity, we just return the minimum, which is the "origin"
    bool infinityDouble = value == std::numeric_limits<double>::infinity() || value == -std::numeric_limits<double>::infinity();
    bool infinityFloat = value == std::numeric_limits<float>::infinity() || value == -std::numeric_limits<float>::infinity();
    if (infinityDouble || infinityFloat)
    {
        return min;
    }

    // If the value is between the origin (inclusive) and the maximum value (exclusive), just return the value
    if (value >= min && value < max)
    {
        return value;
    }

    // The range of the wrapping function
    T range = max - min;

    return ((((value % range) + range) - min) % range) + min;
}

Теперь мой вопрос: правильно ли я проверяю бесконечность в версии C ++? Я не вижу никакого способа сказать «если двойная, сделай эти проверки, если поплавок, сделай эти проверки». Если это не тот тип, который я хочу, он просто вернет false? Кроме того, почему оператор% не определен для float и double? Полагаю, мне придется самому реализовывать оператор по модулю. Метод в значительной степени предназначен для числовых типов - byte, short, int, long, float, double.

Ответы [ 3 ]

3 голосов
/ 24 июня 2010

С возможностями, предоставляемыми numeric_limits, вам на самом деле не нужно использовать какие-либо сложные специализации или что-либо подобное для проверки бесконечности.

template <typename T> 
const T& MathHelper::wrap(const T &value, const T &min, const T &max) {
  bool isInfinity = std::numeric_limits<T>::has_infinity() 
                 && (std::abs(value) == std::numeric_limits<T>::infinity());
  //the rest
}

Ваш последний шаг с участием operator% будет более сложным. Вам нужно будет предоставить пользовательскую функцию mod, которая перегружена для передачи типов с плавающей запятой в std::modf вместо использования operator%. Возможно, вы сможете использовать черты типа [через boost или TR1], чтобы минимизировать повторяющиеся аспекты этого, хотя я не уверен, каким будет самый элегантный способ сделать это. Возможно, что-то вроде:

template<typename T>
typename std::enable_if<std::is_floating_point<T>::value, T>::type mod(T, T) {
   //use std::modf
}

template<typename T>
typename std::enable_if<std::is_integral<T>::value, T>::type mod(T, T) {
   //use %
}
2 голосов
/ 23 июня 2010

Конкретный пример комментария Колина:

#include <iostream>
#include <limits>

template<typename T>
class Foo {
    public:
    const T& wrap (const T& v, const T& min, const T& max) {
        // ...
        return v;
    }

};

template<>
class Foo<double> {
    public:
    const double& wrap (const double& v, const double& miun, const double& max) {
        if (v == std::numeric_limits<double>::infinity ()) {
            std::cout << "It was infinity" << std::endl;
        }
        return v;
    }
};


int main () {
    Foo<double> fd;
    Foo<long> fl;

    std::cout << "fl.wrap(42, 0, 100)      : " << fl.wrap(42, 0, 100) << std::endl;
    std::cout << "fd.wrap(inf, 0.0, 100.0) : " <<
        fd.wrap (std::numeric_limits<double>::infinity (), 0.0, 100.0) << std::endl;
    return 0;

}

Который дает:

fl.wrap(42, 0, 100)      : 42
It was infinity
fd.wrap(inf, 0.0, 100.0) : inf
0 голосов
/ 23 июня 2010

У вас есть fmod для модуля плавания (в cmath). Остерегайтесь соглашения о знаках (результат имеет тот же знак, что и первый операнд).

Проверка на бесконечность в порядке. Однако вы забыли проверить NaN.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...