Search code examples
c++clibuv

Inheriting from libuv handles


The libuv handles have a void* data field to carry around any context information (pretty standard pattern for callbacks in C-land). However, since I'm working in C++-land, I would like to use inheritance to directly store context in the handle. The main advantage is in storing multiple context items which IIUC requires heap allocations to "combine" them into a single pointer, say, std::tuple<...>* or some temporary struct (correct me if I'm wrong here and I can avoid heap allocations even in C-land).

My main apprehensions are:

  1. Pointers to the handles are eventually used in C-land (the libuv library). Not sure about any interop related corner cases here.
  2. libuv has its own pseudo-inheritance system by repeating fields from parents in children. This seems akin to how a compiler might lay out fields from base classes in derived classes. Not sure how this would interact with C++ inheritance.
  3. From what I've researched, the standard doesn't really define any memory layout for inheritance, its up to the compiler. Not sure if that affects things.

So, can I inherit from libuv handles safely in C++? Bonus points if you can clarify the above points as well.


Solution

  • If you are the one allocating the handles yourself and then passing them to libuv, there is nothing stopping you from inheriting them. You are allowed to allocate them however you want. You could make them a member of your class, but you could also make them a base of your class.

    The compiler isn't allowed to change the libuv handle's memory layout just because it's a base class. It's allowed to decide where the base goes in the overall class structure, but it's not allowed to change the base itself. (Otherwise, pointers to the base class would have to work differently, depending on whether you allocated the an instance of the base class or the derived class!)

    Since the compiler can't change the memory layout, whatever libuv does is irrelevant. If it works with normal libuv handle objects, it'll still work when they're used as a base class.

    Note: If inheriting from a libuv handle still makes you uncomfortable, you could allocate the handle as a member of your class, and store a pointer to the whole class in libuv's "context" field.