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
Post a Comment