我有一个称为caRender的类,该类为clientObjectTypes中的每个给定对象类型提供一个caRender :: renderClientObject()方法。因此,以下代码片段显示了这种运行情况:
#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;
问:我还希望我的renderClientObject方法中的第二个参数(上下文)具有参数抽象。因此,目标是获取n * m个生成的renderClientObject方法,其中n被定义为clientObjectTypes的数量,而m是上下文类型的数量。我将添加第二个向量:
typedef boost::mpl::vector<Controller::QtOpenGL::cQOpenGLContext> contextTypes;
但是比起我必须去思考如何进行下去,因为我对元编程这个话题还很陌生。
更新因为我已经提供了一个更新的版本,它更符合你的问题(通过派发客户端对象上,并和背景)。参见Live on Coliru。
更新2发布了一个版本,在混合中添加了类型擦除功能,以获得虚拟界面,同时保留了其他好处。请参阅Live On Coliru将其张贴在底部,以确保将来保留在SO上。
我将我的评论转换为答案,因为它提供了更多的阐述空间。
我得到的印象是,您只是在试图获得类似于语义的多方法(即具有依赖于多个对象类型的多态行为的函数)。
在本演示中,我将使用一些存根类型。假设3个客户对象类型[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;
让我们看看这些对象的故事是如何展开的:)
在那种情况下,我怀疑现成的多态函子可能更重要:
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
{
// some implementation
}
private:
Controller::QtOpenGL::cQOpenGLContext* glContext;
};
您可以像依赖静态分派一样将其像任何可调用对象一样使用:
RenderClientObjects multimethod(&glContext);
multimethod(vertices, vertices);
multimethod(vertices, funky);
multimethod(raw, vertices);
multimethod(funky, vertices);
boost::variant
!跳过将如何// some implementation
提供的问题,让我跳到它的高雅之处:您可以使用以下方法神奇地进行运行时二进制调度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);
}
当然,您仍将希望为这些重载提供实现。你可以
这是使用上述所有方法的完整示例,并显示了详尽的静态信息和运行时调度。
看到它住在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 it a polymorphic functor?
//
// You can make it use an extension point if you're so inclined:
// (note the use of Enable to make it 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 are Funky.\n";
}
template <typename ClientObject2>
void operator()(Model::ClientModel::cFunkyClientObject const& clientObject1, ClientObject2 const& clientObject2) const
{
(*glContext) << "Funky object involved (other is " << typeid(clientObject2).name() << ")\n";
}
template <typename ClientObject1>
void operator()(ClientObject1 const& clientObject1, Model::ClientModel::cFunkyClientObject const& clientObject2) const
{
(*this)(clientObject2, clientObject1); // delegate implementation, for 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 the 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 are of the 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 << "// Fully 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);
}
哦,为了完整起见,这是输出:
g++-4.8 -Os -Wall -pedantic main.cpp && ./a.out | c++filt -t
// Fully static dispatch:
//
Both objects are of the 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 is 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 are of the same type (and not funky) : Model::ClientModel::cRawClientObject
Funky object involved (other is Model::ClientModel::cRawClientObject)
Funky object involved (other is Model::ClientModel::cClientVerticesObject)
Funky object involved (other is Model::ClientModel::cRawClientObject)
Both objects are Funky.
// Runtime dispatch:
//
Both objects are of the 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 is 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 are of the same type (and not funky) : Model::ClientModel::cRawClientObject
Funky object involved (other is Model::ClientModel::cRawClientObject)
Funky object involved (other is Model::ClientModel::cClientVerticesObject)
Funky object involved (other is Model::ClientModel::cRawClientObject)
Both objects are Funky.
“ Update 2 ”的完整代码:
#include <typeinfo>
#include <boost/type_traits.hpp>
#include <iostream>
////// STUBS
struct move_only { // apparently boost::noncopyable prohibits move too
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 it a polymorphic functor?
//
// You can make it use an extension point if you're so inclined:
// (note the use of Enable to make it 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 the 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 to the supports_console_ctx trait! " << typeid(ClientObject).name() << "\n";
}
};
}
/////////////////////////////////////////////////////////////////////
// Added: Dynamic interface
//
// Making this a bit more complex than you probably need, but hey, assuming the
// worst:
#include <memory>
struct IPolymorphicRenderable
{
// you likely require only one of these, and it might not need to be
// 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 << "// Fully 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);
}
输出:
clang++ -std=c++11 -Os -Wall -pedantic main.cpp && ./a.out
// Fully 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
This type has cConsoleContext support due to the supports_console_ctx trait! N5Model11ClientModel18cFunkyClientObjectE
devnull: N5Model11ClientModel18cFunkyClientObjectE
[1](我已经确保该示例不假定客户端对象是可复制的,但实际上,您可能想ClientObjectVariant
在整个库的更多部分中将其用作值类型)
本文收集自互联网,转载请注明来源。
如有侵权,请联系 [email protected] 删除。
我来说两句