LongLong's Blog

分享IT技术,分享生活感悟,热爱摄影,热爱航天。

代码的可移植性

在最开始学习编程的时候,一般的书中都会提到代码的可移植性的问题,其中主要包括硬件平台的可移植性,操作系统的可移植性,运行环境的可移植性等。然而目前很多的代码都是在PC + Windows平台下开发和测试的,开发人员一般也不会考虑太多可移植性的问题。而在很多领域对于平台小型化的要求,不能够使用x86 + Windows的架构,而我们肯定不想为此独立维护两套代码,一般来说充分考虑代码的可移植性就能够解决这一问题。

以下简单说明一下代码在Windows系统和Linux系统下实现可移植的一些问题。

1. 文件名大小写问题

不同的操作系统对于文件名的处理方式不同,而比较大的一个差异在于是否区分文件名的英文大小写,Windows不区分,而Linux和BSD则区分,MacOS需要取决于使用的文件系统。虽然Windows系统下不会区分文件名的大小写,但当文件被复制到其他区分文件名大小写的操作系统时,则会以当前文件命名时的状态被复制。其中对于代码中的影响则主要在include代码文件时,因此在使用include语句时只要保证包含的文件名和文件命名时使用的字母大小写一致就可以做到不同系统的兼容。

/*
目录下的文件如下
Test.h  main.c
这里需要和看到的文件名完全一致
*/
#include "Test.h"

int main() {
    return 0;
}

2. 文件目录结构

Linux等操作系统使用的是单根节点的文件树,目录之间的分割为/符号,而Windows则使用了磁盘分区的概念,同时目录之间的分割符号为\符号。一般可以通过使用相对路径来避免处理根的问题,然后通过预编译条件定义使用的目录分割符号。

#include <string>
#include <iostream>
#ifdef _WIN32
#define DIRECTORY_SEPARATOR '\\'
#else
#define DIRECTORY_SEPARATOR '/'
#endif
int main() {
    std::string path = "usr";
    path += DIRECTORY_SEPARATOR;
    path += "bin";
    std::cout << path << std::endl;
    return 0;
}

以上代码在不同的操作系统下会有不同的输出

Windows下输出为
usr\bin

Linux下输出为
usr/bin

这里也给出了一种判断当前操作系统是否为Windows的方法,即利用预定义变量_WIN32

3. 动态库的导出和导入

Windows的动态库导出和使用时相对Linux等操作系统要复杂一些,导出动态库时要在函数前声明_declspec(dllexport),导入声明时需要在函数前声明_declspec(dllimport)。而对于这一点,通常也可以使用预编译语句进行处理——定义两个宏,在Linux系统下其值为空,Windows系统下为对应的两个声明语句,即

#ifdef _WIN32
#define DLLExport _declspec(dllexport)
#define DLLImport _declspec(dllimport)
#else
#define DLLExport
#define DLLImport
#endif

然后在函数声明时,使用定义的两个宏DLLExport和DLLImport,以达到兼容不同系统的作用。

4. 调用Fortran代码

在Windows中调用Fortran代码需要在声明时增加__stdcall声明,Linux中则不用,为此可以使用上一节中的方法进行处理,另外Linux下引入Fortran代码时,函数名最后要增加一个下划线,可以先声明带有下划线的函数名,然后在通过定义一个宏来实现和原来一致的函数名

例如以下的Fortran代码

real function F(X)
    implicit none
    real :: X
    F = X * X
end function

导入到C语言中

#include <stdio.h>
#ifdef _WIN32
float __stdcall f(float*);
#else
float f_(float*);
#define f f_
#endif
int main() {
    float x = 12.0;
    float y = f_(&x);
    printf("%f\n", y);
    return 0;
}

微信机器人

有些使用我们可能希望使用普通的微信帐号来实现对消息的自动处理和回复功能,之前使用过XMPP协议做过一个GTalk的机器人,能够自动使用歌词不断更换签名档。而微信使用的是自行设计的协议,所以要实现微信的机器人需要先分析其通信协议,不过目前已有不少开源的根据Web版微信分析出的接口库可以使用,主要是使用Python和Nodejs实现的。

1. wxBot的安装

这里使用的是一个名为wxBot的开源项目提供的微信接口,其官方Github地址为https://github.com/liuwons/wxBot

其安装需要依赖两个不太常见的Python的类库——pyqrcodepypng,用于生成登录用的二维码。使用以下的代码可以完成wxBot库及其依赖的下载

git clone https://github.com/liuwons/wxBot.git
cd wxBot
git clone https://github.com/mnooner256/pyqrcode.git pyqrcode-git
git clone https://pypi.python.org/pypi/pypng.git pypng-git
cp -r pyqrcode-git/pyqrcode ./
cp pypng-git/code/png.py ./

由于在使用的过程中需要使用到Python处理中文字符,需要保证系统支持了en_US.UTF-8字符集,并且LANG环境变量设置为en_US.UTF-8

2. wxBot的使用

可以运行代码目录中的test.py脚本进行测试,由于wxBot的代码不能兼容Python3,因此在默认使用Python3的系统中需要使用python2来运行该脚本

python2 test.py
[INFO] Please use WeChat to scan the QR code .

出现以上提示时会在当前目录下生成一个temp目录,其中会包含文件wxqr.png,其就是登录需要使用的二维码,打开手机微信App,扫描此二维码,并点击确认登录后会出现以下输出

[INFO] Please confirm to login .
[INFO] Web WeChat login succeed .
[INFO] Web WeChat init succeed .
[INFO] Get 184 contacts
[INFO] Start to process messages .

说明已经正确获取到了联系人列表,程序开始等待接收消息,如果收到消息,则会输出以下提示

[MSG] XX:
    [Text] [呲牙]

其中第一行为消息的发送者,第二行为消息的类型和消息的内容,如果内容中包含文件,则文件会被下载到当前目录的temp目录下,消息内容会是文件名

[MSG] XX:
    [Image] img_6556032082643325149.jpg

根据自己的需求处理消息则可以参考test.py中的内容,其主要思想就是继承WXBot类,并覆盖相应的消息处理方法既可完成对消息的处理操作,例如test.py中的代码,收到对方的文本消息后会自动回复一个hi

class MyWXBot(WXBot):
    def handle_msg_all(self, msg):
        if msg['msg_type_id'] == 4 and msg['content']['type'] == 0:
            #给对方发送消息
            self.send_msg_by_uid(u'hi', msg['user']['id'])