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

Determine number of bits in integral type at compile time

本文关键字:整型 编译      更新时间:2023-10-16

注意:我在诸如"msg(long("之类的函数的模糊重载中添加了一个类似但大大简化的问题版本,候选函数为"msg(int32_t("和"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的类型 - 它们是实现定义的,在本例中是一个字符数组(在下面的示例中,我省略了转换 - 它不会改变关于根据整型中的位数将 intergral 类型映射到特定函数的问题(。

我有第二个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

由干杯和hth提供。从这一点开始的评论是我自己的 - 随时评论和/或编辑。

优势 - 相当简单(至少与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))) {}

解决方案的简化(单个可编译.cpp!(版本在 像"msg(long("这样的函数的不明确重载与候选"msg(int32_t("和"msg(int64_t("给出

给定 3个第三方函数...

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

不是重载,而是模式匹配呢? 使用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"
}

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

编辑:或者更简单,但覆盖范围可能较小:(使用标准积分类型(

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

正如我们在链接问题中发现的那样,长是这里歧义的原因。该行

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 上遇到类似的长长问题。

正如其他答案中所指出的,这可以在运行时使用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过载组相结合,以获得完整的解决方案。或者,所示技术可以内置到过载集中。

此外,可以通过禁用非整型类型来进一步增强上述内容。有关使用这些函数的完整示例,请参阅函数的不明确重载,例如"msg(long(",候选函数"msg(int32_t("和"msg(int64_t(">

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

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

那么foo(0)是模棱两可的,因为从intunsigned intlong的转化(或可能是促销(排名相同。

选择一个有符号,或者为每个签名编写两个重载集(如果您使用的是使用无符号类型的构造函数重载((并且您关心这一点(。