python中的excepthook

Python中,可以通过sys.excepthook来实现对Python程序中的exception的自定义处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import sys
import traceback

def exception_printer(exc_type, exc_obj, traceback_obj):
print("exception type: %s" % type(exc_type))
print("exception object: %r" % exc_obj)
print("traceback content:\n%s" % '\n'.join(traceback.format_tb(traceback_obj)))

def test():
sys.excepthook = exception_printer
raise StandardError('test')

if __name__ == '__main__':
test()

输出结果:

1
2
3
4
5
6
7
8
exception type: <type 'exceptions.StandardError'>
exception object: StandardError('test',)
traceback content:
File "C:\Users\richard\Desktop\test.py", line 14, in <module>
test()

File "C:\Users\richard\Desktop\test.py", line 11, in test
raise StandardError('test')

参考链接: https://docs.python.org/2/library/sys.html

使用Designer编写PyQt程序的简单流程

PyQt是一个非常方便的图形界面程序库,非常适合快速实现一个图形界面程序。Designer是Qt自带的一个界面设计工具,非常强大易用。

使用Designer的一般流程

  1. 打开Designer,编辑一个界面文件,保存为ui后缀的文件。假设我们创建了一个Main Window界面,这个ui文件名为MyWindow.ui。
  2. 使用pyuic4工具把ui文件转换成代码

    1
    pyuic4 -i 0 MyWindow.ui -o MyWindow.py

    在Windows上,可以写一个bat批处理来方便生成。使用下面这种方式可以在一个bat中添加多个转换。

    1
    cmd /c "pyuic4 -i 0 MyWindow.ui -o MyWindow.py"
  3. 查看生成的MyWindow.py文件,发现里面有一个类Ui_MainWindow。运行下面的代码就可以实现界面的展示了。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    from MyWindow import Ui_MainWindow

    class MainWindow(QtGui.QMainWindow, Ui_MainWindow):

    def __init__(self, parent=None):
    super(MainWindow, self).__init__(parent)
    self.setupUi(self)

    if __name__ == "__main__":
    app = QtGui.QApplication(sys.argv)

    window = MainWindow()
    window.show()

    sys.exit(app.exec_())
  4. 接下来,按照需要扩展其他功能。

C# Type Basic

C#中所有的类型都有同一个基类object

类型种类

  1. 值类型:简单类型、枚举类型、结构体类型、可空类型
  2. 引用类型:类类型、接口类型、数组类型、委托类型
    值类型可以通过boxing转换成引用类型,也可以从引用类型unboxing转换成值类型。转换过程中发生数据拷贝。

内置类型

sbyte/byte short/ushort int/uint long/ulong float double decimal bool char string
注意:
char类型跟C不一样,每一个char类型的变量可保存一个UTF-16字符,大小是2字节
bool不能与整数类型相互转换
decimal是用十进制存储的
string类型是.NET Framework中String的别名,是一个引用类型,其他内置类型都是值类型。

1
2
3
4
5
6
7
8
9
short i = 0, j=1;
//short k = i+j; //error, need conversion
short k = (short)(i+j) //right

// if(i) doSomething(); //error, integer cannot be implicitly convert to boolean type
if(i != 0) doSomething(); //right

float f = 1.0F // F/f is essential. type of 1.0 is double
decimal d = 1.0M // M/m is essential.

class

C#中class结构与Java的形式类似。
class类型的变量必须在堆上申请空间。
class是单继承的,但是可以实现多个接口。
class类型是引用类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class Person
{
private int age;
private string name;
public Person(int age, string name)
{
this.age = age;
this.name = name;
}
}
//inheritance
public class Student:Person
{
private string studentNo;
public Student(int age, string name, string studentNo) : base(age, name)
{
self.studentNo = studentNo;
}
}

struct

struct类型与类类型相似,但是不必在堆上申请空间,也不能被继承。
struct类型不能有无参数构造函数,没有虚函数,没有finalizer。
struct类型的构造函数必须初始化所有数据成员。
struct类型是值类型。

