Posted on 17-01-2012
Filed Under (技术) by waterlin

最近在写 Linux 程序的时候,碰到这样的问题:Log4Cxx 0.10.0 在 Linux 下退出程序时导致程序中断,即提示出现 segmentation fault 错误。

如果你用 gdb 调试,会提示如下信息:

Program received signal SIGSEGV, Segmentation fault.
0x02fa68f3 in apr_vformatter () from /usr/lib/libapr-1.so.0

原因是因为 Log4Cxx 在退出时有非法的资源释放。

有一个解决办法:可以在程序退出时,显示地关闭 Log4Cxx 对象。

I have the same problem if I use log4cxx.AsyncAppender.
I fixed the problem by calling log4cxx::LogManager::shutdown(); before the end of the process.
It's not very clean but it works.

在程序退出时使用语句

log4cxx::LogManager::shutdown();

来清理 log4cxx,而不是让它自己来清理。

注意: 需要在程序所有可能退出的地方,设置这一语句 ,否则依然会有 segmentation fault 的问题。

(0) Comments    Read More   
Posted on 27-12-2011
Filed Under (技术) by waterlin

最近都在写一些跨 Windows 和 Linux 平台的 C++ 代码,略有心得,整理成文,备忘一下。

有关预编译指令

Visual Studio 会自动在源代码里添加

#pragma once

这个指令,在 Linux Gcc 编译器下应该怎么样处理类似的情况呢?

维基百科上有一个权威说法:


http://en.wikipedia.org/wiki/Pragma_once

所以,在编写跨平台的 C++ 代码时,最好使用下面这种方式来获得跨平台的特性:

#pragma once
#ifndef GRANDFATHER_H
#define GRANDFATHER_H

struct foo
{
    int member;
};

#endif /* GRANDFATHER_H */

有关链接库工程的跨平台

Windows 下使用 __declspec(dllexport) 来标明一个动态链接库的函数接口,而在 Linux 下,则完全没有这个必要。

这个时候,如果动态链接库代码需要跨平台,应该怎么处理呢?

你可以用如下宏来进行区分:

#ifdef WIN32
#define EXPORT_XX __declspec(dllexport)
#else
#define EXPORT_XX
#endif

源代码的编码格式

源代码的编码格式,最好统一用 GBK 或是 UTF-8,以避免不同编码器之间转换造成的乱码。

注意代码里的文件名大小写。

Windows 里 include 一个头文件,你的大小写可以随便写。但是在 Linux 上,你得小心了,clsssa.h 你不能写成

#include "ClassA.h"

因为在 Linux 上,文件及目录名是大小写敏感的。

一些类型名称

Windows 处理宽字符集与窄字符集采用的方法,是用宏定义来区分。比如说 TCHAR 在不同的工程下,属于不同的类型。

而在 Linux 下,则需要注意这些问题。为了让你的代码可移植性强。一定要少用 BOOL, TCHAR 这些类型,而要用原生态的 C/C++ 类型,比如说 bool, char 等。

有关 _tmain 等主函数问题。

默认情况下,Visual Studio 新建的 Win32 Console 工程,全是类似于这样的。

int _tmain(int argc, _TCHAR* argv[])
{
}

这些代码,移植到 Linux 下面,就需要我们自己做一部分工作。

如果你在 Visual Studio 用的是 Unicode 编码的,则对应的 Linux 代码应该是另外一套。我则是用宏裁剪了两个主函数出来。如果你有什么更好的方法,可以跟我分享。

注意一些库在 Linux 和 Windows 平台下的不同表现

虽然你的程序可能依赖大量的跨平台库,这些库号称是跨平台的,但是很有可能在不同的平台的表现,会有所不同。例如,log4cxx 在 Linux 和 Windows 下就会有字符集设置的差别

你在编写跨平台代码的时候,就应该特别小心这些库的细微差别,及时调整。

后记

以上是一些跨平台 C/C++ 代码编写的经验,备忘一下,也希望对大家有用。

(0) Comments    Read More   
Posted on 14-12-2011
Filed Under (技术) by waterlin

最近在 Linux 下使用 log4cxx 库,使用的 log4cxx 版本为 0.10.0,结果无法显示中文日志信息。

这可怎么办呢?我不可能把中文日志全部一行一行替换为英文的,这可是一个非常傻B的举动。

