2010-06-23 4 views
1

J'ai une méthode en C# comme suit (qui enveloppe un nombre dans une plage, disons 0 à 360 ... si vous passez 0-359 vous obtenez la même valeur, si vous passez 360 vous obtenez 0, 361 obtient 1, etc.):Type conditionnel dans les modèles C++?

/// <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; 
    } 

J'ai aussi besoin de cette méthode en C++, que je définis comme suit:

/*! 
    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; 
} 

maintenant, ma question est la suivante: suis-je contrôle pour l'infini correctement dans la version C++? Je ne vois aucun moyen de dire "si double, faites ces vérifications, si elles flottent, faites ces vérifications". Si ce n'est pas le type que je veux, est-ce que ça retournera faux? En outre, pourquoi l'opérateur% n'est-il pas défini pour float et double? Je suppose que je vais devoir implémenter moi-même l'opérateur modulo. La méthode est plutôt destinée aux types numériques - byte, short, int, long, float, double.

+2

« si double, ne ces contrôles, si flotteur, font ces contrôles » -> qui est ce que la spécialisation de modèle est pour – Colin

+0

Oui, j'oublièrent la spécialisation ... mais ceux-ci sont des méthodes individuelles qui doivent être spécialisées, donc c'est plus de surcharge que de spécialisation. –

Répondre

3

Avec les facilités offertes par numeric_limits, vous n'avez pas vraiment besoin d'utiliser des spécialisations complexes ou quelque chose comme ça pour le contrôle de l'infini.

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 
} 

Votre dernière étape, impliquant operator% sera plus compliqué. Vous devrez fournir une fonction personnalisée mod, qui est surchargée pour passer les types à virgule flottante dans std::modf au lieu d'utiliser operator%. Vous pourriez être en mesure d'utiliser des caractères de type [soit via boost ou TR1] pour minimiser les aspects répétitifs de ceci, bien que je ne sois pas sûr de la méthode la plus élégante. Peut-être quelque chose le long des lignes de:

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 % 
} 
+0

Fait-il la différence entre l'infini positif et négatif, ou non? Vérification juste pour l'infini est suffisant? –

+0

@Jake: J'ai juste pris la valeur absolue de 'value' et l'ai comparée à l'infini positif. Vous pouvez vérifier la valeur par rapport à '-std :: numeric_limits :: infinity()' si vous voulez un comportement différent pour les deux extrêmes. –

+0

Je vois. Pour le modulo, j'ai fini par définir une méthode template, et la spécialiser/surcharger pour float et double pour utiliser 'std :: fmod' (je pense que vous vouliez dire std :: fmod plus tôt, pas std :: modf, d'ailleurs). Donc, à la fin, j'ai une méthode de template pour swap, une méthode de template pour wrap, une méthode template pour modulo, et deux spécialisations de cela - un float et un double. Merci pour toute l'aide, tout le monde! –

0

Vous avez modifié le module de flottement (en cmath). Attention à la convention de signe (le résultat a le même signe que le premier opérande).

La vérification de l'infini est correcte. Vous avez oublié de vérifier pour NaN cependant.

2

Un exemple concret du commentaire de Colin:

#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; 

} 

Ce qui donne:

fl.wrap(42, 0, 100)  : 42 
It was infinity 
fd.wrap(inf, 0.0, 100.0) : inf 
Questions connexes