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

昨天晚上和同事讨论写 Log 的问题,谈到写到文件,后来谈到写文件用 ReadFile、WriteFile 还是用 fread、fwrite 的问题。我一直对 fread、fwrite 没啥好感,原因是它自作主张的搞了一套缓存机制。可是仅仅这点就鄙视它似乎还说不过去。谈着谈着,后来我们对它的参数设计起了怀疑——这里有一个参数是多余的!从表面看,ReadFile、WriteFile 的参数是恰到好处的,fread、fwrite 作为它们的上层函数,似乎没必要把一个参数拆成 2 个呀。

后来就一直跟 fread,直到出现 ReadFile,都没发现这 2 个参数有什么特别的用处,他们很早就被乘起来了:

count = total = elementSize * count;

所以,目前我仍然对这个设计感到困惑。

有谁知道,这是由于什么样的历史原因/技术原因,才使这个函数变成现在这副模样的?

posted @ 2010-04-04 19:41 溪流 阅读(5258) | 评论 (35)编辑 收藏

我原先不喜欢加 Log,后来我的头儿希望加 Log,于是乎我手头的项目就全是 Log 了。之前一直是定义一个不定参数的宏或者函数,遇到需要的地方就 LOG(...)。后来越来越感觉对于函数进出的信息比较渴求,于是弄了个固定的 LOG_FUNCTION() 来记录函数进入,因为有 __FUNCTION__ 嘛。

对于函数出口,原先一直是手写的,刚刚前几天在这里讨论的资源释放问题让我学到了新的解决方法——使用类似 Loki::ScopeGuard 的机制来输出函数退出。

晚上重新写了一下。见 http://code.google.com/p/xllog/

使用如下:

void bar()
{
   
XL_LOG_FUNCTION();
   
XL_LOG(L"%s\n", L"In function bar.");
}

void foo()
{
   
XL_LOG_FUNCTION();
   
XL_LOG(L"%s\n", L"In function foo.");

   
bar();
}

int main()
{
   
XL_LOG_FUNCTION();

   
foo();

   
return 0;
}

运行结果:

>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> Enter Function main
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> Enter Function foo
In function foo.
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> Enter Function bar
In function bar.
<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< Leave Function bar
<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< Leave Function foo
<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< Leave Function main

基本功能我自己已经可以接受了。

有两点我想改进的,不知道能不能实现:

1、每个用到的函数写一句 XL_LOG_FUNCTION() 太麻烦了,而且有可能被用户写到任意地方,而非函数开始。有没有一种机制,使得可以写成类似(要保持函数定义的形式,不要把函数名括号括起来):

void __log foo()
{
}

从而被展开成:

void foo()
{
    XL_LOG_FUNCTION();
}

呢?

或者有没有一种方法可以不用写任何东西就让每个函数体自动加上这么一句呢?

2、我想记录函数的调用层次。如果上面行能够实现的话,就没问题了,我可以自己记录函数进出次数。如果不能,那么依靠用户写 XL_LOG_FUNCTION() 来记录就不可靠了。有没有类似 __CALL_STACK_DEPTH__ 的预定义宏呢?

最后还有一个实际问题。实际使用中,在写代码的时候可能无法确定所写部位到底是偏底层还是偏上层。如果太偏底层而打了 Log,会输出很多 Log 干扰分析;如果太高层的而没打 Log,有可能遇到问题信息不足。这个问题该如何解决好呢?大家有没有成熟的解决思路呢?

posted @ 2010-04-04 01:31 溪流 阅读(2317) | 评论 (14)编辑 收藏

最近有个东西,需要读 XML 配置文件,于是用 msxml 做了。msxml 是基于 COM 的,使用之前需要 CoInitialize,使用之后需要 CoUninitialize。于是我写成了:

void foo()
{
    CoInitialize(NULL);

    // Reading configuration

    CoUninitialize();
}

刚才我正乐此不彼的把类似这样的东西改成:

void foo()
{
    CoInitialize(NULL);
    LOKI_ON_BLOCK_EXIT(CoUninitialize);

    // Reading configuration
}

前面的同事过来看到了,说,你不该在这里调用 CoInitialize 和 CoUninitialize。如果有的地方也在用 COM,你这里 CoUninitialize 一下,别的地方就会出错了,上次的某个 Bug 就是。

