0

I realize that similar questions have been asked elsewhere, but I couldn't find an answer that's a good fit for my function signatures.

Consider this typical pair of C functions:

int initFoo(Options options, Foo* foo);
void freeFoo(Foo* foo);

initFoo takes some options and a pointer to an uninitialized Foo struct. It initializes this struct and returns a result code that indicates whether the initialization was successful. freeFoo frees an initialized Foo struct.

Now assume I want to use these C functions in my C++ code. I want to use RAII, so I need to construct a unique_ptr<Foo> that will automatically call freeFoo on destruction. The best approach I could come up with is this:

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

lambda_unique_ptr<Foo> createFoo(Options options) {
    Foo* foo = new Foo();
    const int resultCode = initFoo(options, foo);
    if (resultCode != 0) throw ...;

    return lambda_unique_ptr<Foo>(foo, [](Foo* foo) {
        freeFoo(foo);
        delete foo;
    });
}

I'm certain that there must be a more elegant solution. Ideally, something more functional that doesn't require so many individual steps. What I find particularly ugly is the necessity to explicitly allocate the Foo struct on the heap, then explicitly free and delete it in two steps. Any ideas?

6
  • 2
    Does allocFoo not really allocate Foo? It expects a pointer to an already-allocated Foo? Does freeFoo not free the memory? Commented Jun 8, 2018 at 20:46
  • @DrewDormann: I made up those names to make the question more generic. In light of your comment, I renamed allocFoo to initFoo. And yes, initFoo expects a pointer to an existing struct. Commented Jun 8, 2018 at 20:53
  • 1
    @DanielWolf So initFoo and freeFoo don't actually handle memory? They're like constructors and destructors for Foo? Commented Jun 8, 2018 at 20:54
  • 2
    Typically I would expect those calls to be symmetrical. That is initFoo - unintFoo (no dynamic memory management) or allocFoo - freeFoo (both initialization / unitialization and dynamic memory management) Commented Jun 8, 2018 at 20:55
  • Does freeFoo not free the memory? If it doesn't, I'd imagine the C code having as many steps as your C++ wrapper. Commented Jun 8, 2018 at 21:04

1 Answer 1

3

Can't you just wrap Foo in a class?

struct FooWrap {
    Foo foo;

    explicit FooWrap(Options options) {
        if (initFoo(options, &this->foo))
            throw ...;
    }

    ~FooWrap() {
        freeFoo(&this->foo);
    }

    // XXX: either implement or disable assignment and copy construction
};

Now you can choose whether to just define FooWrap x(42); as an automatic variable or whether to allocate it dynamically with new.

Sign up to request clarification or add additional context in comments.

2 Comments

I was going for a unique_ptr because of its established interface. But maybe you're right and this case calls for its own class.
This is better than using a unique_ptr in two ways that I notice - it's faster to access than a pointer reference to the heap, and it can be copied as long as Foo can be copied.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.