经过研究,终于知道需要经过如下步骤才能让 log4cxx 在 Linux 下正常显示中文日志:

  1. 你可以先 locale 检查一下 Linux 终端环境是不是 zh_CN ;

    $ locale
    
  2. 如果你的是 en_US 之类的编码,则需要把 locale 设置为简体中文:
    $ export LC_ALL="zh_CN.UTF-8"
    

    如果你的系统提示说没有安装本字符集,则需要用命令进行安装:

    $ sudo apt-get install language-pack-zh-hans
    
  3. 在程序里设置应用程序的 locale 和终端一样:
    LoggerPtr logger;
    log4cxx::PropertyConfigurator::configure("./log4cxx.properties");
    logger= Logger::getLogger("test") ;
    logger->info(("Start logging"));
    setlocale(LC_ALL, "zh_CN.UTF-8");
    

    关键是最后这一句 setlocale,要设置得和终端一样,都是 zh_CN.UTF-8。

这样,你的程序就可以用 log4cxx0.10.0 输出中文日志信息了。

(1) Comment    Read More   
Posted on 25-11-2011
Filed Under (技术) by waterlin

今天在 Ubuntu 下使用 OpenCV 来读取一个视频,碰到了下述问题:

  1. 自己手动安装的 OpenCV2.2.0 无法正确读取视频内容,用函数 cvCaptureFromAVI() 及 cvGetCaptureProperty() 均提示说失败:

    所有的测试视频在 Windows 下用 OpenCV2.2.0 是可以正常读取的,应该是 ffmpeg 安装不完全或是不正确。

  2. 从源里安装 OpenCV2.1 后,可以打开视频读取数据,可是在读取视频帧内容的时候,却提示如下错误:
    [swscaler @ 0x94b3e80]No accelerated colorspace conversion found from yuv420p to bgr24.
    

    这个问题应该是 OpenCV 在用 ffmpeg 解压 yuv420 数据时出错。

对于上述问题,可以采用下面的办法来解决:

  1. 重新安装 ffmpeg x264:Install and use the latest FFmpeg and x264
  2. 再重新安装 OpenCV,经测试,可以正确安装 OpenCV 2.3.1 版本,安装成功后,视频读写一切正常。

另外:如果是在 Ubuntu-Server 版下安装 OpenCV,在安装 libhighgui-dev 的时候,需要安装很多和桌面相关的依赖包。

(0) Comments    Read More   
Posted on 06-11-2011
Filed Under (技术) by waterlin

想弄一个 Java 内嵌网页服务器 ( Embedded Web Server ),这样,在我的 Java 应用程序里,可以直接提供网页服务,而不用再给用户配置一个网页服务器。对于轻量级的本地网页服务,这样就足够了!

在 Java 应用里内嵌一个网页服务器,Jetty 是一个很好的选择。以下是我在摸索过程中,总结的基本步骤:

1. Jetty 环境的配置

首先从官方网站下载开发包,再解压到你喜欢的目录,Java 的东西就是方便呀!

注意,Jetty 还分好几种包,Jetty@eclipse、Jetty@codehaus 是比较常见的,内容应该差不多,应该只是镜像罢了。

2. 在 Eclipse 里建立 Jetty 内嵌网页服务器 Hello World 程序

Java 开发中使用第三方库的时候,就需要把该库添加到工程里。Jetty 的库文件,全部在解压目录的 lib 目录下,比如说,在我这里就是

D:\eclipse\jetty-hightide-8.0.4.v20111024\lib

目录。在 Eclipse 里新建一个 Java 工程,同时,在工程上点右键,弹出的菜单里,选择”Properties”,打开工程属性对话框,进行如下操作:

在 “Java Build Path” ==> “Libraries” 选项卡里,选择 “Add External JARs” 按钮,然后切换到 Jetty 目录里的 lib 目录,把所有的 jar 包都添加到工程里。

现在,我们可以用最简单的例子来启动一个 Jetty 内嵌网页服务器,但是这个代码,只可以获得一个 404 错误。不过没有关系,这起码可以证明 Jetty Server is Ready!

相比于上面这个 404 错误的例子,相信你会更喜欢下面这个 HelloWorld 例子

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.ServletException;

import java.io.IOException;

import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.server.handler.AbstractHandler;

public class HelloWorld extends AbstractHandler
{
    public void handle(String target,
                       Request baseRequest,
                       HttpServletRequest request,
                       HttpServletResponse response)
        throws IOException, ServletException
    {
        response.setContentType("text/html;charset=utf-8");
        response.setStatus(HttpServletResponse.SC_OK);
        baseRequest.setHandled(true);
        response.getWriter().println("<h1>Hello World</h1>");
    }

