Determine the number of bits in an integral type at compile time

NOTE. I added a similar but significantly simplified version of the problem to the ambiguous overload of functions such as `msg (long)` using ms # int32_t) `and` msg (int64_t) ` . This version has the advantage of a complete compiled example in a single file.

Problem

I have a C library with features like

obj_from_int32(int32_t& i); obj_from_int64(int64_t& i); obj_from_uint32(uint32_t& i); obj_from_uint64(uint64_t& i); 

In this case, the types are int32_t , etc. are not std tags - they are defined in the implementation, in this case an array of characters (in the following example, I omitted the conversion - it does not change the question regarding the comparison of intergroup types with a specific function based on the number of bits in the integral type).

I have a second class C ++ interface that has constructors like

 MyClass(int z); MyClass(long z); MyClass(long long z); MyClass(unsigned int z); MyClass(unsigned long z); MyClass(unsigned long long z); 

Note, I cannot replace this interface with styles like std::int32_t - if I could not ask this question;)

The problem is how to call the correct obj_from_ function based on the number of bits in the integral type.

Suggested Solutions

I put the two solutions proposed, since not one solution for the killer has moved to the top of the list, and there are some that have been broken.

Solution 1

Greetings and hth provided. - Alf . The comments from this paragraph are my own - feel free to comment and / or edit.

Advantages - Pretty simple (at least compared to boost::enable_if ) - Doesn't rely on a third-party library (as long as the compiler supports tr1 )

* Disadvantages ** - If more functions are required (e.g. anotherObj_from_int32 etc.), much more code is required

This solution can be found below - look, it's elegant!

Decision 2

Benefits

  • After executing the ConvertFromIntegral functions, adding new functions that require conversion is trivial - just write a set overloaded with int32_t , int64_t and unsigned equivalents.

  • Saves templates in only one place; they do not spread as the method is reused.

disadvantages

  • Perhaps too complicated using boost::enable_if . To some extent, the fact that this appears in only one place is mitigated.

Since this is mine, I canโ€™t accept it, but you can transfer it if you think itโ€™s neat (and, obviously, some people donโ€™t think that it is incompatible, which, it seems to me, reduces it) Thank you to everyone who contributed ideas!

The solution includes a conversion function from int , long and long long to int32_t and int64_t (and similar for unsigned versions). This is combined with another set of functions overloaded with int32_t , int64_t and unsigned equivalents. The two functions can be combined, but the first conversion functions make a convenient set of utilities that can be reused, and then the second set of functions is trivially simple.

 // Utility conversion functions (reuse wherever needed) template <class InputT> typename boost::enable_if_c<sizeof(InputT)==sizeof(int32_t) && boost::is_signed<InputT>::value, int32_t>::type ConvertFromIntegral(InputT z) { return static_cast<int32_t>(z); } template <class InputT> typename boost::enable_if_c<sizeof(InputT)==sizeof(int64_t) && boost::is_signed<InputT>::value, int64_t>::type ConvertFromIntegral(InputT z) { return static_cast<int64_t>(z); } template <class InputT> typename boost::enable_if_c<sizeof(InputT)==sizeof(uint32_t) && boost::is_unsigned<InputT>::value, uint32_t>::type ConvertFromIntegral(InputT z) { return static_cast<uint32_t>(z); } template <class InputT> typename boost::enable_if_c<sizeof(InputT)==sizeof(uint64_t) && boost::is_unsigned<InputT>::value, uint64_t>::type ConvertFromIntegral(InputT z) { return static_cast<uint64_t>(z); } // Overload set (mock implementation, depends on required return type etc) void* objFromInt32 (int32_t i) { obj_from_int32(i); } void* objFromInt64 (int64_t& i) { obj_from_int64(i); } void* objFromUInt32(uint32_t& i) { obj_from_uint32(i); } void* objFromUInt64(uint64_t& i) { obj_from_uint64(i); } // Interface Implementation MyClass(int z) : _val(objFromInt(ConvertFromIntegral(z))) {} MyClass(long z): _val(objFromInt(ConvertFromIntegral(z))) {} MyClass(long long z): _val(objFromInt(ConvertFromIntegral(z))) {} MyClass(unsigned int z): _val(objFromInt(ConvertFromIntegral(z))) {} MyClass(unsigned long z): _val(objFromInt(ConvertFromIntegral(z))) {} MyClass(unsigned long long z): _val(objFromInt(ConvertFromIntegral(z))) {} 

A simplified (the only compiled .cpp !) Version of the solution is given in the ambiguous overload of functions such as `msg (long)` with candidates `msg (int32_t)` and `tz (int64_t)`

+7
source share
5 answers

Instead of overloading, how about matching the pattern? Use boost::enable_if and a helper pattern to select the type of operation you are looking for?

