1 // Copyright (C) 2009 Codership Oy <info@codership.com>
2 
3 /**
4  * @file Routines for safe integer conversion
5  *
6  * $Id$
7  */
8 
9 #ifndef _gu_convert_hpp_
10 #define _gu_convert_hpp_
11 
12 #include "gu_macros.h"
13 #include "gu_throw.hpp"
14 #include <limits>
15 
16 namespace gu
17 {
18     /*!
19      * Converts from type FROM to type TO with range checking.
20      * Generic template is for the case sizeof(FROM) > sizeof(TO).
21      *
22      * @param from  value to convert
23      * @param to    destination (provides type TO for template instantiation)
24      * @return      value cast to TO
25      */
26     template <typename FROM, typename TO> inline
convert(const FROM & from,const TO & to)27     TO convert (const FROM& from, const TO& to)
28     {
29         if (gu_unlikely(from > std::numeric_limits<TO>::max() ||
30                         from < std::numeric_limits<TO>::min()))
31         {
32             // @todo: figure out how to print type name without RTTI
33             gu_throw_error (ERANGE) << from << " is unrepresentable with "
34                                     << (std::numeric_limits<TO>::is_signed ?
35                                         "signed" : "unsigned") << " "
36                                     << sizeof(TO) << " bytes ("
37                                     << "min " << std::numeric_limits<TO>::min()
38                                     << " max " << std::numeric_limits<TO>::max()
39                                     << ")";
40         }
41 
42         return static_cast<TO>(from);
43     }
44 
45     /* Specialized templates are for signed conversion */
46 
47     template <> inline
convert(const unsigned long long & from,const long long & to)48     long long convert (const unsigned long long& from, const long long& to)
49     {
50         if (gu_unlikely(from > static_cast<unsigned long long>
51                         (std::numeric_limits<long long>::max())))
52         {
53             gu_throw_error (ERANGE) << from
54                                     << " is unrepresentable with 'long long'";
55         }
56 
57         return static_cast<long long>(from);
58     }
59 
60     template <> inline
convert(const long long & from,const unsigned long long & to)61     unsigned long long convert (const long long& from,
62                                 const unsigned long long& to)
63     {
64         if (gu_unlikely(from < 0))
65         {
66             gu_throw_error (ERANGE) << from
67                            << " is unrepresentable with 'unsigned long long'";
68         }
69 
70         return static_cast<unsigned long long>(from);
71     }
72 
73     template <> inline
convert(const unsigned long & from,const long & to)74     long convert (const unsigned long& from, const long& to)
75     {
76         if (gu_unlikely(from > static_cast<unsigned long>
77                         (std::numeric_limits<long>::max())))
78         {
79             gu_throw_error (ERANGE) << from
80                                     << " is unrepresentable with 'long'";
81         }
82 
83         return static_cast<long long>(from);
84     }
85 
86     template <> inline
convert(const long & from,const unsigned long & to)87     unsigned long convert (const long& from, const unsigned long& to)
88     {
89         if (gu_unlikely(from < 0))
90         {
91             gu_throw_error (ERANGE) << from
92                            << " is unrepresentable with 'unsigned long'";
93         }
94 
95         return static_cast<unsigned long>(from);
96     }
97 
98     template <> inline
convert(const unsigned int & from,const int & to)99     int convert (const unsigned int& from, const int& to)
100     {
101         if (gu_unlikely(from > static_cast<unsigned int>
102                         (std::numeric_limits<int>::max())))
103         {
104             gu_throw_error (ERANGE) << from
105                                     << " is unrepresentable with 'long'";
106         }
107 
108         return static_cast<int>(from);
109     }
110 
111     template <> inline
convert(const int & from,const unsigned int & to)112     unsigned int convert (const int& from, const unsigned int& to)
113     {
114         if (gu_unlikely(from < 0))
115         {
116             gu_throw_error (ERANGE) << from
117                            << " is unrepresentable with 'unsigned long'";
118         }
119 
120         return static_cast<unsigned int>(from);
121     }
122 }
123 
124 #endif /* _gu_convert_hpp_ */
125