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.
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.
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.
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!
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
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)`
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 ); }
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);}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With