分类 技术志 下的文章

iterator的C实现

这几天一直处于编码状态,也找回了一些对代码的良好感觉。

昨天晚上闲暇时翻阅“Head First设计模式”,当翻到迭代器模式时,突然有了想法:实现一个iterator。这几天编码时恰好也写了一个简单的带有遍历功能的小数据结构,不妨用iterator改造一下这个数据结构的遍历接口,看是否能成行。

迭代器模式较为简单,网上的文章也多得很,这里就不再贽述了,直接看实现思路和代码吧。

在迭代器模式中,有几个角色不得不说,一就是iterator本身,还有一个就是所谓的“容器类”,容器类三个字显得概念很大,这里不用之。我们换一种轻松的说法,就是带有遍历功能的一类数据结构吧,这里记为类型T。类型T千变万化,iterator则以不变应万变,提供一种在无需知道T内部实现行为前提下的对T实例内各元素的有序遍历的接口。

这样一来iterator的应用场景大致就应该是这样的:

T     *t;
T_item_t *ti;

// …
iterator *itor = T_iterator(t);   
       
for ( ; has_next(itor); ) {
        ti = (T_item_t*)get_next(itor);
        // …
}

iterator_free(itor);

这里我们使用了四个函数has_next, get_next、iterator_free和T_iterator。其中has_next、get_next和iterator_free是iterator提供的函数接口,T_iterator则是需要每个支持iterator的数据结构去实现的接口。

以下是iterator.h的主要代码片段:

/* iterator.h */

#ifndef _ITERATOR_H_
#define _ITERATOR_H_

enum {
        FALSE = 0,
        TRUE  = 1
};
typedef int bool; /* TRUE or FALSE */

typedef struct iterator iterator;

typedef bool (*HAS_NEXT_HOOK_FUNC)(void *collection_instance, void *collection_inner_itor);
typedef void* (*GET_NEXT_HOOK_FUNC)(void *collection_instance, void *collection_inner_itor);

/* 提供给T类型实现T_interator时使用 */
iterator*       iterator_new(void *collection_instance,
                             void *collection_inner_itor,
                             HAS_NEXT_HOOK_FUNC h,
                             GET_NEXT_HOOK_FUNC g);

bool            has_next(iterator *itor);
void*           get_next(iterator *itor);
void            iterator_free(iterator *itor);

#endif /* _ITERATOR_H_ */

iterator_new接口主要供类型T实现T_iterator接口时使用,目的是对类型T屏蔽iterator的内部结构实现;collection_instance指向T的实例;collection_inner_itor则是类型T内部实现的一个保存iterator状态的变量,每个类型T都应该有这样一个内部数据;两个callback函数则分别由类型T内部实现,在T_iterator实现中调用iterator_new时传入。iterator对T有一定的侵入性,这在C语言中似乎是不可避免的,即使在Java中支持iterator接口的类也需要提供一个creatIterator的public接口,另外实现iterator接口的iterator类也需要提供具体的get_next和has_next实现。

以下则是iterator.c的代码,没有太多值得多说的地方,相信大家都可以看懂^_^:

/* iterator.c */
struct iterator {
        void                    *collection_instance;
        void                    *collection_inner_itor;
        HAS_NEXT_HOOK_FUNC      _has_next;
        GET_NEXT_HOOK_FUNC      _get_next;
};

iterator*       iterator_new( void *collection_instance,
                void *collection_inner_itor,
                HAS_NEXT_HOOK_FUNC h,
                GET_NEXT_HOOK_FUNC g) {
    iterator        *itor   = NULL;

    itor    = calloc(1, sizeof(*itor));
    if (!itor) return NULL;

    itor->collection_instance       = collection_instance;
    itor->collection_inner_itor     = collection_inner_itor;
    itor->_has_next = h;
    itor->_get_next = g;

    return itor;
}

void    iterator_free(iterator *itor) {
        if (itor) {
                if (itor->collection_inner_itor) free(itor->collection_inner_itor);
                free(itor);
        }
}

