2009年4月29日星期三

VC2008链接MSVCRT.DLL不完全折腾

Ruby在Windows上有一个很讨厌的问题,其动态链接库文件名中包含了CRT库的文件名。例如用VC6编译的Ruby 1.8.6库名为msvcrt-ruby18.dll,而VC2008编译的则叫msvcr90-ruby18.dll。网上能找到的Ruby C库几乎都是用VC6编译的,如果你用的Ruby不是VC6编译的那就没法用。

造成这个问题的主要原因是不同版本VC的CRT库不兼容,ERRNO、alloc、free等在不同版本的CRT中混用会出问题。虽然这些问题似乎都可以绕过去,可Ruby社区的大牛小牛都不太喜欢搭理Windows的样子。总之现状就是,要么老实用VC6,要么所有东西都自己编译一遍。

我为啥会对这个问题有兴趣?因为新版本的VC编译出来的东西通常情况下会快一点,例如Ruby 1.9.1中的dl模块,VC2008编译出来的代码比VC6编译的至少要快50%。

几天前在网上翻到一个法子,说是链接Windows Driver Kit中包含的msvcrt.lib即可使用旧版CRT,于是忍不住试了试。结果,还是有问题。

尝试用VC2008和WDK编译一个Hello World,的确是依赖msvcrt.dll。可是在编译Ruby 1.9.1的时候,其他错误都可以通过修改代码解决,但最后却卡在environ上,“无法解析的外部符号 __imp___environ”。

写段测试代码:

#include <stdio.h>
#include "stdlib.h"

int main( int argc, char *argv[] ) {
  char **rb_origenviron = environ;
  printf( "Hello World!\n" );
  printf("%s\n", rb_origenviron[0]);
  
  return 0;
}

cl /MD t.c,问题依旧。

折腾失败……

2009年4月9日星期四

Unicode In Ruby 1.9

Ruby 1.9终于抛弃了丑陋的jcode,提供一定程度上的Unicode支持。只不过我总觉得有点儿不对劲,Ruby当前采用的处理方式,很可能会带来一些新的问题。

String有了encoding属性,部分方法的处理单位也由字节改为字符。源码文件的默认编码为US-ASCII,如果在代码中写了中文,就必须指定encoding。如果不想写,也可以用BOM。

# encoding: utf-8

s = '幺贰和叁'
puts s.encoding     # => UTF-8
puts s.length     # => 4
puts s.bytesize     # => 12

不同encoding字符串之间可以直接比较,也就是说从今以后比较字符串都要考虑编码问题,搞不好会有很多Bug因此而产生。

# encoding: utf-8

s = '位'.encode('gbk')
t = 'λ'
puts s.bytes.to_a == t.bytes.to_a # => true
puts s == t # => false
puts s == t.force_encoding('gbk') # => true

正则表达式方面更乱,encoding不同“=~”一定会报错,但match方法却不一定。

# encoding: utf-8

s = 'abc'.encode('gbk')
p s.match(/b/) # => #<MatchData "b">

s =~ /b/ # => incompatible encoding regexp match (UTF-8 regexp with GBK string) (Encoding::CompatibilityError)

s = '位'.encode('gbk')
p s.match('λ') # => incompatible encoding regexp match (UTF-8 regexp with GBK string) (Encoding::CompatibilityError)

IO方面也添加了encoding支持,不过还不支持BOM,所以读取带BOM的文件得多一个步骤。

File.open('utf8_with_bom.txt', 'r:utf-8') do |f|
  f.ungetc c unless (c = f.getc)=="\uFEFF"
  # ...
end

Python 3.0中一个很大的变化就是区分了String和Byte,所有String都是Unicode,说不定有一天Ruby也会走Python的老路,希望这只是我乌鸦嘴吧。

2009年4月7日星期二

Ruby/GD2

已更新,新内容参见:RGD - libgd binding for Ruby

Ruby/GD2是一个GD library的Ruby binding,支持GD2、Ruby 1.9,可运行于MSWin32平台。

本扩展很多代码都源自Ruby/GD v0.8.0,但由于API变化比较大,且不打算支持GD2之前的版本,故改名Ruby/GD2发布。由于我没有查到Ruby/GD采用的是何种许可证,如果将本扩展用于商业用途,请自行联系Ruby/GD当前维护者Alain Hoang,以确定没有法律问题。(真有人去问,也请告诉我一声。)

如果跟我一样在Windows下用Ruby 1.9.1,压缩包中有编译好的文件可以直接用。将bgd.dll放到Ruby的bin下,GD2.so放到site_ruby\1.9.1\i386-msvcrt下即可。我不确定是否可以在Ruby 1.8下编译,应该没有问题。不过用在Linux或OSX下,可能需要修改一下函数指针的定义。

文档暂时没有,RDoc感觉有点不好用且不会用。不过除了Ruby/GD的原始文档外,也可以参考CPAN上的GD模块,尽可能与该模块保持API兼容是我的初衷之一。

我自己肯定用不到GD库的这么多功能,错漏在所难免,有任何意见、建议、补丁,欢迎留言。

最后是一句废话:拿这个扩展去打水印的话,麻烦请考虑一下你是否有修改他人图片的权利,以及水印本身是否美观,谢谢。

require 'GD2'

def copy_rect(tname, fname, offx, offy, width, height, oname)
  timg = GD2::Image.new(tname)
  fimg = GD2::Image.new(fname)
  
  w, h = timg.bounds
  
  timg.copy(fimg, w-offx, h-offy, w-offx, h-offy, width, height)
  
  timg.jpeg(oname, 80)
end