我应该如何将一个值插入到另一个值的"middle"中?
How should I insert a value into the "middle" of another?
我有两个值 v1 和 v2,类型分别为 T1 和 T2,大小为 (T1)>sizeof(T2)。这两种类型都是普通的旧数据。现在,我想替换第 k、k+1、...k+sizeof(T2)-v1 的第 1 个字节,其中字节为 v2。
C++ 在语言中本身不提供此功能,据我所知,在标准库中也没有提供此功能(至少不是直接提供)。一般实施这一点的最佳方法是什么?即实施:
template<typename T1, typename T2>
void replace_bytes(T1& v1, T2 v2, std::size_t k)
或至少
template<typename T1, typename T2, std::size_t k>
void replace_bytes(T1& v1, T2 v2)
?
我的想法是:
- 重新解释转换为字节数组
- 重新解释转换为 std::字节数组
- 使用跨度 地址
- 为 v1 的指针算术
- 对于不太大的类型 - 重新解释为无符号整数并使用位运算:AND 与掩码、shift 或组合现有位和替换位。
笔记:
- 当然,如果
k
太高,这里会有UB(或者我们可以检查它不是太高)。 - 为了简单起见,您可能会假设内存布局是小端序的。
- 如果对齐是一个问题,请明确您的选择。
- 效率/速度当然是一个关键问题。
- 如果你的建议需要更新的C++语言标准,那很好,但一定要提到它。 在
- 编译过程中对代码进行良好的优化非常重要。
// needed include files
#include <iostream> // for cout
#include <stdexcept> // for runtime_error
#include <cstring> // for memcpy
// generic template function that takes 3 arguments
// 1 destination object
// 2 source object
// 3 from which byte to start in the destination
template<class T1, class T2>
void replace_bytes ( T1& t1, const T2& t2, std::size_t k )
{
// at compile time, store the size of T1 type in t1_size
constexpr std::size_t t1_size = sizeof(T1);
// at compile time, store the size of T2 type in t2_size
constexpr std::size_t t2_size = sizeof(T2);
// if we copy t2 bytes to t1, do we run out of memory ?
if ( k + t2_size > t1_size )
{
throw std::runtime_error("Can't copy out of bounds.");
}
// do the copying, casting is required for proper pointer arithmitic
std::memcpy( (void*) (((char*)&t1)+k), (const void*) &t2, t2_size );
}
int main()
{
int x = 0;
char c = 10;
replace_bytes(x, c, 0);
std::cout << x << std::endl;
}
干净的代码版本(无注释):
#include <iostream>
#include <stdexcept>
#include <cstring>
template <class T1, class T2>
void replace_bytes ( T1& t1, const T2& t2, std::size_t k )
{
constexpr std::size_t t1_size = sizeof(T1);
constexpr std::size_t t2_size = sizeof(T2);
if ( k + t2_size > t1_size )
{
throw std::runtime_error("Can't copy out of bounds.");
}
std::memcpy( (void*) (((char*)&t1)+k), (const void*) &t2, t2_size );
}
int main()
{
int x = 0;
char c = 10;
replace_bytes(x, c, 0);
std::cout << x << std::endl;
}
以下内容适用于最大 8 字节的 T1 大小,并且似乎在 GCC、clang 和 MSVC 上得到了足够好的优化 - 至少在内联时:
namespace detail {
template <unsigned NBytes> struct uint;
template<> struct uint<1> { using type = uint8_t; };
template<> struct uint<2> { using type = uint16_t; };
template<> struct uint<4> { using type = uint32_t; };
template<> struct uint<8> { using type = uint64_t; };
} // namespace detail
template <unsigned NBytes>
using uint_t = typename detail::uint<NBytes>::type;
template <typename T1, typename T2>
inline void replace_bytes(T1& v1 ,T2 v2, std::size_t k)
{
static_assert(sizeof(T1) > sizeof(T2), "invalid sizes");
static_assert(std::is_trivial<T1>::value, "T1 must be a trivial type");
static_assert(std::is_trivial<T2>::value, "T2 must be a trivial type");
auto shift_amount = k * CHAR_BIT;
using uint_1_t = uint<sizeof(v1)>;
using uint_2_t = uint<sizeof(v2)>;
auto& v1_as_uint = *reinterpret_cast<uint_1_t*>(&v1);
const auto& v2_as_uint = *reinterpret_cast<uint_2_t*>(&v2);
auto v1_mask = ~( (uint_1_t{1} << (sizeof(T2) * CHAR_BIT) - 1) << shift_amount);
auto shifted_v2 = uint_1_t{v2_as_uint} << shift_amount;
v1_as_uint = (v1_as_uint & v1_mask ) | shifted_v2;
}
但我觉得最好避免参数输出 - 事实上,这样做允许函数实现严格在寄存器中:
template <typename T1, typename T2>
T1 replace_bytes(T1 v1 ,T2 v2, std::size_t k)
{
static_assert(sizeof(T1) > sizeof(T2), "invalid sizes");
static_assert(std::is_trivial<T1>::value, "T1 must be a trivial type");
static_assert(std::is_trivial<T2>::value, "T2 must be a trivial type");
auto shift_amount = k * CHAR_BIT;
using uint_1_t = uint_t<sizeof(v1)>;
using uint_2_t = uint_t<sizeof(v2)>;
auto& v1_as_uint = *reinterpret_cast<uint_1_t*>(&v1);
const auto& v2_as_uint = *reinterpret_cast<uint_2_t*>(&v2);
auto v1_mask = ~( (uint_1_t{1} << (sizeof(T2) * CHAR_BIT) - 1) << shift_amount);
auto shifted_v2 = uint_1_t{v2_as_uint} << shift_amount;
return (v1_as_uint & v1_mask ) | shifted_v2;
}
相关文章:
- 将一个向量插入另一个向量的某个位置
- 在一个类中插入另一个类的多重集
- 如何在另一个字符串的x位置插入一个字符串?
- 如何从一个容器中获取某些元素并将其转换插入到另一个容器中?
- 在另一个字符串中插入文件扩展名之前的字符串
- C++将 X 向量数据插入到另一个向量
- 将向量{x,y,z}插入另一个向量
- LLVM插入功能调用到另一个函数中
- 我应该如何将一个值插入到另一个值的"middle"中?
- C++将向量插入到另一个覆盖向量中
- QT将小部件插入另一个小部件
- 将一个映射插入到同一多重映射的另一个映射中会导致 SEG 错误
- C 将HEAP对象插入std ::用insert()插入映射,而另一个则存在删除新的对象
- C++-将链表(A)插入另一个链表(B)的位置n
- std::map插入另一个map
- 如何将一个链表插入另一个链表后面
- 如果键已经存在,为什么 stl map 会插入另一个值,而不仅仅是更改它
- 将一个字符串插入另一个字符串..(冲刺)
- 如何将 BSONObj 插入另一个 BSONObj in C++ (Mongo)
- 将矢量插入另一个矢量