[prev in list] [next in list] [prev in thread] [next in thread] 

List:       kfm-devel
Subject:    Re: using qcom interfaces wit parts (was Re: Plugins for non-major Parts)
From:       Simon Hausmann <hausmann () kde ! org>
Date:       2001-09-13 13:01:48
[Download RAW message or body]

On Thu, Sep 13, 2001 at 01:08:13PM +0200, Simon Hausmann wrote:
> Hi,
> 
> I keep on thinking and thinking about it but I can't seem to find an obvious solution to
> the ownership problem...
> 
> What I have in mind is a virtual QUnknownInterface *qcomInterface() method in KParts::Part,
> with a default implementation returning 0.
> 
> A real implementation could look like this:
> 
> QUnknownInterface *MyPart::qcomInterface()
> {
>     Q_CREATE_INSTANCE( MyInterfaceImplementation )
> }
> 
> Or in expanded form
> 
> QUnknownInterface *MyPart::qcomInterface()
> {
>     MyInterfaceImplementation *impl = new MyInterfaceImplementation;
>     QUnknownInterface *result = 0;
>     impl->queryInterface( IID_QUnknown, &result );
>     return result;
> }
> 
> The problem with this is that it's a bit wrong :) We don't really want to
> instantiate an interface implementation each time nor do we want to 
> return an already ref'ed object (and we can't just return new MyInterfaceImpl
> because it requires casting in case of multiple inheritance and that's
> already implemented in MyInterfaceImpl's queryInterface, so we should re-use
> that)
> 
> So what we could do is to add a level of indirection:
> 
> (non-virtual!)
> QUnknownInterface *Part::qcomInterface()
> {
>     if ( !d->m_qcomInterface )
>         d->m_qcomInterface = createQComInterface();
>     return d->m_qcomInterface;
> }
> 
> With d->m_qcomInterface being a QUnknownInterface * and with
> createQComInterface() being a virtual factory method which does 
> the above Q_CREATE_INSTANCE. Also we would have to do
> 
> Part::~Part()
> {
>     ...
>     if ( d->m_qcomInterface )
>         d->m_qcomInterface->release();
>     ...
> }
> 
> for proper refcounting (the factory returns an initial object with
> refcount == 1, due to the queryInterface call, so we have to
> deref)
> 
> The main problem I have with that is that it means that this looks
> nice from a kparts point of view, but it means there's an ugly
> side-effect from the component implementation point of view:
> The component destructor (the one implementation the QUI based interfaces)
> is likely to be called from the KParts::Part::~Part() destructor.
> I would quite often expect a component implementation to either
> 
>  a) depend on the part component, by having a pointer to it and
>     calling methods on it 
> 
>  or
> 
>  b) be the part itself, using multiple inheritance. 
> 
> The problem with a) is that it means that the component destructor
> has to be _very_ careful about not calling anything on the part,
> because it's too late (we're called from the ~Part dtor already) .
> 
> The problem with b) is that it means that we will never get
> deleted because the Part baseclass holds a reference to itself.
>  
> 
> So I for one am a bit lost how to solve this in a clean way. I think it
> is definitely desirable to have support for pure abstract interfaces
> to allow writing plugins without a binary library dependency. And for
> that it would just make sense to re-use QUI (re-inventing that wheel
> would be stupid IMHO) . Also it would make sense IMHO if we would have
> a method to discover/retrieve an initial QUI object in our base
> classes/interfaces, KParts::Part so to say (well, in PartBase in fact,
> to automatically have it in MainWindow and KoView, too, but that's
> unrelated to the actual above described ownership problem) . 
> 
> (in addition we have the problem of library unloading, described in
> the previous mail, which doesn't seem easy to me either, but I might
> be missing something)

...hm, another idea:

(virtual)
QRESULT Part::queryQComInterface( const QUuid &iid, QUnknownInterface **iface )
{
    *iface = 0;
    return QS_FALSE; // (or something like that ;)
}

Then a part can implement it as:

QRESULT MyPart::queryQComInterface( const QUuid &iid, QUnknownInterface **iface )
{
    if ( !m_iface )
    {
        m_iface = new MyInterfaceImplementation;
        m_iface->addRef();
    }
    return m_iface->queryInterface( iid, iface );
}

MyPart::MyPart()
    : KParts::Part( ... ), m_iface( 0 )
{
    ...
}

MyPart::~MyPart()
{
    if ( m_iface )
        m_iface->release();
}

For initial discovery a client then just does something along the lines of

void MyPlugin::foo()
{
    KParts::Part *part = dynamic_cast<KParts::Part *>( parent() );
    if ( !part ) return; // something really went wrong...

    QInterfacePtr<MyPluginInterface> iface;
    if ( part->queryQComInterface( IID_MyPlugin, &iface ) != QS_OK )
        return;

    doIt( iface );
}

This solves a) and b) in such a way that the part implementation has full control.
The disadvantage is that queryQComInterface violates the five principles of
QCom's queryInterface (see QUI docu) . In a rather rough way, I admit :)
Maybe that could be expressed by choosing a different naming, maybe something along
the lines of queryInitialQComInterface?

Thoughts, anyone?

(the overall goal for that is to solve Kurt's library dependency problem when writing
plugins then)

Simon

[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic