在编译时确定整数类型的位数

时间:2012-05-14 06:27:21

标签: c++

注意:我在Ambiguous overload of functions like `msg(long)` with candidates `msg(int32_t)` and `msg(int64_t)`添加了类似但大大简化的问题版本。该版本具有在单个文件中完整可编译示例的优势。

问题

我有一个带有

等功能的C库
obj_from_int32(int32_t& i);
obj_from_int64(int64_t& i);
obj_from_uint32(uint32_t& i);
obj_from_uint64(uint64_t& i);

在这种情况下,类型int32_t std - 它们是实现定义的,在本例中是一个字符数组(在下面的示例中我是'我省略了转换 - 它没有改变关于根据整数类型中的位数将整数类型映射到特定函数的问题。)

我有第二个C ++接口类,它有像

这样的构造函数
MyClass(int z);
MyClass(long z);
MyClass(long long z);
MyClass(unsigned int z);
MyClass(unsigned long z);
MyClass(unsigned long long z);

注意,我无法用std::int32_t样式替换此接口 - 如果可以的话,我不需要问这个问题;)

问题是如何根据整数类型中的位数调用正确的obj_from_函数。

提议的解决方案

我正在提出两个建议的解决方案,因为没有任何杀手解决方案浮动到列表的顶部,并且有一些解决方案已被破坏。

解决方案1 ​​

Cheers and hth. - Alf提供。从这一点开始的评论是我自己的 - 随意评论和/或编辑。

优点   - 相当简单(至少与boost::enable_if比较)   - 不依赖第三方库(只要编译器支持tr1

*缺点**   - 如果需要更多功能(如anotherObj_from_int32等),则需要更多代码

这个解决方案可以在下面找到 - 看看,它很漂亮!

解决方案2

优点

  • 完成ConvertFromIntegral函数后,添加需要转换的新函数非常简单 - 只需在int32_tint64_t和无符号等效项上重写一个集合。

  • 仅将模板用于一个地方,随着技术的重复使用,它们不会传播。

缺点

  • 使用boost::enable_if可能过于复杂。因为它只出现在一次这一事实而有所缓解。

因为这是我自己的,所以我不能接受它,但如果你觉得它很整洁就可以投票(很明显有些人认为它很整洁,那就是它的命运因为,我想!) 感谢所有贡献想法的人!

该解决方案涉及从intlonglong longint32_tint64_t的转换功能(对于无符号版本,类似)。这与在int32_tint64_t和无符号等价物上重载的另一组函数相结合。这两个函数可以组合在一起,但是第一个转换函数创建了一个可以重用的方便实用工具集,然后第二组函数非常简单。

// 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))) {}

Ambiguous overload of functions like `msg(long)` with candidates `msg(int32_t)` and `msg(int64_t)`

给出了解决方案的简化(单个可编辑.cpp!)版本

5 个答案:

答案 0 :(得分:3)

给出3个 rd 派对函数......

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

您可以为内置类型调用“正确”的此类函数,如下所示:

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

答案 1 :(得分:2)

正如我们在链接问题中发现的那样,长期是造成歧义的原因。 这条线

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

应改为:

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

请注意,在64位gcc上你可能会面临类似的问题。

答案 2 :(得分:2)

而不是重载,模式匹配呢?使用boost::enable_if和帮助程序模板选择您要查找的操作类型?

这样的事情:

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

http://ideone.com/pIhdq上更完整的列表(并证明它至少适用于GCC)。

编辑:或者更简单,但覆盖范围可能更少:(使用标准整数类型)

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

答案 3 :(得分:1)

正如其他答案中所指出的,这可以在运行时使用if(sizeof(int)==sizeof(int32_t))样式分支轻松解决。要在编译时执行此操作,可以使用boost::enable_if

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

您需要将整数类型转换为int32_tint64_tuint32_tuint64_t的任何地方,只需调用:

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

ConvertIntegral函数可以与int32_tint64_t重载集合结合使用,以获得完整的解决方案。或者,所示的技术可以内置于过载集。

此外,通过禁用非整数类型可以进一步增强上述功能。有关使用这些函数的完整示例,请参阅Ambiguous overload of functions like `msg(long)` with candidates `msg(int32_t)` and `msg(int64_t)`

答案 4 :(得分:0)

模糊性很容易源于有符号和无符号类型的重载。例如,给定

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

然后foo(0)含糊不清,因为从intunsigned intlong的转化(或促销)排名相同。

如果您正在使用使用无符号类型的构造函数重载(并且您关心它),请选择一个签名,或为每个签名写两个重载集。