2008/11

C++标准函数查询

man page只能查Linux系统调用,C标准函数和其它一些glibc函数,不能查C++标准库函数。怎么才能像man那样查询C++的函数呢?我写了下面这么个Perl脚本来完成。

用法:

$ ./mancpp cmath::sin
$ ./mancpp cmath::cos
$ ./mancpp vector::size
$ ./mancpp algorithm::count_if

代码:

[perl]

!/usr/bin/perl

use strict;
use warnings;
use LWP::Simple;

die “Please provide one argument.n” unless @ARGV==1;
die “Use class::function format.n” unless $ARGV[0] =~ /S::S/;

my ($class, $func) = split(/::/, $ARGV[0]);
my $file = “/tmp/$class.$func.html”;

if ( ! -f “$file”) {
my $url;

if ($class =~ /^c/) {
    $url = "http://www.cplusplus.com/reference/clibrary/$class"."/".$func.".html";
} elsif ($class eq "filebuf" || $class eq "fstream"
    || $class eq "ifstream" || $class eq "ios"
    || $class eq "iostream" || $class eq "ios_base"
    || $class eq "istream" || $class eq "istringstream"
    || $class eq "ofstream" || $class eq "ostream"
    || $class eq "ostringstream" || $class eq "streambuf"
    || $class eq "stringbuf" || $class eq "stringstream"){
    $url = "http://www.cplusplus.com/reference/iostream/$class"."/".$func.".html";
} elsif($class eq "vector" || $class eq "bitset" || $class eq "deque"
    || $class eq "list" || $class eq "stack" || $class eq "map"
    || $class eq "queue" || $class eq "set" || $class eq "multiset"
    || $class eq "priority_queue" || $class eq "multimap") {
    $url = "http://www.cplusplus.com/reference/stl/$class"."/".$func.".html";
} elsif ($class eq "functional" || $class eq "iterator"
    || $class eq "memory" || $class eq "utility") {
    $url = "http://www.cplusplus.com/reference/misc/$class"."/".$func.".html";
} elsif ($class eq "algorithm") {
    $url = "http://www.cplusplus.com/reference/$class"."/".$func.".html";
} elsif ($class eq "string") {
    $url = "http://www.cplusplus.com/reference/string/$class"."/".$func.".html";
}

print "Retrieving $url", "...n";
LWP::Simple::getstore($url, "$file");

}

open my $fd, “$file” or die “No such class or function!n”;
my $found = 0;
my @ret;

while(){
if (m//) {
$found = 1;
}
if ($found) {
s/]*>//g;
s//g;
s/</</g;
s/Parameters/n==Parameters==/;
s/Example/n==Example==/;
s/See also/n==See also==/;
push @ret, $_;
}
if (m/SuOptions()/) {
$found = 0;
pop @ret;
}
}

open my $hd, "|less" or die "Can't open pipe!n";

foreach (@ret){
print $hd $_;
}

close $hd;

close $fd;
exit 0;

[/perl]

谁都没错?

$ cat abs.cpp

include <cstdlib>

include <iostream>

using namespace std;

int main()
{
cout<<abs(-19.50)<<endl;
return 0;
}

编译你会得到如下错误:

abs.cpp: In function ‘int main()’:
abs.cpp:7: error: call of overloaded ‘abs(double)’ is ambiguous
/usr/include/stdlib.h:699: note: candidates are: int abs(int)
/usr/lib/gcc/i386-redhat-linux/4.3.0/../../../../include/c++/4.3.0/cstdlib:175: note: long long int __gnu_cxx::abs(long long int)
/usr/lib/gcc/i386-redhat-linux/4.3.0/../../../../include/c++/4.3.0/cstdlib:144: note: long int std::abs(long int)

为啥呢?因为abs()在<cmath>中,而不是C++标准参考手册中提到的<cstdlib>!

谁错了捏?其实谁都没错。这里解释到:

The Standard C++ library is supposed to
overload abs the way you expect, but it’s common for implementations
to omit the overloads, or put them in the wrong place (<cmath> instead
of <math.h>).
这个真的让人很无语,一边是出错,另一边是明知道出错也这么做。。。冏。。。

星星山之行

周六和Marco一起去了传说中的葡萄牙(陆地)最高峰,星星山。

