The following function template tries to locate an object in an array of bytes.
We may assume that the argument buffer
"usually" holds an object of the given type, but I need a diagnostic (assertion check) against possible errors.
template <typename T>
T* get_object_from_buffer(std::uint8_t* buffer, std::size_t size)
{
// EDIT: This constraint was missing in original version of the question.
if (size != sizeof(T))
{
// Invalid usage of the function.
return nullptr;
}
void* tmp_buffer = buffer;
if (nullptr != std::align( alignof(T),
sizeof(T),
tmp_buffer,
size ) )
{
return reinterpret_cast<T*>(buffer);
}
else
{
// Caller can check returned pointer to implement diagnostic reactions
return nullptr;
}
}
Questions:
Is it allowed (and portable) to do this using std::align
?
Do you see a better solution - more robust, more elegant?
Conditions:
The function will be used on an embedded system. Hence, some google hits for std::align usage on PC platforms are not applicable.
Buffer is filled by a function like the following:
template <typename T>
std::pair<std::uint8_t*, std::size_t> put_object_into_buffer(T* object)
{
return std::pair<std::uint8_t*, std::size_t>(
reinterpret_cast<std::uint8_t*>(object),
sizeof(object)
);
}
Between the put/get functions, I have to pass the buffer through a library that is not
aware of user-defined types like T
.
Edit/Update: The buffer representation of uint8_t*
and size_t
is only meant to hold a single T
object. This was missing in the original question post.
I have added the check that the size
argument actually matches the size of the object.
Without this check, std::align()
can easily locate an aligned T*
in buffer
so that the criterion is not relevant any more.
Thanks to Ted Lyngmo for pointing this out!
Answer motivated by comments of @Ted Lyngmo - see comments to question
The check against size == sizeof(T)
is essential.
Otherwise, std::align()
will not return nullptr
whenever the size
beyond a misaligned buffer
pointer
allows to place an entire, aligned T
instance
somewhere between &(buffer[0])
and &(buffer[size-1])
.
Using the nullptr
function return value for error cases,
it is not necessary to distinguish between buffer
and tmp_buffer
if the return value is a type cast of std::align()
.
Then, the function can be shortened:
template <typename T>
T* get_object_from_buffer(std::uint8_t* buffer, std::size_t size)
{
if (size != sizeof(T))
{
// Invalid usage of the function.
// Caller shall check returned pointer to implement diagnostic reactions
return nullptr;
}
else
{
void* tmp_buffer = buffer;
return reinterpret_cast<T*>(std::align(alignof(T), sizeof(T), tmp_buffer, size));
}
}
or even
template <typename T>
T* get_object_from_buffer(std::uint8_t* buffer, std::size_t size)
{
void* tmp_buffer = buffer;
// Caller shall check returned pointer against 'nullptr' to implement diagnostic reactions
return reinterpret_cast<T*>(
size == sizeof(T)
? std::align(alignof(T), sizeof(T), tmp_buffer, size)
: nullptr);
}