gpt4 book ai didi

c++ - boost::mpl::fold 用于双参数抽象

转载 作者:塔克拉玛干 更新时间:2023-11-03 07:24:24 26 4
gpt4 key购买 nike

我有一个名为 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 是上下文类型的数量。我会添加第二个 vector :

typedef boost::mpl::vector<Controller::QtOpenGL::cQOpenGLContext> contextTypes;

但是我不得不考虑如何继续,因为我对元编程这个话题还很陌生。

最佳答案

Update I've since provided an updated version that does match your question more closely (by dispatching on client object and and context). See Live On Coliru.

Update 2 Posted a version adding type-erasure to the mix to get a virtual interface while retaining the other benefits. See Live On Coliru Posting it at the bottom to ensure future retention on SO.

我正在将我的评论转化为答案,因为它提供了更多的空间来阐述。

我的印象是您“只是”试图获得类似语义的多方法(即具有依赖于不止一种对象类型的多态行为的函数)。

请输入 Actor

在本次演示中,我将使用一些 stub 类型。让我们假设 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;
};

您可以像使用任何可调用对象一样使用它,依赖于静态分派(dispatch):

RenderClientObjects multimethod(&glContext);

multimethod(vertices, vertices);
multimethod(vertices, funky);
multimethod(raw, vertices);
multimethod(funky, vertices);

运行时调度:输入boost::variant!

跳过如何提供 //一些实现 的问题,让我跳到它的优雅之处:你可以神奇地使用运行时二进制调度boost::变体:

/////////////////////////////////////////////////////////////////////
// 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);
}

提供(自定义)实现

当然,您仍然希望为这些重载提供实现。你可以

  • 提供显式重载
  • 委托(delegate)给一个可以专门化的实现类(有时称为自定义点扩展点用户定义的钩子(Hook)等)<
  • 这些的组合

完整样本

这里是一个完整的示例,使用了所有提及的方法并且展示了详尽的静态和运行时分派(dispatch)。

查看 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 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.

类型删除接口(interface)版本

更新 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 作为值类型)

关于c++ - boost::mpl::fold 用于双参数抽象,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21483102/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com