mrc-ide / covid-sim

This is the COVID-19 CovidSim microsimulation model developed by the MRC Centre for Global Infectious Disease Analysis hosted at Imperial College, London.
GNU General Public License v3.0
1.23k stars 257 forks source link

Rework calls to memory routines #406

Closed matt-gretton-dann closed 4 years ago

matt-gretton-dann commented 4 years ago

This set of commits reworks memory routine usage. It does two things:

matt-gretton-dann commented 4 years ago

For later - do you think there is value some in sort of optional (#define TEST_MEMORY) automatic deconstruction at the end with these?

I did try this - but the sequence is complicated enough and with conditionals that I got confused and stopped with other higher priority things. Which isn't to say this is a bad idea - getting a clean bill from valgrind is useful as then any reports it does give are definitely issues.

I think the ultimate solution is to move to C++ classes,use RAII, and will free their memory automatically as the objects are destructed. This will take time though.

ZedThree commented 4 years ago

What's the policy on templates in the code? Lots of the calls look like

variable = (type*)Memory::xcalloc(nelems, sizeof(type));

which involves repeating the type, as well as a C-style cast. Adding something like:

template <typename T>
T* Memory::xcalloc(std::size_t nelems) noexcept {
  return static_cast<T*>(Memory::xcalloc(nelems, sizeof(T)));
}

would make the calls look like:

variable = Memory::xcalloc<type>(nelems);

and in some places can even avoid repeating the type three times by using auto:

-   char** AdunitNames, * AdunitNamesBuf;
-   if (!(AdunitNames = (char**)malloc(3 * ADUNIT_LOOKUP_SIZE * sizeof(char*)))) ERR_CRITICAL("Unable to allocate temp storage\n");
-   if (!(AdunitNamesBuf = (char*)malloc(3 * ADUNIT_LOOKUP_SIZE * 360 * sizeof(char)))) ERR_CRITICAL("Unable to allocate temp storage\n");
+   auto AdunitNames = Memory::xcalloc<char*>(3 * ADUNIT_LOOKUP_SIZE);
+   auto AdunitNamesBuf = Memory::xcalloc<char*>(3 * ADUNIT_LOOKUP_SIZE * 360);
matt-gretton-dann commented 4 years ago

What's the policy on templates in the code? Lots of the calls look like

variable = (type*)Memory::xcalloc(nelems, sizeof(type));

which involves repeating the type, as well as a C-style cast. Adding something like:

template <typename T>
T* Memory::xcalloc(std::size_t nelems) noexcept {
  return static_cast<T*>(Memory::xcalloc(nelems, sizeof(T)));
}

would make the calls look like:

variable = Memory::xcalloc<type>(nelems);

and in some places can even avoid repeating the type three times by using auto:

- char** AdunitNames, * AdunitNamesBuf;
- if (!(AdunitNames = (char**)malloc(3 * ADUNIT_LOOKUP_SIZE * sizeof(char*)))) ERR_CRITICAL("Unable to allocate temp storage\n");
- if (!(AdunitNamesBuf = (char*)malloc(3 * ADUNIT_LOOKUP_SIZE * 360 * sizeof(char)))) ERR_CRITICAL("Unable to allocate temp storage\n");
+ auto AdunitNames = Memory::xcalloc<char*>(3 * ADUNIT_LOOKUP_SIZE);
+ auto AdunitNamesBuf = Memory::xcalloc<char*>(3 * ADUNIT_LOOKUP_SIZE * 360);

Templates are fine. However, I take x* routines as being simple wrappers around standard library functions with error handling and implementation defined behaviours 'smoothed out'. This PR is that change.

I don't dislike your suggestions - however I wonder whether being a bit more piecemeal but radical in changes using standard library features (unique_ptr, shared_ptr and others) will be better.

zlatanvasovic commented 4 years ago

@matt-gretton-dann Great change! You may be interested to close #195, since this supersedes it.