Sunday, 15 June 2014

c++ - Pimpl idiom combined with multiple virtual inheritance -



c++ - Pimpl idiom combined with multiple virtual inheritance -

background

i trying explore bit of available patterns , clever usage possibilities (in c++). there lot of info how utilize multiple virtual inheritance or pimpl idiom , there nice topics bridges or nested generalization not able find topic this.

i aware of (somehow) similar questions like:

pimpl idiom inheritance

but think not duplicate , worth it's own answer. approach absolutely stupid, please hint me helpful direction. not homework , there not restriction on answers/languages/paradigms, not have give-and-take personal preference, please seek reason answers why specific pattern/solution valid, either because other presented solutions wrong/error prone/... .

question

what way implement next construction (maybe composition instead of inheritance?). possible different answers valid if reuse of code, easier read/write, easier extend main focus (maybe can think of other problems?)

the main problem see in current implementation not extensable @ 1 class every combination of values each property want model. sake of simplicity of illustration utilize photographic camera properties projection (either ortographic or perspective) , behaviour (firstperson or freelook) thought same entity properties p1,p2,p3,p4 each can 1 out of many subtypes p1=>p1.1,p1.2,... p2=>p2.1,p2.2,...

projection shall define implementation of

virtual const glm::mat4& getprojectionmatrix() = 0;

while behaviour shall define implementation of

virtual const glm::vec3& getforwardvector() = 0; possible solution 1: combine pimpl idiom multiple virtual inheritance

sidenote: utilize name _this pimpl pointer _this->variable leads readable code point of view. of course of study there danger of confusing , _this in case there exists members same name (unlikely indeed possible).

camera.h

class camera{ protected: /*pimpl idiom extended multiple virtual inheritance abstractimpl subclasses*/ class abstractimpl; std::unique_ptr<abstractimpl> _this; /*private subclasses abstractimpl*/ class ortographic; class perspective; class firstperson; class freelook; class ortographicfirstperson; class ortographicfreelook; class perspectivefirstperson; class perspectivefreelook; };

abstractimpl.h

class camera::abstractimpl { public: /*also contains private info members of class photographic camera (pimpl idiom)*/ virtual const glm::vec3& getforwardvector() = 0; virtual const glm::mat4& getprojectionmatrix() = 0; }; class camera::ortographic:virtual camera::abstractimpl{public:virtual const glm::mat4& getprojectionmatrix() override;}; class camera::perspective:virtual camera::abstractimpl{public:virtual const glm::mat4& getprojectionmatrix() override;}; class camera::firstperson:virtual camera::abstractimpl{public:virtual const glm::vec3& getforwardvector() override;}; class camera::freelook:virtual camera::abstractimpl{public:virtual const glm::vec3& getforwardvector() override;}; class camera::ortographicfirstperson:virtual camera::ortographic,virtual camera::firstperson{}; class camera::ortographicfreelook:virtual camera::ortographic, virtual camera::freelook{}; class camera::perspectivefirstperson:virtual camera::perspective, virtual camera::firstperson{}; class camera::perspectivefreelook:virtual camera::perspective, virtual camera::freelook{};

this not useable in case of many properties or values per property @ first glance looks code reused both virtual methods implemented 1 time per necessity. think code part of subclasses of abstractimpl nicely readable feels assigning attributes. "i want class have properties". subclasses have total access datamembers , functions of abstractimpl might necessary (let's homecoming value of overriden virtual method depends on values of private info members).

also looks implementation supported code generators need right inheritance new subclass.

possible solution 2:composition

another solution think of utilize composition.

camera.h

class camera{ protected: projectiontype _projectiontype; camerabehaviour _camerabehaviour; private: class impl; std::unique_ptr<impl> _this; };

if projectiontype or camerabehaviour sublcasses depend values of camera::impl instance necessary pass , think clutter code lot. on pro side end way less classes need 1 class per projectiontype , 1 class per camerabehaviour. might solution go if have many possible values type , behaviour.

possible solution 3: inheritance

of course of study utilize subclasses of camera. in case pimpl might or not might used @ all info members require protected visibilty shouldn't part of pimpl anyway.

camera.h

class abstractcamera{ protected: /*all protected info members*/ private: /*maybe pimpl idiom makes no sense here because of variables might protected*/ class impl; std::unique_ptr<impl> _this; };

perspectivefreelookcamera.h

class perspectivefreelookcamera: virtual abstractcamera{ /*override both methods*/ };

perspectivefirstpersoncamera.h

class perspectivefirstpersoncamera: virtual abstractcamera{ /*override both methods*/ };

and same ortographic classes. here virtual methods implemented multiple times perspective/ortographic... classes share same implementation method

virtual const glm::mat4& getprojectionmatrix() = 0;

while freelook/firstperson classes share same implementation

virtual const glm::vec3& getforwardvector() = 0;

thank taking time, hope can considered question have set quite thought it, such interesting many people :)

edit: if can imagine improve title question, please sense free edit it, had quite hard time finding title.

possible solution 4: templates (added 4.11 19:00 gmt+2)

based on reply of yakk have worked on solution templates.

camera.h

class photographic camera { public: /*forward declaration of public classes of photographic camera of course of study possible utilize own header files have stricter 1 class per file nature not see need this*/ template<typename t_projection, typename t_behaviour> class impl; class abstractimpl; camera(abstractimpl *impl); ~camera(); /* other public functions camera*/ /* illustration method want implement based on tags/traits */ const glm::mat4& getprojectionmatrix(); private: std::unique_ptr<abstractimpl> _this; };

camera.cpp

#include "camera.h" #include "impl.h" camera::camera(abstractimpl *impl) : _this(impl){} camera::~camera() = default; /*and implementations*/ /*pimpl facade function*/ const glm::mat4& camera::getprojectionmatrix(){ homecoming _this->getprojectionmatrix(); }

impl.h

namespace projection{ struct ortographic{}; struct perspective{}; } namespace behaviour{ struct firstperson{}; struct freelook{}; } /* abstract base of operations class different implementations */ class camera::abstractimpl { public: /* contains pimpl-idiom members of photographic camera */ /* contains pure virtual function our trait/tag-based method */ virtual const glm::mat4& getprojectionmatrix() = 0; }; template<typename t_projection, typename t_behaviour> class camera::impl : public camera::abstractimpl { public: impl(){} const glm::mat4& getprojectionmatrix(){ homecoming getprojectionmatrix(t_projection{}); } const glm::mat4& getprojectionmatrix(projection::ortographic){ /* code ortographic projection */ } const glm::mat4& getprojectionmatrix(projection::perspective){ /* code perspective projection */ } };

and can utilize in way

camera * c = new camera(new camera::impl<projection::ortographic,behaviour::freelook>()); c->getprojectionmatrix();

benefit: should statically typed beside obvious dynamic typing of abstractimpl subclasses. possible switch concrete implementation of photographic camera during runtime. new traits can added easily. possible utilize approach needed adding subclasses of abstractimpl.

drawback: debugging can become quite pain. lone during trying out realized, altough many errors might show during compile/linking time still way more hard find problems.

i appreciat feedback on solution 4 added because of yaaks implementation. did utilize suggestion correctly? did oversee or used templates wrongly - have not used them before.

struct ortographic_tag {}; struct perspective_tag {}; struct first_person_tag {}; struct freelook_tag {}; template<class projection_tag, class location_tag> struct camera_impl : camera::abstractimpl { virtual const glm::mat4& getprojectionmatrix() override { homecoming get_projection_matrix(*this, projection_tag{}); } virtual const glm::vec3& getforwardvector() override { homecoming get_forward_vector(*this, location_tag{}); } }; template<class camera> const glm::mat4& get_projection_matrix( photographic camera const& c, ortographic_tag ) { // code } template<class camera> const glm::mat4& get_projection_matrix( photographic camera const& c, perspective_tag ) { // code } template<class camera> const glm::vec3& get_forward_vector( photographic camera const& c, first_person_tag ) { // code } template<class camera> const glm::vec3& get_forward_vector( photographic camera const& c, freelook_tag ) { // code }

camera_impl< x, y > forwards various virtual methods non-virtual functions.

if need store different info in _impl depending on projection/location, traits:

template<class tag> struct camera_data;

and store store camera_data<projection_tag> , camera_data<location_tag> in _impl, if going far i'd rethink approach.

we can go step farther , create our impl pass every tag every free function, , allow free function work out tags care about.

template<class... tags> struct many_tags {}; template<class t0, class...tags> struct many_tags:t0, many_tags<tags...> {};

pass many_tags<a, b, c>, , if overload on tag a match. if there overloads on more one, ambiguity unless go work of handling themselves. if want or b, have write custom sfinae using is_base_of, or wait concepts lite.

c++ oop inheritance

No comments:

Post a Comment