4

I have a C library function:

uint8_t* c_func();

This either returns a valid uint8_t pointer allocated with malloc(), or NULL on error. I want to wrap it in a std::unique_ptr() as follows:

struct FreeDeleter {
  void operator()(void *p) const {
    std::free(p);
  }
};

template <typename T>
using unique_fptr = std::unique_ptr<T, FreeDeleter>;

std::free() does nothing if the pointer being passed to it is NULL, so this should work as expected.

Is this design correct, and does it follow the best practices of wrapping a raw pointer in C++?

Jan Schultke
  • 17,446
  • 6
  • 47
  • 96
D.J. Elkind
  • 367
  • 2
  • 8
  • @JeJo lol C++ evolves so fast...`std::unique_ptr` is still considered "new" by a lot of users.. – D.J. Elkind Jun 12 '23 at 05:59
  • 6
    @JeJo `std::out_ptr_t`/`std::inout_ptr_t` aren't really useful here. They're for when a function returns by out parameter (i.e. `void c_func(foo** ret)`), which doesn't apply here. You also still have to own a `std::unique_ptr` when you use them; you just don't need an intermediate raw pointer. Since OP's C function actually _returns_ a pointer just a `std::unique_ptr` is perfectly fine. – Miles Budnek Jun 12 '23 at 06:07
  • The code is fine. I do the same all the time including similar things for `FILE*` where the deleter calls `fclose`. For this particular case you could limit it to types that are [`std::is_trivially_destructible`](https://en.cppreference.com/w/cpp/types/is_destructible) but that's just a gimmick – Homer512 Jun 12 '23 at 06:15
  • @Homer512 If you know it is `FILE*` why dont you just use stuff like `iostream`? – D.J. Elkind Jun 12 '23 at 06:32
  • Interoperability with C libraries and the ability call [`fileno`](https://www.man7.org/linux/man-pages/man3/fileno.3.html) or [`fdopen`](https://www.man7.org/linux/man-pages/man3/fdopen.3p.html) to use POSIX routines. I also consider `fprintf` easier to read and likely faster (fewer mutex lock/unlock cycles) – Homer512 Jun 12 '23 at 06:41
  • @Homer512 at least try `std::format` in conjunction with `std::fputs` instead of `printf`; get some type safety in your code. – Red.Wave Jun 12 '23 at 06:48
  • @Red.Wave Thanks but I'm targetting C++-17 at the moment. We are also getting way off-topic here. – Homer512 Jun 12 '23 at 06:50
  • @Homer512 last comment: C++17 is too old in various ways; Greatest miss is ranges. – Red.Wave Jun 12 '23 at 06:53
  • @Homer512 got it. I am personally fine with this. Just I am aware that many members of the C++ community (many of them are active on SE) are very hostile towards mixing C functions with C++ lol – D.J. Elkind Jun 12 '23 at 07:02
  • Speaking of mixing C and C++: I assume you have a good reason to stick with malloc and can't just switch to the array-specialization of `unique_ptr` such as `std::unique_ptr ptr = std::make_unique(number_elements)`? – Homer512 Jun 12 '23 at 08:03
  • Related: [std::unique_ptr for C functions that need free](https://stackoverflow.com/q/27440953/430766) – bitmask Jun 12 '23 at 09:52
  • @Homer512 you might assume that the `uint8_t* c_func();` function is something given to me and I have no control over it. Just its specs say it uses `malloc()`. – D.J. Elkind Jun 12 '23 at 09:54
  • Yes this code is absolutely fine. – n. m. could be an AI Jun 12 '23 at 10:05

1 Answers1

4

Yes, this is best practice.

I would go one step further and wrap the C function in a function that just immediatly wraps the result in the unique_ptr, so that you can never forget to do it.

However, big caveat! If the C function is in a shared library on Windows, it might use a different heap and thus using free in your main program would be invalid. There's nothing you can do about that however unless the library provides its own custom free function, in which case you should really use that anyway.

Sebastian Redl
  • 69,373
  • 8
  • 123
  • 157
  • 1
    The caveat is spot on! I wish I could give more than +1. – Eljay Jun 12 '23 at 13:41
  • As I normally dont program on Windows so I am not aware of this. Just out of curiosity, does it mean that on Windows my program canNOT `free()` the memory `malloc()`ed by a shared library function? – D.J. Elkind Jun 13 '23 at 01:13
  • That's a potential issue, yes. It depends on the exact setup. In theory, if both the EXE and the DLL link dynamically to the same version of the CRT DLLs, you can malloc/free across module boundaries. But if either links statically, or to a different version, the code breaks. Because we're talking DLLs, this means it could break when the DLL is replaced without even recompiling the EXE! So it's better to just not risk it and take the relevant precautions. – Sebastian Redl Jun 13 '23 at 07:30