    public static void main(String[] args) throws Exception
    {
        Server server = new Server(8080);
        server.setHandler(new HelloWorld());

        server.start();
        server.join();
    }
}

3. 其它添加 Jetty 库文件的方法

如果需要在 Java 工程里进行 Embeded Server 开发,也可以把 Jetty 的库目录包含到 CLASSPATH 环境变量里。事实上,只要能找到下面几个文件就可以了:

servlet-api-3.0.jar
jetty-util-8.0.4.v20111024.jar
jetty-8.x.jar

手册上是这样写的,不过最后一个 jetty-8.x.jar 文件,我没有找到。

(0) Comments    Read More   
Posted on 31-10-2011
Filed Under (技术) by waterlin

在调试 Visual Studio 2008 程序时,经常有一些动态链接库(即 dll 文件)需要加载到工程里,这样才能依赖第三方库进行程序调试。

这些动态链接库,往往都是测试版本或是开发中的版本,或者会有若干个版本;这个时候,如果直接把 dll 所在目录加到 PATH 里,则会有潜在冲突的危险;如果直接拷贝到 Visual Studio 的目录下,假如测试工程太多,每次有新版本的动态链接库更新时,你需要更新若干次,拷贝、粘贴苦不堪言。

在开发过程中,究竟怎样来让 Visual Studio 链接这些 lib 及 dll 文件会比较好呢?

总体上来说,有几种方法可以改变 Visual Studio 的环境变量设置:

  1. 直接添加到系统的 PATH 变量里

    这个方法最简单,也最直接,但是坏处是会影响全局的 PATH 设置,尤其是你包含着大量测试用的 dll 时。

  2. 在 Visual Studio 全局设置里,把 dll 所在目录添加到 PATH 里:

    通过 Visual Studio 菜单 ==> 工具 ==> 选项 ==> 项目和解决方案 ==> VC++目录,在下拉框里选择”可执行文件”,然后把 dll 所在路径添加进去。

  3. 直接把所有 dll 拷贝到 Visual Studio 工程目录下,或是拷贝到生成可执行文件的文件夹(默认情况下是 Debug 或 Release 目录)下:

    这个方法也很简单,但是当你有若干个工程时,你每次更新 SDK 及其 dll 文件,你就要把所有的工程都更新,这个不符合文件唯一性的工程性准则。

  4. 在调试程序时,让 Visual Studio 帮你切换当前工作目录到 dll 相应的目录下:

    在 Visual Studio ==> Project ==> Properties ==> Select Configuration ==> Configuration Properties ==> Debugging ==> Working directory 里填上 dll 所在目录,这样当在调试程序时,Visual Studio 会把当前工作目录切换到这个目录下,从而会自动读取本目录下的 dll 文件。

    这个方法的优点很明显,简单!副作用也很明显,在你切换了当前工作目录后,你可能会找不到程序的配置文件,在程序里写的诸如”./config.ini”全部都找不到了;另外,你要把所有的 dll 都放到这个工作目录里,否则一样会提示说找不到 xxx.dll 的问题。

  5. 最后一个方法,也是我认为最好的一个方法,在 Visual Studio 工程属性里把一个目录临时添加到 PATH 环境变量里:

    MSDN 上也有类似的介绍:How to: Set Environment Variables for Projects,方法很简单,在 “工程属性” ==> “调试” ==> “环境”里,添加类似如下所示的内容:

    PATH=%PATH%;$(TargetDir)\DLLS
    

    这样就可以把 $(TargetDir)\DLLS 临时添加到该工程所属的系统 PATH 里。

大家可以根据项目的实际情况,灵活选用以上方法。

注:本文撰写时参考了 StackOverflow 上的讨论话题:How do I set a path in visual studio?

(0) Comments    Read More   
Posted on 09-10-2011
Filed Under (技术) by waterlin

Emacs Org Mode 里,默认情况下 _ 这种字符会被当成标记语言来进行转义。有的时候,如果你只是写写文章,这种默认的转义,会让你很不方便,尤其是你在写一篇介绍技术的文章,里面出现的变量名有很多的下划线。这时,你会说:”Oh, my god! 难道要我一个一个去标记不让 _ 转义么?”

如何设置让 Org Mode 在默认情况下,不转义 _ 字符呢?

单个 org 文件的解决办法

你可以在一个 org 文件的开头,用下面的设置来关闭这个功能。

#+OPTIONS: ^:nil