星星山离Marco家Lapa很近,所以我们先到了他家,吃完中午饭之后才动身。走之前他还叫上了他的小外甥,一个才上小学四年级的小鬼,于是我们三个一起上路了。那个小鬼叫Mario,就是“超级玛里”中的“玛里”,是个活泼,聪明伶俐的孩子。小鬼的英语比我的葡语还糟,他和我说的英语还不如我和他说的葡语多。不过对于这个年纪的孩子来说已经很不错了。

我们在路上花了相当多的时间,本来是计划到了山顶之后下来再去附近的一个城堡,所以我们绕道走了另一条比较远的路上山。谁知道那条路那个长啊,先是在山下的镇上绕来绕去,还好我们有GPS,终于绕到山上去了。到山上还是得继续绕,山路绕得比F1赛道还多,绕得坐到后面那个小鬼昏昏欲睡。

最后终于绕到了山顶,可是天色已经渐暗,山上天黑得很快,才拍了没多少张照片天色就基本上已经全黑了。山顶上风很大,气温很低,只有三四度,还好我们有提前准备的衣服,拍照时也不得不裹得严严实实的。那小鬼似乎带的衣服太少,只好又穿上了Marco的外套,那对他来说太大啦,结果看起来很是滑稽。

山顶上有商店,我们钻进去看了看,我在那里买了个小纪念品。然后我们又进了旁边的一家咖啡吧,我要了一杯热咖啡热身,喝完感觉暖和多了。在国内,在山顶的商店卖的东西一般是山下的N倍,而这里的东西比山下贵了不多少,让我感到一丝惊讶,大概因为能开车到这里的缘故吧。

等我们出来天已经完全黑了,本来计划要去看的湖泊和城堡也只好作罢了。匆匆忙忙赶回去吃晚饭去了。

吃完饭准备回Coimbra时,我们又去了那个酒吧。那老板娘还记得我,这次又和我开玩笑,她问我喝什么啤酒?大瓶的还是小瓶的?我说小瓶的,然后比划了一下小的有多小,过了一会儿她拿来一个小瓶的,说你要的是这个吧?我看了看说是,谁知道她后面还藏着一个更小的呢。。。她后面的人都笑了。然后她还拿出一个特大号的来,说让我下次再来喝那个。。。

喝完要走时,遇到一个喝醉的,非得拉着我和我说葡萄牙语,我站在那里无语得很,用葡语告诉他我不说葡语,谁知道他说,你这不就是在说葡语嘛,冏。。。。要不是其他人给我解围我是走不出那个酒吧了~

非常难忘的一个周末~!

gcc 4.3 改变了 -Wconversion

gcc 4.3之前,-Wconversion是这样的:

Warn if a prototype causes a type conversion that is different from what would
happen to the same argument in the absence of a prototype.

Also, warn if a negative integer constant expression is implicitly converted to
an unsigned type.
到了4.3就变了,发布日志上解释到:
The -Wconversion option has been modified. Its purpose now is to warn for
implicit conversions that may alter a value. This new behavior is available for
both C and C++. Warnings about conversions between signed and unsigned integers
can be disabled by using -Wno-sign-conversion. In C++, they are disabled by
default unless -Wsign-conversion is explicitly requested. The old behavior of
-Wconversion, that is, warn for prototypes causing a type conversion that is
different from what would happen to the same argument in the absence of a
prototype, has been moved to a new option -Wtraditional-conversion, which is
only available for C

这个改变其实挺大的,下面通过这个程序就可以展示这个问题:

[c]
void foo(short i);

void foo(short i)
{
i++; //dummy
}

int main(void)
{
int a = 1;
short b =2;
b = a;
foo(a);
return 0;
}
[/c]

$ gcc -Wall -Wtraditional-conversion -o conv conv.c
conv.c: In function ‘main’:
conv.c:14: warning: passing argument 1 of ‘foo’ with different width due to prototype
$ gcc -Wall -o conv conv.c
$ gcc -Wall -Wconversion -o conv conv.c
conv.c: In function ‘main’:
conv.c:13: warning: conversion to ‘short int’ from ‘int’ may alter its value
conv.c:14: warning: conversion to ‘short int’ from ‘int’ may alter its value

键盘符号的英文读法

键盘上一些符号的英文读法真的让人头疼,很多根本就没有统一的念法。上葡语课时我问老师dash在葡语里怎么用,结果她不知道什么是dash,最后经过探讨我们达成共识,我问的是hyphen…… 因为我整天和程序打交道,“-”更多被读作dash,而非程序员更多读作hyphen。比较烦~~

