News for hackers. Stuff that matters.
Re:C++14 and SDL2: Managing Resources by onedognight on Friday April 18, 2014 @02:36PM (#7610571)
This article starts with a flawed premise, that the right way to create a managed pointer is something like this[1].

    std::unique_ptr<void,void(*)(void*)> big_ptr(malloc(1), free);
The wasteful thing about this is that you have to store the deleter (free() in this case) right next to your void* for every pointer you manage, so the unique_ptr is twice as large as it needs to be. What you'd like to do is store the deleter in the type of the pointer rather than the pointer itself. It turns out unique_ptr can handle this. If the second template parameter is a type with an overloaded function call operator, then unique_ptr will use that to delete. We can wrap it up like so.

    template <class T, void F(T *)> struct unique {
        struct deleter { void operator()(T *p) { F(p); }; };
        typedef std::unique_ptr<T, deleter> ptr;
    };
With this helper type you can now declare a similar managed pointer

    unique<void, free>::ptr small_ptr(malloc(1));
but this time notice that the deleter function is the second template parameter and not an argument to the constructor and the following holds.

    assert(2 * sizeof(small_ptr) == sizeof(big_ptr));
[1] I used malloc() and free() so those of you following along at home can easily try this out for yourselves.
Re:C++14 and SDL2: Managing Resources by humanrebar on Friday April 18, 2014 @07:55PM (#7611926)
> The wasteful thing about this is that you have to store the deleter (free() in this case) right next to your void* for every pointer you manage

In the examples given, the function pointers in question would be stored on the stack. How many windows should an application be juggling at once that an extra 8 bytes per window is a concern? This seems like a premature optimization to me.

If you're individually rendering blades of grass in a goat simulator, sure. But by the time you're dealing with arbitrary-length application-defined strings (like window names), an extra pointer isn't a big deal.

For me to believe that a small_ptr was worthwhile here, I would have to see some benchmarks, which is how good optimization happens anyway.

Re:C++14 and SDL2: Managing Resources by devcodex on Friday April 18, 2014 @11:26PM (#7612475)
Thanks for the comment and suggestion. How might this look in the general solution though? The type of the resource has to be deduced somewhere and I wasn't able to figure out a good way to deduce that and declare this style of deleter within the std namespace.

I'm all for eliminating that extra bit of overhead and don't claim to be an expert, the whole purpose for me writing this article was to specifically learn about how to write reusable library code. If you have any suggestions for how to pull this off in the general solution I'm all ears!

Thanks again for taking time to comment!

[deleted]
Re:C++14 and SDL2: Managing Resources by onedognight on Friday April 18, 2014 @03:03PM (#7610706)
Here's a less reusable version that I hope is more understandable. First create an otherwise empty type with an overloaded function call operator that just calls free() on its argument

    struct freer { void operator()(void *p) { free(p); }; };
and can be used like so.

    freer f;
    f(malloc(1));  // This will malloc() and then call free()
With this type you can now declare your pointer like this.

    std::unique_ptr<void, freer> still_small_ptr(malloc(1));
Of course both kinds of unique_ptrs, fat and skinny, can useful. Simulated virtual objects for example might need custom destructors per pointer. However most uses I've seen want the skinny one while they implement the fat one because it's simpler to declare.
Re:C++14 and SDL2: Managing Resources by michaelrmmiller on Friday April 18, 2014 @03:41PM (#7610898)
The other downside to their approach is that you have to pass the function into the constructor every time you construct an object.

A similar alternative would be to template the deleter on the type of the function and then a specific function. This way you can wrap functions with non-void return values. Something like:

  template<typename FuncType, FuncType& F> struct deleter { ... };
  using unique_cptr = std::unique_ptr<void, deleter<decltype(free), free>>;
The other cool thing I learned recently is you can actually use the custom deleter to override the wrapped type by typedef'ing pointer in your deleter class. You can use this to wrap C-style file descriptors in a unique_ptr.
Re:C++14 and SDL2: Managing Resources by humanrebar on Friday April 18, 2014 @08:01PM (#7611940)
> The other downside to their approach is that you have to pass the function into the constructor every time you construct an object.

That's why the suggest a make_window function at the bottom of the post. The end-user just calls:

    auto window = sdl2::make_window("Title", /* window sizes and stuff */);
The optimizer should inline all the extra wrappers away, so there's no overhead for doing as the article suggests.
Re:C++14 and SDL2: Managing Resources by michaelrmmiller on Friday April 18, 2014 @09:04PM (#7612121)
True but that means you need a wrapper function for each type you want to create or you need to use the more general function, defeating the purpose.

It also obscures the type of the object. That can be okay if it's only used as a temporary local, but problematic if you ever need to store it as a member or return it from another function. The type signature is verbose and not very descriptive, either. Compare that to:

  namespace sdl2 {
    using window = std::unique_ptr<SDL_Window, deleter<decltype(SDL_DestroyWindow), SDL_DestroyWindow>>;
  }
  ...
  sdl2::window window{SDL_CreateWindow(...)};
If you wanted to do something like what they have, I would create a variant on C++14's std::make_unique. That could look like:

  template<typename T> struct deleter_traits;

  template<> struct deleter_traits<SDL_Window>
  {
    using custom_deleter = deleter<decltype(SDL_DestroyWindow), SDL_DestroyWindow>;
  }

  template<typename T, typename... Args> create_unique(Args&&... args)
  {
    return std::unique_ptr<T, typename deleter_traits<T>::custom_deleter>{new T(std::forward(args))};
  }

  sdl2::window window = create_unique<SDL_Window>(...);
That looks a lot cleaner to me, at least. And instead of having to write a new function for each type, you just add a new deleter_traits specialization.

(I haven't actually compiled any of this so it's probably riddled with syntax errors... hopefully the point still comes across!)

Re:C++14 and SDL2: Managing Resources by devcodex on Friday April 18, 2014 @11:34PM (#7612498)
I definitely like the cleanliness of this solution but I'm still not sure how to work this into the general solution yet. The purpose was to take an opportunity to learn about how to implement reusable library code (as well as test drive a few C++14 features).

That said, I do like this approach and it gives me more to consider when approaching a problem like this again. I'm writing to learn so I welcome the constructive criticism.

Re:C++14 and SDL2: Managing Resources by michaelrmmiller on Saturday April 19, 2014 @10:20AM (#7613475)
Ah, good point! My code is pretty flawed... it's using new to allocate an SDL_Window! All of this is just aesthetics at a certain point and what you prefer. For me, I'd get bored writing make<SDL_Window, SDL_CreateWindow, SDL_DestroyWindow> over and over. One solution is to wrap that in a function make_window like you've done. The other would be to wrap it in a template function that uses traits to lookup the creator and deleter functions. Pretty much equivalent. I only slightly prefer the latter because it exposes the type of the object in the call.

Thanks for writing the article! I'm much more in this camp of writing thin resource management rather than full-blown wrappers. And even if I were writing a wrapper, I would use these same techniques you're exploring to manage the resource as a member. It removes the need for any boilerplate whatsoever and in one line expresses all you need to know about the semantics of the object.

Re:C++14 and SDL2: Managing Resources by azth on Friday April 18, 2014 @03:29PM (#7610836)
Whoops, deleted my previous comment since I quickly found out the answer after I submitted the question :) The compiler already knows the location of the function, and should be able to call it directly, if not inline it.

Thanks for your answer.