如果你需要更方便的设置,可以把上面这个改为

#+OPTIONS: ^:{}

这样,当你写

a_{b}

时,_ 后被 {} 括起来的内容就会被转义,而写 a_b 时,就按普通的方式来显示。

org sites 的解决办法

如果你是用 org sites 来写笔记,想让某个 site 的所有 org 文件不转义 _ 字符,则也可以直接在 org sites 配置文件里,配置下面这么一句,一了百了:

(setq org-export-with-sub-superscripts nil)

也可以在 org sites 里设置这个属性,只作用于某一个 site:

:sub-superscript nil

如果需要像上面一样,采用相对智能的 {} 方式,可以设置成

:sub-superscript {}

或是直接用 elisp 来设置全局的属性:

(setq org-export-with-sub-superscripts '{})

这样就会用 {} 来转义了。

(0) Comments    Read More   
Posted on 09-09-2011
Filed Under (技术) by waterlin

最近折腾得够呛,天天写代码,都没时间更新博客了。

总结一下最近碰到的两个问题,写到中文 Wiki 里了,希望对大家有点帮助。

  1. OpenCV 在 Ubuntu 11.04 平台上的编译

    最近要迁移大把工作成果到 Linux 平台,即要在 Linux 下大量地折腾与 OpenCV 相关的代码。很久没有在 Linux 上折腾了,时过境迁,同样碰到了不少问题,写成日志《OpenCV 各种安装错误汇总》

  2. Berkeley DB 数据库的操作还是蛮有技巧的,如果需要把内存里结构比较复杂的数据写到 Berkeley DB 里,我当然拥有我的技巧,以把 IplImage 数据写入到 Berkeley DB 为例,介绍我对这一技术的理解。
(0) Comments    Read More   
Posted on 05-09-2011
Filed Under (技术) by waterlin

在 Visual Studio 2008 MFC 工程中,利用 Berkeley DB 来构建数据存储引擎时,在编译 db.h 文件时出现编译错误,错误提示内容如下:

错误    3       error C2143: 语法错误 : 缺少"}"(在"("的前面)        e:\water\berkeleydb\include\db.h        1226

微软的 MSDN 上有对 error C2143 的编译器错误进行解释,不过基本上没有太多可读性、可借鉴性,大意应该是一些宏定义、命名出错等。

最后,还是通过万能的 Google 大神找到了解答方法。错误的原因是 DB_TYPE, DB_UNKNOWN 类型已经在 MFC 系统头文件中被定义过,解决办法之一是在 db.h 中定义 DB_TYPE, DB_UNKNOWN 的语句之前加上如下语句即可:

#ifdef DB_UNKNOWN
#undef DB_UNKNOWN
#endif
#ifdef DBTYPE
#undef DBTYPE
#else
#define DBTYPE BDBTYPE
#endif

看来,C 和 C++ 混在一块,命名、类型定义真是一个大问题。以后碰到类似的问题,也可以采用类似的解决办法。

参考资料:

  1. 解决在vs2008的mfc工程中编译BerkeleyDB出错问题
  2. VS2005中的MFC程序使用BerkeleyDB
  3. MSDN Visual Studio 2010 Compiler Error C2143
(0) Comments    Read More   
Posted on 24-08-2011
Filed Under (技术) by waterlin

今天为这件事情折腾了一天!

我用 Java 写的中间件,会根据用户的需要,启动一个子进程,这个进程是用 C++ 写的,正好用到了 Log4Cxx 来记录日志以便分析。这个进程,单独从控制终端运行,没有任何问题;但是一旦用 Java 启动子进程的方式来启动,则这个子进程刚一启动就阻塞不动了,然后大概等上几十分钟到两个小时不等,该子进程则又能顺利执行下去。

第一次碰到这样的问题,很怪异,经过反复测试及万能的 Google 帮忙,找到Java调用外部程序挂起原因,Log4Cxx 原来是罪魁祸首,因为它重定向了大量的日志信息到控制台,由于 Java 进程没有清空程序写到缓冲区的内容,结果导致程序一直在等待。

解决这个问题有两个办法:

  1. 按上文中的说法,在遇到 Java 启动外部程序而导致线程阻塞的时候,可以考虑使用两个线程来同时清空 process 获取的两个输入流;
  2. 直接把 Log4Cxx 重定向到控制台的日志消息给取消掉,这个可以通过 Log4Cxx 的配置文件来实现

问题搞定了,世界又变得很美好了!

(0) Comments    Read More