这里简单整理一下键盘上所有特殊符号的英文读法,最后还有葡语中特殊符号的英文读法。参考资料见本文最后。

! 叹号 exclamation mark/bang
? 问号 question mark
, 逗号 comma
. 点号 dot/period/point
: 冒号 colon
; 分号 semicolon
“ 双引号 quotation marks/double quote
‘ 单引号/撇号 apostrophe/single quote
` 重音号 backquote/grave accent

  • 星号 asterisk/star
  • 加号 plus sign
  • 减号/横线 hyphen/dash/minus sign/
    = 等号 equal sign
    / 斜线 slash
    反斜线 backslash/escape
    | 竖线 bar/pipe/vertical bar
    _ 下划线 underline/underscore
    $ 美元符号 dollar sign
    @ at at sign

    井号 crosshatch/sharp/hash

    % 百分号 percent sign/mod
    & and/和/兼 and/ampersand
    ^ 折音号 circumflex/caret
    ~ 波浪号 tilde
    {} (左右)花括号/大括号 (left/right|open/close) braces
    [] (左右)方括号/中括号 (left/right|open/close) brackets
    () (左右)圆括号/小括号 (left/right|open/close) parentheses
    <> 尖括号 angle brackets
    < 大于号 less than
    > 小于号 greater than

葡语中的变音符号(diacritic mark)在英文中的读法:

^ circumflex/caret
~ tilde/squiggle
´ acute
` grave
ç cedilla

参考资料:

1. http://www.codinghorror.com/blog/archives/001133.html
2. http://ascii-table.com/pronunciation-guide.php
3. http://www.learningportuguese.co.uk/language/diacritics.html

光棍节快乐!

一年过了又一年,只是每年这个节日还是没变。:-)

希望明年有所改观,不过貌似我每年都这么说……

Linux下的console和terminal

consoleterminal是很容易让人迷惑的两个概念。根据wikipedia上的定义,小型计算机的console应该就是键盘加显示器;而terminal则是输入数据进去,和显示数据来源的设备,通常是一个计算机系统。

Linux下的console除了真实的硬件设备外,还有virtual console,也就是你按alt+Fn或者alt+ctrl+Fn切换到的东西。所谓虚拟就是这些console共享同一个真实的设备,只有一个活动的console才显示在前面。这些console对应的设备是:/dev/ttyN,其中1 ≤ N ≤ 63。而/dev/tty0则是指向当前的terminal;/dev/console是指向当前console,但它现在并不是对/dev/tty0的符号链接。更多可参考console(4)。

/dev/tty是另一个特殊设备,它指向控制终端(controlling terminal)。如果某个进程的控制终端是/dev/tty3,那么/dev/tty就指向/dev/tty3了。控制终端是什么概念?它是一个进程的某个属性,是依附带该进程上的终端。比如我们在某个终端下输入ctrl+C,那么它控制的前台进程就会收到SIGINT,而后台进程会收到SIGTTIN或SIGTTOU ,如果它们读写该终端的话。被同一个终端控制的所有进程被称为一个会话(session),会话的领导就是创建改会话的进程,其子进程也会被该终端控制。所以,1) 需要交互的命令行程序通常会从/dev/tty这个设备进行读写;2) Unix后台进程都需要在fork之后调用setsid(2),3) 需要加O_NOCTTY,当你open一个可能是终端的文件时。

另外,想要确定/dev/tty究竟是指向哪个设备,可以调用TIOCCONS ioctl。参考tty(4)。

下面是另外一个概念——伪终端(pseudo-terminal),根据pty(7)的介绍,伪终端一对虚拟设备,提供端到端双向通信的通路,一端称为master,另一端称为slave。在slave那端看到的和在真实终端看到的效果一样。所以伪终端一般被ssh等网络登录程序使用。历史上,有两套伪终端接口,一个是Unix 98伪终端,另一个是BSD伪终端。

BSD提供的接口很简单:/dev/pty[p-za-e][0-9a-f] 是master; /dev/tty[p-za-e][0-9a-f] 是slave,它们都是配好对的。这样看起来很简单,但对程序员来说不容易,要找到一个合适的终端需要一个个从头尝试。所以这种方式已经被遗弃。而Unix 98伪终端则完全不同,它始终使用/dev/ptmx作为master复制设备,然后在每次打开它的时候才得到一个master设备的fd,同时在/dev/pts/目录下得到一个slave设备。这样编程就相对容易了,根据pts(4)介绍,需要三个新的API: ptsname(3),grantpt(3)和unlockpt(3)。我们可以通过一个实例看一下如何使用:

