c++ - boost::mpl::fold for double parameter abstraction -


i have class called carender, provides 1 carender::renderclientobject() method per given object type in clientobjecttypes. following code snipped shows running situation:

#define unused(x) (void)(x)  typedef boost::mpl::vector<model::clientmodel::cclientverticesobject,         model::clientmodel::crawclientobject> clientobjecttypes;  template <class t>     struct methodforward{         virtual void renderclientobject(t* clientobject,             controller::qtopengl::cqopenglcontext* glcontext) {                 unused(clientobject);                 unused(glcontext);                 };     };  struct base {     template<class baseclass, class t>     struct apply {          struct deriverender: baseclass, methodforward<t> {             virtual ~deriverender(){};              using baseclass::renderclientobject;             using methodforward<t>::renderclientobject;         };          typedef deriverender type;     };      template<class t>     struct apply<void, t> {          struct startrender : methodforward<t> {             virtual ~startrender(){};         };          typedef startrender type;     };  };  typedef boost::mpl::fold<clientobjecttypes, void, base>::type carender; 

q: want parameter abstraction second parameter (context) inside renderclientobject method. goal n*m generated renderclientobject methods, n defined number of clientobjecttypes , m number of context types. add second vector:

typedef boost::mpl::vector<controller::qtopengl::cqopenglcontext> contexttypes; 

but have idea how proceed, because new topic of meta programming.

update i've since provided updated version match question more closely (by dispatching on client object and , context). see live on coliru.

update 2 posted version adding type-erasure mix virtual interface while retaining other benefits. see live on coliru posting @ bottom ensure future retention on so.

i'm converting comment answer because provides more space elaborate.

i impression you're "just" trying multi-method semantics (i.e. functions have polymorphic behavior depends on more 1 object type).

enter actors

for demonstration i'll use stub types. let's assume 3 client object types[1]:

namespace model { namespace clientmodel {       struct cclientverticesobject : boost::noncopyable {};      struct crawclientobject      : boost::noncopyable {};      struct cfunkyclientobject    : boost::noncopyable {}; } }  model::clientmodel::cclientverticesobject vertices; model::clientmodel::crawclientobject      raw; model::clientmodel::cfunkyclientobject    funky; 

let's see how story of these objects unfolds :)


static dispatch; starting basics

in case suspect run-of-the-mill polymorphic functor more point:

struct renderclientobjects {     typedef void result_type;      renderclientobjects(controller::qtopengl::cqopenglcontext* glcontext)          : glcontext(glcontext)      { }      template <typename clientobject1, typename clientobject2>          void operator()(clientobject1 const& clientobject1, clientobject2 const& clientobject2) const         {              // implementation         }  private:      controller::qtopengl::cqopenglcontext* glcontext; }; 

you can use calleable object, relying on static dispatch:

renderclientobjects multimethod(&glcontext);  multimethod(vertices, vertices); multimethod(vertices, funky); multimethod(raw, vertices); multimethod(funky, vertices); 

runtime dispatch: enter boost::variant!

skipping question of how // implementation provided, let me jump ahead elegance of shines: can magically have runtime binary dispatch using boost::variant:

///////////////////////////////////////////////////////////////////// // variant dispatch (boost apply_visitor supports binary dispatch) typedef boost::variant<         model::clientmodel::cclientverticesobject&,          model::clientmodel::crawclientobject&,         model::clientmodel::cfunkyclientobject&     > clientobjectvariant;  void variant_multimethod(controller::qtopengl::cqopenglcontext& ctx, clientobjectvariant const& a, clientobjectvariant const& b) {     boost::apply_visitor(renderclientobjects(&ctx), a, b); } 

providing (custom) implementations

of course, still want provide implementations overloads. can

