3

We can use std::unique_ptr to hold a pointer allocated with malloc which will be freed appropriately. However, the resulting std::unique_ptr's size will be 2 pointers, one for the pointer to the object and one for the pointer to the deleter function instead of the usual 1 pointer to object and an implicit delete. As one answer points out this can be avoided by writing a custom Unique_ptr that knows the proper deleter function. This function can be made known using a template parameter to support any deleter function like so:

template <class T, void (*Deleter)(T *)>
struct Unique_ptr {
    explicit Unique_ptr(T *t, void (*)(T *))
        : t{t} {}
    ~Unique_ptr() {
        Deleter(t);
    }
    //TODO: add code to make it behave like std::unique_ptr

    private:
    T *t{};
};

template <class T>
void free(T *t) {
    std::free(t);
}

char *some_C_function() {
    return (char *)malloc(42);
}

int main() {
    Unique_ptr<char, free> p(some_C_function(), free); //fine
    Unique_ptr q(some_C_function(), free);             //should be fine
                                                       //with the right
                                                       //deduction guide
}

This would be really nice if we could use deduction guides to not have to specify the template parameters. Unfortunately I can't seem to get the syntax right. These attempts fail to compile:

template <class T, auto Deleter>
Unique_ptr(T *, Deleter)->Unique_ptr<T, Deleter>;

template <class T, void (*Deleter)(T *)>
Unique_ptr(T *, void (*Deleter)(T *))->Unique_ptr<T, Deleter>;

Alternatively one could write Unique_ptr<free> q(some_C_function()); in order to manually specify the function template parameter, but that creates issues with deducing T.

What is the correct deduction guide to make Unique_ptr q(some_C_function(), free); or Unique_ptr<free> q(some_C_function()); compile?

Deduplicator
  • 44,692
  • 7
  • 66
  • 118
nwp
  • 9,623
  • 5
  • 38
  • 68
  • 1
    Does your unique pointer support arrays? If yes, then you really *don't* want to have a deduction guide (can't differentiate between pointers from `new T()` and `new T[n]`) – Rakete1111 Aug 22 '17 at 20:19
  • `std::unique_ptr` is normally implemented with a specialisation when the deleter is `std::default_delete`. There will be no overhead. – Richard Hodges Aug 23 '17 at 07:44

1 Answers1

5

Why write your own unique_ptr? Just use std::unique_ptr with a custom delete pointer. With C++17, that's very straightforward:

template <auto Deleter>
struct func_deleter {
    template <class T>
    void operator()(T* ptr) const { Deleter(ptr); }
};

template <class T, auto D>
using unique_ptr_deleter = std::unique_ptr<T, func_deleter<D>>;

Or, as Yakk suggests, more generally:

template <auto V>
using constant = std::integral_constant<std::decay_t<decltype(V)>, V>;

template <class T, auto D>
using unique_ptr_deleter = std::unique_ptr<T, constant<V>>;

which gets you to:

unique_ptr_deleter<X, free> ptr(some_c_api());

Sure, you have to actually write X, but you have no space overhead. In order to accomplish the same thing with deduction guides, you'd need to wrap the function deleter in order to lift it to a template parameter:

template <class T, auto D>
Unique_ptr(T*, func_deleter<D>) -> Unique_ptr<T, func_deleter<D> >;

Which would be used like:

Unique_ptr ptr(some_c_api(), func_deleter<free>());

I'm not sure that's necessarily better, and you run into all the same problems that led to the standard not having deduction guides for std::unique_ptr (i.e.: differentiating between pointers and arrays). YMMV.

Barry
  • 286,269
  • 29
  • 621
  • 977
  • The first version requires me to write `unique_ptr_deleter> ptr(some_C_function());`. [The second version](http://coliru.stacked-crooked.com/a/955df4ba3c15a7e4) I can't seem to get working at all. – nwp Aug 22 '17 at 20:58
  • @nwp Not `free`, just `free`. – Barry Aug 22 '17 at 21:12
  • Why is `func_deleter` called `_deleter`? – Yakk - Adam Nevraumont Aug 22 '17 at 21:50
  • To be specific, `template using constant_t=std::integral_constant, f>;` seems less constrained and more universally useful than `func_deleter`. And shorter. (`()` support comes from operator pointer to function on integral constant of a function pointer.) (And similar `constant` value-template natch) – Yakk - Adam Nevraumont Aug 22 '17 at 21:55
  • @Yakk Yeah, I like that better. – Barry Aug 22 '17 at 22:12
  • 1
    Clever? Yes. Readable? Mmmmm.... A simple `malloc_deleter` might be the answer here. – Kerrek SB Aug 23 '17 at 00:22
  • @Kerrek Sure, if all you need is `free`. But once you have multiple different C APIs, something like `unique_ptr_deleter` is really useful. – Barry Aug 23 '17 at 00:40