使用模板输入双关语
type punning with template
我不得不检查某人使用 20 多个联合的笨拙代码,并根据从字节数组复制数据和向字节数组复制数据的方法数量,以便通过模板实现这些方法(第二个用于 char)
template <class T> class type_punner
{
T& p;
unsigned char* pun;
public:
type_punner(T& ref): p (ref), pun(reinterpret_cast<unsigned char*>(&p))
{
static_assert(std::is_pod<T>::value, "type_punner can be used only for POD");
}
inline unsigned char& at(std::size_t i)
{
#ifdef QT_DEBUG
if(!(i < size())) throw std::out_of_range( __FUNCTION__ );
#endif
#if (Q_BYTE_ORDER == Q_LITTLE_ENDIAN)
return pun[i];
#else
return pun[size() - i - 1];
#endif
}
inline unsigned char& reverse_at(std::size_t i)
{
#ifdef QT_DEBUG
if(!(i < size())) throw std::out_of_range(__FUNCTION__);
#endif
#if (Q_BYTE_ORDER == Q_LITTLE_ENDIAN)
return pun[size() - i - 1];
#else
return pun[i];
#endif
}
// = 0 is LSB
inline unsigned char& operator[](std::size_t i)
{
return at(i);
}
inline std::size_t size()
{
return sizeof(T);
}
};
只有我担心如果我保持标准兼容并通过返回的引用分配新值才有效。
Only what I worry about that if I'm staying standard compliant and assigning new values via returned references is valid.
有效,因为那些引用都是窄字符类型,比较特殊
您的代码似乎是合法的。有一些改进:
constexpr bool k_little_endian
#if (Q_BYTE_ORDER == Q_LITTLE_ENDIAN)
= true;
#else
= false;
#endif
constexpr bool k_debug
#ifdef QT_DEBUG
= true;
#else
= false;
#endif
template <class T,
bool little_endian = k_little_endian,
bool debug = k_debug
>
class type_punner {
T* p;
unsigned char* pun() const { return reintepret_cast<unsigned char*>(p); }
public:
static_assert(std::is_pod<T>::value, "type_punner can be used only for POD");
type_punner(T& ref):
p (std::addressof(ref))
{}
type_punner(type_punner const&)=default;
type_punner()=delete;
unsigned char& at(std::size_t i) const noexcept(!debug) {
if (debug && !(i<size())) throw std::out_of_range( __FUNCTION__ );
if (little_endian)
return pun()[i];
else
return pun()[size() - i - 1];
}
unsigned char& reverse_at(std::size_t i) const noexcept(!debug) {
if(debug && !(i < size())) throw std::out_of_range(__FUNCTION__);
if (little_endian)
return pun()[size() - i - 1];
else
return pun()[i];
}
// = 0 is LSB
unsigned char& operator[](std::size_t i) const noexcept(!debug) {
return at(i);
}
static constexpr std::size_t size() noexcept(true) { return sizeof(T); }
};
首先,这会将宏移开。您正在查看的代码往往更容易推理,并且编译器完全能够消除死分支。除非你想在你的类型上使用引用语义,否则存储引用很少是个好主意,而且在同一个 class/struct.
其次,class 正文中的内联是多余的。
第三,size
既是constexpr又是static。
第四,默认copy/assign 的T&
没有做任何合理的事情。 T*
确实如此。所以我用了T*
.
第五,不用存储pun
。每次都以零成本生成它。
请注意,上面的 type_punner
可以在 big endian 环境中用 little_endian
进行实验,或者只对有问题的部分打开调试。这样做的代价是很小的编译时代价。
我不得不检查某人使用 20 多个联合的笨拙代码,并根据从字节数组复制数据和向字节数组复制数据的方法数量,以便通过模板实现这些方法(第二个用于 char)
template <class T> class type_punner
{
T& p;
unsigned char* pun;
public:
type_punner(T& ref): p (ref), pun(reinterpret_cast<unsigned char*>(&p))
{
static_assert(std::is_pod<T>::value, "type_punner can be used only for POD");
}
inline unsigned char& at(std::size_t i)
{
#ifdef QT_DEBUG
if(!(i < size())) throw std::out_of_range( __FUNCTION__ );
#endif
#if (Q_BYTE_ORDER == Q_LITTLE_ENDIAN)
return pun[i];
#else
return pun[size() - i - 1];
#endif
}
inline unsigned char& reverse_at(std::size_t i)
{
#ifdef QT_DEBUG
if(!(i < size())) throw std::out_of_range(__FUNCTION__);
#endif
#if (Q_BYTE_ORDER == Q_LITTLE_ENDIAN)
return pun[size() - i - 1];
#else
return pun[i];
#endif
}
// = 0 is LSB
inline unsigned char& operator[](std::size_t i)
{
return at(i);
}
inline std::size_t size()
{
return sizeof(T);
}
};
只有我担心如果我保持标准兼容并通过返回的引用分配新值才有效。
Only what I worry about that if I'm staying standard compliant and assigning new values via returned references is valid.
有效,因为那些引用都是窄字符类型,比较特殊
您的代码似乎是合法的。有一些改进:
constexpr bool k_little_endian
#if (Q_BYTE_ORDER == Q_LITTLE_ENDIAN)
= true;
#else
= false;
#endif
constexpr bool k_debug
#ifdef QT_DEBUG
= true;
#else
= false;
#endif
template <class T,
bool little_endian = k_little_endian,
bool debug = k_debug
>
class type_punner {
T* p;
unsigned char* pun() const { return reintepret_cast<unsigned char*>(p); }
public:
static_assert(std::is_pod<T>::value, "type_punner can be used only for POD");
type_punner(T& ref):
p (std::addressof(ref))
{}
type_punner(type_punner const&)=default;
type_punner()=delete;
unsigned char& at(std::size_t i) const noexcept(!debug) {
if (debug && !(i<size())) throw std::out_of_range( __FUNCTION__ );
if (little_endian)
return pun()[i];
else
return pun()[size() - i - 1];
}
unsigned char& reverse_at(std::size_t i) const noexcept(!debug) {
if(debug && !(i < size())) throw std::out_of_range(__FUNCTION__);
if (little_endian)
return pun()[size() - i - 1];
else
return pun()[i];
}
// = 0 is LSB
unsigned char& operator[](std::size_t i) const noexcept(!debug) {
return at(i);
}
static constexpr std::size_t size() noexcept(true) { return sizeof(T); }
};
首先,这会将宏移开。您正在查看的代码往往更容易推理,并且编译器完全能够消除死分支。除非你想在你的类型上使用引用语义,否则存储引用很少是个好主意,而且在同一个 class/struct.
其次,class 正文中的内联是多余的。
第三,size
既是constexpr又是static。
第四,默认copy/assign 的T&
没有做任何合理的事情。 T*
确实如此。所以我用了T*
.
第五,不用存储pun
。每次都以零成本生成它。
请注意,上面的 type_punner
可以在 big endian 环境中用 little_endian
进行实验,或者只对有问题的部分打开调试。这样做的代价是很小的编译时代价。