软件开发: 08 2007存档

做项目时我需要序列化STL的map,当然,map本身没有这个功能,我只能自己写。必须写这么各类:能方便的序列化各种map,比如 map<string,sizet>、map< string,vector<sizet> >。我首先想到的是模板特化:

template<class T>
class ContainerSerial
{
};

typedef map<string,size_t> StringSizeMap;

template<>
class ContainerSerial<StringSizeMap>
{
public:
    ContainerSerial(StringSizeMap& m):m_container(m){}

    //对应StringSizeMap的实现
    save(){...}
    load(){...}

private:
   StringSizeMap& m_container;

};

typedef map< string,vector<size_t> > StringVecSizeMap;

template<>
class ContainerSerial<StringVecSizeMap>
{
public:

    ContainerSerial(StringVecSizeMap& m):m_container(m){}

    //对应StringVecSizeMap的实现
    save(){...}
    load(){...}

private:
   StringVecSizeMap& m_container;

};
这样在使用时就可以挑选想要使用的ContainerSerial:

StringSizeMap myMap;
ContainerSerail<StringSizeMap> serialer(myMap);
serialer.save();
serialer.load();
 
这个实现有几个问题:
1
.   ContainerSerial的生命周期依赖于StringSizeMap,看以上代码,如果myMap析构了,serialer却还在那里save、 load,肯定是core dump.在c++中,对象的生命周期管理是很费精力的事,所以要避免两个对象产生生命期依赖。

2.   由于是模板,整个实现代码都放在一个ContainerSerial.h文件里,其它cpp文件来include它,于是各个类对应的object文件都带有ContainerSerial的实现,最后链接时造成"function xxx redefined"...这个问题虽然可以绕开,但使用模板特化毕竟是比较复杂的事情,它的引入难免对源代码结构产生冲击。

其实干嘛想这么复杂呢?用c++的函数重载就可以做到了!

//ContainerSerial.h:

saveContainer(StringSizeMap& m);
loadContainer(StringSizeMap& m);

saveContainer(StringVecSizeMap& m);
loadContainer(StringVecSizeMap& m);

//ContainerSerial.cpp:

saveContainer(StringSizeMap& m)
{...}

loadContainer(StringSizeMap& m)
{...}

saveContainer(StringVecSizeMap& m)
{...}

loadContainer(StringVecSizeMap& m)
{...}

可以用PBreak到youtube等使用flv格式的视频网站上方便的下载视频。
试过很多号称能下载视频的插件,只有这个能用 :)

1. 下载安装
2. 进入youtube的页面,选择你喜欢的视频网页
3. 此时firefox右下角会有一个斜箭头,点击即可下载视频!

插件地址: https://addons.mozilla.org/en-US/firefox/addon/4252
操作数量较大的文件:
billing1.edu.cnb /home/y/var/p4p% find clickrec -name "BILL???200507*" > c07.lst

billing1.edu.cnb /home/y/var/p4p% tar -T c07.lst -czf clickrec.200507.tar.gz

billing1.edu.cnb /home/y/var/p4p% cat c07.lst | xargs rm
Boost.assignment库的使用及简单分析

相信大多数使用STL的人都是为了使用里面的容器,使用vector、list、map的程序员对以下代码可以说是非常熟悉了:

vector i_v;

i_v.push_back(1);

i_v.push_back(2);

i_v.push_back(3);

i_v.push_back(4);

i_v.push_back(5);
挺枯燥,是吧?用boost的assignment库可以让这一过程简洁得多:


#include "boost/assign/std/vector.hpp"

using namespace
boost::assign;

vector i_v;

i_v += 1,2,3,4,5;
效果与上面的程序一致,可读性却好很多,看上去有点“脚本语言”的感觉了。 对于关联容器,也有类似的便洁方法:

#include "boost/assign/list_inserter.hpp"

#include "string"

using namespace
std;

using namespace
boost::assign;map months;

insert( months )

(
"january", 31 )( "february", 28 )

(
"march", 31 )( "april", 30 )

(
"may", 31 )( "june", 30 )

(
"july", 31 )( "august", 31 )

(
"september", 30 )( "october", 31 )

(
"november", 30 )( "december", 31 );

分析assignment的源代码,先看 assign
/std/vector.hpp:

template
< class V, class A, class V2 >

inline
list_inserter< assign_detail::call_push_back< std::vector >, V >

operator
+=( std::vector & c, V2 v )

{


return
push_back( c )( v );

}


//assign/List_inserter.hpp:


template
< class C >

inline
list_inserter< assign_detail::call_push_back ,

BOOST_DEDUCED_TYPENAME C::value_type >

push_back( C& c )

{


static
BOOST_DEDUCED_TYPENAME C::value_type* p = 0;

return
make_list_inserter( assign_detail::call_push_back ( c ), p );

}


template
< class Function, class Argument >

inline
list_inserter

make_list_inserter( Function fun, Argument* )

{


return
list_inserter ( fun );

}


重载了+=操作符,所以才有 i_v+=1,2,3,4,5 这种写法。可以看出 push_back函数返回一个list_inserter,对应例子,push_back(c)(v)即为push_back(i_v)(1),返回一个仿函数list_inserter <:call_push_back ="">>, int>(assign_detail::call_push_back(i_v))(1) 。 那数字间的逗号又是怎么处理的呢?不知道大家还记不记得 std::cout<< 1 << 2 << 3; 此处的<<和assign中的逗号其实是一个道理,<<操作符返回一个iostream,而逗号操作符返回一个 list_inserter (+=操作符返回第一个list_inserter,其它的逗号“跟进”):

template
< class Function, class Argument = assign_detail::forward_n_arguments >

class
list_inserter

{


list_inserter( Function fun ) : insert_( fun )

{}

....


template
< class T >

list_inserter& operator,( const T& r )

{


insert_( r );

return
*this;

}



private
: Function insert_;

}
这里的insert_就是先前的assign_detail::call_push_back ( c ),所以insert_( r )就是assign_detail::call_push_back ( c )( r ), 看看
:

template
< class C >

class
call_push_back

{


C& c_; public: call_push_back( C& c ) : c_( c )

{ }


template
< class T > void operator()( T r )

{


c_.push_back( r );

}

};
实际是调用了c.push_back(r),对应到上面的例子,也就是调用了 i_v.push_back(1) 。 至于map的insert,实现与此类似。
Python , Delphi , Loki 三个单词有趣的来源

许多程序设计语言和开发工具以及库的名字都来源与神话,颇有意思:

     阿波罗是同时出现在希腊和罗马神话中的奥林帕斯十二大神之一。他除了拥有太阳神的身分,同时还有预言、艺术、医术之神的称号。阿波罗是宙斯和乐朵的儿子,和阿蒂蜜斯是双胞胎兄妹。

  阿波罗出生几天后便长大成人。为了寻找预言的圣地,他特地前往德尔斐(Delphi)。路途中,他先到达哈利亚多斯,原本打算将宁芙仙子泰尔普沙(Thelpusa)所在的水泉──一个拥有预言能力的水泉──立为圣地,但是泰尔普沙推荐,德尔斐应该更适合阿波罗。事实上泰尔普沙是不希望自己的圣地被夺走,明明知道德尔斐是一只拥有预言能力的母巨蟒毕颂(Python)的地盘,还故意让阿波罗到德尔斐去。

当然python语言的开发者是喜欢一个叫python的马戏团才给自己的脚本语言起名的,但Python这个单词的出处应该是在此。

至于c++中的Loki库:

      Loki为北欧神话中的火神,也是邪恶与灾难之神。虽为巨人族之子却有着与北欧众神一样的神族地位,但最终与诸神对立并引发了最终战争Ragnarok——诸神之黄昏。
1. 用LWP::Simple中的get下了一个HTML,用模式匹配解析url,可气的是明明看见了某个url,用模式匹配却匹配不到,考虑到html中有换行,所以我用了参数/s以 将多行折起来,但是依然无效。后来发现必须用 s/[ s]//g 把其中的stopword全去掉才行。因为模式匹配遇到这些符号会中断。

