5
\$\begingroup\$

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 asserts 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?

\$\endgroup\$

2 Answers 2

3
\$\begingroup\$

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:

  1. When you are reusing the struct/array (but which library function really needs that?)
  2. 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).

\$\endgroup\$
3
\$\begingroup\$

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?

\$\endgroup\$
6
  • \$\begingroup\$ I agree with you about vector and array, but this is more for cases where you have to interoperate with C libraries. And for the int[] case, yes I too prefer the ={0} initialization, but sometimes you have to reset the array to zero again. \$\endgroup\$
    – glampert
    Commented Oct 1, 2014 at 19:27
  • 1
    \$\begingroup\$ std::vector<int> works perfectly with C libraries. Cfunc(&vec[0], vec.size()); And reset with vec = std::vector<int>(vec.size(), 0); \$\endgroup\$ Commented Oct 1, 2014 at 19:32
  • 2
    \$\begingroup\$ Or reset with std::fill(std::begin(vec), std::end(vec), 0); \$\endgroup\$ Commented 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
    Commented 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 that std::fill() would use memset() internal if that was the fastest way to do it and was appropriate. \$\endgroup\$ Commented Oct 1, 2014 at 22:53

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.