Re: C++ Design question
denis_browne@hotmail.com wrote:
Hi All,
I'm making a class which derives(implements) from 5 interfaces:
class myClass: public base1, public base2, public base3,.......
I want to be able to ask from any interface about a pointer to another
interface
What I mean is:
myClass _myImpObj;
base1 *b1 = &_myImpObj;
base *b2 = b1->QueryInterafce("b2");
base *b3 = b2->QueryInterafce("b3");
base *b1N = b3->QueryInterface("b1");
Its is very simialir to COM mechanism.Anyone knows how to implement it?
Thanks,
Denis
There are a number of ways to do this. Say I had this:
tr1::shared_ptr<base1 > make(){
return tr1::shared_ptr<myClass> mc(new myClass);
}
If myClass has at least one virtual function or base (and that is a
given in this design) then this works on almost all compilers:
tr1::shared_ptr<base1 > b1=make();//don't know implementation class
tr1::shared_ptr<base2> b2=tr1::dynamic_pointer_cast<base2>(b1);
tr1::shared_ptr<base3> b3=tr1::dynamic_pointer_cast<base3>(b2);
This also has the advantage of keeping up with all the reference counts
correctly. See boost::shared_ptr for details. Note that if I just
wanted to use raw pointers (and dont ask me how you would keep track of
when you freed these pointers in anything other than a trivial design)
myClass _myImpObj;
base1 *b1 = &_myImpObj;
base2* b2=dynamic_cast<base2>(b1);
base3* b3=dynamic_cast<base3>(b3);
I call this "cross casting" (I think I've heard the term elsewhere
too). There are a few compiler where this does not work, xlC6 comes to
mind. Then you would want to do something like this:
struct iBase{};//NOT like iUnknown, just a kludge
//to make it possible to "navigate" using dynamic_cast
struct base1: virtual iBase{}; //Note the virtual here
struct base2: virtual iBase{}; //does NOT work with
struct base3: virtual iBase{}; // non-virtual inhertance
Then you have this
tr1::shared_ptr<base1 > b1=make();//don't know implementation class
tr1::shared_ptr<base2>
b2=tr1::dynamic_pointer_cast<base2>(tr1::dynamic_pointer_cast<iBase>(b1)
);
tr1::shared_ptr<base3>
b3=tr1::dynamic_pointer_cast<base3>(tr1::dynamic_pointer_cast<iBase>(b2)
);
Of course, this is tedious to write, I make up a function I normally
call "interface_cast" that handles the mess
template <class To, class From>
tr1::shared_ptr<To> interface_cast( tr1::shared_ptr<From> const& _r ){
return tr1::dynamic_pointer_cast<To>(
tr1::dynamic_pointer_cast<iUnknown>(_r));
}
tr1::shared_ptr<base1 > b1=make();//don't know implementation class
tr1::shared_ptr<base2> b2=interface_cast<base2>(b1 );
tr1::shared_ptr<base3> b3=interface_cast<base3>(b2 );
Are there other ways? Assuming that you have no information about which
object actually implements these interfaces (which is the goal of this
style design) Here is one that will only cost you one dynamic_cast,
that works on all compilers, with no virtual inheritance (there is,
still (sigh), a LOT of fear and loathing of virtual inheritance, so
this style may save you a lot of stupid arguments. Note that this is
not necessarily faster or slower that the method above)
struct iBase{//much like IUnknown in COM
virtual tr1::shared_ptr<iBase> GetBase() const=0;
};
struct base1: iBase{};
struct base2: iBase{};
struct base3: iBase{};
class myClass: public base1, public base2, public base3
,tr1::enable_shared_from_this<myClass>{
tr1::shared_ptr<iBase> GetBase()const{
return shared_from_this(); //
}
};
and a new function:
template <class To> tr1::shared_ptr<To>
interface_cast1(tr1::shared_ptr<iBase> const& _r ){
return tr1::dynamic_pointer_cast<To>(_r->GetBase());
}
tr1::shared_ptr<base1 > b1=make();//don't know implementation class
tr1::shared_ptr<base2> b2=interface_cast1<base2>(b1 );
tr1::shared_ptr<base3> b3=interface_cast1<base3>(b2 );
Is it possible to just make GetBase return the pointer you want already
casted, and still have my shared_ptrs? Yes, however you would need a
specail implementation of shared_ptr that had *intrusive* reference
counting. This is what I have set up in my project -- I have my own
shared_ptr implementation that is 100% standard compliant, but uses
intrusive counter (sorry not publicly available). YOu should be able to
implement this useing boost::intrusive_ptr, but I have never tried it.
This is the idea:
//better ways to do this but this is the essentials
//same as COM Interface IID
// better and faster than strings
enum{base1ID ,base2ID ,base2ID };
template< class T>struct interface2id;
struct iBase2{
virtual tr1::shared_ptr<iBase2> GetInterface(int ,iBase2**) const=0;
//other stuff for internals
//add_ref, release, add_ref_copy, weak_ref, weak_release, etc
};
struct base1: iBase2{};
struct base2: iBase2{};
struct base3: iBase2{};
//a few helpers
template< class base1> struct interface2id{static const int id=base1ID
};
template< class base2> struct interface2id{static const int id=base2ID
};
template< class base3> struct interface2id{static const int id=base3ID
};
class myClass: public base1, public base2, public base3
,mystuff::enable_shared_from_this<myClass>//has counter mechanism
inside
{
tr1::shared_ptr<iBase2>
GetInterface( int arg,iBase2**_p)const{
//*_p ==0 precondition
switch (arg){
case interface2id<base1>::id;
case interface2id<base2>::id;
case interface2id<base3>::id;
*_p = this;
break;
}
return shared_from_this();
}
};
template <class To>mystuff::shared_ptr<To>
interface_cast3( mystuff::shared_ptr<iBase2> const& _r ){
iBase2*_a=0;
//hold a pointer to the object while we construct
// a new pointer (needed for MT)
mystuff::shared_ptr<iBase2> b=
_r->GetInterface(interface2id<To>::id,&_a);
//THIS ONLY WORKS WITH INTRUSIVE REFERENCE COUNTING
//NOT STANDARD
if (_a) return mystuff::shared_ptr< To>(static_cast<To>(_a));
return mystuff::shared_ptr<To>();//empty
}
This would probably work with boost::instrusive_ptr, if you dont need
weak_ptrs support (probably not)
There are even more methods, however, this should be enough to chew on
for a while.
Hope this helps
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]