在 Linux 下用 GCC 编译 C++ 代码,提示类似
SSE2 instruction set not enabled
这样的错误,则可以在 GCC 里添加参数来完成编译:
-msse2
电视剧里都是这样,Mac是用来装的,而不是用的~~
事实证明,拍电视还是用 Mac 的外观抢眼,但是配置嘛,还是用 Windows 部署比较方便剧组人员~~
看《夫妻那些事》第26集21分49秒~~

昨天总算把最近要交的项目忙完了,今天可以闲下来琢磨一下其它好玩的东西了。今天先围绕自己天天要上的新浪微博和水木社区,说说最近自己的两个看法。
现在上班必玩微博,没办法,这个东西刷新快,最适合上班看笑话、看NBA战报之类的。可是,当你关注的对象多了之后,会发现你首页里的内容繁杂,对于一些需要特别关注的人的信息,往往会忽略。朋友发布的信息,往往是更加应该重点关注的,因为内容少、与自己密切相关。但是在你关注了大量的名人、栏目之后,朋友的信息混杂在里面,往往都错过了。
目前,我的办法是,在自己的关注列表里,找到某个朋友,然后去他的微博看。这种做法的缺点是:1. 朋友资讯获取不够及时;2. 在自己忙的时候,不会去点朋友的微博;3. 有若干个朋友,要一个一个点,得点到啥时候?一点效率都没有。
如果新浪微博可以提供一个自定义页面功能就好了!我可以把我的若干个朋友放到一个页面里,这个页面只浏览他们发布的微博;我也可以把我的同事放到一个页面里,这样,刷新这个页面的时候,看到的只是和同事有关的信息。通过这样,我还可以把经济相关、人文相关的关注对象,放到一起,方便分类阅读资讯。即,单个的关注对象粒度太细,而同一属性的关注对象则属于比较好控制的阅读粒度。
不知道有没有类似的插件或是工具?如果有,请推荐给我。
水木社区绝对是一个人声鼎沸的地方,哈,我喜欢在里面的技术版块里转悠,当然,也喜欢里面的八卦版面。以前你在水木里面提问,如果你要查找别人的回复,那相对来说还蛮麻烦的:你需要找到原帖,然后同主题阅读。这个步骤看似轻松,操作起来却很繁琐,如果在 telnet 下还略算方便,但是在 Web 界面下,抱歉,我实在懒得去折腾。
现在水木与时俱进了,添加了 @ 功能:当有人回复你的文章,或是直接在发文的时候用 @ 加上你的名字,你就可以收到这个回复。这看起来像不像是微博功能的移植?
水木的这个功能不错,希望水木多多改进,在如今网络社区化的战场里,依然保有鲜活的生命力。毕竟,水木社区留下来的用户,具备很多其它社区所不具备的素质,的确是最宝贵的财富。
最近在尝试用新的方式来写一个新的 Windows 客户端,当作练习。主要是用 C++ 把复杂的算法写成链接库,然后用 C# 做界面前端,从 C# 调用用 C++ 写好的非托管代码。
尝试通过这种方式解决两个问题:
其实,这种架构可以看成是一种 C/S 架构的简化版,但是省去了 Socket 通信这一层。
我采用的是最简单的 P/Invokes 的方式来实现 C# 调用 C++ 链接库,详细的教程,可以看一下 Using dumpbin.exe as an Aid for Declaring P/Invokes 这篇文章。
我在刚开始从 C# 里链接 C++ 链接库的 API 时,想当然地以为就是函数名称。但是这样操作无论如何也调用不成功,需要在 C++ 链接库里添加一个 extern 关键字,否则链接库编译出来的 API 名称,是混淆过的,不方便你在 C# 里作为 EntryPoint 来书写。
比如说,有如下 C++ 链接库的 API 函数(建议链接库给外面调用的 API 最好用 C 风格来实现,方便减少头文件依赖关系):
extern "C" __declspec(dllexport) bool Function1(const char* param1, const char* param2, const char* param3);
翻译成 C# 函数则如下:
[DllImport("Example.dll", EntryPoint = "Function1", ExactSpelling = false] [return: MarshalAs(UnmanagedType.U1)] public static extern bool Function1([MarshalAs(UnmanagedType.LPStr)] String param1, [MarshalAs(UnmanagedType.LPStr)] String param2, [MarshalAs(UnmanagedType.LPStr)] String param3);
这里注意,找 EntryPoint 一定要准确,否则不容易找到。为了明确地找到 API 函数的 EntryPoint 名称,可以使用 Dumpbin.exe 工具。
dumpbin.exe 工具默认在以下目录:
C:\Program Files\Microsoft Visual Studio 9.0\VC\bin
如果从这个目录里运行 dumpbin.exe 会提示找不到动态链接库 mspdb80.dll 的错误,可以把 dumpbin.exe 拷贝到目录
C:\Program Files\Microsoft Visual Studio 9.0\Common7\IDE
,并从这个目录下运行 dumpbin.exe 来解决这个问题。
运行命令
dumpbin.exe /EXPORTS dllname.dll
后,你会看到很多 ? @ 混合在一起的名称,所以,为了使你在 C# 里的代码可读性比较强,需要改造这些名称。在链接库里,我们可以通过用 extern 关键字来标明,这样生成的链接库 EntryPoint 依然会是原始的名称。
如果按上述方法编写好了代码,一运行 C# 程序却提示说:
未处理的"System.DllNotFoundException"类型的异常出现在 example.exe 中。 其他信息: 无法加载 DLL"cppexample.dll": 找不到指定的模块。 (异常来自 HRESULT:0x8007007E)。
这个时候,你需要找一找你的 dll 是否在可执行目录下,或是你写的 dll 是否依赖于其它第三方dll,一定要确保所有的 dll 都能顺利被找到。
你的函数肯定有若干个参数,那这些参数应该和 C# 里的类型如何一一对应呢?在 C# 里,这种映射关系叫做 marshal。
类型映射,需要仔细检查一下手册,比如说,const char* 就应该这样映射:
[MarshalAs(UnmanagedType.LPStr)]
Using C++ Interop 文章的未尾,有列出一大串的类型映射列表。
初步用 C# 来写界面,感觉更方便、快速,起码比 MFC 来得简单、直接;从 C# 里直接调用 C++ 链接库,也很方便。但这两者结合起来写应用,稳定性还有待进一步测试。
在 C/C++ 多线程编程下,如果不注意,采用普通变量传递参数值给线程会有一些误区,需要特别小心。
下面浏忙绪绪就举两个例子来说明一下。
最近在用 Boost 库写多线程程序时,需要启动若干个线程,这些线程分别处理不同的事情,线程会获取一个字符串参数,用来标识内容。在编写代码的时候,出现了一个很怪异的现象,例子代码如下:
const int tNum = 4;//并发线程数 vector<boost::thread*> tBox; for (int i=0; i < tNum; i++ ) { char strThread[20]; sprintf(strThread, "thread%d",i); //strThread 字符串是传入的 const char* 类型 boost::thread* thread_0 = new boost::thread( StreamProcesser, param1, param2, strThread); tBox.push_back(thread_0); boost::xtime xt; boost::xtime_get(&xt, boost::TIME_UTC); xt.sec += 2; boost::thread::sleep(xt); } for (int i=0; i < tNum; i++ ) { boost::thread* thread_0 = tBox[i]; thread_0->join(); delete thread_0; }
在线程函数启动参数中,有一个参数是 const char* 类型。如果我在线程中,没有先对 char 字符串拷贝一个副本,则当4个线程都跑起来后,再去读取这个参数,很有可能会读到同一个字符串。
这是因为编译器把 strThread 的地址编码为同一个内存地址造成的,所以,所有的线程读取的都是最后一次设置 strThread 的值。
结论:
同样,如果传入参数是整型或是其它类型的时候,也会有上述类似的问题。拿 Win32 的 CreateThread 函数来说,同样需要保证传入的参数不被修改,例如下面的代码就非常危险:
DWORD WINAPI CloseThreadFun( LPVOID param) { int* pHandle = (int*)param; const int handle = *pHandle; //打印句柄 printf("get handle is %d", handle); return 0; } int _tmain(int argc, _TCHAR* argv[]) { const int THREAD_NUM = 4; HANDLE* lphandle = new HANDLE[THREAD_NUM]; for (int j = 0; j < THREAD_NUM; j++) { HANDLE hthread; hthread = CreateThread(NULL, 0, CloseThreadFun, (LPVOID)&j, 0, NULL); lphandle[j] = hthread; } WaitForMultipleObjects(THREAD_NUM, lphandle, TRUE, INFINITE); delete [] lphandle; return 0; }
在调用 CloseThreadFun 来启动一个线程后,j 的值很有可能已经被修改掉了:线程启动总是需要时间的,而参数指针指向地址的内容,很有可能在此期间被修改了。比如,上面的代码,运行后,打印的内容如下:
get handle is 3 get handle is 4 get handle is 4 get handle is 4
这就说明了 j 值被重复修改后,会导致线程参数不对的现象。
解决办法:用一个 int 数组把需要传入到各个线程的参数缓存起来,尽量保证地址不一样。
传入线程的参数,应该尽量采用动态分配内存的方式来生成。否则如果采用临时变量,则随着变量生命周期的消逝,该变量的指针,很有可能会变成一个毫无意义的指针(或是被新的值覆盖,或是被成为一个遗留数)。
采用动态分配的变量作为线程启动时的参数,在线程结束后再销毁这个动态分配的变量,则是一个安全法则。
最近在写 Linux 程序的时候,碰到这样的问题:Log4Cxx 0.10.0 在 Linux 下退出程序时导致程序中断,即提示出现 segmentation fault 错误。
如果你用 gdb 调试,会提示如下信息:
Program received signal SIGSEGV, Segmentation fault. 0x02fa68f3 in apr_vformatter () from /usr/lib/libapr-1.so.0
原因是因为 Log4Cxx 在退出时有非法的资源释放。
有一个解决办法:可以在程序退出时,显示地关闭 Log4Cxx 对象。
I have the same problem if I use log4cxx.AsyncAppender. I fixed the problem by calling log4cxx::LogManager::shutdown(); before the end of the process. It's not very clean but it works.
log4cxx::LogManager::shutdown();
来清理 log4cxx,而不是让它自己来清理。
注意: 需要在程序所有可能退出的地方,设置这一语句 ,否则依然会有 segmentation fault 的问题。
最近都在写一些跨 Windows 和 Linux 平台的 C++ 代码,略有心得,整理成文,备忘一下。
Visual Studio 会自动在源代码里添加
#pragma once
这个指令,在 Linux Gcc 编译器下应该怎么样处理类似的情况呢?
维基百科上有一个权威说法:
http://en.wikipedia.org/wiki/Pragma_once
所以,在编写跨平台的 C++ 代码时,最好使用下面这种方式来获得跨平台的特性:
#pragma once #ifndef GRANDFATHER_H #define GRANDFATHER_H struct foo { int member; }; #endif /* GRANDFATHER_H */
Windows 下使用 __declspec(dllexport) 来标明一个动态链接库的函数接口,而在 Linux 下,则完全没有这个必要。
这个时候,如果动态链接库代码需要跨平台,应该怎么处理呢?
你可以用如下宏来进行区分:
#ifdef WIN32 #define EXPORT_XX __declspec(dllexport) #else #define EXPORT_XX #endif
源代码的编码格式,最好统一用 GBK 或是 UTF-8,以避免不同编码器之间转换造成的乱码。
Windows 里 include 一个头文件,你的大小写可以随便写。但是在 Linux 上,你得小心了,clsssa.h 你不能写成
#include "ClassA.h"
因为在 Linux 上,文件及目录名是大小写敏感的。
Windows 处理宽字符集与窄字符集采用的方法,是用宏定义来区分。比如说 TCHAR 在不同的工程下,属于不同的类型。
而在 Linux 下,则需要注意这些问题。为了让你的代码可移植性强。一定要少用 BOOL, TCHAR 这些类型,而要用原生态的 C/C++ 类型,比如说 bool, char 等。
默认情况下,Visual Studio 新建的 Win32 Console 工程,全是类似于这样的。
int _tmain(int argc, _TCHAR* argv[]) { }
这些代码,移植到 Linux 下面,就需要我们自己做一部分工作。
如果你在 Visual Studio 用的是 Unicode 编码的,则对应的 Linux 代码应该是另外一套。我则是用宏裁剪了两个主函数出来。如果你有什么更好的方法,可以跟我分享。
虽然你的程序可能依赖大量的跨平台库,这些库号称是跨平台的,但是很有可能在不同的平台的表现,会有所不同。例如,log4cxx 在 Linux 和 Windows 下就会有字符集设置的差别。
你在编写跨平台代码的时候,就应该特别小心这些库的细微差别,及时调整。
以上是一些跨平台 C/C++ 代码编写的经验,备忘一下,也希望对大家有用。
最近在 Linux 下使用 log4cxx 库,使用的 log4cxx 版本为 0.10.0,结果无法显示中文日志信息。
这可怎么办呢?我不可能把中文日志全部一行一行替换为英文的,这可是一个非常傻B的举动。
经过研究,终于知道需要经过如下步骤才能让 log4cxx 在 Linux 下正常显示中文日志:
$ locale
$ export LC_ALL="zh_CN.UTF-8"
如果你的系统提示说没有安装本字符集,则需要用命令进行安装:
$ sudo apt-get install language-pack-zh-hans
LoggerPtr logger;
log4cxx::PropertyConfigurator::configure("./log4cxx.properties");
logger= Logger::getLogger("test") ;
logger->info(("Start logging"));
setlocale(LC_ALL, "zh_CN.UTF-8");
关键是最后这一句 setlocale,要设置得和终端一样,都是 zh_CN.UTF-8。
这样,你的程序就可以用 log4cxx0.10.0 输出中文日志信息了。
今天在 Ubuntu 下使用 OpenCV 来读取一个视频,碰到了下述问题:
所有的测试视频在 Windows 下用 OpenCV2.2.0 是可以正常读取的,应该是 ffmpeg 安装不完全或是不正确。
[swscaler @ 0x94b3e80]No accelerated colorspace conversion found from yuv420p to bgr24.
这个问题应该是 OpenCV 在用 ffmpeg 解压 yuv420 数据时出错。
对于上述问题,可以采用下面的办法来解决:
另外:如果是在 Ubuntu-Server 版下安装 OpenCV,在安装 libhighgui-dev 的时候,需要安装很多和桌面相关的依赖包。
想弄一个 Java 内嵌网页服务器 ( Embedded Web Server ),这样,在我的 Java 应用程序里,可以直接提供网页服务,而不用再给用户配置一个网页服务器。对于轻量级的本地网页服务,这样就足够了!
在 Java 应用里内嵌一个网页服务器,Jetty 是一个很好的选择。以下是我在摸索过程中,总结的基本步骤:
首先从官方网站下载开发包,再解压到你喜欢的目录,Java 的东西就是方便呀!
注意,Jetty 还分好几种包,Jetty@eclipse、Jetty@codehaus 是比较常见的,内容应该差不多,应该只是镜像罢了。
Java 开发中使用第三方库的时候,就需要把该库添加到工程里。Jetty 的库文件,全部在解压目录的 lib 目录下,比如说,在我这里就是
D:\eclipse\jetty-hightide-8.0.4.v20111024\lib
目录。在 Eclipse 里新建一个 Java 工程,同时,在工程上点右键,弹出的菜单里,选择”Properties”,打开工程属性对话框,进行如下操作:
在 “Java Build Path” ==> “Libraries” 选项卡里,选择 “Add External JARs” 按钮,然后切换到 Jetty 目录里的 lib 目录,把所有的 jar 包都添加到工程里。
现在,我们可以用最简单的例子来启动一个 Jetty 内嵌网页服务器,但是这个代码,只可以获得一个 404 错误。不过没有关系,这起码可以证明 Jetty Server is Ready!
相比于上面这个 404 错误的例子,相信你会更喜欢下面这个 HelloWorld 例子:
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.ServletException; import java.io.IOException; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.Request; import org.eclipse.jetty.server.handler.AbstractHandler; public class HelloWorld extends AbstractHandler { public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { response.setContentType("text/html;charset=utf-8"); response.setStatus(HttpServletResponse.SC_OK); baseRequest.setHandled(true); response.getWriter().println("<h1>Hello World</h1>"); } public static void main(String[] args) throws Exception { Server server = new Server(8080); server.setHandler(new HelloWorld()); server.start(); server.join(); } }
如果需要在 Java 工程里进行 Embeded Server 开发,也可以把 Jetty 的库目录包含到 CLASSPATH 环境变量里。事实上,只要能找到下面几个文件就可以了:
servlet-api-3.0.jar jetty-util-8.0.4.v20111024.jar jetty-8.x.jar
手册上是这样写的,不过最后一个 jetty-8.x.jar 文件,我没有找到。