我狡辩道:我假定这里没有多线程环境(实际上也是),并且约定别的地方用 COM 的时候调用 CoInitialize 时不要判断返回值。

同事:应该和大众习惯保持一致,最好就是全项目最开始的时候 CoInitialize 一次,结束的时候 CoUninitialize 一次。

我:我这里是较底层功能函数。

同事:可以以文档的方式注明,使用该模块前必须自己 CoInitialize,使用完毕后自己 CoUninitialize。

我:我只是想要用起来方便一点,用的时候不要有那么多先决条件和后置条件。再说,人家本来可以不知道我用了 COM,我这么一说明,就暴露了内部信息了不是?

其实我被动摇了。

各位大大,你们怎么处理呢?

------------------------------华丽的分割线(13:27 p.m. 增加)----------------------------------

好,既然 CoInitialize 和 CoUninitialize 有引用计数机制,那么这个具体问题已经解决。

那么,有没有类似的成对使用的 API,会对进程全局产生影响的呢?如果有,在底层要用到的时候该怎么处理?

posted @ 2010-04-02 10:02 溪流 阅读(24887) | 评论 (17)编辑 收藏

如题。

没有界面、不带任何文档的软件怎么办?

posted @ 2010-03-31 17:23 溪流 阅读(5358) | 评论 (10)编辑 收藏

先看一个例子。首先,我要写一个vector;其次,为了使用方便,我需要提供一个带 size 参数的构造函数。要求就这两点。

那么,势必要:

class vector
{
public:
    vector(size_t size)
    {
        // ...
        m_pData = new int[size]; // 假设就是 int 这样的基本类型好了,以避免下面可能出现的离题
        // ...
    }
};

问题来了。new 不是有可能失败吗?失败了在老编译器里会返回 NULL(这个情形也先无视),在新编译器里会抛异常。那么,在这里要不要进行检查呢?如果检查:

try
{
    m_pData = new int[size];
}
catch (...)
{

}

catch到了。那么在这里可以干啥呢?似乎。。。啥也干不了!作为构造函数,没法使用返回值,自然只能使用异常来提示外界;既然本来就是异常,我又何必在这里 try 一次呢?(假设这里没有其他错误要处理,也假设这里的类型是int之类的基本类型,不会出现执行元素的构造函数失败的情形)

既然这里的 try 让我们如此无奈,那么就不必 try 了。这个时候,我需要给 vector(size_t size) 标记上 throw 吗?如果不标记,使用者怎么知道这里可能会有异常?如果标记了,或者没标记但使用者意识到了,那么他会这样用:

try
{
    vector v(10);
    // Task with v
    // ...
    // ...
    // ...
}
catch (...)
{
    // Error handler
}

因为 v 的作用域被限制在了 try 内,所以所有的与 v 相关的逻辑代码全部要放在 try 内部了。这种样子似乎与 C# 很像!在 C# 里,try...catch... 是标准的做法;但是在 C++ 里,似乎不会如此经常地用 try catch,要不然,为什么我见过的 C++ 代码都不是这样子的呢?两年前在金山实习的时候,有一次我把 try...catch 当做通用的错误处理来做,所有的错误都搞成一种异常,返回值仅返回正常值。结果董波叔叔说,这样子是不对滴,但是没给出让我信服理由,可能就是,C++ 的 try...catch 的性能很不好之类的。(C# 以及 Java 的 try...catch 的性能好吗?)

好,既然大家都不这么办,是不是这里也不用 try 了?于是,内存分配错误就让它自生自灭了……记得以前某本书上看到,说这种情形下的处理,仅仅是一个道德问题而已。真的无解吗?

如果放宽要求,不要求在构造函数提供内存分配,那倒是有一种解法——分两阶段构造:

class vector
{
public:
    vector()
    {
        // ...
    }
    bool allocate(size_t size)
    {
        try
        {
            m_pData = new int[size];
        }
        catch (...)
        {
            return false;
        }
        if (m_pData == NULL)
        {
            return false;
        }
        // Other code ...
        return true;
    }
};

但是使用起来就不“方便”了。现实中,这种情形倒是存在,如 CWindow 的 Create,还有啥啥啥的 Init 等等。

真的没有办法兼顾方便与安全吗?

posted @ 2010-03-30 22:31 溪流 阅读(2356) | 评论 (15)编辑 收藏
仅列出标题
共18页: First 10 11 12 13 14 15 16 17 18