提高:: MPL ::折叠双参数抽象 [英] boost::mpl::fold for double parameter abstraction

查看:145
本文介绍了提高:: MPL ::折叠双参数抽象的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个名为caRender类,它提供1个caRender :: renderClientObject()每个给定的对象类型clientObjectTypes方法。所以下面code剪断显示了这种运行情况:

 的#define未使用(X)(无效)(X)TYPEDEF提振:: MPL ::矢量<型号:: ClientModel :: cClientVerticesObject,
        型号:: ClientModel :: cRawClientObject> clientObjectTypes;模板<类T>
    结构methodForward {
        虚拟无效renderClientObject(T * clientObject,
            控制器:: QtOpenGL :: cQOpenGLContext * glContext){
                未使用(clientObject);
                未使用(glContext);
                };
    };结构基地{
    模板<类基类,类T>
    结构适用{        结构deriveRender:基类,methodForward< T> {
            虚拟〜deriveRender(){};            使用基类:: renderClientObject;
            使用methodForward< T> :: renderClientObject;
        };        的typedef deriveRender类型;
    };    模板<类T>
    结构采用与LT;无效,T> {        结构startRender:methodForward< T> {
            虚拟〜startRender(){};
        };        的typedef startRender类型;
    };};TYPEDEF提振:: MPL ::折叠< clientObjectTypes,无效的,基地> ::类型caRender;

问:我想参数抽象也为renderClientObject方法内我的第二个参数(上下文)所以,我们的目标是让N * M产生renderClientObject方法,其中n定义为编号。 clientObjectTypes和m是上下文类型的数目。我想补充第二个向量:

 的typedef的boost :: MPL ::矢量<控制器:: QtOpenGL :: cQOpenGLContext> contextTypes;

不过比我有想法如何着手,因为我是很新的元编程的话题。


解决方案

  

    

更新因为我已经提供了一个更新的版本确实更符合您的问题(由调度客户端对象的并上下文) 。请参阅 住在Coliru


    
    

更新2 发布一个版本将类型擦除的组合获得一个虚拟接口,同时保留其他的好处。请参阅 住在Coliru 底部发布,以确保未来保留上左右。


  

我转换我的评论到答案,因为它提供了更多的空间来阐述。

我得到的IM pression你只是试图让多方法类似语义(具有多态行为依赖于多个对象类型,即函数)。

输入角色

对于本演示中,我会使用一些存根类型。假设3客户端对象类型 [1]

 型号命名空间{{ClientModel
     结构cClientVerticesObject:提高:: noncopyable进行{};
     结构cRawClientObject:提高:: noncopyable进行{};
     结构cFunkyClientObject:升压:: noncopyable进行{};
}}型号:: ClientModel :: cClientVerticesObject顶点;
型号:: ClientModel :: cRawClientObject生;
型号:: ClientModel :: cFunkyClientObject时髦的;

让我们来看看这些对象的故事如何展开:)


静态调度;的从最基本的开始

在这种情况下,我怀疑一个运行中的磨多态仿函数可能会更加的一点是:

 结构RenderClientObjects
{
    无效的typedef result_type的;    RenderClientObjects(控制器:: QtOpenGL :: cQOpenGLContext * glContext)
        :glContext(glContext)
    {}    模板< typename的ClientObject1,类型名ClientObject2>
        void运算符()(ClientObject1常量和放大器; clientObject1,ClientObject2常量和放大器; clientObject2)常量
        {
             //一些实施
        }私人的:
    控制器:: QtOpenGL :: cQOpenGLContext * glContext;
};

您可以使用此像任何calleable对象,依靠静态调度:

  RenderClientObjects多方法(安培; glContext);多方法(顶点,顶点);
多方法(顶点,时髦);
多方法(生的,顶点);
多方法(时髦,顶点);


运行调度:输入的boost ::变种