Something like that:

 #include <boost/utility/enable_if.hpp> #include <boost/type_traits/is_integral.hpp> #include <iostream> template <typename T, typename Dummy=void> struct helper; // Handle signed integers of size 1 (8 bits) template <typename T> struct helper<T, typename boost::enable_if_c< boost::is_integral<T>::value && (sizeof(T)==1) && (static_cast<T>(-1) < static_cast<T>(0)) >::type> { static void do_stuff(T const& ) {std::cout<<"signed, size 1"<<std::endl;} }; // Handle unsigned integers of size 1 (8 bits) template <typename T> struct helper<T, typename boost::enable_if_c< boost::is_integral<T>::value && (sizeof(T)==1) && (static_cast<T>(-1) > static_cast<T>(0)) >::type> { static void do_stuff(T const& ) {std::cout<<"unsigned, size 1"<<std::endl;} }; // Handle signed integers of size 2 (16 bits) template <typename T> struct helper<T, typename boost::enable_if_c< boost::is_integral<T>::value && (sizeof(T)==2) && (static_cast<T>(-1) < static_cast<T>(0)) >::type> { static void do_stuff(T const& ) {std::cout<<"signed, size 2"<<std::endl;} }; // And so on and so forth.... // Use a function for type erasure: template <typename T> void do_stuff(T const& value) { helper<T>::do_stuff(value); } int main() { do_stuff(static_cast<unsigned char>(0)); // "unsigned, size 1" do_stuff(static_cast<signed short>(0)); // "signed, size 2" } 

A more complete list (and proof of his work with GCC at least) is at http://ideone.com/pIhdq .

Edit: Or easier, but with less scope: (using standard integral types)

 template <typename T> struct helper2; template <> struct helper2<uint8_t> {static void do_stuff2(uint8_t ) {...}}; template <> struct helper2<int8_t> {static void do_stuff2(int8_t ) {...}}; template <> struct helper2<uint16_t> {static void do_stuff2(uint16_t ) {...}}; template <> struct helper2<int16_t> {static void do_stuff2(int16_t ) {...}}; // etc. template <typename T> void do_stuff2(T value) {helper2<T>::do_stuff2(value);} 
+2
source

Given 3 rd party functions & hellip;

 void obj_from_int32( int32_bytes_t& i ); void obj_from_int64( int64_bytes_t& i ); void obj_from_uint32( uint32_bytes_t& i ); void obj_from_uint64( uint64_bytes_t& i ); 

you can call the โ€œcorrectโ€ function for the built-in type as follows:

 template< int nBytes, bool isSigned > struct ThirdParty; template<> struct ThirdParty< 4, true > { template< class IntegralT > static void func( IntegralT& v ) { obj_from_int32( v ) } // Add whatever conversion is required. }; // Etc., specializations of ThirdParty for unsigned and for 8 bytes. template< class IntegralT > void myFunc( IntegralT& v ) { ThirdParty< sizeof( v ), std::is_signed< IntegralT >::value >::func( v ); } 
+3
source

As we found in the related issue, the long reason for the ambiguity is here. Line

MyClass(long z): _val(objFromInt(z)) {}

should be changed to something like:

MyClass(long z): _val(sizeof(long) == 4 ? static_cast<int32_t>(z) : static_cast<int64_t>(z)))) {}

Note that you are likely to encounter a similar problem with the long long 64-bit gcc.

+2
source

As pointed out in other answers, this can be trivially solved at runtime using if(sizeof(int)==sizeof(int32_t)) style branches. For this, at compile time boost::enable_if can be used.

 template <class InputT> typename boost::enable_if_c<sizeof(InputT)==sizeof(int32_t) && boost::is_signed<InputT>::value, int32_t>::type ConvertIntegral(InputT z) { return static_cast<int32_t>(z); } template <class InputT> typename boost::enable_if_c<sizeof(InputT)==sizeof(int64_t) && boost::is_signed<InputT>::value, int64_t>::type ConvertIntegral(InputT z) { return static_cast<int64_t>(z); } template <class InputT> typename boost::enable_if_c<sizeof(InputT)==sizeof(uint32_t) && boost::is_unsigned<InputT>::value, uint32_t>::type ConvertIntegral(InputT z) { return static_cast<uint32_t>(z); } template <class InputT> typename boost::enable_if_c<sizeof(InputT)==sizeof(uint64_t) && boost::is_unsigned<InputT>::value, uint64_t>::type ConvertIntegral(InputT z) { return static_cast<uint64_t>(z); } 

Anywhere you need to convert an integral type to int32_t , int64_t , uint32_t or uint64_t , just call:

 ConvertIntegral(long(5)); // Will return a type compatible with int32_t or int64_t 

The ConvertIntegral function can be combined with a set of int32_t and int64_t for a complete solution. Alternatively, the illustrated method may be integrated into an overload set.

In addition, the foregoing can be further improved by disabling for non-integer types. For a complete example of the use of functions, see B Ambiguous overloading functions such as `msg (long)` with candidates `msg (int32_t)` and `msg (int64_t)`

+1
source

Ambiguity can easily arise due to overload for both signed and unsigned types. For example, given

 void foo(unsigned int); void foo(long); 

then foo(0) ambiguous, since the conversions (or possibly advances) from int to both unsigned int and long are evaluated equally.

Either select one signature or write two sets of overloads for each subscription if you use constructor overloads that use unsigned types (and you are interested).

0
source

All Articles