0

I have a common problem to write a C++ wrapper for a C library… my main storage a C-Struct pointers who represent always a C++ class containing just ONE data filed.

class MyClassC {
  struct MyStructS * hdl
  ...
}

creating all the constructors, static and method function is NO problem… my design problem is right now… should I use a value class design or a pointer class design.

value class always return the object and NOT the pointer:

a value class has a ONE to MANY relationship between MyStructS and MyClassC

MyClassC myMethod () {
  ...
  return MyClassC(struct MyStructS *...);
}

Destructor of a value class never free the struct MyStructS pointer and is mostly empty

Destructor of a value is an static or method mainly called destry or delete

MyClassC::delete() {
   DestroyMyPointer(&hdl)
}
...
MyClassC obj = {...};
...
obj.delete();
...

a value class is the argument on methods a value as well:

some_proc_or_method (MyClassC arg1, MyClassC arg2, …) {
   ...
} 

and there is an other question:

how do i create default arguments for a value class argument?

some_proc_or_method (MyClassC arg1, MyClassC arg2 = MyClassC {...} ???? ) {
   ...
} 

pointer class always return the pointer of the object:

a pointer class has a ONE to ONE relationship between MyStructS and MyClassC

MyClassC* myMethod () {
  ...
  return this or getThisFrom(myStructS_pointer)
}

Destructor of a pointer class always free the struct MyStructS pointer

~MqClassC () {
   DestroyMyPointer(&hdl)
 }

...
MyClassC* obj = new MyClassC(...);
...
delete obj;
...
Andreas Otto
  • 311
  • 1
  • 10

1 Answers1

0

I'd go for option 3: a thin wrapper that gets the ownership correct first. The C code is already useful in C++. The only actual benefit to gain in wrapping it is to get the ownership semantics right. Both of your approaches try to place the burden of keeping track of ownership onto the user of your API. That's not a useful wrapper, it just adds a superfluous layer of indirection. Your clients will still need to mind the internals of the C API to be effective.

What I suggest instead is that you return a smart pointer with clear ownership semantics. Write a bunch of inline free functions that accept the smart pointer and forward to the C API. The net result is that you have almost the same API (less of a learning curve), with C++ ownership semantics. That's a gain, and the result would be less cumbersome.

#include <memory>
#include <CLibAPI.h>

namespace CPPWrapC {
  namespace detail {
    struct DestroyMyPointer {
      void operator()(MyStructS *hdl) {
        ::DestroyMyPointer(&hdl);
      }
    };
  }

  using Handle = std::unique_ptr<MyStructS, detail::DestroyMyPointer>; 


  Handle myMethod() {
    MyStructS *ret = /* init it */;
    return Handle{ret};
  }

  inline void some_proc_or_method(Handle const& hdl) {
    c_lib_some_proc_or_method(hdl.get());
  }
}

So now you have a simple wrapper that supports handles with single ownership semantics. Exactly what you wanted, I think. And using it is almost exactly like using the original wrapped API, so clients can leverage whatever they know about the C library to work.

And this can serve as a stepping stone. Now you have a C++ type you can include as a member to wrap things further.

StoryTeller - Unslander Monica
  • 165,132
  • 21
  • 377
  • 458