I have these three helper functions that I've used on some projects as a slightly better alternative to the C memset
function. The objective is to simplify and clarify code, as well as add assert
s where possible:
// Zero fills a POD type, such as a structure or union.
template<class T>
void ZeroStruct(T & s)
{
std::memset(&s, 0, sizeof(T));
}
// Zero fills a statically allocated array of POD or built-in types. Array length inferred by the compiler.
template<class T, size_t N>
void ZeroArray(T (&arr)[N])
{
std::memset(arr, 0, sizeof(T) * N);
}
// Zero fills an array of POD or built-in types, with array length provided by the caller.
template<class T>
void ZeroArray(T * arr, size_t arrayLength)
{
assert(arr != nullptr);
assert(arrayLength != 0);
std::memset(arr, 0, sizeof(T) * arrayLength);
}
Usage example:
int arr[128];
ZeroArray(arr);
struct S {
int x;
float y;
};
S s;
ZeroStruct(s);
I'd like to further improve those functions with C++11/14 features. I think it would be a good idea to static_assert
with std::is_pod
to ensure they are never called on a class instance. Do you agree?
Also, should I make them noexept
?
Any other recommendations?
2 Answers 2
I think it would be a good idea to static_assert
with std::is_pod
to ensure they are never called on a class instance. Do you agree?
Making your coding easier and preventing errors with memset is the only reason I can see for your helpers. You are actually creating something like improved version of bzero
(which was deprecated, but that is different story). One answer here.
More than once I've come across a bug that looked like:
memset(someobject, size_of_object, 0); // clear object
The compiler won't complain ...
There are still very few cases when you need to zero the memory of a struct or an array:
- When you are reusing the struct/array (but which library function really needs that?)
- For security reasons (not to leave passwords in memory).
Other cases are covered by initialization (= {0}
or = the_struct()
).
Finally, why PascalCasing
on the name? bzero
or zero_fill
would be better for C++ (as PascalCasing is for types, unless you are in Microsoft world).
Also, should I make them noexcept?
Sounds like a good idea (for first two versions, when the size is determined from the type, not provided explicitly). The third version is problematic and a bit unclear, I would completely remove it and use memset
/std::fill
in such cases to make your code more obvious (readable and understandable), because you already have to take care to provide proper array size - you should think twice here and rather type a bit more instead of using unclear helpers (just personal advice).
Don't do that it is so easy to have incorrect usage.
All your cases can be done using normal syntax in C++.
// int arr[128];
// ZeroArray(arr);
int arr[128] = {0}; // Zero whole array.
// Or better yet use a vector.
std:vector<int> arr(128); // zero init the vector of 128 elements.
// Or alternatively an array
std::array<int,128> arr(0);
// S s;
// ZeroStruct(s);
S s = S(); // Calls the default constructor for zero initialization.
See:
C++ implicit copy constructor for a class that contains other objects
Proper way to initialize C++ structs
Does a c++ struct have a default constructor?
-
\$\begingroup\$ I agree with you about
vector
andarray
, but this is more for cases where you have to interoperate with C libraries. And for theint[]
case, yes I too prefer the={0}
initialization, but sometimes you have to reset the array to zero again. \$\endgroup\$glampert– glampert2014年10月01日 19:27:51 +00:00Commented Oct 1, 2014 at 19:27 -
1\$\begingroup\$
std::vector<int>
works perfectly with C libraries.Cfunc(&vec[0], vec.size());
And reset withvec = std::vector<int>(vec.size(), 0);
\$\endgroup\$Loki Astari– Loki Astari2014年10月01日 19:32:23 +00:00Commented Oct 1, 2014 at 19:32 -
2\$\begingroup\$ Or reset with
std::fill(std::begin(vec), std::end(vec), 0);
\$\endgroup\$Loki Astari– Loki Astari2014年10月01日 21:01:03 +00:00Commented Oct 1, 2014 at 21:01 -
\$\begingroup\$ So, would you say that for the rare cases where
memset
applies, should I just ditch those templates and use the function directly? \$\endgroup\$glampert– glampert2014年10月01日 22:36:07 +00:00Commented Oct 1, 2014 at 22:36 -
1\$\begingroup\$ Yes. I would say that they never apply. The normal syntax covers most situations and the stl algorithms (such as
std::fill()
will do the same job (but make sure it is correct in all situations)). If I was a betting man: I would put money thatstd::fill()
would usememset()
internal if that was the fastest way to do it and was appropriate. \$\endgroup\$Loki Astari– Loki Astari2014年10月01日 22:53:03 +00:00Commented Oct 1, 2014 at 22:53