bool    has_next(iterator *itor) {
        return itor->_has_next(itor->collection_instance, itor->collection_inner_itor);
}

void*   get_next(iterator *itor) {
        return itor->_get_next(itor->collection_instance, itor->collection_inner_itor);
}

有了iterator,我们再举一个支持iterator遍历的list的例子,这里就不列出全部代码了,仅将关键的接口实现放出:

/* x_list.c */
struct x_list_inner_itor {
        x_list_item_t *item;
};

static bool x_list_has_next(void *collection_instance, void *collection_inner_itor) {
        x_list_t *list = (x_list_t*) collection_instance;
        struct x_list_inner_itor *p = (struct x_list_inner_itor *)collection_inner_itor;
        x_list_item_t *item = p->item;

        return (X_LIST_NEXT(item) != X_LIST_DUMMY_HEAD(list));
}

static void* list_get_next(void *collection_instance, void *collection_inner_itor) {
        struct x_list_inner_itor *p = (struct x_list_inner_itor *)collection_inner_itor;
        x_list_item_t *item = p->item;

        p->item = X_LIST_NEXT(item);

        return p->item;
}

iterator* x_list_iterator(x_list_t *list) {
        struct x_list_inner_itor *p = NULL;
        p = calloc(sizeof(*p), 1);
        if (!p) return NULL;

        p->item = X_LIST_DUMMY_HEAD(list);

        return iterator_new(list,
                (void*)p,
                x_list_has_next,
                x_list_get_next);              
}

以上只是提供了iterator的C实现的一种思路,大家见仁见智吧。

Thunderbird跨平台共享邮件设置

对于我个人来说,将工作环境切换到Ubuntu上来有几个“坎儿”要迈过,其中最为迫切的一个就是Mail如何在Windows和Linux下共享的问题,今天我找到了解决方法。

Thunderbird和Firefox一样,都来自Mozilla组织。和Outlook等软件不同的是,Thunderbird是可以跨平台的,更有甚者,Thunderbird可以帮助我们在Windows和Linux共享邮件,当然需要作简单设置。

我的机器上安装了三系统:WinXP、Ubuntu 9.10Slax,其中常用的是WinXP和Ubuntu。在Windows上最常用的Mail Client端软件为Outlook 2003,但Outlook是收费软件,不支持夸平台,更谈不上Mail跨平台共享。而这些恰恰是Thunderbird吸引眼球之所在。

所谓的跨平台共享邮件是指计算机上只存储一份Mail Data,而你在该计算机上所安装的不同OS上都有某个特定应用可以正确存取该数据文件,这样带来的方便是很大的。Thunderbird就是这样的一个特定应用,它可以被安装在不同的OS上,并且可以在不同OS上运行并正确存取同一份Mail Data文件。

以我自己的本子为例,记录一下如何进行共享邮件设置:
首先,分别在Windows和Ubuntu下面下载并安装最新中文版本的Thunderbird(目前是2.0.0.23)。
在某个OS上运行并配置Thunderbird,这里我是在Windows上进行配置的。这里有一个注意事项,那就是你的mail数据的存储位置的选择。在“帐号配置”的“服务器”配置标签中,找到“本地目录”配置项,这个配置项将决定了Thunderbird将Mail Data存储在什么位置。如果你要在Windows和Ubuntu之间共享这个Mail Data,那么就需要让Windows和Ubuntu都能看得到、访问得到这个文件。我这里选择在一个FAT32分区上存储Mail数据。

配置完Mail帐户,Thunderbird默认会在C:\Documents and Settings\[username]\Application Data\Thunderbird\Profiles\xxxxxxxx.default中存储该帐户的配置信息。为了能在两个OS间共享Mail Data,我们就需要让这个配置在两个OS上都能发挥作用。

我们将该配置文件移动到一个FAT32分区上,比如这里以E:\Thunderbird_profile为例,将xxxxxxxx.default移动到E:\Thunderbird_profile目录下。这时你要是启动Thunderbird,那么Thunderbird就无法找到该配置文件了。但是我们需要Thunderbird找到并读取该配置文件,怎么做呢?我们需要修改另外一个文件,在Windows上这个文件就是C:\Documents and Settings\[username]\Application Data\Thunderbird\profiles.ini。

