Eu tenho uma grande variedade de um tipo de dados não assinado T*
.
Deve ser convertido para unsigned char*
valores de escala. Por exemplo, ao passar do unsigned short*
intervalo unsigned char*
de {0,..,255} deve ser mapeado para 0 e qualquer coisa em {65281,..,65535} para 255.
Ingenuamente, isso poderia ser feito em loop, usando algo como uma mudança de bit:
const T x = some_large_value;
unsigned char y = (x >> (8u * (sizeof(T) - 1u))); //<< Effectively a truncated "x/(256^{sizeof(t)-1})".
Mas me pergunto se existe uma maneira de evitar os for (..)
envolvidos em tal solução. Sonhando com um parâmetro super-memcpy
de suporte step
que pudesse ser usado assim:
// Intel processor. So I guess, little endian is needed. Assume there are N items.
const T* src = ..;
const unsigned char* src_uchar = reinterpret_cast<const unsigned char*>(src);
memcpy(dst_uchar, src_uchar + (sizeof(T) - 1u), N, step=sizeof(T)-1u);
Esse step
parâmetro putativo pularia tantos bytes após cada byte copiado. Infelizmente, é claro, isso não existe. No entanto, existe algum outro comando que permite isso ou seu efeito?
Apêndice: Adicionando um programa de demonstração funcional ao aprender uma solução
Um brilho de gratidão vai para pptaszni . No entanto, como ainda precisei de algum processamento para entender: Aqui está um pequeno programa de demonstração usando sua proposta. Ele converte de uma uint16
matriz para uint8
, reduzindo o intervalo de valores graças a uma mudança de bit. Também funciona em tipos assinados.
#include <algorithm>
#include <iostream>
using namespace std;
int main()
{
// Input A and Output B.
const unsigned short A[] = {60000u, 50000u, 30000u, 1000u, 500u, 300u, 200u};
unsigned char B[] = { 0u, 0u, 0u, 0u, 0u, 0u, 0u};
// lambda expression.
auto transformation = [](const unsigned short& elem)
{ return static_cast<unsigned char>(elem >> 8u * (sizeof(decltype(elem))-1)); };
// Actual downsizing of UINT16 to UINT8.
std::transform(A, A+7, B, transformation);
// Show the results.
for (size_t j=0; j<7; ++j)
{ cout << A[j] << " => " << ((unsigned short)B[j]) << endl; }
return 0;
}
Saída:
60000 => 234
50000 => 195
30000 => 117
1000 => 3
500 => 1
300 => 1
200 => 0
No seu caso, usar
std::transform
pode ser bom o suficiente, mesmo que não seja tão rápido quanto uma versão ajustadastd::memcpy
poderia ser. Supondo que suaT --> std::uint8_t
função de transformação seja:E seus buffers são:
Você pode simplesmente escrever:
Ou usando intervalos:
Quando comparei essas 3 abordagens (Ubuntu22 com gcc11 -O3) e comparei com o
for
loop clássico, obtive:o que significa que o loop clássico é o mais lento, e o uso de algoritmos de biblioteca padrão permitiu ao compilador usar algumas otimizações sofisticadas. Resultados semelhantes usando quickbench com Clang 17 :