How to wrap a c library with opaque pointers in C ++ binding

I look at the packaging of some c libraries in C ++, and I'm not sure if the best way is to wrap opaque pointers.

When a C structure is part of a public API

typedef struct _SomeType { int a; int b; } SomeType_t; 

If there are multiple member functions:

 void SomeTypeFoo( SomeType_t* obj, ... ); void SomeTypeBar( SomeType_t* obj, ... ); 

I like the approach coming from the base to just associate these “members” as real members of the class. i.e:.

 class SomeTypeWrapper: public SomeType_t { void foo( ... ); void bar( ... ); }; 

As I understand it, I believe SomeTypeWrapper is “binary compatible” with struct _SomeType , so SomeTypeWrapper* can be dropped before SomeType_t* , so the C ++ method implementation can be:

 SomeTypeWrapper::foo( ... ) { SomeTypeFoo( (SomeType_t*)this, ... ); } 

See note [1].

However, in some libraries, they prefer the structure definition to be private (I assume this is to allow changes to the implementation without changing the headers / APIs). So in the headers I have something like this:

 typedef struct _SomeType SomeType_t; 

And then all the member functions deal with these opaque pointers. To wrap these methods in an interface, what I have done so far is to provide a class containing a pointer.

 class SomeTypeWrapper { private: SomeType_t* m_data; public: SomeTypeWrapper( SomeType_t* data ): m_data(data){} void foo(...); void bar(...); }; 

This means that for any c library function that returns such an opaque pointer, I must allocate a new object to carry this pointer. For many libraries, which are probably fine, but this project complicates the implementation of the shell and for high-speed c-libraries that deal with a large number of small objects, I suspect that this additional object overhead can lead to a significant decrease in performance. In addition, the base object can be considered a link, so in some cases I have to make decisions about whether SomeWrapperType increases the reference count, implements a copy constructor, has a private constructor, etc.

In most cases, I am happy to pay a fine for a better interface, but I am looking for other options. Is there a cleaner way to deal with opaque c-pointers in the C ++ wrapper library? Related: is there a good way to implement wrapper classes so that C ++ headers do not include c-library headers And do not require the placement of wrapper objects?

Note. [1]: I believe that this cast is safe as long as the same compiler is used, and SomeTypeWrapper does not add additional members and does not have virtual methods. In the same compiler, I assume that the c-library compiled by gcc will work with the C ++ shell compiled by the same gcc. Perhaps this is not guaranteed?

+4
source share
3 answers

Your final version of SomeTypeWrapper (in question) is a way forward.

This means that for any c library function that returns such an opaque pointer, I must allocate a new object to carry this pointer. For many libraries, which are probably fine, but this project complicates the implementation of the shell and for fast c-libraries that deal with the distribution of small objects, I suspect that this additional object overhead can lead to a significant decrease in performance.

No overhead. An instance of class with one data element and virtual not larger than its only member. If you define inline methods in the header, there will also be no overhead for the function. So, until you allocate these instances with new , but on the stack:

 SomeTypeWrapper some_object(make_some_object()); // one allocation on the heap 

then you have a zero utility solution.

+4
source

If you wrap your opaque pointer, you certainly cannot extract from it. Apparently an attractive method for getting some functions for free, it soon becomes messy when you use inheritance where aggregation is intended: your wrapper is not one of the library objects, instead ... it wraps the library object.

If you process your wrapper object with the same care as you would for a library object, there is always one wrapper for each opaque pointer (at most). You can share your wrapping objects, for example. a unique_ptr (C ++ 11 has them!). This is good considering that a wrapped object is a valuable resource.

Speaking of generic pointers, they can take the deleter function as a constructor argument:

 struct Wrapper : boost::non_copyable { private: std::unique_ptr<SomeType> wrapped; public: Wrapper() :wrapped(SomeType_create(), &SomeType_destroy) // upon destruction of wrapped, use SomeType_destroy { } void foo() { SomeType_foo(wrapped.get()); } int bar(int i) { return SomeType_bar(wrapped.get(),i); } }; 

Yes: you will need a method wrapper for each member function in SomeType ; which is completely normal, since C ++ uses a different calling convention than C, and you must replace every C-style function with C ++.

And you need to make sure that you create exactly one wrapper for the library object; this object is under your control, and you can share it with someone you trust, but it will be just one object.

+3
source

In general, what you offer above is an ugly hack that can lead to various troubles. It is normal to give (and down when it makes sense) a hierarchy of inheritance, but it is undefined when casting from a pointer to a wrapper class a pointer to the object that the first element points to). There is absolutely no guarantee that the address of the first member will be aligned with your wrapper address.

In my opinion, you are trying to solve the wrong problem here. Maybe creating a C ++ wrapper for each kind of small C "object" in the wrong strategy? Why not have a library that wraps the functionality of the C base library instead of wrapping individual objects? And when he does this, he manages the underlying C objects in a meaningful, economical way (i.e. using RIAA, common pointers, etc.).

0
source

All Articles