初始的profiles.ini文件内容如下:
[General]
StartWithLastProfile=1

[Profile0]
Name=default
IsRelative=1
Path=/Profiles/xxxxxxxx.default

其中IsRelative用来指示Path这个字段配置的是相对路径还是绝对路径,默认是相对路径。由于我们已经将配置数据移动到E:\Thunderbird_profile目录下了,那么我们就不能在使用相对路径了。修改结果如下:
[General]
StartWithLastProfile=1

[Profile0]
Name=default
IsRelative=0
Path=E:\Thunderbird_profile\xxxxxxxxx.default

启动Windows上的Thunderbird,该配置被正确读取,收发Mail正常。

切换到Ubuntu下面,同样道理,你需要修改profiles.ini文件,该文件在~/.mozilla-thunderbird下面,修改方式与Windows上相同。不同的是Path字段要配置成Linux格式的路径。

启动Ubuntu下的Thunderbird,哇,一切与Windows上的Thunderbird一模一样。你可以看到全部的在Windows上收到和发送出去的那些邮件。

共享Mail设置成功!

后记:你在ubuntu下按上面所描述的完成配置后启动thunderbird,如果你得到的是一个对话框:"Thunderbird is already running, but not responding. To open a new
window you must close the existing thunderbird process, or restart your
system",这种情况多是因为你的Windows分区没有被自动挂载。我在Ubuntu 9.10和10.04两个系统上都遇到了这种情况。简单处理方法:进入计算机,双击Mail Data和Mail配置所在的那个Windows FAT32分区逻辑盘,Ubuntu会自动将之挂载。其后你再启动Thunderbird,一切就OK了。这种登录后手动挂载的方法分区挂载点为/media/xxx,其中xxx是一个类似UUID的字符串,不那么具有可读性。如果你想通过设置fstab来让Ubuntu在启动后就自动挂载分区,那你可要小心了,自动挂载的分区下的文件和目录的owner和group都是root,这样你正常启动thunderbird依旧会得到那个对话框。你可以尝试使用sudo thunderbird启动; 或改变文件和目录的owner和group;或钻研一下fstab的设置,以达到自动挂载后就有普通用户访问权限。另外自动挂载还涉及到字符编码问题,稍有不慎你就会看到被挂载分区内大量以中文乱码为名字的文件。

如发现本站页面被黑,比如:挂载广告、挖矿等恶意代码,请朋友们及时联系我。十分感谢! Go语言第一课 Go语言进阶课 Go语言精进之路1 Go语言精进之路2 Go语言第一课 Go语言编程指南
商务合作请联系bigwhite.cn AT aliyun.com

欢迎使用邮件订阅我的博客

输入邮箱订阅本站,只要有新文章发布,就会第一时间发送邮件通知你哦!

这里是 Tony Bai的个人Blog,欢迎访问、订阅和留言! 订阅Feed请点击上面图片

如果您觉得这里的文章对您有帮助,请扫描上方二维码进行捐赠 ,加油后的Tony Bai将会为您呈现更多精彩的文章,谢谢!

如果您希望通过微信捐赠,请用微信客户端扫描下方赞赏码:

如果您希望通过比特币或以太币捐赠,可以扫描下方二维码:

比特币:

以太币:

如果您喜欢通过微信浏览本站内容,可以扫描下方二维码,订阅本站官方微信订阅号“iamtonybai”;点击二维码,可直达本人官方微博主页^_^:
本站Powered by Digital Ocean VPS。
选择Digital Ocean VPS主机,即可获得10美元现金充值,可 免费使用两个月哟! 著名主机提供商Linode 10$优惠码:linode10,在 这里注册即可免费获 得。阿里云推荐码: 1WFZ0V立享9折!


View Tony Bai's profile on LinkedIn
DigitalOcean Referral Badge

文章

评论

  • 正在加载...

分类

标签

归档



View My Stats