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.

live example.

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

Popular posts from this blog

node.js - Node js - Trying to send POST request, but it is not loading javascript content -

javascript - Replicate keyboard event with html button -

javascript - Web audio api 5.1 surround example not working in firefox -