如果要 typedef,搞出来的都是些很恶心的名字,自己看了也生气,还是不搞好了,就 size_t 和 int 玩到底吧。
把 Array 的接口又改得一塌糊涂了,重新贴一下:
namespace xl
{
template <typename ValueType>
class Array
{
public:
Array(size_t nSize = 0);
Array(size_t nSize, const ValueType &tValue);
Array(const Array<ValueType> &that);
~Array();
public:
class Iterator
{
public:
Iterator();
Iterator(ValueType *pValue);
Iterator(ValueType *pValue, ValueType *pStart, ValueType *pEof);
Iterator(const Iterator &that);
private:
ValueType *m_pStart;
ValueType *m_pEof;
ValueType *m_pCurrent;
public:
ValueType &operator * ();
ValueType *operator -> ();
public:
Iterator &operator = (const Iterator &that);
bool operator == (const Iterator &that) const;
bool operator != (const Iterator &that) const;
public:
Iterator &operator ++ ();
Iterator operator ++ (int);
Iterator &operator -- ();
Iterator operator -- (int);
public:
Iterator operator +(int nDistance) const;
Iterator operator -(int nDistance) const;
Iterator &operator +=(int nDistance);
Iterator &operator -=(int nDistance);
};
class ReverseIterator : public Iterator
{
public:
ReverseIterator &operator ++ ();
ReverseIterator operator ++ (int);
ReverseIterator &operator -- ();
ReverseIterator operator -- (int);
};
public:
Iterator Begin() const;
Iterator End() const;
ReverseIterator RBegin() const;
ReverseIterator REnd() const;
public:
Array<ValueType> &operator=(const Array<ValueType> &that);
bool operator==(const Array<ValueType> &that) const;
bool operator!=(const Array<ValueType> &that) const;
public:
ValueType &operator[](size_t nIndex);
const ValueType &operator[](size_t nIndex) const;
public:
bool Empty();
size_t Size() const;
void SetSize(size_t nSize);
public:
void Insert(const Iterator &itBeforeWhich, const ValueType &tValue);
void Insert(const ReverseIterator &itBeforeWhich, const ValueType &tValue);
void PushFront(const ValueType &tValue);
void PushBack(const ValueType &tValue);
template <typename IteratorType>
void Insert(const Iterator &itBeforeWhich, const IteratorType &itFirstToInsert, const IteratorType &itAfterLastToInsert);
template <typename IteratorType>
void Insert(const ReverseIterator &itBeforeWhich, const IteratorType &itFirstToInsert, const IteratorType &itAfterLastToInsert);
Iterator Delete(const Iterator &itWhich);
ReverseIterator Delete(const ReverseIterator &itWhich);
void PopFront();
void PopBack();
Iterator Delete(const Iterator &itFirstToInsert, const Iterator &itAfterLastToDelete);
Iterator Delete(const ReverseIterator &itFirstToInsert, const ReverseIterator &itAfterLastToDelete);
void Clear();
void SetValue(const Iterator &itWhich, const ValueType &tValue);
void SetValue(const ReverseIterator &itWhich, const ValueType &tValue);
void SetValue(const Iterator &itFirstToSet, const Iterator &itAfterLastToSet, const ValueType &tValue);
void SetValue(const ReverseIterator &itFirstToSet, const ReverseIterator &itAfterLastToSet, const ValueType &tValue);
private:
ValueType *m_pData;
size_t m_nSize;
size_t m_nStart;
size_t m_nEof;
private:
void Release();
size_t GetWellSize(size_t nSize) const;
void MoveData(size_t nIndex, size_t nCount, int nDistance);
void CopyData(size_t nIndex, size_t nCount, ValueType *pNewMem) const;
};
}
主要的考虑,还是想实现“跨容器的 iterator”(抱歉,我还是觉得这么称呼挺符合我的预期的)。只是,在没有语言层面的 concepts 支持的情况下,如何显式地让用户知道模板参数要符合哪些条件呢?
在这里再次感谢一下 OwnWaterloo 同学,上篇评论里的东西我会继续慢慢琢磨的。^_^
posted @
2009-09-28 23:13 溪流 阅读(740) |
评论 (18) |
编辑 收藏
摘要: 打算先把基础的东西组织起来,作为一个“大库”(见上一篇《小库还是大库?》)。然后填加各个实用功能,作为一个个小库或者大库,过段时间再想办法组织整理了。 首先是类型系统,我想来想去,觉得还是必须整理一下,尤其是 unsigned XXX 写起来太难看了,可是这又带来了问题——WinDef.h 把好听的名字都占去了。然后,我只能在自己的命名空间下这样来一番了: xlDef.h #ifndef ...
阅读全文
posted @
2009-09-26 17:43 溪流 阅读(660) |
评论 (18) |
编辑 收藏
我不知道这里有多少朋友是积累了自己的一套库的。
嗯……说明白点,比如想读写文件了,操作系统 API 的那堆参数我记不住,也用不着那么灵活,于是自己写一个自己记得住的,下次碰到同样情况继续用,并不断完善。哪天想读写注册表了,想读写 INI 文件了,想读写 XML 文件了,想用个动态数组了,想要个链表、树了,可能都会形成自己的一套东西。这套东西可能是基于已有的第三方库,也可能是纯粹自己一点一滴写起来的。好了,我想现在我大概表达得够明白了,这就是我说的“库”,这个库可能不是非常完备,但起码是自己积累的,有着(起码对自己来说)友好接口的东东。
可能有朋友会说,你要自己的动态数组、链表干吗?STL 很好啊!你要读写文件的干吗?CFile 哪里不好?你要读 INI?不是有 API 吗?……诸如此类。如果有朋友持这样的观点,我想我们是不同的一类人。如果您只是能完成某项任务就好,那么确实,不需要这些玩意儿。但是,如果哪一天这种普通的工作做得麻木了,来思考一下另一个层面的事情,您也许会觉得这些也是比较有意思的事。废话到此。
那么,不知道这些库,是以什么形式存在的呢?稍微极端开来讲,可能有两个做法——
第一种做法。我每写成一个功能模块,都是一个(或几个).h、一个(或几个).cpp,它们是自我独立的,不依赖于任何别的东西(或者不依赖标准库以外的东西、不依赖于操作系统 API 以外的东西)——总之是不依赖于当前编译系统以外的东西。以后需要使用,就把那几个文件拷到当前项目来使用。然后一个个这样的互不依赖的功能模块构成了我现在所拥有的库。
第二种做法呢,就是我把这个库作系统的规划,划分为很多小的功能模块,这些功能模块可能会彼此依赖,当库庞大以后,甚至连你自己都该不太清楚谁依赖谁了。要使用这个库的功能,就必须把整个库拿进来。到最后,我将这整套东西编译为一个 .lib,这个 .lib 的源程序会一直维护下去。但使用的时候,我就拿编译好的 .lib 来用。
前一种做法就是标题里所说的小库,后一种做法我称之为大库。我的问题是,作为个人的积累,小库好还是大库好?如果可能,我是比较喜欢小库的。但是,经常会有这样的问题,各个功能模块中可能会涉及同一个基础功能,而这个基础功能我已经做过了的,到底是用还是不用?如果用,“互不依赖”就会被打破,最终会发展成一个凌乱的大库;如果不用,我必须把代码抄一遍,那么这两份完全一样的代码在以后同步更新就比较麻烦了。再说大库,一个规划的很好的大库也是不错。但是前期积累的时候,往往没法规划;就算等到有一定的积累了以后再来积累,也会在模块组织上犹豫不决:我到底要不要来一个统一的 typedef 作为我的类型系统?当我实现了 MyVector,MyString 以后,我的后续代码势必都会使用它们,那么与别人之间的代码交流就成了问题了。
我最近一直困惑于这个问题。而我本人对此的理解也就如上文所述。希望有朋友指教、赐教。谢谢~~!
posted @
2009-09-24 21:30 溪流 阅读(658) |
评论 (2) |
编辑 收藏
前几天公司里一个项目要做 MUI 支持,于是要生成一堆 XXX.dll.mui 的文件。如果这些 MUI DLL 的工程手动去建立、维护的话,那就太!@#@!#!了。当时是另外一个同事去做这方面的工作的,后来他给了个工具,按照它定义的简单格式来书写多语言字符串,这个工具会从一个已经设定好的 DLL 项目出发,更改 RC 文件里的字符串,然后调用 VS 的 IDE 来生成 DLL。再然后调用 MUIRCT.exe 来生成 MUI 文件。
这可以节省很多时间。但是,由于是调用 VS IDE 来编译的,一个带有近百个 Project 的 Solution 编译起来并不快,需要一到两分钟。这让我有了另辟蹊径的念头。
何不自己来“编译”生成 DLL 呢?
不错,后来我就往这个方向琢磨了。之前曾写过一个修改 PE 文件版本号的小工具,所以现在对于 PE 的资源格式有点并不那么恐惧了。但是,往细处做下去,问题就来了。现在网上的关于 PE 格式的文章,对 NTHeader 解释得很详细,而资源段往往只讲到资源目录、资源项,具体各项的存储结构却没有详细说明了。
这里,关于 PE 头等就不多说了,请参考网上的文章,特别是 http://bbs.pediy.com/showthread.php?threadid=21932。本文将着眼于资源段。
首先来看一下几个数据结构(这些内容好多文章也有提及):
typedef struct _IMAGE_RESOURCE_DIRECTORY {
DWORD Characteristics;
DWORD TimeDateStamp;
WORD MajorVersion;
WORD MinorVersion;
WORD NumberOfNamedEntries;
WORD NumberOfIdEntries;
} IMAGE_RESOURCE_DIRECTORY, *PIMAGE_RESOURCE_DIRECTORY;
这是资源目录,共 16 字节,其中最后两个 WORD 加起来是紧跟在后面的子项的数目。
typedef struct _IMAGE_RESOURCE_DIRECTORY_ENTRY {
union {
struct {
DWORD NameOffset:31;
DWORD NameIsString:1;
};
DWORD Name;
WORD Id;
};
union {
DWORD OffsetToData;
struct {
DWORD OffsetToDirectory:31;
DWORD DataIsDirectory:1;
};
};
} IMAGE_RESOURCE_DIRECTORY_ENTRY, *PIMAGE_RESOURCE_DIRECTORY_ENTRY;
这个就是紧跟在目录后面的资源目录项,共 8 字节。其中第一个成员为数据成员,最高位 1 表示数据是字符串,剩下 31 位是字符串的偏移;否则就是数值。第二个成员最高位为 1 表示下一层仍然是目录,后 31 位指向另一个 IMAGE_RESOURCE_DIRECTORY 结构;否则整个成员指向一个 IMAGE_RESOURCE_DATA_ENTRY 结构(这个马上会讲到)。需要注意的是,这里的两个 Offset 都表示从资源段开头到目标位置的偏移。
最后来看 IMAGE_RESOURCE_DATA_ENTRY:
typedef struct _IMAGE_RESOURCE_DATA_ENTRY {
DWORD OffsetToData;
DWORD Size;
DWORD CodePage;
DWORD Reserved;
} IMAGE_RESOURCE_DATA_ENTRY, *PIMAGE_RESOURCE_DATA_ENTRY;
这个结构是资源数据项,也就是资源树的叶子,共 16 字节。其中第一个成员 OffsetToData 指向具体的数据,这个偏移是个 RVA,跟前面两个不一样。Size 表示具体数据的总字节数。后两个成员可以为 0,CodePage 不建议使用。
PE 文件中的资源就是通过这三个结构表示的,它们都在 WinNT.h 中定义。通常会有 3 层结构,第一层表示资源类型,第二层表示 ID,第三层标识语言。
以上所说的是我能查到的资料里能够提到的最大程度的内容了。但是具体的数据如何存储,却几乎没有文章提及。于是,花了一两天时间来慢慢的看、加上试验,我认为我对字符串资源的格式基本清楚了。(下面内容是我自己分析得出,其正确性我并不保证)。
我们先来看一个具体的例子。这是一个资源 DLL,用 Resource Hacker 查看如图:
其资源段数据如下:
我用桔色框起来的是资源目录,用粉色框起来的是资源目录项,用浅绿色框起来的是资源数据项。
先看第一行,这是第一层目录,最后两个 WORD 是 0x0000 和 0x0001,表示后面“命名”的目录项有 0 个,使用 ID 的目录项有 1 个。第二行开头的 8 字节就是这个目录项,DWORD 0x00000006 表示资源类型是 6,也就是字串表,后面的地址是 0x80000018,最高位为 1,表示指向的仍然是一个目录,其偏移是 0x00000018,也就是 0218h 处。
0218h 处这个资源目录是第二层了。最后仍然是 0 和 1,于是我们来看 0228h 处的目录项。第一个 DWORD 是 1,这个跟 ID 有关,稍候讨论。他的第二个 DWORD 是 0x80000030,仍然指向目录。
0230 处的目录是第三层目录。注意到最后是 0 和 2,下面将有连续两个目录项。第一个目录项值为 0x00000409(1033,英语(美国)),偏移地址 0x00000050,最高位 0,表示指向的是数据项,而不是目录了。第二个目录项值为 0x00000804(2052,中文(中国)),偏移地址 0x0000009C。
这三层结构和 Resource Hacker 中显示的是一一对应的。
我们先来看英语的那个数据项,OffsetToData 是 0x00001060(RVA),Size 是 0x0000003C。这个 DLL 文件的资源段的 VirtualAddress 是 1000h,1060h-1000h+200h = 260h,我们来看 260h 处(其实就是紧接着的地方)。我第一次看这段数据的时候也很奇怪,为什么前面空了 2 个字节,后面有多出好多字节。于是我改它的 ID,试了好些次,终于找到规律了。资源目录第二层的 ID(下文称 ResID)和最终的字符串 ID(下文称 StrID)有这么一个对应关系:ResID = StrID / 16 + 1。StrID 0 到 15 所对应的 ResID 都是 1, StrID 16 到 31 对应 ResID 2,……。反过来说,资源目录中的 ResID 不能完全表达 StrID 的信息。所以,在 260h 开始的 3Ch 个字节的数据块里,其实要存储 16 个字符串,其 StrID 分别是 0,1,2,……,15。这 16 个字符串是连续存储的,结构是:字符串长度(WORD)+字符串内容(不含结束符 0)。那些空位就由一个 WORD 0 来填充(也可理解为长度为 0 的字符串)。我在图中用红褐色的竖线划出了这 16 个字符串的界限。后面那个中文的也是如此,就不重复说了。
到现在为止,对于字串表的结构,应该说差不多清楚了。于是拿程序去生成似乎不是难事了,不过要注意的是,目录项必须紧跟在目录后面,目录项指向的位置可以随意。
事实上上面这个 DLL 是我用程序生成的。我现在做到了从内部数据结构到资源 DLL 这个过程的实现。如果这也可以被称为“编译”的话,现在是实现了后端。至于前端,我还没想好原始资源格式。要想让这个工具有点用处,原始资源格式必须要:1、足够简单(至少比 RC 文件简单),并且维护方便;2、足够存储多语言字符串。这方面我希望大家能给我一些建议。
当然,本文的主要内容还是讨论字串表的格式,这个已经讲完了,所以,over~ bow~
posted @
2009-09-23 22:57 溪流 阅读(2306) |
评论 (3) |
编辑 收藏
开篇语:这是在这里写的第一篇日志。关于来到这里,主要源于前两天在这里看到一个牛人(vczh)的文章,花了近两天断断续续拜读了其文章。他的文章我不是全部能看懂,事实上只看懂了一小部分。还看到一些评论的朋友,也都很牛。因此想到这里来更好的与牛人们交流。如此而已。我原先的博客在 CSDN(http://blog.csdn.net/cnStreamlet/),由于一直以来都比较浮躁,也没写什么有用的东西。现在想想,人家是 05 级,我也是 05 级,人家已经这么牛了,我却还在金字塔的底层徘徊。人生短短几个秋,年轻的时候是个学习的黄金时间,浪费了岂不太可惜?总之呢,不管能不能静下心来,现在开始努力静下心来,多学点技术,即便成不了牛人,至少可以多些茶余饭后的谈资。
==========华丽的分割线==========
好了,言归正传。今年 3 月份,也就是上班的第一个月,那时候我还算比较淡定的,经常研究些玩意儿。那时写了个很轻量级的智能指针。现在不妨拿出来复习一下,如果有朋友路过,欢迎指教。
我所理解的“智能指针”,就是达到 new 了之后不用 delete 的效果。利用栈变量在作用域结束后会自动释放(对象自动析构)的机制,可以达到这个效果。设想有一个类,它以一个现有指针为参数进行构造,这个析够的时候去 delete 这个指针,就可以了。然后问题来了,在这种情形下,这个类本身充当了指针这个角色,那么难免要被复制来复制去,这个类中的原始指针也要被复制,那么,显然析构函数里不能简单地 delete 了。这时候,比较流行的做法之一是使用引用计数,当某个对象被复制一次,计数加 1;被析构一次,计数减 1。当且仅当计数为 0 的时候才执行 delete。现在,这个类的雏形大概是:
template <typename T>
class QIPtr
{
public:
QIPtr(T *pData);
~QIPtr();
private:
T *m_pData;
size_t m_cRef; // TBD
private:
void AddRef();
void Release();
};
我现在很随意地放了一个 size_t m_cRef,但是细想一下这样是不行的。假设有 QIPtr
p1(new int);,又有 QIPtr p2 = p1(当然,拷贝构造函数以及赋值函数现在还没实现,但这不妨碍我们想象他们的功能),p1 和 p2 里的 m_pData 共享一块内存,而 m_cRef 却是独立的,也就是说,p1 的 Release() 操作将无法影响到 p2。为了解决这个问题,可以将引用计数也定为指针 size_t *m_pcRef,当一个对象被使用原始指针构造的时候,同时给 m_pcRef new 一个出来;如果是 QIPtr 对象之间拷贝拷贝去,则将他们的 m_pcRef 也同步拷贝,并且让 *m_pcRef 自增即可。
当时我就做到这种程度(现在还是)。不过留有一个问题,这个智能指针不是线程安全的,原因在于 AddRef() 和 Release() 期间没有加锁。
代码比较短,就 200 行左右,如下:
/*******************************************************************************
Copyright (C) Streamlet. All rights reserved.
File Name: xlQIPtr.h
Author: Streamlet
Create Time: 2009-03-22
Description: Smart pointer
Version history:
2009-03-22 Created by Streamlet.
2009-03-27 Released first version.(1.0.0.1)
*******************************************************************************/
#ifndef __XLQIPTR_H_B0788703_ABD1_457D_8FEC_E527581FD9EF_INCLUDED__
#define __XLQIPTR_H_B0788703_ABD1_457D_8FEC_E527581FD9EF_INCLUDED__
namespace xl
{
#ifndef NULL
#define NULL 0
#endif
/// @brief Smart Pointer.
template <typename T>
class QIPtr
{
public:
/**
* @brief Default constructor.
*/
QIPtr();
/**
* @brief Constructor. Must give an heap address. Sample use: QIPtr<int> p(new int);.
* @param pData [in] A heap address, usually returned by operator new.
* @remark operator delete must not be called, if using QIPtr.
*/
QIPtr(T *pData);
/**
* @brief Copy construction.
* @param that [in] The pointer to be copied.
*/
QIPtr(const QIPtr<T> &that);
/**
* @brief Destroyer. Inside this function, the heap address will be released if there is no more references.
*/
~QIPtr();
public:
/**
* @brief Operator *, use it as usual.
* @return return a reference of T-typed object.
*/
T &operator*() const;
/**
* @brief Operator ->, use it as usual.
* @return return the address of the object.
*/
T *operator->() const;
/**
* @brief Copy operator, use it as usual.
* @param that [in] The pointer to be copied.
* @return Reference of this object
*/
QIPtr<T> &operator=(const QIPtr<T> &that);
/**
* @brief Compare operator, use it as usual.
* @param that [in] The pointer to be compared.
* @return Return true if the two points equals, return false otherwise.
*/
bool operator==(const QIPtr<T> &that) const;
/**
* @brief Compare operator, use it as usual.
* @param that [in] The pointer to be compared.
* @return Return true if the two points do not equals, return false otherwise.
*/
bool operator!=(const QIPtr<T> &that) const;
private:
void AddRef();
void Release();
private:
T *m_pData;
size_t *m_pcRefs;
};
template <typename T>
inline void QIPtr<T>::AddRef()
{
if (this->m_pcRefs == NULL)
{
this->m_pcRefs = new size_t;
*this->m_pcRefs = 0;
}
++*this->m_pcRefs;
}
template <typename T>
inline void QIPtr<T>::Release()
{
if (this->m_pcRefs == NULL)
{
return;
}
if (--*this->m_pcRefs > 0)
{
return;
}
delete this->m_pcRefs;
//if (this->m_pData == NULL)
//{
// return;
//}
delete this->m_pData;
}
template <typename T>
inline QIPtr<T>::QIPtr() : m_pData(NULL), m_pcRefs(NULL)
{
}
template <typename T>
inline QIPtr<T>::QIPtr(T *pData) : m_pData(NULL), m_pcRefs(NULL)
{
this->m_pData = pData;
this->AddRef();
}
template <typename T>
inline QIPtr<T>::QIPtr(const QIPtr<T> &that) : m_pData(NULL), m_pcRefs(NULL)
{
this->m_pData = that.m_pData;
this->m_pcRefs = that.m_pcRefs;
this->AddRef();
}
template <typename T>
inline QIPtr<T>::~QIPtr()
{
this->Release();
}
template <typename T>
inline T &QIPtr<T>::operator*() const
{
return *this->m_pData;
}
template <typename T>
inline T *QIPtr<T>::operator->() const
{
return this->m_pData;
}
template <typename T>
inline QIPtr<T> &QIPtr<T>::operator=(const QIPtr<T> &that)
{
//if (this == &that)
//{
// return *this;
//}
if (this->m_pData == that.m_pData)
{
return *this;
}
this->Release();
this->m_pData = that.m_pData;
this->m_pcRefs = that.m_pcRefs;
this->AddRef();
return *this;
}
template <typename T>
inline bool QIPtr<T>::operator==(const QIPtr<T> &that) const
{
return this->m_pData == that.m_pData;
}
template <typename T>
inline bool QIPtr<T>::operator!=(const QIPtr<T> &that) const
{
return this->m_pData != that.m_pData;
}
} // namespace xl
#endif // #ifndef __XLQIPTR_H_B0788703_ABD1_457D_8FEC_E527581FD9EF_INCLUDED__
写了这么粗浅的文字,希望大家不要笑话。请多指教。
posted @
2009-09-23 08:07 溪流 阅读(570) |
评论 (4) |
编辑 收藏