visual c++ - Self modifying virtual table entries to point to concrete implementation -
short version:
can com class modify own virtual table entries @ runtime? (disregarding thread issues)
full version:
i'm providing number of c++ classes implement com interface. com interface defined in third-party framework.
(edited: vtable of com interface standard on windows platform; relationship between c++ vtable , com vtable isn't clear me.)
in order inject implementations correctly framework, needs use two-step initialization: first create object no parameters, call initialize
method parameters. moment when 1 of implementations can chosen.
to make happen, added "resolver" class (or wrapper class), sole responsibility choose implementation in initialize
method. after that, every call com methods forwarded actual implementation.
the resolver class injected framework. gets around framework limitation.
now, seeing resolver class doesn't have use after initialization, i'm wondering if there way rid of virtual method indirection cost? idea copy each com vtable entry concrete implementation vtable of resolver class.
will work?
example:
// (fyi) #define hresult unsigned long struct iinterface { // ... usual addref, release , qi omitted virtual hresult initialize(variant v) = 0; // initialize method, implementation gets chosen virtual hresult dowork() = 0; // can call after initialization. }; class myresolver : public iinterface { // ... usual addref, release , qi omitted public: virtual hresult initialize(variant v) { if ( /* conditions based on v */ ) return implem_one.create((void**) &m_pimpl); else return implem_two.create((void**) &m_pimpl); "here, can copy vtable entries m_pimpl vtable of myresolver (*this) ?"; (int k = 0; k < num_virtual_methods; ++k) this->vtbl[k] = m_pimpl->vtbl[k]; } virtual hresult dowork() { if (!m_pimpl) return error_not_initialized; m_pimpl->dowork(); } public: // creation method injected framework static hresult create(void**) { /* create instance of class , return */ } private: myresolver() : m_pimpl(null) {} virtual ~myresolver() { if (m_pimpl) m_pimpl->release(); } iinterface* m_pimpl; };
you can't safely copy vtable entries; since this
pointer still refer proxy class, real implementation won't find private data.
if base classes support com aggregation, can use avoid overhead however. when constructing base object, you'd pass outer class's iunknown outer aggregation iunknown. means queryinterface/addref/release on derived interfaces of base object delegate outer object, making part of outer object.
now can have queryinterface return original object (with proxying methods) prior initialization, or base object's interface directly when initialization complete. example:
class myproxy : public iinterface { long refct; iunknown *punkinner; iinterface *pifaceinner; ~myproxy() { addref(); // punkinner may take references in destruction; prevent reentrancy per aggregating object rules if (punkinner) { punkinner->release(); } } public: myproxy() : refct(1), punkinner(null), pifaceinner(null) { } stdmethodimp queryinterface(refiid riid, void **ppvobject) { if (!ppvobject) return e_pointer; if (riid == iid_iunknown) { addref(); *ppvobject = (void *)static_cast<iunknown *>(this); return s_ok; } else if (riid == iid_iinterface && punkinner) { // increments refcount of _outer_ object return punkinner->queryinterface(riid, ppvobject); } else if (riid == iid_iinterface) { addref(); *ppvobject = (void *)static_cast<iinterface *>(this); return s_ok; } else { return e_nointerface; } } stdmethodimp_(dword) addref(void) { return interlockedincrement(&refct); } stdmethodimp_(dword) release(void) { if (!interlockeddecrement(&refct)) delete this; } hresult initialize(variant v) { // can use protocol create object long supports aggregation hresult res = cocreateinstance(clsid_innerclass, this, clsctx_inproc_server, iid_iunknown, (lpvoid *)&punkinner); if (failed(res)) return res; res = punkinner->queryinterface(iid_iinterface, (void **)&pifaceinner); if (failed(res)) { punkinner->release(); punkinner = null; return res; } release(); // above queryinterface incremented _outer_ refcount, don't want that. return s_ok; } hresult dowork() { if (!pifaceinner) return error_not_initialized; return pifaceinner->dowork(); } };
although initial iinterface
pointer client gets has double-call overhead, once initialization complete, can re-queryinterface
more direct pointer. what's more, if can move initialization different interface, can force client re-queryinterface
, ensuring direct pointer.
that said, it's vitally important aggregated objects support aggregation protocol; otherwise you'll end inconsistent reference counts , other badness. read msdn documentation before implementing aggregation.
Comments
Post a Comment