首页 插件 自己动手写插件框架(9)

自己动手写插件框架(9)

0 1

ActorInfoContainer用于管理ActorInfo对象集合。它实现了 C++ 接口IActorInfoIterator,在其内部保存集合索引。当调用 next() 的时候,返回该索引位置的对象;如果索引大于集合元素数目,则返回 NULL。当调用reset()时,则将索引置 0。当然,索引的初始化为 0。

struct ActorInfoContainer :
    IActorInfoIterator,
    C_ActorInfoIterator
{
    ...
    ActorInfoContainer() : index(0)
    {
        ...
    }

    void reset()
    {
        index = 0;
    }

    ActorInfo * next()
    {
        if (index %gt;= vec.size())
            return NULL;
        return vec[index++];
    }

    apr_uint32_t index;
    std::vector<ActorInfo *> vec;
};

C_ActorInfoIterator实现了 C 接口。在构造函数中,将reset_()next_()static 函数赋给父结构的resetnext函数指针。它也要将this赋值为该句柄:

static void reset_(C_ActorInfoIteratorHandle handle)
{
    ActorInfoContainer * aic = reinterpret_cast<ActorInfoContainer *>(handle);
    aic->reset();
}

static C_ActorInfo * next_(C_ActorInfoIteratorHandle handle)
{
    ActorInfoContainer * aic = reinterpret_cast<ActorInfoContainer *>(handle);
    return aic->next();
}

ActorInfoContainer() : index(0)
{
    C_ActorInfoIterator::handle = (C_ActorInfoIteratorHandle)this;
    C_ActorInfoIterator::reset = reset_;
    C_ActorInfoIterator::next = next_;
}

现在,让我们揭开支持双对象的内部秘密!双对象模型的真实实现的关键都是在双对象的 C++ 部分。 C 函数指针始终指向 C++ 对象的 static 函数,这些函数实际是将真正的操作委托给的 C++ 接口。这正是最有技巧的一步。虽然 C 和 C++ 接口都是ActorInfoContainer的父接口,但是在 C++ 中,并没有好的办法能够从一个基类转移至另一个基类。为了实现这一点,static 的 C 函数需要访问ActorInfoContainer接口(也就是子类)。这就是技巧所在。每一个 static 的 C 函数都将句柄转换成ActorInfoContainer指针(使用reinterpret_cast),然后调用相应的 C++ 函数。reset()函数没有参数,没有返回值。next()函数没有参数,返回ActorInfo指针,该指针对于 C 和 C++ 接口是相同的。当需要在两种对象之间转换时,我们会遇到一点儿困难。该对象需要实现 ITurnC++ 接口和C_TurnC 接口:

struct ITurn
{
    virtual ActorInfo * getSelfInfo() = 0;
    virtual IActorInfoIterator * getFriends() = 0;
    virtual IActorInfoIterator * getFoes() = 0;
    virtual void move(apr_uint32_t x, apr_uint32_t y) = 0;
    virtual void attack(apr_uint32_t id) = 0;
};

typedef struct C_TurnHandle_ { char c; } * C_TurnHandle;
typedef struct C_Turn_
{
    C_ActorInfo * (*getSelfInfo)(C_TurnHandle handle);
    C_ActorInfoIterator * (*getFriends)(C_TurnHandle handle);
    C_ActorInfoIterator * (*getFoes)(C_TurnHandle handle);
    void (*move)(C_TurnHandle handle, apr_uint32_t x, apr_uint32_t y);
    void (*attack)(C_TurnHandle handle, apr_uint32_t id);
    C_TurnHandle handle;
} C_Turn;

Turn对象同ActorInfoContainer类似,有一个 static C 函数,同构造函数中C_Turn接口的函数指针相关联,并将底层实现委托给 C++ 函数。下面再来看看getFriends()函数。该函数在 C++ 的ITurn接口中返回IActorInfoIterator,在C_Turn返回C_ActorInfoIterator。也就是返回值不同。static 的getFriends_()函数不能直接返回getFriends()的结果,因为IActorInfoIterator指针不能通过reinterpret_cast或者 C 转换为C_ActorInfoIterator。这是由于C_Turn基类的偏移不同。解决方法是使用一些内部信息。ITurn::getFriends()的结果确实是IActorInfoIterator,但它返回的是ActorInfoContainer,这个类即实现了IActorInfoIterator又实现了C_ActorInfoIterator

为了将IActorInfoIterator转换成C_ActorInfoIteratorgetFriends_()将其向上转型为ActorInfoContainer(通过 static_cast<ActorInfoContainer>)。一旦有了ActorInfoContainer实例,我们就可以得到C_ActorInfoIterator。这是现阶段最好的实现方式。

我们的核心思想史,全部对象模型都同时实现 C/C++ 接口,因此就可以即使用 C 接口,又使用 C++ 接口(利用某些转换技巧)。当然,我们也可以使用基本类型和 类似ActorInfo的 C 结构。注意,这些实际在应用程序对象模型的实现内部都是安全的。应用程序剩余的代码以及插件代码都不需要关心多种语言实现的多继承、复杂的类型转换以及利用子类切换接口等。这种设计够复杂的,但是一旦你设计实现完成,那么在以后的编码过程中就不需要太关心这一点了。

不过,现在我们还没完成,只是快要完成了。我是说,IActorC_Actor还没有涉及到。这些接口用于表示真实的插件对象,这些对象是由PF_CreateFunc创建的。我们并没有一个 Actor对象同时实现了IActorC_Actor

struct IActor
{
    virtual ~IActor() {}
    virtual void getInitialInfo(ActorInfo * info) = 0;
    virtual void play(ITurn * turnInfo) = 0;
};

typedef struct C_ActorHandle_ { char c; } * C_ActorHandle;
typedef struct C_Actor_
{
    void (*getInitialInfo)(C_ActorHandle handle, C_ActorInfo * info);
    void (*play)(C_ActorHandle handle, C_Turn * turn);
    C_ActorHandle handle;
} C_Actor;

同时实现了IActorC_Actor的对象由插件提供。它们不是应用程序对象模型的一部分。它们是用户定义的业务对象,其接口在应用程序的业务对象模型头文件中进行定义(object_model.h 和 c_object_model.h)。每个插件对象要么实现 C++IActor接口,要么实现 CC_Actor接口(它们都由PluginManager进行注册)。PluginManager会把实现了C_Actor接口的 C 对象适配成基于IActor的 C++ 对象。

本章到此为止。在下面的内容中,我们将实际开始代码的编写,开始我们的简单的程序及其插件。

发表评论

关于我

devbean

devbean

豆子,生于山东,定居南京。毕业于山东大学软件工程专业。软件工程师,主要关注于 Qt、Angular 等界面技术。

主题 Salodad 由 PenciDesign 提供 | 静态文件存储由又拍云存储提供 | 苏ICP备13027999号-2