如何跳过问题//一些实施将被提供,让我向前跳转到这个优雅眼前一亮:可以的神奇在使用二进制运行时调度的boost ::变种

  ////////////////////////////////////// ///////////////////////////////
//变讯(升压apply_visitor的支持二进制分发)
TYPEDEF提振::变体LT;
        型号:: ClientModel :: cClientVerticesObject&放大器;,
        型号:: ClientModel :: cRawClientObject&放大器;,
        型号:: ClientModel :: cFunkyClientObject&安培;
    > ClientObjectVariant;无效variant_multimethod(控制器:: QtOpenGL :: cQOpenGLContext&放大器; CTX,ClientObjectVariant常量和放大器;一,ClientObjectVariant常量和b)
{
    提高:: apply_visitor的(RenderClientObjects(安培; CTX),A,B);
}

提供了(自定义)的实施

当然,你仍然要为那些重载实现。您可以


  • 供应重载明确

  • 委托到可以专门一个实现类(有时也被称为的自定义对焦点扩展点的或的用户自定义挂钩的等)

  • 的组合

全样本

下面是一个使用提到的所有方法和显示详尽的静态和运行时调度一个完整的样本。

看它的 住在Coliru

 的#include<升压/ variant.hpp>
#包括LT&;升压/ utility.hpp>
#包括LT&;&iostream的GT;//////存根
型号命名空间{{ClientModel
     结构cClientVerticesObject:提高:: noncopyable进行{};
     结构cRawClientObject:提高:: noncopyable进行{};
     结构cFunkyClientObject:升压:: noncopyable进行{};
}}
控制器命名空间{{QtOpenGL
    的typedef的std :: ostream的cQOpenGLContext;
}}
////// END存根////////////////////////////////////////////////// ///////////////////
//为什么不** **只是使它成为一个多态仿函数?
//
//你可以,如果你愿意用它的扩展点:
//(注意使用启用,使其SFINAE友好型)
命名空间UserTypeHooks
{
    模板< typename的ClientObject1,typename的ClientObject2,类型名称启用=无效>
    结构RenderClientObjectsImpl
    {
        无效静态调用(
                控制器:: QtOpenGL :: cQOpenGLContext * glContext,
                ClientObject1常量和放大器; clientObject1,
                ClientObject2常量和放大器; clientObject2)
        {
            (* glContext)LT;< __ preTTY_FUNCTION__<< \\ n;
        }
    };
}结构RenderClientObjects
{
    无效的typedef result_type的;    RenderClientObjects(控制器:: QtOpenGL :: cQOpenGLContext * glContext)
        :glContext(glContext)
    {}    //
    void运算符()(型号:: ClientModel :: cFunkyClientObject常量和放大器; clientObject1,型号:: ClientModel :: cFunkyClientObject常量和放大器; clientObject2)常量
    {
        (* glContext)LT;< 这两个对象是质朴\\ n;
    }    模板< typename的ClientObject2>
        void运算符()(型号:: ClientModel :: cFunkyClientObject常量和放大器; clientObject1,ClientObject2常量和放大器; clientObject2)常量
        {
            (* glContext)LT;< 参与质朴的对象(另一种是<< typeid的(clientObject2)。名称()<<)\\ n;
        }    模板< typename的ClientObject1>
        void运算符()(ClientObject1常量和放大器; clientObject1,型号:: ClientModel :: cFunkyClientObject常量和放大器; clientObject2)常量
        {
            (*此)(clientObject2,clientObject1); //代表实现中,例如
        }    //捕捉所有:
    模板< typename的ClientObject1,类型名ClientObject2>
        void运算符()(ClientObject1常量和放大器; clientObject1,ClientObject2常量和放大器; clientObject2)常量
        {
            返回UserTypeHooks :: RenderClientObjectsImpl< ClientObject1,ClientObject2> ::调用(glContext,clientObject1,clientObject2);
        }  私人的:
    控制器:: QtOpenGL :: cQOpenGLContext * glContext;
};////////////////////////////////////////////////// ///////////////////
//演示用户定义的扩展点机制:
命名空间UserTypeHooks
{
    模板< typename的ClientObject>
    结构RenderClientObjectsImpl< ClientObject,ClientObject>
    {
        无效静态调用(
                控制器:: QtOpenGL :: cQOpenGLContext * glContext,
                ClientObject常量和放大器; clientObject1,
                ClientObject常量和放大器; clientObject2)
        {
            (* glContext)LT;< 这两个对象是同一类型的(和不时髦):&所述;&下; typeid的(ClientObject)。名称()<< \\ n;
        }
    };
}////////////////////////////////////////////////// ///////////////////
//变讯(升压apply_visitor的支持二进制分发)
TYPEDEF提振::变体LT;
        型号:: ClientModel :: cClientVerticesObject&放大器;,
        型号:: ClientModel :: cRawClientObject&放大器;,
        型号:: ClientModel :: cFunkyClientObject&安培;
    > ClientObjectVariant;无效variant_multimethod(控制器:: QtOpenGL :: cQOpenGLContext&放大器; CTX,ClientObjectVariant常量和放大器;一,ClientObjectVariant常量和b)
{
    RenderClientObjects多元法(安培; CTX);
    提高:: apply_visitor的(多方法,A,B);
}诠释的main()
{
    控制器:: QtOpenGL :: cQOpenGLContext glContext(的std :: cout.rdbuf());
    RenderClientObjects多元法(安培; glContext);    型号:: ClientModel :: cClientVerticesObject顶点;
    型号:: ClientModel :: cRawClientObject生;
    型号:: ClientModel :: cFunkyClientObject时髦的;    glContext<< //完全静态调度:\\ N的;
    glContext<< // \\ N的;
    多方法(顶点,顶点);
    多方法(顶点,生);
    多方法(顶点,时髦);
    //
    多方法(生的,顶点);
    多方法(原料,生);
    多方法(原始,质朴);
    //
    多方法(时髦,顶点);
    多方法(质朴,原始的);
    多方法(时髦,时髦);    glContext<< \\ n;
    glContext<< //运行调度:\\ n;
    glContext<< // \\ N的;    variant_multimethod(glContext,顶点的顶点);
    variant_multimethod(glContext,顶点生);
    variant_multimethod(glContext,顶点,时髦);
    //
    variant_multimethod(glContext,原材料,顶点);
    variant_multimethod(glContext,生,生);
    variant_multimethod(glContext,原始,质朴);
    //
    variant_multimethod(glContext,质朴,顶点);
    variant_multimethod(glContext,质朴,原始的);
    variant_multimethod(glContext,时髦,时髦);
}

呵呵,完整性,这里的输出:

  G ++  -  4.8 -Os -Wall -pedantic的main.cpp和放大器;&安培; ./a.out | C ++ FILT -t
//完全静态调度:
//
两个对象是同一类型的(和不时髦):型号:: ClientModel :: cClientVerticesObject
静态无效UserTypeHooks :: RenderClientObjectsImpl< ClientObject1,ClientObject2,启用> ::调用(控制器:: QtOpenGL :: cQOpenGLContext *,常量ClientObject1和放大器;,常量ClientObject2&安培;)与ClientObject1 =型号:: ClientModel :: cClientVerticesObject; ClientObject2 =型号:: ClientModel :: cRawClientObject;启用=无效;控制器:: QtOpenGL :: cQOpenGLContext =的std :: basic_ostream<烧焦>]
参与质朴对象(另一种是型号:: ClientModel :: cClientVerticesObject)
静态无效UserTypeHooks :: RenderClientObjectsImpl< ClientObject1,ClientObject2,启用> ::调用(控制器:: QtOpenGL :: cQOpenGLContext *,常量ClientObject1和放大器;,常量ClientObject2&安培;)与ClientObject1 =型号:: ClientModel :: cRawClientObject; ClientObject2 =型号:: ClientModel :: cClientVerticesObject;启用=无效;控制器:: QtOpenGL :: cQOpenGLContext =的std :: basic_ostream<烧焦>]
两个对象是同一类型的(和不时髦):型号:: ClientModel :: cRawClientObject
参与质朴对象(另一种是型号:: ClientModel :: cRawClientObject)
参与质朴对象(另一种是型号:: ClientModel :: cClientVerticesObject)
参与质朴对象(另一种是型号:: ClientModel :: cRawClientObject)
两个对象都是质朴。//运行调度:
//
两个对象是同一类型的(和不时髦):型号:: ClientModel :: cClientVerticesObject
静态无效UserTypeHooks :: RenderClientObjectsImpl< ClientObject1,ClientObject2,启用> ::调用(控制器:: QtOpenGL :: cQOpenGLContext *,常量ClientObject1和放大器;,常量ClientObject2&安培;)与ClientObject1 =型号:: ClientModel :: cClientVerticesObject; ClientObject2 =型号:: ClientModel :: cRawClientObject;启用=无效;控制器:: QtOpenGL :: cQOpenGLContext =的std :: basic_ostream<烧焦>]
参与质朴对象(另一种是型号:: ClientModel :: cClientVerticesObject)
静态无效UserTypeHooks :: RenderClientObjectsImpl< ClientObject1,ClientObject2,启用> ::调用(控制器:: QtOpenGL :: cQOpenGLContext *,常量ClientObject1和放大器;,常量ClientObject2&安培;)与ClientObject1 =型号:: ClientModel :: cRawClientObject; ClientObject2 =型号:: ClientModel :: cClientVerticesObject;启用=无效;控制器:: QtOpenGL :: cQOpenGLContext =的std :: basic_ostream<烧焦>]
两个对象是同一类型的(和不时髦):型号:: ClientModel :: cRawClientObject
参与质朴对象(另一种是型号:: ClientModel :: cRawClientObject)
参与质朴对象(另一种是型号:: ClientModel :: cClientVerticesObject)
参与质朴对象(另一种是型号:: ClientModel :: cRawClientObject)
两个对象都是质朴。


的类型擦除接口版本

全部codeFOR'的更新2

 的#include<&所属类别GT;
#包括LT&;升压/ type_traits.hpp>
#包括LT&;&iostream的GT;//////存根
结构move_only {//明显提高:: noncopyable进行禁止动过
    move_only(move_only常量和放大器;)=删除;
    move_only(move_only&安培;&安培)=默认值;
    move_only()=默认值;
};型号命名空间{{ClientModel
     结构cClientVerticesObject:move_only {};
     结构cRawClientObject:move_only {};
     结构cFunkyClientObject:move_only {};
}}
命名空间控制器{
    命名空间QtOpenGL {
        结构cQOpenGLContext:move_only {};
    }
    结构cConsoleContext:move_only {};
    结构cDevNullContext:move_only {};
}命名空间特质
{
    模板< typename的T>结构supports_console_ctx:提振:: MPL :: false_ {};
    模板<>
        结构supports_console_ctx<型号:: ClientModel :: cFunkyClientObject> :提高:: MPL :: true_ {};
}
////// END存根////////////////////////////////////////////////// ///////////////////
//为什么不** **只是使它成为一个多态仿函数?
//
//你可以,如果你愿意用它的扩展点:
//(注意使用启用,使其SFINAE友好型)
命名空间UserTypeHooks
{
    模板< typename的ClientObject,typename的背景下,类型名称启用=无效>
    结构RenderClientObjectsImpl
    {
        无效静态调用(ClientObject常量和放大器; clientObject,上下文常量和放大器;上下文)
        {
            // static_assert(假的,没有实现);
            //扔?
            性病::法院LT&;< 尚未实现:\\ t的<< __ preTTY_FUNCTION__<< \\ n;
        }
    };    模板< typename的ClientObject>
        结构RenderClientObjectsImpl< ClientObject,控制器:: QtOpenGL :: cQOpenGLContext>
    {
        无效静态调用(ClientObject常量和放大器; clientObject,控制器:: QtOpenGL :: cQOpenGLContext常量和放大器;上下文)
        {
            性病::法院LT&;< cQOpenGLContext:\\ t的<< typeid的(ClientObject)。名称()<< \\ n;
        }
    };    模板< typename的ClientObject>
        结构RenderClientObjectsImpl< ClientObject,控制器:: cDevNullContext>
    {
        无效静态调用(ClientObject常量和放大器; clientObject,控制器:: cDevNullContext常量和放大器;上下文)
        {
            性病::法院LT&;< devnull:\\ t \\ t的<< typeid的(ClientObject)。名称()<< \\ n;
        }
    };
}结构RenderClientObjects
{
    无效的typedef result_type的;    模板< typename的ClientObject,typename的语境GT;
        void运算符()(ClientObject常量和放大器; clientObject,上下文常量和放大器;上下文)常量
        {
            返回UserTypeHooks :: RenderClientObjectsImpl< ClientObject,语境与GT; ::调用(clientObject,背景);
        }
};////////////////////////////////////////////////// ///////////////////
//演示用户定义的扩展点机制:
命名空间UserTypeHooks
{
    模板< typename的ClientObject>
    结构RenderClientObjectsImpl< ClientObject,控制器:: cConsoleContext,
        TYPENAME的boost :: enable_if<性状:: supports_console_ctx< ClientObject> > ::类型>
    {
        无效静态调用(
                ClientObject常量和放大器; clientObject,
                控制器:: cConsoleContext常量和放大器;上下文)
        {
            性病::法院LT&;< 这种类型的有cConsoleContext支持由于supports_console_ctx特质!<< typeid的(ClientObject)。名称()<< \\ n;
        }
    };
}////////////////////////////////////////////////// ///////////////////
//补充:动态界面
//
//使这一更复杂一点比你可能需要,但嘿,假设
//最差:
#包括LT&;内存和GT;结构IPolymorphicRenderable
{
    //你可能只需要其中的一个,并且可能并不需要
    //虚拟
    虚拟无效渲染(控制器:: QtOpenGL :: cQOpenGLContext&安培; CTX)= 0;
    虚拟无效渲染(控制器:: cConsoleContext&安培; CTX)= 0;
    虚拟无效渲染(控制器:: cDevNullContext&安培; CTX)= 0;
};结构IClientObject:IPolymorphicRenderable
{
    模板< typename的T> IClientObject(T&安培;&放大器; VAL):_erased(新擦除< T>(的std ::向前< T>(VAL))){}    虚拟无效渲染(控制器:: QtOpenGL :: cQOpenGLContext&安培; CTX){返回_erased->渲染(CTX); }
    虚拟无效渲染(控制器:: cConsoleContext&安培; CTX){返回_erased->渲染(CTX); }
    虚拟无效渲染(控制器:: cDevNullContext&安培; CTX){返回_erased->渲染(CTX); }  私人的:
    模板< typename的T>结构擦除:IPolymorphicRenderable
    {
        擦除(T VAL):_val(性病::移动(VAL)){}        无效渲染(控制器:: QtOpenGL :: cQOpenGLContext&安培; CTX){返回RenderClientObjects()(_ VAL,CTX); }
        无效渲染(控制器:: cConsoleContext&安培; CTX){返回RenderClientObjects()(_ VAL,CTX); }
        无效渲染(控制器:: cDevNullContext&安培; CTX){返回RenderClientObjects()(_ VAL,CTX); }        Ť_val;
    };    的std ::的unique_ptr< IPolymorphicRenderable> _erased;
};诠释的main()
{
    控制器:: QtOpenGL :: cQOpenGLContext glContext;
    控制器:: cConsoleContext控制台;
    控制器:: cDevNullContext devnull;    性病::法院LT&;< //完全虚拟调度\\ N的;
    性病::法院LT&;< // \\ N的;    IClientObject的obj =型号:: ClientModel :: cClientVerticesObject();
    obj.render(glContext);
    obj.render(控制台);
    obj.render(devnull);
    //
    OBJ =型号:: ClientModel :: cRawClientObject();
    obj.render(glContext);
    obj.render(控制台);
    obj.render(devnull);
    //
    OBJ =型号:: ClientModel :: cFunkyClientObject();
    obj.render(glContext);
    obj.render(控制台);
    obj.render(devnull);
}

输出:

 铛++ -std = C ++ 11 -Os -Wall -pedantic的main.cpp和放大器;&安培; ./a.out
//完全虚拟调度
//
cQOpenGLContext:N5Model11ClientModel21cClientVerticesObjectE
尚未实现:静态无效UserTypeHooks :: RenderClientObjectsImpl<型号:: ClientModel :: cClientVerticesObject,控制器:: cConsoleContext,无效> ::调用(常量ClientObject和放大器;,常量语境和放大器;)[ClientObject =型号:: ClientModel :: cClientVerticesObject,上下文=控制器:: cConsoleContext,启用=无效]
devnull:N5Model11ClientModel21cClientVerticesObjectE
cQOpenGLContext:N5Model11ClientModel16cRawClientObjectE
尚未实现:静态无效UserTypeHooks :: RenderClientObjectsImpl<型号:: ClientModel :: cRawClientObject,控制器:: cConsoleContext,无效> ::调用(常量ClientObject和放大器;,常量语境和放大器;)[ClientObject =型号:: ClientModel :: cRawClientObject,上下文=控制器:: cConsoleContext,启用=无效]
devnull:N5Model11ClientModel16cRawClientObjectE
cQOpenGLContext:N5Model11ClientModel18cFunkyClientObjectE
该产品具有cConsoleContext支持由于supports_console_ctx特质! N5Model11ClientModel18cFunkyClientObjectE
devnull:N5Model11ClientModel18cFunkyClientObjectE

[1] 我确信,客户对象不是假设此示例可拷贝,但事实上,你可能想使用 ClientObjectVariant 作为值类型更全库的的)

I have a class called caRender, which provides one caRender::renderClientObject() method per given object type in clientObjectTypes. So the following code snipped shows this 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: I want the parameter abstraction also for my second parameter (context) inside the renderClientObject method. So the goal is to get n*m generated renderClientObject methods, where n is defined as the number of clientObjectTypes and m is the number of context types. I would add a second vector:

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

But than i have to idea how to proceed, because i am very new to the topic of meta programming.

解决方案

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.

I'm converting my comment into an answer because it provides more space to elaborate.

I get the impression you're "just" trying to get multi-method like semantics (i.e. functions which have polymorphic behavior that depends on more than one object type).

Enter the actors

For this demonstration I'll use some 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 the story of these objects unfolds :)


Static Dispatch; Starting with the basics

In that case I suspect that a run-of-the-mill polymorphic functor could be much more to the 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
        {
             // some implementation
        }

private: 
    Controller::QtOpenGL::cQOpenGLContext* glContext;
};

You can use this like any 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 the question of how // some implementation will be provided, let me jump ahead to where the elegance of this shines: you 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, you will still want to provide implementations for those overloads. You can

  • supply explicit overloads
  • delegate to an implementation class that can be specialized (sometimes known as customization point, extension point or userdefined hook etc.)
  • combinations of that

Full Sample

Here's a full sample using all the approaches mentioned and showing exhaustive static and runtime dispatch.

See it 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);
}

Oh, for completeness, here's the output:

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.


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

Output:

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] (I've made sure that the client objects are not assumed to be copyable for this sample, but in fact, you might want to use ClientObjectVariant as the value type throughout more of your library)

这篇关于提高:: MPL ::折叠双参数抽象的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