1
2
3
4
5
6
7
8
9
public struct Point
{
int x,y;
public Point(int x, int y)
{

this.x = x;
this.y = y;
}
}

interface

class类型和struct类型都可以实现interface。
interface所有成员都是public并且是abstract的。
interface只能包含方法成员。
interface也可以扩展interface。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public interface Phone
{
void Call(string number);
void SendSMS(string content);
}
public interface SmartPhone : Phone
{
void InstallApp(string app);
}
public class Android: SmartPhone
{
void Call(string number)
{

System.Console.WriteLine("calling " + number);
}
void SendSMS(string content)
{

System.Console.WriteLine("sending message: " + content);
}
void InstallApp(string app)
{

System.Console.WriteLine("installing app: " + app);
}
}

enum

enum默认底层用int实现。

1
2
3
4
5
6
enum Direction{ East, West, North, South}
enum Direction2 : byte { East, West, North, South } //use byte as underlaying type
[Flags]
enum Direction3{ East=0, West=1, North=2, South=4}
enum Direction3 d = Direction3.East | Direction3.North;
System.Console.WriteLine(d); //will print "East, North"

可空类型

可空类型是C#中比较特殊的类型。比如,int? a; int?是一种可空类型,a可以是一个int类型的值,也可以是null。

委托类型

委托类型类似C语言中的函数指针,但是它是类型安全的,并且是面向对象的。

1
2
3
4
5
delegate double Function(double x); //defined a new type 'Function'
static double Square(double x){
return x*x;
}
Function f = Square;

构造随机数生成器

最近在准备找工作,碰到一些有意思的题目,其中一种就是利用现有的随机数生成器来构造新的随机数生成器。做了两道题之后,有一点体会,就此记录下来。
假设原来的随机数生成器每个可能值出现的概率为p,新的随机数生成器每个可能值出现的概率为q。当p和q的大小关系不同时,有不同的做法。

p=q

这种情况下,新的随机数生成器是最好构造的,只需要将新的生成器的可能值与原有的生成器的可能值一一对应即可。

例如,已有r1是随机生成1-5的随机数生成器,那么随机生成7-11的随机数生成器r2=r1+6。更一般的情况可以使用switch-case结构来实现一一对应。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//simple example
int r1();
int r2(){
return r1()+6;
}

//general example
int r1();
int r2(){
int result = r1();
switch(result){
case r1_v1:
return r2_v1;
...
case r1_vn:
return r2_vn;
}
}

p<q

这种情况也比较好处理。只需要将一部分现有随机数生成器的可能值与新的随机数生成器的可能值做一一对应即可。

例如,已有r1是随机生成1-5的随机数生成器,那么随机生成0-1的随机数生成器r2的实现如下。更一般的情况可以使用switch-case结构来实现。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
//simple example
int r1();
int r2(){
int result;
do{
result = r1();
}while(result<3);
return result-1;
}

//general example
int r1();
int r2(){
int result;
do{
result = r1();
switch(result){
case r1_v1:
return r2_v1;
...
case r1_vn:
return r2_vn;
default:
//do nothing with other r2 possible values
}
}while(1);
}

p>q

这种情况想对困难一些,也是经常被考察的一种情况。如果能够将情况转换成p\<q情况,那么就能很好的解决。看一个具体的实例。已有一个随机数生成器r1,随机生成0或1,现在需要构造一个新的随机生成器r2,使得r2能够随机生成1-5。

r2的可能值有6个,而r1只有两个。要用r1生成6个以上的值,可以通过加法的形式来完成。如果用两个r1相加,最多可以生成4个值,如果用3个r1相加,最多可以生成8个值,所以至少要3个r1相加。如果是r1+r1+r1,虽然生成了8个值,但是8个值中有相同的,导致每个值的概率是不一样的,不能用作随机数生成器。需要找到一个方法,让生成的8个值互不相同。考虑r3=r1+2r1+4r1,可能的生成值为0, 1, 2, 4, 3, 5, 6, 7。这8个值互不相同,每个出现的概率都是相同的。这个时候r3就是一个新的随机数生成器,并且符合p\<q的情况,可以用p\<q的解法来解决问题。

