Hacker News new | comments | ask | show | jobs | submit login

This is meant as library code, not application code, though for template work, this is actually quite lightweight. You'd see scarier creatures than this if you write C++ libraries. Perhaps you should take a look at the implementation of your standard libraries or your favourite boost library sometime.

Let's break it down:

If this weren't library code you mightn't care about proper forwarding (which isn't important anyway since the function you're calling is written in C and C doesn't have lvalue-references!! - unnecessary complexity), so you would sloppily write this as:

    template<typename Creator, typename Destructor, typename... Arguments>
    auto make_resource(Creator c, Destructor d, Arguments&&... args)
    {
        typedef decltype(*c(args...)) ResourceType;
        return std::unique_ptr<ResourceType, void(*)(ResourceType*)>(c(args...), d);
    }
Which is much easier to understand, yes? Your standard passing multiple arguments along thing like you'd normally do when writing a custom printf.

Actually, now that I've had time to think about it, you can simpify the original code:

    template<typename Creator, typename Destructor, typename... Arguments>
    auto make_resource(Creator c, Destructor d, Arguments&&... args)
    {
        auto r = c(std::forward<Arguments>(args...));
        typedef typename std::decay<decltype(*r)>::type ResourceType;
        return std::unique_ptr<ResourceType, void(*)(ResourceType*)>(r, d);
    }
And again, written sloppily:

    template<typename Creator, typename Destructor, typename... Arguments>
    auto make_resource(Creator c, Destructor d, Arguments&&... args)
    {
        auto r = c(args...);
        typedef decltype(*r) ResourceType;
        return std::unique_ptr<ResourceType, void(*)(ResourceType*)>(r, d);
    }
Or, if you don't mind repetition of ResourceType:

    template<typename Creator, typename Destructor, typename... Arguments>
    auto make_resource(Creator c, Destructor d, Arguments&&... args)
    {
        auto r = c(args...);
        return std::unique_ptr<decltype(*r), void(*)(decltype(r))>(r, d);
    }
Clearer?



The standard dictates that decltype(*r) deduces to T&, not T. The std::decay is there to collapse the reference type into a value type. Therefore your simplification results in a compilation error.

An alternative way to get the value type is to use std::remove_pointer_t:

    using ResourceType = std::remove_pointer_t<decltype(c(args...))>;


Thank you for taking the time to write this out, you hit the head exactly on what I was trying to accomplish with this article. I saw an opportunity to make a reusable tool for myself while at the same time being able to work on some of the best practices I've seen for developing library code. The real payoff is in the usage code.

Your simplification of the original code is great! I like how pulling out the creation of the the resource to its own line makes the typedef and unique_ptr initialization much easier to read (as far as templates go that is, a real sticking point here today).

I have updated my article and attributed the clarification to you, thanks again for your feedback and comments!




Applications are open for YC Summer 2019

Guidelines | FAQ | Support | API | Security | Lists | Bookmarklet | Legal | Apply to YC | Contact

Search: