In general, my question was simple, I want to imlement some design pattern, which allows following:
-
there is exists some predefined interface (
Interface
class); -
and exists class (
Utilizer
), which accepts another class (via pointer, reference, smart-pointer, whatever else...) implementing predefined interface, and stars using this class via the interface; -
class
Utilizer
should be able to own other class passed to it (which implementsInterface
) and delete it whenUtilizer
is destroyed.
In managed languages (like C#, Java) this can be implemented in simple way: class Utilizer
might accept reference to base class (Interface
) and hold this reference in the class, and use interface via the reference. On destruction of Utilizer
class, the garbage collector might delete class, which implements `Interface'.
In C++ we have no garbage collector... Ok, we can use some smart_pointer, but this might be not generic smart pointer, but smart pointer of some particular type (for example, unique_ptr with user specified deleter, because class, which implements Interface
is resided in shared memory and regular operator delete() can't be applied to this class...)
And second nuisance: virtual functions. Of course, when you are using managed languages you may not notice this. But if you made Interface
class as abstract base class (with virtual keyword), then you will notice, that in test
function (see the code below) compiler performs indirect calls (via function pointers). This happens because compiler needs to access virtual functions table. The call via function pointer is not very heavy (few processor ticks, or event tens of ticks), but the major issue is that compiler doesn't see that happens next, after the indirection. Optimizer stops here. Functions can't be inlined anymore. And we get not optimal code, which doesn't reduces to few machine instructions (for example test
function reduces in the example to loading of two constant and calling printf
function), we get unoptimal "generic" implementation, which effectively nullifies all the benefits of C++.
There is typical solution to avoid getting of unoptimal code -- avoid using virtual functions (prefer CRTP pattern instead), avoid type erasure (in the example, Utilizer
class might store not Accessor
, but std::function<Interface<T>&()>
-- this solution is nice, but indirection in std::function leads to generation of unoptimal code again).
And the essence of the question, how to implement the logic described above (class which owns other abstract, non some particular, class and uses it) in C++ effectively?
Not sure if I was able to clearly express my thought. Below is the my implementation with the comments. It generates optimal code (see disassembly of test
function in live demo live demo), all is inlined as expected. But the whole implementation looks cumbersome.
I would like to hear how can I improve the code.
#include <utility>
#include <memory>
#include <functional>
#include <stdio.h>
#include <math.h>
// This type implements interface: later Utilizer class
// accept Accessor type, which was able to return reference
// to object of some type, which implements this interface,
// and Utilizer class uses returned object via this interface.
template <typename Impl> class Interface
{
public:
int oper(int arg) { return static_cast<Impl*>(this)->oper(arg); }
const char *name() const { return static_cast<const Impl*>(this)->name(); }
};
// Class which uses object, returned by Accessor class, via
// predefined interface of type Interface<Impl>.
// Utilizer class can perform operations on any class
// which inherited from Interface class, but Utilizer
// doesn't directly owns parficular instance of the
// class implementing Interface: Accessor serves for
// getting of particular implementation of Interface
// from somewhere.
template <typename Accessor> class Utilizer
{
private:
typedef typename std::remove_reference<decltype(std::declval<Accessor>()())>::type Impl;
Accessor accessor;
// This static_cast allows only such Accessor types, for
// which operator() returns class inherited from Interface
Interface<Impl>& get() const { return static_cast<Interface<Impl>&>(accessor()); }
public:
template <typename...Args> Utilizer(Args&& ...args) : accessor(std::forward<Args>(args)...) {}
// Following functions is the public interface of Utilizer class
// (this interface have no relations with Interface class,
// except of the fact, that implementation uses Interface class):
double func(int a, int b)
{
if (a > 0) return sqrt(get().oper(a) + b);
else return get().oper(b) * a;
}
const char *text() const
{
const char *result = get().name();
if (result == nullptr) return "unknown";
return result;
}
};
// This is implementation of Interface<Impl> interface
// (program may have multiple similar classes and Utilizer
// can work with any of these classes).
struct Implementation : public Interface<Implementation>
{
Implementation() { puts("Implementation()"); }
Implementation(const Implementation&) { puts("copy Implementation"); }
~Implementation() { puts("~Implementation()"); }
// Following functions are implementation of functions
// defined in Interface<Impl>:
int oper(int arg) { return arg + 42; }
const char *name() const { return "implementation"; }
};
// This is class which owns some particular implementation
// of the class inherited from Interface. This class only
// owns the class which was given to it and allows accessing
// this class via operator(). This class is intendent to be
// template argument for Utilizer class.
template <typename SmartPointer> struct Owner
{
SmartPointer p;
Owner(Owner&& other) : p(std::move(other.p)) {}
template <typename... Args> Owner(Args&&...args) : p(std::forward<Args>(args)...) {}
Implementation& operator()() const { return *p; }
};
typedef std::unique_ptr<Implementation> PtrType;
typedef Utilizer<Owner<PtrType> > UtilType;
void test(UtilType& utilizer)
{
printf("%f %s\n", utilizer.func(1, 2), utilizer.text());
}
int main()
{
PtrType t(new Implementation);
UtilType utilizer(std::move(t));
test(utilizer);
return 0;
}
Aucun commentaire:
Enregistrer un commentaire