在更一般的情况下,构造r3 = a1*r1+a2*r1+…+am*r1。为了生成足够的可能值,需要满足n_r1^m >= n_r2,其中n_r1和n_r2分别是r1和r2可能值的数目。在此基础上,还要让a1*r1+a2*r1+…+am*r1不出现重复值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
//simple example
int r1();
int r2(){
int result;
do{
result = r1()+2*r1()+4*r2;
}while(result<1 || result > 5);
return result;
}

//general example
int r1();
int r2(){
int result;
do{
result = a1*r1() + a2*r1() + ... + am*r1();
switch(result){
case v1:
return r2_v1;
...
case vn:
return r2_vn;
default:
//do nothing
}
}while(1);
}

Python笔记

这篇文章里将会总结Python中的各种小知识点。

input和raw_input

使用方法:

1
2
s = raw_input(tip)  # 'tip' is a tip
number = input(tip)

raw_input读入之后以字符串的形式存储,而input则会讲读入的字符串做一次求值,得到的值得类型跟字符串内容相同。
实际上,input是用raw_input来实现的。input(tip) — eval(raw_input(tip))

unpack

使用方法:

1
first, second, third, forth = sys.argv  # There must be enough data for unpacking, otherwise exception will be raised

Python常用标准库

sys

sys.argv
sys.exit([arg])
sys.path
sys.stdin
sys.stdout
sys.stderr

os

os.system(command)
os.environ

一些数据结构






数据结构所在包
set默认
heapheapq
dequecollections

time

time.time()
time.strftime(format)
time.strptime(string[, format])

random

random.random()
random.choice(seq)
random.shuffle(seq[, random])

shelve

shelve用于将内容存储到文件。

1
2
3
4
import shelve
s = shelve.open("output.dat")
s['key'] = value
s.close()

re

re.compile(pattern[, flags])
re.search(pattern, string[, flags])
re.match(pattern, string[, flags])
re.split(pattern, string[, maxsplit=0])
re.findall(pattern, string)
re.sub(pattern, replace, string[, count=0])
matchObj.group([groupNum1, …])
matchObj.start([groupNum])
matchObj.end([groupNum])
matchObj.span([groupNum])

LD_PRELOAD的作用

LD_PRELOAD用于指定提前加载一些动态库,这些动态库比libc.so等库装载更早,它们提供的函数能够屏蔽后加载的动态库中的函数。这个特性可以方便地用来截获库函数调用。

例如,有一个已经编译好的程序使用malloc分配内存,你想使用Google开发的tcmalloc来提升效率,使用LD_PRELOAD可以实现这个目的

下面举一个小例子来说用LD_PRELOAD的使用。

源文件

malloc.c
1
2
3
4
5
6
#include <stdio.h>
typedef unsigned long size_t;
void *malloc(size_t size){
printf("malloc(%ld)\n", size);
return NULL;
}
test.c
1
2
3
4
5
6
7
8
#include <stdio.h>
#include <stdlib.h>
int main(){
printf("start.\n");
malloc(10);
printf("end.\n");
return 0;
}

运行

首先,使用普通方法运行。

1
2
gcc test.c -o test
./test

程序输出:

1
2
start.
end.

接下来,讲malloc.c编译成动态链接库

1
2
gcc -fPIC -shared malloc.c -o malloc.so
LD_PRELOAD=./malloc.so ./test

程序输出:

1
2
3
start.
malloc(10)
end.

可以看出,使用LD_PRELOAD之后,我们自定义的malloc取代了库函数中的malloc。

Undefined reference to typeinfo

