55

In using std::unique_ptr with a custom deleter I desire to use std::make_unique rather than a raw new. I am using VC++2013. It appears to me that there is no way to use std::unique_ptr if you are using a custom deleter. Did I miss something or is this really the case?


Additional Information:

I am using a std::unique_ptr<HANDLE, custom_deleter> to hold a Windows HANDLE for an opened COM port.

I could write a custom RAII class for this, and it wouldn't be terribly difficult, but I was seeing how hard/difficult/bad it would be to use std::unique_ptr.

Graznarak
  • 3,626
  • 4
  • 28
  • 47

3 Answers3

43

The whole point of make_unique is to encapsulate the notion of "use new to create a T from given constructor arguments and use delete to destroy it".

If you wanted a custom deleter, you would also have to specify how to create the object, and then there would be nothing more gained from having the emplacing maker function.

I wrote some examples of custom maker functions for certain unique resource handles in this post.

Community
  • 1
  • 1
Kerrek SB
  • 464,522
  • 92
  • 875
  • 1,084
  • 4
    Theoretically it would be possible to specify a "creator" as well as a deleter, and then you'd have the benefit of the exception safety. – user3286380 Feb 14 '14 at 19:51
  • 1
    @LucDanton: Yes and no. `allocate_shared` only uses a user-defined *allocator*, not a *deleter*. That's significantly less powerful a concept. For example, you can't wrap an `std::FILE*` in that. In other words, `allocate_shared` does not expose the full power of `std::shared_ptr`, it only lets you customize the memory allocator. – Kerrek SB Feb 18 '14 at 13:08
  • 1
    Ugh! They way they've defined make_unique is next to useless, in my opinion. What does it really gain us over just using the unique_ptr constructor? What I would really like is a make_unique variant that accepts a deleter as an argument so that it's type can be _deduced_. That would let me create unique_ptrs with unutterable types for the deleter. For example: `auto p = std::make_unique(x, y, z, [] (Foo* obj) { FancyFreeFunction(obj); });` – Peter Ruderman Jan 28 '15 at 19:15
  • That's a good point. I guess it gets us into the whole `allocate_unique` debate. In my off-the-cuff example above, the only way would be operator new. – Peter Ruderman Jan 29 '15 at 16:09
  • @PeterRuderman: allocation and deletion are somehow separate things. Think of `fopen`/`fclose`. You could have an `allocator_deleter` if you just want an ordinary unique-pointer with memory obtained from an allocator, but that would only address part of the problem domain. – Kerrek SB Jan 30 '15 at 13:02
  • 1
    There is a pretty general case that would benefit from a make_unique with a deleter, which is dealing with incomplete types inside unique_ptrs, where you have a deleter that actually only calls delete, but its operator() is defined in a TU that has the complete type. – etarion Oct 04 '16 at 15:34
3

Here is a way to wrap c style memory management into a std::unique_ptr using a custom deleter that calls a custom free function. This has a make function helper similar to std::make_unique LIVE:

#include <iostream>
#include <functional>
#include <memory>

// Some C style code that has some custom free function ptr...
extern "C" {

struct ABC { };

enum free_type_e {
    FREE_ALL,
    FREE_SOME
};

typedef void (free_f)(enum free_type_e free_type, void *ptr);
struct some_c_ops { free_f* free_op; };

void MY_free(enum free_type_e free_type, void *ptr)
{
    printf("%s:%d ptr=%ld\n", __func__, __LINE__, (long)ptr);
    (void)free_type;
    free(ptr);
}

}; // extern "C"

template<typename T>
using c_unique_ptr = std::unique_ptr<T,std::function<void(T*)>>;

template <typename T>
c_unique_ptr<T> make_c_unique(some_c_ops* op, free_type_e free_type)
{
    return c_unique_ptr<T>(static_cast<T*>(calloc(1, sizeof(T))),
                           std::bind(op->free_op, free_type, std::placeholders::_1));
}

void foo(c_unique_ptr<ABC> ptr)
{
    std::cout << __func__ << ":" << __LINE__
        << " ptr=" << reinterpret_cast<size_t>(ptr.get()) <<     std::endl;
}

int main()
{
    some_c_ops ops = { MY_free };
    c_unique_ptr<ABC> ptr = make_c_unique<ABC>(&ops, FREE_ALL);
    std::cout << __func__ << ":" << __LINE__
        << " ptr=" << reinterpret_cast<size_t>(ptr.get()) << std::endl;

    foo(std::move(ptr));

    std::cout << __func__ << ":" << __LINE__
        << " ptr=" << reinterpret_cast<size_t>(ptr.get()) << std::endl;
}

Possible output:

main:48 ptr=50511440
foo:40 ptr=50511440
MY_free:20 ptr=50511440
main:53 ptr=0
Jon Ringle
  • 133
  • 6
  • I like the custom alias - definitely makes things easier. After that, I create my `unique_ptr` custom pointer wrappers like so - defining: `class MyClass { private: c_unique_ptr my_wrapper; }`, then initing: `my_wrapper = { externalCreatorFunc(args), [](auto p) { externalReleaserFunc(p); } };` – Guss Jan 26 '22 at 14:49
-4

As far as I know there is no make_unique function in the C++11 standard. See

So I would assume that the make_unique is an implementation from Microsoft that is at least not included in the standard.

But nevertheless you can use a custom deleter with unique_ptr. When using unique_ptr you have to specify the type of the deleter as a second template argument and then pass an appropriate object to the constructor.

Community
  • 1
  • 1
sleepy1771
  • 313
  • 1
  • 8
  • 5
    C++14 will add std::make_unique. Unfortunately there is no way to specify the delete in std::make_unique, so it can only return a unique pointer with the default delete. – Graznarak Feb 14 '14 at 19:56