boost :: mpl :: fold用于双参数抽象

昆瑟

我有一个称为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] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章