在项目中遇到了这样一个问题:C++文件编译都OK,但链接的时候报错:undefined reference to `typeinfo for xxx’。typeinfo是C++中的RTTI(RunTime Type Identification)机制中记录类型信息用的,dynamic_cast和typeid操作符会使用这些信息。

以”undefined reference to typeinfo”为关键字在网络上搜索,大多数都是说有虚函数定义了但是未实现导致的。但是我的代码显然不是这个情况。在我即将放弃的时候,终于在StackOverflow上发现有人提出,这种错误的原因也可能是混合使用了带RTTI信息和不带RTTI信息的代码导致的。对比检查,发现我的项目里的问题正是这个。最后用了一点dirty hack,解决了bug。下面就仔细分析一下”undefined reference to `typeinfo for xxx’”产生的原因。

虚函数未实现

产生”undefined reference to `typeinfo for xxx’”最常见的原因就是基类的虚函数未实现了。由于C++类的实现可以分布在多个源文件中,所以生成目标文件时,基类的虚函数没有定义是不会报错的。但是链接成可执行文件时,需要将虚函数的信息放进typeinfo中,这个时候虚函数未实现就会引发这个错误。

混用了no-RTTI代码和RTTI代码

我碰到的正是混用了no-RTTI和RTTI代码的情形。项目中我们自己写的程序必须开启RTTI,而我们使用的外部的一个库使用no-RTTI编译。我们在自己的代码中需要重载一个外部库中的带虚函数的类,结果链接的时候就出现了问题。外部库中的基类使用-fno-rtti选项编译,生成的代码没有typeinfo信息,而我们的代码使用-frtti选项编译,要求基类必须要有typeinfo信息。最后,我在编译系统中做了一些dirty hack,让那个派生类所在的源文件以-fno-rtti选项编译,解决了问题。

在LLVM IR中绑定外部的变量和函数

前面的文章中提到了如何利用LLVM的IR进行编程,这篇文章将讲述如何将LLVM IR外部的变量和函数与LLVM IR结合起来。

LLVM IR外部变量和外部函数

外部变量value和外部函数foo

1
2
3
4
5
int value = 10;

int foo(int x){
return 2*x;
}

声明LLVM IR中的外部变量和函数

声明一个外部变量value,为int类型。

1
2
LLVMContext & context = llvm::getGlobalContext();
GlobalVariable *v = cast<GlobalVariable>(module->getOrInsertGlobal("value", Type::getInt32Ty(context)));

声明一个外部函数foo,这个函数的原型为int foo(int x)

1
2
Function *f = cast<Function>(module->getOrInsertFunction("foo", Type::getInt32Ty(context),
Type::getInt32Ty(context), NULL));

构建一个LLVM IR表示的函数

接下来构建一个LLVM IR表示的函数bar。在bar中读入value的值,并作为foo的参数调用foo,bar再返回foo的返回值。

1
2
3
4
5
6
7
8
9
10
11
12
13
//create a LLVM function 'bar'
Function* bar = cast<Function>(module->getOrInsertFunction("bar", Type::getInt32Ty(context),NULL));

//basic block construction
BasicBlock* entry = BasicBlock::Create(context, "entry", bar);
IRBuilder<> builder(entry);

//read 'value'
Value * v_IR = builder.CreateLoad(v);
//call foo(value)
Value * ret = builder.CreateCall(f, v_IR);
//return return value of 'foo'
builder.CreateRet(ret);

创建ExecutionEngine

1
2
3
//create execution engine first
InitializeNativeTarget();
ExecutionEngine *ee = EngineBuilder(module).setEngineKind(EngineKind::JIT).create();

绑定LLVM IR外部的变量和函数

1
2
3
4
5
//map global variable
ee->addGlobalMapping(v, &value);

//map global function
ee->addGlobalMapping(f, (void *)foo);

JIT并运行

1
2
3
4
5
void *barAddr = ee->getPointerToFunction(bar);
typedef int (*FuncType)();
FuncType barFunc = (FuncType)barAddr;

std::cout << barFunc() << std::endl;

运行结果是20,正好符合语义。

重新绑定LLVM IR外部变量或者外部函数

重新绑定LLVM IR外部变量或函数可以通过updateGlobalMapping来实现。

1
2
ee->updateGlobalMapping(v, &value1);
ee->updateGlobalMapping(f, (void *)foo1);

不过重新绑定之后,需要重新JIT才可以将改变反应到生成代码上来。

本文示例地址:llvm-ir-global-mapping

使用llc C++后端辅助LLVM IR编程

llc是LLVM提供的一个工具,以LLVM bitcode或LLVM汇编为输入,根据指定的后端生成相应的代码。通常情况下,后端跟某一种ISA相关,比如x86、mips、arm等,指定这些后端就会生成在相应处理器上运行的机器码。但是llc还支持一种特殊的后端:cpp。指定这种后端的时候,输出的是C++代码,而这些C++代码正好就是利用LLVM的IR API编程。所以可以利用这种方法来学习如何进行LLVM的IR编程。

检查llc是否支持cpp后端

有时候发行版自带的llc可能并不支持cpp后端,比如ubuntu 13.04。查看llc支持的后端可以用以下命令

1
llc --version

如果输出中包含”cpp - C++ backend”,那么说明支持C++后端了。

编写源文件

这里给一个最简单的示例test.c

1
int main(){return 0;}

生成bitcode

生成LLVM的bitcode可以用clang,也可以用llvmgcc。

clang:

1
clang -emit-llvm -c test.c

llvmgcc:

1
llvmgcc -emit-llvm -c test.c

生成的LLVM bitcode代码文件后缀为.o

生成LLVM汇编(可选)

可以从源文件生成LLVM汇编,也可以从bitcode生成汇编。

使用clang从源文件生成LLVM汇编:

1
clang -emit-llvm -S test.c

使用llvmgcc从源文件生成LLVM汇编:

1
llvmgcc -emit-llvm -S test.c

使用llvm-dis从bitcode生成LLVM汇编

1
llvm-dis test.o

生成的LLVM汇编内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
; ModuleID = 'test.o'
target datalayout = "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-s0:64:64-f80:128:128-n8:16:32:64-S128"
target triple = "x86_64-unknown-linux-gnu"

; Function Attrs: nounwind uwtable
define i32 @main() #0 {
entry:
%retval = alloca i32, align 4
store i32 0, i32* %retval
ret i32 0
}

attributes #0 = { nounwind uwtable "less-precise-fpmad"="false" "no-frame-pointer-elim"="true" "no-frame-pointer-elim-non-leaf"="true" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "unsafe-fp-math"="false" "use-soft-float"="false" }

生成C++代码

使用llc生成C++代码,输入可以是bitcode,也可以是LLVM汇编。
使用bitcode作为输入:

1
llc -march=cpp test.o

使用LLVM汇编作为输入,假设汇编文件为test.s:

1
llc -march=cpp test.s

会生成一个test.o.cpp或test.s.cpp文件,从这个文件可以看到如何利用LLVM的IR进行编程。

生成的代码有一百多行,在此就不贴出来了。

如何使用Calibre Recipe

Calibre Recipe是Calibre用来抓取网页或RSS并制作成电子书的脚本。Calibre Recipe的使用方法有两种:

图形界面下使用

添加Recipe

  1. 首先打开Calibre
  2. 点击“抓取新闻”右边的的“v”,选择添加“自定义新闻源”
  3. 点击“切换到高级模式”
  4. 将下载的Recipe文件的内容复制到高级模式下的文本框里,完全覆盖原来的内容
  5. 点击“添加/更新订阅清单”
  6. 关闭,选“是”即可

下载转换

  1. 在Calibre主界面点击“抓取新闻”
  2. 选择“自定义”
  3. 选择要下载的条目,点击右下角的“立即下载”
  4. 或者勾选“计划下载”,设置按计划下载。

命令行下使用

在安装了Calibre的机器上,可以使用如下命令来使用Recipe下载并转换电子书:

假设Recipe的名称为myrecipe.recipe。

生成mobi

1
ebook-convert myrecipe.recipe .mobi

生成epub

1
ebook-convert myrecipe.recipe .epub

欢迎下载使用我写的recipe