[Interest] Best practices for making a Qt facade for a C library
Dmitriy Purgin
dpurgin at gmail.com
Tue Jan 13 19:57:24 CET 2015
Hello all,
I'm developing a small Qt project for SailfishOS (a Linux distribution,
Meego descendant, Qt 5 based) and had to use some of PulseAudio (a sound
server for POSIX OSes) API functions which are pure C. I've wrapped them in
a couple of classes but it's evolving to something bigger now and I'm
trying to detach these into a shared library which seems to become a basic
PulseAudio binding. It's my first facading of a C library and I'm kind of
stuck on the architecture now and hope that someone can share ideas on
that.
Obviously, I'm looking forward to encapsulate all the C code in my library
and provide library users with Qt-style classes, signals and slots, thus
eliminating libpulse dependency for them. My basic architecture is quite
simple: for the Qt classes I use d-pointers containing relevant PulseAudio
data and static methods to use for PulseAudio callbacks which are called
for any kind of events. To emit Qt signals upon PulseAudio events I use
q-pointers in these static methods. So this goes like this:
// in pseudocode suspiciously resembling C++
class QtPulseAudioContext : public QObject
{
...
signals:
void connected();
private:
QtPulseAudioContextPrivate* d;
};
class QPulseAudioContextPrivate
{
pa_context* paContext; // PulseAudio C data
QtPulseAudioContext* q;
// callback for PulseAudio event
static void onContextCallback(..., void* userData)
{
QtPulseAudioContextPrivate* d = reinterpret_cast<
QtPulseAudioContextPrivate* >(userData);
....
// processed an event from PulseAudio, redo it Qt style
emit d->q->connected();
}
};
As for now I'm dealing with three PulseAudio objects: "context"
representing connection to PulseAudio server, "sinks" representing audio
outputs and "sources" representing audio inputs. Naturally, I've wrapped
them as three separate classes. Both sinks and sources operate within
single context and any PulseAudio API call involving sinks and sources must
provide the corresponding context. When these classes were bundled in my
application, I've passed PulseAudio's pa_context* to a ctor of my class,
used it internally and was pretty happy with that:
// in pseudocode suspiciously resembling C++
class QtPulseAudioContext
{
QtPulseAudioSource* getSource(...);
};
QtPulseAudioSource* QtPulseAudioContext::getSource(...)
{
...
new QtPulseAudioSource(d->paContext);
...
}
class QtPulseAudioSource
{
QtPulseAudioSource(pa_context* context); // pa_context is PulseAudio
stuff
// the implementation of the following would need to call
// PulseAudio API and use pa_context
void setDefaultPort(const QString& name);
};
Now passing pa_context is a no-go since it exposes PulseAudio dependency to
a library user. So my first call is to somehow provide
QtPulseAudioContext's private data to a source or sink constructors or even
construct their private data in QtPulseAudioContext. I don't think any of
both is a good way. The former violates the incapsulation of
QtPulseAudioContext, the latter moves initialization of a private data
outside the class that owns this data.
So with the problem stated, my question is: does anyone know any "good
practices" in designing this kind of relationships between classes or can
share similar designs to look at? There's no technical problem for me to
expose the inner parts of some classes to some other classes, I just
wondered if there's a subtle way of doing this.
Thanks in advance.
Best regards,
Dmitriy
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.qt-project.org/pipermail/interest/attachments/20150114/7f7b0381/attachment.html>
More information about the Interest
mailing list