Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Determine number of bits in integral type at compile time

Tags:

c++

NOTE: I added a similar but greatly simplified version of the problem at Ambiguous overload of functions like `msg(long)` with candidates `msg(int32_t)` and `msg(int64_t)`. That version has the advantage of a complete compilable example in a single file.

Problem

I have a C library with functions 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 int32_t etc are not the std ones - they are implementation defined, in this case an array of chars (in the following example I've omitted the conversion - it doesn't change the question which is about mapping intergral types to a particular function based on the number of bits in the integral type).

I have a second C++ interface class, 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 can't replace this interface with std::int32_t style types - if I could I wouldn't need to ask this question ;)

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

Proposed Solutions

I'm putting two proposed solutions, since no killer solution has floated to the top of the list, and there are a few that are broken.

Solution 1

Provided by Cheers and hth. - Alf. Comments from this point on are my own - feel free to comment and/or edit.

Advantages - Fairly simple (at least compared to boost::enable_if) - Doesn't rely on 3rd party library (as long as compiler supports tr1)

*Disadvantages** - If more functions (like anotherObj_from_int32 etc) are needed, a lot more code is required

This solution can be found below - take a look, it's nifty!

Solution 2

Advantages

  • Once the ConvertFromIntegral functions are done, adding new functions that need the conversion is trivial - simply write a set overloaded on int32_t, int64_t and unsigned equivalents.

  • Keeps use of templates to one place only, they don't spread as the technique is reused.

Disadvantages

  • Might be overly complicated, using boost::enable_if. Somewhat mitigated by the fact this appears in once place only.

Since this is my own I can't accept it, but you can upvote it if you think it's neat (and clearly some folks do not think it is neat at all, that's what downvote it for, I think!) Thanks to everyone who contributed ideas!

The solution involves a conversion function from int, long and long long to int32_t and int64_t (and similar for the unsigned versions). This is combined with another set of functions overloaded on int32_t, int64_t and unsigned equivalents. The two functions could be combined, but the first conversion functions make a handy utility set 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 (single compilable .cpp!) version of the solution is given at Ambiguous overload of functions like `msg(long)` with candidates `msg(int32_t)` and `msg(int64_t)`

like image 740
Zero Avatar asked May 14 '12 06:05

Zero


2 Answers

Given 3rd party functions …

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" such function for a 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 ); }
like image 67
Cheers and hth. - Alf Avatar answered Oct 06 '22 00:10

Cheers and hth. - Alf


Instead of overloading, what about pattern matching? Use boost::enable_if and a helper template to select the type of operation you're looking for?

Something like this:

#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"
}

More complete listing (and proof it works with GCC at least) at http://ideone.com/pIhdq.

Edit: Or more simply, but with perhaps less coverage: (using the 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);}
like image 20
Managu Avatar answered Oct 06 '22 00:10

Managu