c++ - Statically wrapping a library's polymorphic iterator without exposing the library to the user -
i integrating datastore library application. need able mock datastore (which i/o intensive) unit tests, therefore creating wrapper around library's interface.
unfortunately, in interface, library returns iterators pointers , not values, because polymorphic @ runtime.
my issue because of layer of polymorphism adding, seems unavoidable add iterators polymorphic @ runtime, therefore incurring new level of indirection , more dynamic allocation...
// library code class libiterator { // pure virtual methods }; class libdatastore { libiterator* getit(); }; // interface class imyiterator{ // pure virtual methods }; class mylibiterator : public imyiterator { std::unique_ptr<libiterator> m_iterator; }; class myiterator { std::unique_ptr<mylibiterator> m_iterator; }; class imydatastore { myiterator getit(); };
that awful lot of pointers dereference, of virtual dispatch on each use of method of iterator, plus @ least 2 dynamic allocations (the lib iterator + mine) each iterator creation...
i thinking of using crtp this, can't figure out way prevent code using imydatastore
see concrete implementation of iterator bleeding through myiterator
's type.
is there trick might have missed?
template<class t, std::size_t sz, std::size_t algn> struct poly {
if not afraid yet should be
poly_vtable<t> const* vtable=0; std::aligned_storage_t<sz, algn> data;
we can cover vtable later.
t* get() { return vtable->get(&data); } t const* get() const { return vtable->get((void*)&data); }
example use of vtable. here setup:
template<class u, class...args> u* emplace(args&&...args){ static_assert(sizeof(u)<=sz && alignof(u)<=algn, "type large"); clear(); u* r = ::new((void*)&data) u(std::forward<args>(args)...); vtable = get_poly_vtable<t,u>(); return r; }
copy:
poly(poly const& o){ if (!o.vtable) return; o.vtable->copy( &data, &o.data ); vtable=o.vtable; } poly(poly&& o){ if (!o.vtable) return; o.vtable->move( &data, &o.data ); vtable=o.vtable; } poly& operator=(poly const& rhs) { if (this == &rhs) return *this; clear(); if (!rhs.vtable) return *this; rhs.vtable->copy( &data, &rhs.data ); vtable = rhs.vtable; return *this; } poly& operator=(poly&& rhs) { if (this == &rhs) return *this; clear(); if (!rhs.vtable) return *this; rhs.vtable->move( &data, &rhs.data ); vtable = rhs.vtable; return *this; }
destruction:
void clear(){ if (!vtable) return; vtable->dtor(&data); vtable=nullptr; } ~poly(){clear();}
pointer operations:
explicit operator bool()const{return vtable;} t& operator*(){ return *get();} t const& operator*() const{ return *get();} t* operator->(){ return get();} t const* operator->() const{ return get();}
construct type derived t:
template<class u, class du=std::decay_t<u>, class=std::enable_if_t<!std::is_same<du, poly>{}>, class=std::enable_if_t<std::is_base_of<t, du>{}> > poly(u&& u) { emplace<std::decay_t<u>>( std::forward<u>(u) ); } };
note type when const refers const value.
the idea poly<t>
polymorphic value of type t
. has size limits.
you can use t*
vtable arrange polymorphism of other operations.
template<class t> struct poly_vtable{ t*(*get)(void*)=0; void(*copy)(void*,void const*)=0; void(*move)(void*,void*)=0; void(*dtor)(void*)=0; }; template<class t, class u> poly_vtable<t> make_poly_vtable() { return { [](void* ptr)->t*{ return static_cast<u*>(ptr); }, [](void* dest, void const* src){ ::new(dest) u(*static_cast<u const*>(src)); }, [](void* dest, void* src){ ::new(dest) u(std::move(*static_cast<u*>(src))); }, [](void* ptr){ static_cast<u*>(ptr)->~u(); } }; } template<class t, class u> poly_vtable<t> const* get_poly_vtable() { static const auto r = make_poly_vtable<t,u>(); return &r; }
get_poly_vtable<t,u>()
returns pointer static local poly_vtable<t>
each operation implemented.
now can have vtable based polymorphic value type.
the same technique can extended more operations; cast-to-base , using real vtables easier.
using this, store poly<imyiterator, 64, alignof(imyiterator)>
. value type containing buffer of 64 bytes.
another approach reduce indirection replace concept of per-item visitation possibly repeated range visitation.
if visit range of 10 items @ once per callback, overhead of invoking virtual methods 10 times less 1 per callback.
you can create input iterators range object has buffer 10 items in , automatically rebuild when reach end, if there more available, getting data in batches.
Comments
Post a Comment