I am currently integrating a datastore library into my application. I need to be able to mock this datastore (which is I/O intensive) for my unit tests, therefore creating a wrapper around that library's interface.
Unfortunately, in its interface, this library returns iterators as pointers and not as values, because they are polymorphic at runtime.
My issue is that because of the layer of polymorphism I am adding, it seems unavoidable to add iterators that are polymorphic at runtime, therefore incurring a new level of indirection and some more dynamic allocation...
// Library code
class LibIterator
{
// pure virtual methods
};
class LibDataStore
{
LibIterator* getIt();
};
// My 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 is an awful lot of pointers to dereference, of virtual dispatch on each use of any method of the iterator, plus at least 2 dynamic allocations (the lib iterator + mine) for each iterator creation...
I was thinking of using CRTP to help with this, but I can't figure out a way to prevent code using IMyDataStore
to see the concrete implementation of the iterator bleeding through MyIterator
's type.
Is there any trick I might have missed?
if you are not afraid yet you should be
we can cover the vtable later.
example use of vtable. Here is setup:
copy:
destruction:
pointer like operations:
construct from a type derived from T:
note that this type when const refers to a const value.
The idea is that
poly<T>
is a polymorphic value of typeT
. It has size limits.You can use the
T*
vtable to arrange for polymorphism of other operations.get_poly_vtable<T,U>()
returns a pointer to a static localpoly_vtable<T>
with each operation implemented.Live example.
Now you can have a vtable based polymorphic value type.
The same technique can be extended to more operations; simply cast-to-base and using real vtables is easier.
Using this, you store a
poly<IMyIterator, 64, alignof(IMyIterator)>
. This is a value type containing some buffer of 64 bytes.Another approach to reduce indirection would be to replace the concept of per-item visitation with possibly repeated range visitation.
If you visit a range of 10 items at once per callback, then the overhead of invoking virtual methods is up to 10 times less than one per callback.
You can create input iterators with a range object that has a buffer for up to 10 items in it and who automatically rebuild it when you reach the end, if there are more available, getting the data in batches.