  • supply explicit overloads
  • delegate implementation class can specialized (sometimes known customization point, extension point or userdefined hook etc.)
  • combinations of that

full sample

here's full sample using approaches mentioned and showing exhaustive static , runtime dispatch.

see live on coliru

#include <boost/variant.hpp> #include <boost/utility.hpp> #include <iostream>  ////// stubs namespace model { namespace clientmodel {       struct cclientverticesobject : boost::noncopyable {};      struct crawclientobject      : boost::noncopyable {};      struct cfunkyclientobject    : boost::noncopyable {}; } } namespace controller { namespace qtopengl {      typedef std::ostream cqopenglcontext; } } ////// end stubs  ///////////////////////////////////////////////////////////////////// // why not **just** make polymorphic functor? // // can make use extension point if you're inclined: // (note use of enable make sfinae-friendly) namespace usertypehooks {     template <typename clientobject1, typename clientobject2, typename enable = void>      struct renderclientobjectsimpl     {         void static call(                 controller::qtopengl::cqopenglcontext* glcontext,                  clientobject1 const& clientobject1,                 clientobject2 const& clientobject2)         {             (*glcontext) << __pretty_function__ << "\n";         }     }; }  struct renderclientobjects {     typedef void result_type;      renderclientobjects(controller::qtopengl::cqopenglcontext* glcontext)          : glcontext(glcontext)      { }      //     void operator()(model::clientmodel::cfunkyclientobject const& clientobject1, model::clientmodel::cfunkyclientobject const& clientobject2) const     {         (*glcontext) << "both objects funky.\n";     }      template <typename clientobject2>          void operator()(model::clientmodel::cfunkyclientobject const& clientobject1, clientobject2 const& clientobject2) const         {             (*glcontext) << "funky object involved (other " << typeid(clientobject2).name() << ")\n";         }      template <typename clientobject1>          void operator()(clientobject1 const& clientobject1, model::clientmodel::cfunkyclientobject const& clientobject2) const         {             (*this)(clientobject2, clientobject1); // delegate implementation, example         }      // catch all:     template <typename clientobject1, typename clientobject2>          void operator()(clientobject1 const& clientobject1, clientobject2 const& clientobject2) const         {             return usertypehooks::renderclientobjectsimpl<clientobject1, clientobject2>::call(glcontext, clientobject1, clientobject2);         }    private:      controller::qtopengl::cqopenglcontext* glcontext; };  ///////////////////////////////////////////////////////////////////// // demonstrating user-defined extension point mechanics: namespace usertypehooks {     template <typename clientobject>     struct renderclientobjectsimpl<clientobject, clientobject>     {         void static call(                 controller::qtopengl::cqopenglcontext* glcontext,                  clientobject const& clientobject1,                 clientobject const& clientobject2)         {             (*glcontext) << "both objects of same type (and not funky) : " << typeid(clientobject).name() << "\n";         }     }; }  ///////////////////////////////////////////////////////////////////// // variant dispatch (boost apply_visitor supports binary dispatch) typedef boost::variant<         model::clientmodel::cclientverticesobject&,          model::clientmodel::crawclientobject&,         model::clientmodel::cfunkyclientobject&     > clientobjectvariant;  void variant_multimethod(controller::qtopengl::cqopenglcontext& ctx, clientobjectvariant const& a, clientobjectvariant const& b) {     renderclientobjects multimethod(&ctx);     boost::apply_visitor(multimethod, a, b); }  int main() {     controller::qtopengl::cqopenglcontext glcontext(std::cout.rdbuf());     renderclientobjects multimethod(&glcontext);      model::clientmodel::cclientverticesobject vertices;     model::clientmodel::crawclientobject      raw;     model::clientmodel::cfunkyclientobject    funky;      glcontext << "// static dispatch:\n";     glcontext << "//\n";     multimethod(vertices, vertices);     multimethod(vertices, raw);     multimethod(vertices, funky);     //     multimethod(raw, vertices);     multimethod(raw, raw);     multimethod(raw, funky);     //     multimethod(funky, vertices);     multimethod(funky, raw);     multimethod(funky, funky);      glcontext << "\n";     glcontext << "// runtime dispatch:\n";     glcontext << "//\n";      variant_multimethod(glcontext, vertices, vertices);     variant_multimethod(glcontext, vertices, raw);     variant_multimethod(glcontext, vertices, funky);     //     variant_multimethod(glcontext, raw, vertices);     variant_multimethod(glcontext, raw, raw);     variant_multimethod(glcontext, raw, funky);     //     variant_multimethod(glcontext, funky, vertices);     variant_multimethod(glcontext, funky, raw);     variant_multimethod(glcontext, funky, funky); } 

oh, completeness, here's output:

g++-4.8 -os -wall -pedantic main.cpp && ./a.out | c++filt -t // static dispatch: // both objects of same type (and not funky) : model::clientmodel::cclientverticesobject static void usertypehooks::renderclientobjectsimpl<clientobject1, clientobject2, enable>::call(controller::qtopengl::cqopenglcontext*, const clientobject1&, const clientobject2&) [with clientobject1 = model::clientmodel::cclientverticesobject; clientobject2 = model::clientmodel::crawclientobject; enable = void; controller::qtopengl::cqopenglcontext = std::basic_ostream<char>] funky object involved (other model::clientmodel::cclientverticesobject) static void usertypehooks::renderclientobjectsimpl<clientobject1, clientobject2, enable>::call(controller::qtopengl::cqopenglcontext*, const clientobject1&, const clientobject2&) [with clientobject1 = model::clientmodel::crawclientobject; clientobject2 = model::clientmodel::cclientverticesobject; enable = void; controller::qtopengl::cqopenglcontext = std::basic_ostream<char>] both objects of same type (and not funky) : model::clientmodel::crawclientobject funky object involved (other model::clientmodel::crawclientobject) funky object involved (other model::clientmodel::cclientverticesobject) funky object involved (other model::clientmodel::crawclientobject) both objects funky.  // runtime dispatch: // both objects of same type (and not funky) : model::clientmodel::cclientverticesobject static void usertypehooks::renderclientobjectsimpl<clientobject1, clientobject2, enable>::call(controller::qtopengl::cqopenglcontext*, const clientobject1&, const clientobject2&) [with clientobject1 = model::clientmodel::cclientverticesobject; clientobject2 = model::clientmodel::crawclientobject; enable = void; controller::qtopengl::cqopenglcontext = std::basic_ostream<char>] funky object involved (other model::clientmodel::cclientverticesobject) static void usertypehooks::renderclientobjectsimpl<clientobject1, clientobject2, enable>::call(controller::qtopengl::cqopenglcontext*, const clientobject1&, const clientobject2&) [with clientobject1 = model::clientmodel::crawclientobject; clientobject2 = model::clientmodel::cclientverticesobject; enable = void; controller::qtopengl::cqopenglcontext = std::basic_ostream<char>] both objects of same type (and not funky) : model::clientmodel::crawclientobject funky object involved (other model::clientmodel::crawclientobject) funky object involved (other model::clientmodel::cclientverticesobject) funky object involved (other model::clientmodel::crawclientobject) both objects funky. 

the type-erased interface version

full codefor 'update 2':

#include <typeinfo> #include <boost/type_traits.hpp> #include <iostream>  ////// stubs struct move_only {  // apparently boost::noncopyable prohibits move     move_only(move_only const&) = delete;     move_only(move_only&&) = default;     move_only() = default; };  namespace model { namespace clientmodel {       struct cclientverticesobject : move_only {};      struct crawclientobject      : move_only {};      struct cfunkyclientobject    : move_only {}; } } namespace controller {      namespace qtopengl {          struct cqopenglcontext : move_only {};     }      struct cconsolecontext : move_only {};     struct cdevnullcontext : move_only {}; }  namespace traits {     template <typename t> struct supports_console_ctx : boost::mpl::false_ {};     template <>          struct supports_console_ctx<model::clientmodel::cfunkyclientobject> : boost::mpl::true_ {}; } ////// end stubs  ///////////////////////////////////////////////////////////////////// // why not **just** make polymorphic functor? // // can make use extension point if you're inclined: // (note use of enable make sfinae-friendly) namespace usertypehooks {     template <typename clientobject, typename context, typename enable = void>      struct renderclientobjectsimpl     {         void static call(clientobject const& clientobject, context const& context)         {             // static_assert(false, "not implemented");             // throw?             std::cout << "not implemented:\t" << __pretty_function__ << "\n";         }     };      template <typename clientobject>          struct renderclientobjectsimpl<clientobject, controller::qtopengl::cqopenglcontext>     {         void static call(clientobject const& clientobject, controller::qtopengl::cqopenglcontext const& context)         {             std::cout << "cqopenglcontext:\t" << typeid(clientobject).name() << "\n";         }     };      template <typename clientobject>          struct renderclientobjectsimpl<clientobject, controller::cdevnullcontext>     {         void static call(clientobject const& clientobject, controller::cdevnullcontext const& context)         {             std::cout << "devnull:\t\t" << typeid(clientobject).name() << "\n";         }     }; }  struct renderclientobjects {     typedef void result_type;      template <typename clientobject, typename context>          void operator()(clientobject const& clientobject, context const& context) const         {             return usertypehooks::renderclientobjectsimpl<clientobject, context>::call(clientobject, context);         } };  ///////////////////////////////////////////////////////////////////// // demonstrating user-defined extension point mechanics: namespace usertypehooks {     template <typename clientobject>     struct renderclientobjectsimpl<clientobject, controller::cconsolecontext,         typename boost::enable_if<traits::supports_console_ctx<clientobject> >::type>     {         void static call(                 clientobject const& clientobject,                 controller::cconsolecontext const& context)         {             std::cout << "this type has cconsolecontext support due supports_console_ctx trait! " << typeid(clientobject).name() << "\n";         }     }; }  ///////////////////////////////////////////////////////////////////// // added: dynamic interface // // making bit more complex need, hey, assuming // worst: #include <memory>  struct ipolymorphicrenderable {     // require 1 of these, , might not need     // virtual     virtual void render(controller::qtopengl::cqopenglcontext& ctx) = 0;     virtual void render(controller::cconsolecontext& ctx) = 0;     virtual void render(controller::cdevnullcontext& ctx) = 0; };  struct iclientobject : ipolymorphicrenderable {     template <typename t> iclientobject(t&& val) : _erased(new erasure<t>(std::forward<t>(val))) { }      virtual void render(controller::qtopengl::cqopenglcontext& ctx) { return _erased->render(ctx); }     virtual void render(controller::cconsolecontext& ctx)           { return _erased->render(ctx); }     virtual void render(controller::cdevnullcontext& ctx)           { return _erased->render(ctx); }    private:     template <typename t> struct erasure : ipolymorphicrenderable     {         erasure(t val) : _val(std::move(val)) { }          void render(controller::qtopengl::cqopenglcontext& ctx) { return renderclientobjects()(_val, ctx); }         void render(controller::cconsolecontext& ctx)           { return renderclientobjects()(_val, ctx); }         void render(controller::cdevnullcontext& ctx)           { return renderclientobjects()(_val, ctx); }          t _val;     };      std::unique_ptr<ipolymorphicrenderable> _erased; };  int main() {     controller::qtopengl::cqopenglcontext glcontext;     controller::cconsolecontext           console;     controller::cdevnullcontext           devnull;      std::cout << "// virtual dispatch\n";     std::cout << "//\n";      iclientobject obj = model::clientmodel::cclientverticesobject();     obj.render(glcontext);     obj.render(console);     obj.render(devnull);     //     obj = model::clientmodel::crawclientobject();     obj.render(glcontext);     obj.render(console);     obj.render(devnull);     //     obj = model::clientmodel::cfunkyclientobject();     obj.render(glcontext);     obj.render(console);     obj.render(devnull); } 

output:

clang++ -std=c++11 -os -wall -pedantic main.cpp && ./a.out // virtual dispatch // cqopenglcontext:    n5model11clientmodel21cclientverticesobjecte not implemented:    static void usertypehooks::renderclientobjectsimpl<model::clientmodel::cclientverticesobject, controller::cconsolecontext, void>::call(const clientobject &, const context &) [clientobject = model::clientmodel::cclientverticesobject, context = controller::cconsolecontext, enable = void] devnull:        n5model11clientmodel21cclientverticesobjecte cqopenglcontext:    n5model11clientmodel16crawclientobjecte not implemented:    static void usertypehooks::renderclientobjectsimpl<model::clientmodel::crawclientobject, controller::cconsolecontext, void>::call(const clientobject &, const context &) [clientobject = model::clientmodel::crawclientobject, context = controller::cconsolecontext, enable = void] devnull:        n5model11clientmodel16crawclientobjecte cqopenglcontext:    n5model11clientmodel18cfunkyclientobjecte type has cconsolecontext support due supports_console_ctx trait! n5model11clientmodel18cfunkyclientobjecte devnull:        n5model11clientmodel18cfunkyclientobjecte 

[1] (i've made sure client objects not assumed copyable sample, in fact, might want use clientobjectvariant value type throughout more of library)


Comments

Popular posts from this blog

html - Sizing a high-res image (~8MB) to display entirely in a small div (circular, diameter 100px) -

java - IntelliJ - No such instance method -

identifier - Is it possible for an html5 document to have two ids? -