(以下代码摘自netvirt)
[c]
char mptname = “/dev/ptmx”; / master pseudo-tty device */
//…
void
getmaster()
{
struct stat stb;

if ((master = open(mptname, O_RDWR)) &gt;= 0) { /* a pseudo-tty is free */
    (void) ioctl(0, TCGETS, (char *)&amp;b);
    (void) ioctl(0, TIOCGWINSZ, (char *)&amp;size);
    return;
} else {                /* out of pseudo-tty's */
    perror(mptname);
    fprintf(stderr, gettext("Out of pseudo-tty'sn"));
    fail();
}

}

void
getslave()
{
char slavename; / name of slave pseudo-tty */

grantpt(master);        /* change permissions of slave */
unlockpt(master);            /* unlock slave */
slavename = ptsname(master);        /* get name of slave */
slave = open(slavename, O_RDWR);    /* open slave */
if (slave &lt; 0) {            /* error on opening slave */
    perror(slavename);
    fail();
}
ioctl(slave, I_PUSH, &quot;ptem&quot;);    /* push pt hw emulation module */
ioctl(slave, I_PUSH, &quot;ldterm&quot;);        /* push line discipline */

(void) ioctl(slave, TCSETSF, (char *)&amp;b);
(void) ioctl(slave, TIOCSWINSZ, (char *)&amp;size);

}
[/c]
然后我们再来看一下glibc中对ptsname(3)的实现:

(源文件sysdeps/unix/sysv/linux/ptsname.c)
[c]

define _PATH_DEVPTS "/dev/pts/"

char *
ptsname (int fd)
{
return __ptsname_r (fd, buffer, sizeof (buffer)) != 0 ? NULL : buffer;
}

int
__ptsname_r (int fd, char *buf, size_t buflen)
{

if (ioctl (fd, TIOCGPTN, &ptyno) == 0)

numbuf[sizeof (numbuf) - 1] = '';
p = _itoa_word (ptyno, &numbuf[sizeof (numbuf) - 1], 10, 0);

memcpy (
stpcpy (buf, devpts), p, &numbuf[sizeof (numbuf)] - p);
[/c]

我们可以看出,实际上是调用ioctl TIOCGPTN,通过内核,而Linux内核又是通过devpts这种文件系统实现了这一切:

$ mount | grep devpts
devpts on /dev/pts type devpts (rw,gid=5,mode=620)

这样我们终于把一切搞清楚了。:-)

Make pthread suck less

pthread的API一直让我感到头疼,种类多而且名字又长,今天下决心把它们理清楚。

仅从名字上来看,pthread的API可以分为这么几类:

pthread_XXX:
此类API一般是对thread本身进行管理的。共包括如下API:

pthread_atfork()
pthread_create()
pthread_exit()
pthread_cancel()
pthread_join()
pthread_once()
pthread_self()
pthread_equal()
pthread_kill()
pthread_detach()
pthread_yeild()
pthread_sigmask()
pthread_key_create()
pthread_key_delete()
pthread_cleanup_push()
pthread_cleanup_pop()
pthread_testcancel()

它还可以分出两个子类,包括pthread_setWWW和pthread_getWWW,其中有:

pthread_getconcurrency()
pthread_getcpuclockid()
pthread_getschedparam()
pthread_getspecific()

pthread_setcancelstate()
pthread_setconcurrency()
pthread_setschedparam()
pthread_setschedprio()
pthread_setspecific()

pthread_attr_YYY:
YYY一般包括:init, destory, setZZZ, getZZZ。此类API是对thread本身的属性进行管理的。共包括如下API:

pthread_attr_destroy()
pthread_attr_getinheritsched()
pthread_attr_getschedparam()
pthread_attr_getschedpolicy()
pthread_attr_getscope()
pthread_attr_getstackaddr()
pthread_attr_getstack()
pthread_attr_init()
pthread_attr_setdetachstate()
pthread_attr_setguardsize()
pthread_attr_setinheritsched()
pthread_attr_setschedparam()
pthread_attr_setschedpolicy()
pthread_attr_setscope()
pthread_attr_setstackaddr()
pthread_attr_setstack()
pthread_attr_setstacksize()

pthread_MMM_XXX:
XXX一般是上面和那个XXX集合类似的操作,但MMM一般是thread的一个工具,比如:mutex,cond等。此类API是对thread的MMM工具进行操作。共包括如下API:

mutex类:
pthread_mutex_init()
pthread_mutex_destroy()
pthread_mutex_lock()
pthread_mutex_unlock()
pthread_mutex_trylock()
pthread_mutex_setprioceiling()
pthread_mutex_getprioceiling()

cond类:
pthread_cond_init()
pthread_cond_destroy()
pthread_cond_signal()
pthread_cond_broadcast()
pthread_cond_wait()
pthread_cond_timedwait()

rwlock类:
pthread_rwlock_destroy()
pthread_rwlock_init()
pthread_rwlock_rdlock()
pthread_rwlock_timedrdlock()
pthread_rwlock_timedwrlock()
pthread_rwlock_tryrdlock()
pthread_rwlock_trywrlock()
pthread_rwlock_unlock()
pthread_rwlock_wrlock()

spin类:
pthread_spin_destroy()
pthread_spin_init()
pthread_spin_lock()
pthread_spin_trylock()
pthread_spin_unlock()

barrier类:
pthread_barrier_destroy()
pthread_barrier_init()
pthread_barrier_wait()

pthread_MMMattr_YYY:
MMM和YYY同上(spin除外),此类API是对MMM工具的属性进行操作。和上面有着密切的关系。共包括如下API:

mutex类:
pthread_mutexattr_destroy()
pthread_mutexattr_getprioceiling()
pthread_mutexattr_getprotocol()
pthread_mutexattr_getpshared()
pthread_mutexattr_gettype()
pthread_mutexattr_init()
pthread_mutexattr_setprioceiling()
pthread_mutexattr_setprotocol()
pthread_mutexattr_setpshared()
pthread_mutexattr_settype()

cond类:
pthread_condattr_destroy()
pthread_condattr_getclock()
pthread_condattr_getpshared()
pthread_condattr_init()
pthread_condattr_setclock()
pthread_condattr_setpshared()

rwlock类:
pthread_rwlockattr_destroy()
pthread_rwlockattr_getpshared()
pthread_rwlockattr_init()
pthread_rwlockattr_setpshared()

barrier类:
pthread_barrierattr_destroy()
pthread_barrierattr_getpshared()
pthread_barrierattr_init()
pthread_barrierattr_setpshared()

这还没完,还有N多的新类型,比如pthread_t,pthread_attr_t,pthread_once_t,以及随之而来的宏。。。我在这就不总结了。

最后,介绍pthread的书籍有:

“PThreads Primer”. Lewis, Bill and Daniel J. Berg. California: Prentice Hall.
“Pthreads Programming”. B. Nichols et al. O’Reilly and Associates.
“Programming With POSIX Threads”. D. Butenhof. Addison Wesley
“Programming With Threads”. S. Kleiman et al. Prentice Hall

查询SNMP OID的程序

网上有个查询的网站,可惜什么结果都查不出来!靠!我实在看不下去了,动手写一个python程序来搞定,不过仍有局限性,那就是只能查询.iso.org子树。。。啥也不说了,上代码!

[python]

!/usr/bin/env python

import os,sys
import string
import re
import urllib2

if name == ‘main‘:

if len(sys.argv) != 2:
    sys.stderr.write("Please provide one OID number or string to lookup.n");
    sys.exit(1)

flag = 0
found = False

r = re.compile('^[0-9\.]+$')
if r.match(sys.argv[1]):
    r = re.compile('^1\.3')
    if r.match(sys.argv[1]):
        flag = 1
    else:
        sys.stderr.write("Please provide the full OID number under .iso.org!n")
        sys.exit(1)

try:
    req = urllib2.Request('http://www.kix.in/plan9/mirror/sources/contrib/gabidiaz/root/lib/ndb/snmp')
    resp = urllib2.urlopen(req)
    oid = resp.readline()
    name = resp.readline()
    while oid and name:
        if flag == 1:
            if oid.find(sys.argv[1]) != -1:
                print name
                found = True
                break
        else:
            n = name.lower().find(sys.argv[1].lower())
            if n != -1:
                print oid
                found = True
                if n+len(sys.argv[1]) &lt; len(name)-1:
                    print name
        oid = resp.readline()
        name = resp.readline()
    if not found:
        print &quot;Not found!&quot;
    sys.exit(0)
except IOError:
    sys.stderr.write(&quot;Probably you don&#039;t have Internet.n&quot;)
    sys.exit(1)

[/python]