2. 著名的Text::Iconv可以把UTF-8转为GBK( 路人皆知了:) ),但html中有很多符号会打断这一转码过程,结果输出的是空。应该用 new Convert("utf-8", "gbk//ignore) 创建convert,这样即使发现了不能顺利转换某个字符,也能跳过它继续处理其它字符,避免输出为空。

开发中遇到问题:一个用xml格式写的后缀名为.mod的文件,用vim打开它没有语法颜色,输入命令 set filetype=xml 可以了,但不可能每次打开它都输这么一句,所以修改~/.vimrc,加入以下几句:

if has("autocmd")

       autocmd BufNewFile,Bufread *.mod set syntax=xml

endif

以后每次打开后缀为.mod的文件,vim就以xml语法颜色处理。

利用setjmp和longjmp处理异常

      c语言种可以利用setjmp和longjmp来实现程序的直接跳转,这种机制并不适用于在程序开发中设计逻辑流程,但用来实现异常处理机制则是非常的适用。

      比如写一个实现内存分配的函数,可以这样:

void *allocate(unsigned n)
{
 void *new=malloc();

 if (new)
  return new;

 if (Allocation_handled)    //如果new为NULL,则执行到此判断
  longjmp(Allocate_Failed,1); //跳转

 assert(0);
}

      而在调用时,可以先设好跳转的“目的地”,再调用allocate:

char *buf;
Allocation_handled=1;

if ( setjmp(Allocate_Failed) )
{
 fprintf(stderr,"Couldn't allocate the buffer! ");
 exit(1);
}

buf = allocate(1000000);

Allocation_handled=0

      为了使用方便可以把以上setjmp和longjmp做一个宏定义,这里就不细说了。但总的来说这个异常处理机制是不如c++的那么方便,但对于c语言来说也算不错了。

c语言通过动态链接库调用c++函数c++调用c函数是比较简单的,但c调用c++函数则要麻烦一些,除了用动态链接库,我暂时也没有找到其它方法.

// vector.cpp:

#include <vector>
#include <string>

using namespace std;

extern "C"
{

void vector_add_string(char* str);
void vector_list_strings(char* buff);

vector<string> _stringList;

void vector_add_string(char* str)
{
_stringList.push_back( string(str) );
}

void vector_list_strings(char* buff)
{
int len=0;

for( vector<string>::iterator it=_stringList.begin();it!=_stringList.end();++it)
{
strcpy( buff+len,(*it).c_str());
len+=it->length();
}
}

}

编译程动态链接库:
g++ -shared -o vector.so vector.cpp

// test.c

#include "stdio.h"
#include "dlfcn.h"

void (*add)(char*);
void (*list)(char*);

int main(void)
{
char buff[1024];

void *dp;
dp=dlopen("./vector.so",RTLD_LAZY);

add=dlsym(dp,"vector_add_string");
list=dlsym(dp,"vector_list_strings");

add("hello");
add("bye");

list(buff);

printf("%s",buff);

return 0;
}

编译:
gcc -rdynamic -s -o test test.c -ldl
(注意: -ldl 是必须加的)

1.分两个程序:boot.asm 和 prog.asm其中boot.asm负责将软盘中第2个扇区开始的内容载入内存的 1000:0的地址然后跳转到此。而prog.asm负责进入实模式并输出字符"A"

2.使用nasm编译,用partcopy拷入软盘(或镜像),用bochs启动。

3.源代码:

boot.asm:

[org 0x7c00]
jmp start

start:

mov ax,cs
mov ds,ax
mov es,ax

reset:
;重置软驱
mov ax,0
mov dl,0
int 13h

jc reset

read:
;从第二个扇区开始读,将其后六个扇区的内容读到内存 0x1000:0 的位置
mov ax,1000h
mov es,ax
mov bx,0h

mov ah,2
mov al,6
mov ch,0
mov cl,2
mov dh,0
mov dl,0
int 13h

jc read

jmp 1000h:0h

times 510-($-$$) db 0
dw 0AA55h


prog.asm:

[org 0]

[bits 16]
mov ax,cs
mov ds,ax

jmp init
gdtr :
dw gdt_end-gdt-1 ; gdt的长度
dd gdt+0x10000 ; gdt的物理地址
gdt
gdt0 ; 空描述符
dd 0
dd 0 ; 所有的段描述符都是64位的
codesel equ $-gdt ; 这是8h也就是gdt的第二个描述符
code_gdt ;代码段描述符
dw 0x07ff ; 段描述符的界限是4Gb
dw 0x0000
db 0x01
db 0x09a
db 0x0c0
db 0x00

datasel equ $-gdt
data_gdt ;数据段描述符,这里只是写出来,并没有使用
dw 0x07ff
dw 0x0000
db 0x01
db 0x092
db 0x0c0
db 0x00

videosel equ $-gdt
video_gdt ;视频缓冲区的基址
dw 0xffff
dw 0x8000 ; 基址是0xb8000
db 0x0b
db 0x92
db 0xc0
db 0x00
gdt_end


[bits 16]
init:

mov ebx,0x10000
mov ecx,gdt

protect:

cli
lgdt [gdtr] ;加载GDT

push ax ;开A20线
in al,92h
or al,00000010b
out 92h,al
pop ax

mov eax,cr0 ;置位,切换到保护模式
or al,00000001b
mov cr0,eax

jmp codesel:code_32 ;跳入32位代码

[bits 32]
code_32:

mov ax,videosel
mov gs,ax

mov word[gs:0],0x0741 ;输出白色大写字母'A'

over:
jmp over

times 2048-($-$$) db 0

#TITLE=ASM
;===================================================================
; 80x86
; Assembly Language Syntax File
;
; Written
; by
; Chris Hobbs
;
; Owner: Lightning Software
; Http://www.fastsoftware.com
;
; This file is provided as a syntax file for ASM source code.
; It has been arranged for MASM code but will highlight
; most TASM/NASM code as well. Some special cases may exist in the
; keywords that you will need to change.
;
; The keywords are arranged as follows:
;
; 1 - Pseudo-Ops
; 2 - Mnuemonics
; 3 - Types
; 4 - MASM High Level Syntax
; 5 - Registers
;===================================================================


#DELIMITER=,[]-+*%/="'~!&|<>?:;#()
#QUOTATION1='
#QUOTATION2="
#CONTINUE_QUOTE=n
#LINECOMMENT=!
#LINECOMMENT2=;
#LINECOMMENT3=#
#COMMENTON=/*
#COMMENTOFF=*/
#COMMENTON2=
#COMMENTOFF2=
#ESCAPE=
#CASE=n
#PREFIX1=
#PREFIX2=
#PREFIX3=
#PREFIX4=
#PREFIX5=
#SUFFIX1=
#SUFFIX2=
#SUFFIX3=
#SUFFIX4=
#SUFFIX5=

;==========================================
; These are the psuedo-ops
;==========================================
#KEYWORD
COMM
CATSTR
@CATSTR
ORG
END
EQU
ALIGN
ASSUME
.CODE
.CONST
.CREF
.ERR
.ERRB
.ERRNB
.ERRDEF
.ERRNDEF
.ERRDEFI
.ERRIDNI
.ERRE
.ERRNZ
.NOCREF
.MODEL
.RADIX
.DATA?
.STACK
ECHO
ENDM
ENDS
TEXTEQU
EXITM
EXTERN
EXTERNDEF
FOR
FORC
GOTO
INCLUDE
INCLUDELIB
INVOKE
@INSTR
INSTR
LOCAL
MACRO
OPTION
CASEMAP
LANGUAGE
LJMP
OFFSET
PROC
PROLOGUE
EPILOGUE
SCOPED
NOSCOPED
PROTO
PUBLIC
PURGE
PUSHCONTEXT
POPCONTEXT
RECORD
REPEAT
REPT
STRUCT
SIZESTR
@SIZESTR
SUBSTR
@SUBSTR
TYPEDEF
UNION
USES
IFB
IFNB
IFDEF
IFNDEF
STRUC
ADDR
SIZEOF
PTR
;==========================================
; End of the psuedo-ops
;==========================================

;==========================================
; These are all of the mnuemonics
;==========================================
#KEYWORD
AAA
AAD
AAM
AAS
ADC
ADD
ADDW
ADDL
AND
ANDB
ANDW
ANDL
ARPL
BOUND
BSF
BSFL
BSR
BSWAP
BT
BTC
BTR
BTRL
BTS
CALL
CBW
CDQ
CLC
CLD
CLI
CLTS
CMC
CMP
CMPW
CMPL
CMPS
CMPXCHG
CWD
CWDE
DAA
DAS
DEC
DIV
ENTER
ESC
HLT
IDIV
IMUL
IN
INB
INW
INC
INCW
INCL
INS
INT
INTO
INVD
INVLPG
IRET
IRETD
JCXZ
JECXZ
JMP
JMPI
LAHF
LAR
LDS
LEA
LEAVE
LES
LFS
LGDT
LIDT
LGS
LLDT
LMSW
LOCK
LODS
LOOP
LOOPE
LOOPZ
LOOPNZ
LOOPNE
LSL
LSS
LTR
MOV
MOVB
MOVW
MOVL
MOVS
MOVSX
MOVZX
MUL
NEG
NOP
NOT
NOTL
OR
OUT
OUTB
OUTW
OUTS
POP
POPW
POPL
POPA
POPAD
POPF
POPFW
POPFL
POPFD
PUSH
PUSHL
PUSHA
PUSHAD
PUSHF
PUSHFL
PUSHFD
RCL
RCR
REP
REPE
REPZ
REPNE
REPNZ
RET
RETF
ROL
ROR
RORL
SAHF
SAL
SHL
SAR
SBB
SCAS
SETAE
SETNB
SETB
SETNAE
SETBE
SETNA
SETE
SETZ
SETNE
SETNZ
SETL
SETNGE
SETNL
SETGE
SETLE
SETNG
SETG
SETNLE
SETS
SETNS
SETC
SETNC
SETO
SETNO
SETP
SETPE
SETNP
SETPO
SGDT
SIDT
SHL
SHR
SHLD
SHRD
SLDT
SMSW
STC
STD
STI
STOS
STR
SUB
TEST
TESTL
VERR
VERW
WAIT
FWAIT
WBINVD
XCHG
XCHGL
XLAT
XLATB
XOR
XORL
JA
JAE
JB
JBE
JC
JGE
JE
JG
JL
JLE
JNA
JNAE
JNB
JNBE
JNC
JNE
JNG
JNGE
JNL
JNLE
JNO
JNP
JNS
JNZ
JO
JP
JPE
JPO
JS
JZ
;==========================================
; End of the mnuemonics
;==========================================

;==========================================
; These are all of the types
;==========================================
#KEYWORD
DB
DW
DD
DF
DQ
DT
BYTE
WORD
DWORD
FWORD
QWORD
TBYTE
SBYTE
SWORD
REAL4
REAL8
REAL10
SDWORD
;==========================================
; End of the types
;==========================================

;==========================================
; These are all of the MASM HL syntax
;==========================================
#KEYWORD
.BREAK
.CONTINUE
.IF
.ELSEIF
.ENDIF
.ELSE
.REPEAT
.UNTIL
.UNTILCXZ
.WHILE
.ENDW

#KEYWORD
.word
.long
.quad
.fill
.align
.globl
.text
.data
.bss
.byte
.ascii
;==========================================
; End of the MASM HL syntax
;==========================================

;==========================================
; These are all of the registers
;==========================================
#KEYWORD
AL
AH
AX
BL
BH
BX
CL
CH
CX
DL
DH
DX
SI
DI
ESI
EDI
EAX
EBX
ECX
EDX
CS
DS
SS
ES
FS
GS
SP
BP
IP
EIP
;==========================================
; End of the registers
;==========================================
        项目要求对某个串进行blowfish的加密,而php又没有现成的blowfish加密算法(好像是有一个,但需要重新编译PHP,太麻烦了,公司几十台机器,没空一台台的编译),于是在blowfish的网站上下了源代码,再根据PHP Module的框架,写了一个简单的Module。
        遇到的第一个麻烦是:blowfish是对64bit数据进行加密的,长的待加密串必须切分成8个字节一组进行加密,但如果要加密三个字符串,那该怎么办?在网上找了好一阵才知道:后面补零。
        但不久又遇到一个麻烦:对一段字符加密后再解密发现还原不了,更糟糕的是有的字符串是这样,有的字符串却又能还原。晕。问了苏宁,他也觉得匪夷所思。最后才想到原因:PHP Module里二进制串直接赋值是不可靠的,必须先base64进行encode!也就是说blowfish加密后面必须紧跟base64的encode,不然得出的二进制串是难以直接使用的。
        写PHP Module还挺长见识,对了,我可以为FlatDB也写一个PHP Module来方便调用。