我有一个名为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;
但我不得不知道如何继续,因为我对元编程的主题非常新。
答案 0 :(得分:1)
更新我之后提供了一个更贴近您的问题的更新版本(通过调度客户端对象 和 和上下文) 。请参阅 Live On Coliru 。
更新2 发布了一个版本,将mix-erasure添加到混合中以获得虚拟界面,同时保留其他优势。请参阅 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);
}
当然,您仍然希望为这些重载提供实现。你可以
以下是使用和提及的详尽静态和运行时调度的所有方法的完整示例。
#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.
“更新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
作为值类型)