随笔-90  评论-947  文章-0  trackbacks-0
 

如果要 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 溪流 阅读(726) | 评论 (18)编辑 收藏
     摘要: 打算先把基础的东西组织起来,作为一个“大库”(见上一篇《小库还是大库?》)。然后填加各个实用功能,作为一个个小库或者大库,过段时间再想办法组织整理了。 首先是类型系统,我想来想去,觉得还是必须整理一下,尤其是 unsigned XXX 写起来太难看了,可是这又带来了问题——WinDef.h 把好听的名字都占去了。然后,我只能在自己的命名空间下这样来一番了: xlDef.h #ifndef ...  阅读全文
posted @ 2009-09-26 17:43 溪流 阅读(650) | 评论 (18)编辑 收藏

我不知道这里有多少朋友是积累了自己的一套库的。

嗯……说明白点,比如想读写文件了,操作系统 API 的那堆参数我记不住,也用不着那么灵活,于是自己写一个自己记得住的,下次碰到同样情况继续用,并不断完善。哪天想读写注册表了,想读写 INI 文件了,想读写 XML 文件了,想用个动态数组了,想要个链表、树了,可能都会形成自己的一套东西。这套东西可能是基于已有的第三方库,也可能是纯粹自己一点一滴写起来的。好了,我想现在我大概表达得够明白了,这就是我说的“库”,这个库可能不是非常完备,但起码是自己积累的,有着(起码对自己来说)友好接口的东东。

可能有朋友会说,你要自己的动态数组、链表干吗?STL 很好啊!你要读写文件的干吗?CFile 哪里不好?你要读 INI?不是有 API 吗?……诸如此类。如果有朋友持这样的观点,我想我们是不同的一类人。如果您只是能完成某项任务就好,那么确实,不需要这些玩意儿。但是,如果哪一天这种普通的工作做得麻木了,来思考一下另一个层面的事情,您也许会觉得这些也是比较有意思的事。废话到此。

那么,不知道这些库,是以什么形式存在的呢?稍微极端开来讲,可能有两个做法——

第一种做法。我每写成一个功能模块,都是一个(或几个).h、一个(或几个).cpp,它们是自我独立的,不依赖于任何别的东西(或者不依赖标准库以外的东西、不依赖于操作系统 API 以外的东西)——总之是不依赖于当前编译系统以外的东西。以后需要使用,就把那几个文件拷到当前项目来使用。然后一个个这样的互不依赖的功能模块构成了我现在所拥有的库。

第二种做法呢,就是我把这个库作系统的规划,划分为很多小的功能模块,这些功能模块可能会彼此依赖,当库庞大以后,甚至连你自己都该不太清楚谁依赖谁了。要使用这个库的功能,就必须把整个库拿进来。到最后,我将这整套东西编译为一个 .lib,这个 .lib 的源程序会一直维护下去。但使用的时候,我就拿编译好的 .lib 来用。

前一种做法就是标题里所说的小库,后一种做法我称之为大库。我的问题是,作为个人的积累,小库好还是大库好?如果可能,我是比较喜欢小库的。但是,经常会有这样的问题,各个功能模块中可能会涉及同一个基础功能,而这个基础功能我已经做过了的,到底是用还是不用?如果用,“互不依赖”就会被打破,最终会发展成一个凌乱的大库;如果不用,我必须把代码抄一遍,那么这两份完全一样的代码在以后同步更新就比较麻烦了。再说大库,一个规划的很好的大库也是不错。但是前期积累的时候,往往没法规划;就算等到有一定的积累了以后再来积累,也会在模块组织上犹豫不决:我到底要不要来一个统一的 typedef 作为我的类型系统?当我实现了 MyVector,MyString 以后,我的后续代码势必都会使用它们,那么与别人之间的代码交流就成了问题了。

我最近一直困惑于这个问题。而我本人对此的理解也就如上文所述。希望有朋友指教、赐教。谢谢~~!

posted @ 2009-09-24 21:30 溪流 阅读(642) | 评论 (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 查看如图:

image

image

 

其资源段数据如下:

image

我用桔色框起来的是资源目录,用粉色框起来的是资源目录项,用浅绿色框起来的是资源数据项。

先看第一行,这是第一层目录,最后两个 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 溪流 阅读(2260) | 评论 (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(*pData);
    ~
QIPtr();
private:
    
*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(*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.
         */
        
&operator*() const;

        
/**
         * @brief Operator ->, use it as usual.
         * @return return the address of the object.
         */
        
*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> &thatconst;

        
/**
         * @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> &thatconst;

    
private:
        
void AddRef();
        
void Release();

    
private:
        
*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(*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 &QIPtr<T>::operator*() const
    
{
        
return *this->m_pData;
    }

    
template <typename T>
    
inline *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> &thatconst
    
{
        
return this->m_pData == that.m_pData;
    }

    
template <typename T>
    
inline bool QIPtr<T>::operator!=(const QIPtr<T> &thatconst
    
{
        
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 溪流 阅读(552) | 评论 (4)编辑 收藏
仅列出标题
共18页: First 10 11 12 13 14 15 16 17 18