• About
    • Resume
A Game Developer also plays some guitar

Category Archives: Ok Computer

This is only about computer, computer science.

让管理lua_State的类指针安全

August 15, 2010 4:35 pm / 1 Comment / Benny Chen

最近因为在公司的项目中接手了Lua脚本模块的开发,所以研究了很多关于Lua脚本的东西,秉着“多看多想多记”的原则,我时刻敦促自己要及时记录下遇到的一些问题和想法。

在Lua中,几乎所有的东西都是围绕着lua_State转,所以,一般我们都会写一个类来封装管理它,比如:

class LuaObject
{
public:
	LuaObject()
	{
		m_L = luaL_newstate();
		luaL_openlibs( m_L );
	}
	~LuaObject()
	{
		if ( m_L )
		{
			lua_close( m_L );
			m_L = NULL;
		}
	}

private:
	lua_State *m_L;
};

这很好,不过它不是指针安全的。试想,如果一个LuaObject对象被复制,结果将会怎样。

LuaObject luaObject1;
LuaObject luaObject2( luaObject1 );

上面这段代码将会导致运行时crash,因为luaObject1和luaObject事实上指向了同一块lua_State,这样当luaObject1和luaObject2被析构时,lua_State会被两次lua_close,这不crash才怪呢!

事实是,当一个类包含了一个指针时,我们就需要开始变得格外谨慎,除了在构造函数和析构函数中要处理指针的初始化和清理外,我们还需要考虑深拷贝(deep copy),浅拷贝(shallow copy))的问题。如果使用编译器默认生成的拷贝构造函数,它只会浅拷贝指针,而指针所指向的内存区域不会被拷贝。就像上面一样,两个LuaObject实则共享了一个lua_State。

那该如何处理让管理LuaObject类的指针安全呢,深拷贝?厄,首先我也没有深究深度拷贝lua_State具体该如何完成,不过我猜想这可是一个复杂而重型的操作,仅仅为了带来指针安全而选择此可不是一个明智的选择。

如果你是开发LuaObject类的程序员,也许你会对使用LuaObject的程序员(或许有时使用者就是你自己)说:“你不要对它进行拷贝操作不就OK了”,但这是一种严重不负责任的行为,因为这种皮球会越踢越远的。比如,程序员A使用了你的LuaObject类,他写了一个包含LuaObject的指针的类,同样A也不考虑拷贝指针安全问题,然后A又将它的类传递给了B,B封装了A的类指针,然后传递给了C,接着如此重复,C再给D,D再给E,最后E在对类进行拷贝操作时,程序crash掉了,因为在某个最底层,他们共享了lua_State。不过因为这时候已经嵌套了这么多层,E程序员或许根本不知道也不关心什么是LuaObject(他只跟D的类打交道)。这时,要想追踪到BUG之源——万恶的不考虑指针安全的LuaObject,已经非常困难了,God bless you all.

事实是,如果你不想让你的类被复制,你就应该明确而显示的禁止它。其实lua_State的这个问题非常类似于Effective C++书中的第14个条款中所提到的问题:在资源管理类中小心copying行为。对于这个问题,书中提供了两种解决方案。第一种就是禁止复制。第二种则是对底层资源的“引用计数法”(reference count)。

第一种禁止复制很简单。(关于Uncopyable)

class LuaObject : private Uncopyable
{
public:
    ...
}

第二种则是使用智能指针来封装lua_State,我们可以使用boost的shared_ptr来封装lua_State,即shared_ptr< lua_State > m_L。如何对m_L进行初始化是另一个需要注意的问题,如果使用shared_ptr的常见初始化形式:m_L = shared_ptr< lua_State >( luaL_newstate() ),这样是不对的,因为在这种形式下,当lua_State的计数变为0时,shared_ptr会去调用lua_State的的析构函数,这显然是错误的,对lua_State的释放动作是lua_close而不是删除。事实上,这样编译器也无法通过,如果这么写,会报出“use of undefined type ‘lua_State’”的错误,提示lua_State是一个非完成的类型(incomplete type)。

我们应该为shared_ptr的初始化传入一个删除器(deleter)。很显然,lua_State的deleter是lua_close()函数,这样最终的代码如下。

#include <boost/shared_ptr.hpp>
using boost::shared_ptr;

class LuaObject
{
public:
	LuaObject()
	{
		m_L = shared_ptr< lua_State >( luaL_newstate(), lua_close );
		luaL_openlibs( m_L.get() );
	}

private:
	shared_ptr< lua_State > m_L;
};

这样再回到前面拷贝的那个例子,luaObject1和luaObject2共同引用了一个lua_State,但因为使用了shared_ptr,所以只有在lua_State的引用次数变为0时,它的deleter(这里是lua_close)才会被调用,安全了!

在我的项目中,因为没有使用boost库,也没有提供任何智能指针,所以使用禁止复制来保证安全。

Posted in: C++, Game Programming / Tagged: boost, copy, lua, lua_State, pointer, safe, shared_ptr, 安全, 指针

Onine Courses

August 1, 2010 9:19 pm / 2 Comments / Benny Chen

第一次听说online course是同事给我推荐的MIT的讲algorithm的课程,随后自己上youtube看了几集,真是大赞。并不是说崇洋,而是国外的老师确实讲得好,条理清晰,而且不乏个人风格,幽默风趣。更难得的,计算机的课程他们居然都是全部只用黑板,而很少用slides。并不是说slides不好,而是他们不像国内的老师,讲课通篇都是过PPT,发催眠曲。online course真是个好东西,让我们这些土鳖能够不用留洋就能感受到国外计算机的课程,况且还是像MIT,Stanford这样的名校。通过这些课程,不仅能领略到国外老师的风采,还能学英语,因为如果听不懂,有些课程还配置了整个课程的transcript,真是非常优质的学习材料。

不过这些课程,对于我这样的已经走出学校的人来说,只能粗看感受一下精华了,不可能真的跟着课程一集一集的细嚼慢咽,已经过了全额学习的那个时期了,要是早几年在大学里能发现这样的材料就好了(或许那时候也没有)。

http://see.stanford.edu/see/courses.aspx

Stanford的关于computer science的课程,课程不多,但都非常详细完整的提供了video, handout, assignment, transcript,甚至还有考试试题和答案。

http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/

MIT的,课程很丰富,但只有部分提供了video,推荐Introduction to Algorithms.

Posted in: Some Experiences / Tagged: MIT, Stanford, 在线课程

[译]3D为互联网准备好了么?

June 17, 2010 11:31 pm / Leave a Comment / Benny Chen

原文:Is 3D Finally Ready for the Web?
作者:Sixto Ortiz Jr.
来源:Computer archive, Volume 43, Issue 1 (January 2010) table of contents, Pages: 14-16, 2010

Introduction 介绍

最初的互联网是以一个简陋的鼠标点击环境开始的,但如今,互联网上已经到处是各种眼花缭乱的页面,充满着各种丰富的应用程序,有的用来娱乐,有的用来提高生产率,等等等等。用户可以在互联网上完成各式各样的任务,不管是购买商品,还是与全世界的用户实时交互。然而,一个关键的元素却迟迟没有在互联网上打上自己的烙印——3D。

在今天,3D的在线应用主要是游戏和虚拟世界,这些都需要强大的电脑和特殊的软件来支撑。然而,商业,工程企业和其他一些用户同样也需要3D,他们同样也需要3D所带来的真实感和更多细节。用户希望他们能够在浏览器中获得和在PC中一样的体验。并且由于3D技术在电影、游戏和其他各种娱乐行业中的广泛应用,消费者已经对它习以为常。因此,对于互联网上更多且更易于访问的3D内容的需求变得越来越强烈。况且更好的浏览器体验同时也意味着更多潜在的收入。

然而,互联网3D还处于一种很不成熟的状态,因为很难在典型的PC和浏览器上广泛使用这种复杂的技术。事实上,一般浏览器也无法承载复杂的3D内容,更无法提供高帧率或全屏的图形效果。并且,在实时的合作程序和其他应用程序中包含3D让本来已经很繁琐的开发过程会变得更加复杂。

尽管如此,现在是有一些组织在为网络3D技术而努力工作的。他们尝试将浏览器转为一种更为强大的计算平台,以在该平台上获得如PC上一样的体验,例如3D内容的展示。这些工作将会为网络衍生出更多的应用程序,包括产品建模,展示和配置;3D在线会议和工人协作;对于手术或机械过程的模拟;虚拟旅游;增强现实等等。

不管怎样,在互联网3D技术变得可依赖和成为主流之前,仍然有重重的困难需要克服。

3D on the web 网络上的3D

早期的网络是没有图形界面的,直到NCSA(美国国家超计算应用程序中心)在1993年发布了Mosaic,这是首个可以同时展示图片和文字的浏览器。

当前在互联网上,已经有一些为3D服务的技术在使用,他们基本使用同样的方式工作,但是使用不同的文件格式。

VRML和X3D
网络3D开始于1994年,在这一年,VRML组织发布了虚拟现实标记语言(Virtual Reality Markup Language)。然而,VRML从未流行过,因为它限制开发者只可以写3D相关的内容。为了创建完整的引人注目的应用程序,开发者必须能够同时开发3D,2D,视频和音频。在处理器和软件能够支持图形之前,VRML表现还是不错的,因为它能支持图形。但是VRML太慢,并且没有能力渲染复杂高仿真的模型和场景。

在1997年,Web3D联盟推出了X3D,它是一种基于XML的文件格式,同时也是VRML的扩展,以用来表现3D图形。X3D同样也没有流行过。游戏和3D开发人员广泛忽视了X3D,X3D只被极少数的商业工具所支持。

其他方法
3D工业论坛于2003年推出的Universal 3D技术,是一种压缩的3D图形文件格式。然而,它的推崇者也只是推动Universal 3D使其成为了一种主要使用在制造业或建筑业相关应用程序的文件格式。

开源的网络3D标记语言(3DMLW,3D Markup Language for Web)是一种基于XML的文件格式,它用于创建网络上的3D和2D内容。3DMLW由3D Technologies R&D于2009年推出,它可以通过插件工作在大多数流行的浏览器中。

Technical Developments 技术开发

今天的硬件能力能够比过去更好的处理3D。更快的CPU,更快的图形处理器和显卡,以及四处可见的3D图形加速器,这些都为网络3D的出现提供了可能。

JavaScript和HTML 5.0
开发者使用JavaScript语言开发各种网络应用程序。浏览器的 JavaScript引擎性能的提升,使得网络3D成为可能。比如说,IE9中的JavaScript引擎将可以使用宿主机器的图形处理器来快速的处理图形相关的任务。同样的,Mozilla也增强了其Firefox浏览器的JavaScript引擎,他们使用了一种追踪( tracing )技术,该技术通过优化代码的执行方式来提升性能。

JavaScript的访问HTML 5.0的能力可以让开发者更加方便的将视频,音频,3D和2D内容无缝合并到应用程序中。HTML5.0中的画布(canvas)元素同样也会促成互联网3D。该元素使得浏览器可以通过它们的JavaScript引擎,本能(无需插件技术)且动态的渲染位图图片,使得渲染3D变得更加容易。

WebGL
Mozilla 基金会是一个创建和支持开源应用程序的组织,Khronos是一个为并行计算,图形和动态媒体设计标准的联盟。它们俩正在开发WebGL。该技术可以在不需要插件的情况下为网络带来硬件加速的3D图形效果。任何支持Khronos的OpenGL或OpenGL ES规范的浏览器都可以运行WebGL。

跨语言跨平台的OpenGL定义了一组API,以供开发出提供2D和3D图形效果的应用程序。而OpenGL ES(Embedded Systems)规范为从简单元素(或者成为图元,比如线或者多边形)绘制3D场景提供了编程工具。OpenGL ES可以工作在智能手机这样的小型设备上。然而,这些能力不能离开插件的支持。许多用户倾向于不使用插件,因为它们不易安装,且难于排除故障和管理。

WebGL使得浏览无需插件便可渲染3D内容。该技术提供可以让软件通过编程访问PC机3D渲染硬件的API,从而扩展了OpenGL。本质上,WebGL提供了JavaScript应用程序和OpenGL软件库之间的连接,而后者负责访问宿主系统的图形处理器。这可以充分利用硬件的能力来渲染3D内容。

Khronos已经建立了一个WebGL工作组,该工作组将在今年上半年发布 WebGL的第一个release版本。但是程序人员已经开始把WebGL构建到Firefox的开发版和Webkit开源浏览器引擎中,该浏览器引擎被苹果的Safari和Google的Chrome浏览器所使用。

O3D
Google也是 WebGL工作组的成员,但同时Google也开发了一款基于浏览器的3D图形技术,O3D,如下图所示。

O3D Software Stack

图片来源:http://code.google.com/apis/o3d/docs/techoverview.html

O3D是一个基于IE,Firefox,Safari 和Chrome这些浏览器的插件。Google现在正在把该技术构建到Chrome中,并希望未来能够直接构建到其它的浏览器中。O3D可以在Windows,Mac,Linux上运行,它是一套开源的JavaScript API,用于开发互动的3D图形应用程序,比如游戏,广告,或者虚拟产品展示,都运行在一个浏览器里。该API为基于JavaScript的程序提供了接口,使得JavaScript应用程序可以与包含在O3D插件中的O3D核心软件进行通信,从而直接利用计算机的图形硬件。

Google 发言人说O3D是一个保留模式(retained-mode)的技术,因为它只设置场景一次,然后每帧只绘制场景改变的部分。与WebGL这样的立即模式(immediate-mode)技术不同,O3D不会每次都重新绘制整个场景。这提供了更好的性能,但却弱化了开发者的控制能力。

Adobe Flash
Adobe正在将更强的3D能力加入到它所属的Flash浏览器插件中。Adobe是在2008年Flash Player 10的发行版中推出了3D能力。该技术通过加入新的类和方法,从而包含了对于3D效果的支持,尤其可以通过Flash的 ActionScript编程语言在三维空间中设置一个物体的位置。通过这种方法,即便是没有丰富3D经验的开发者也可以通过在2D中设计物体来制作3D内容,并能通过新加入的类和方法修改它们。Flash Player 10.0,现在是beta版,将会为智能手机和其他移动设备带来3D效果。

Standing in the Way 在路上

在3D Web真正成熟之前,它还需要克服重重的障碍。

比如说,插件,在某些情况下,时常会导致浏览器崩溃或者其他的问题。让浏览器能够天生就有渲染3D的能力,并且克服当前3D技术不能和所有的浏览器、操作系统和应用程序兼容的问题,这些都是很大的挑战。另外,缺乏标准化也是一个问题。如果没有标准化,网络将会出现各种不同的互不兼容的格式和技术,这使得开发者为了在不同的浏览器上运行需要创建很多版本的程序。同时,开发网络上的3D内容需要很长的开发时间,并且很少的开发者对该方法很熟悉。

对于上网本和智能手机使用的迅猛增长,这些具有较慢的处理器,意味着更多的人在使用无法运行3D内容的设备。技术硬件能力和网络带宽已经大幅度的提升了,但它们仍然不能满足高度复杂的3D模型的需求。并且网络3D技术的推崇者们并没有为普通用户设计在线3D技术,这是在犯和VRML一样的错误。

Conclusion 结论

WebGL是一个非常有趣的网络3D开发技术,因为它无需插件。一旦WebGL推出,我们将在网络上看到3D内容的迅速增多。然而,3D在10年内是很难成功的,直到迅猛增长的流行上网本和智能手机拥有足够强大的处理器能力来播放数据密集的内容。不过总有一天,3D将在网络上做到像现在视频所做到的一样,网络3D的时代终会到来。

Posted in: 3D / Tagged: 3D, web, 浏览器, 网络

My Footsteps of Programming 3DS Max Exporter Plug-in (continued)

May 12, 2010 4:21 pm / 2 Comments / Benny Chen

Oh my goodness, how long has it been since I wrote the first part of this article? What on hell was I buzzing around for during this period which seemed years to me? This plug-in is nearly becoming a legacy for me. Now it’s time to bring it back and finish the remaining parts. And I will also upload the whole project’s source code and related docs later.

First part of this article: http://www.bennychen.cn/?p=640

Predefine mesh file format

After we’ve got some basic concepts about 3DS Max, now it’s time to define our own mesh file format. It’s very hard to directly define a format from the scratch. Researching some already typical and mature file formats is a good starting point. Recommended by my tutor, I chose .md2 & .md3 formats as my learning material which are famous for being used in the Quake series game. Based on several days’ research on them, I defined my own mesh file format, which is sort of like .md2 & .md3 but also differs from them at some point.

I did not just define one format. They are a suite of formats, see the following table.

File Type
File Format
Basic mesh file
*.CSM (Crowd Simulation Model)
Texture
*.jpg, *.bmp, ……
Animation
*.AM
Animation Configuration
*.CFG
Mounting Configuration
*.CSM_PACK

There are 5 parts in total. But some of them are not necessary. Only one file is essential – the basic mesh file, which is named as CSM( Crowd Simulation Model ), because at that time I programmed this plug-in mainly for a crowd simulation project. The texture part is the textures file used by the mesh. If a mesh includes animation, the AM file( short for animation ) and animation configuration file(.CFG) are also needed. My format also supports mesh mounting, so if a mesh is mounted by several sub-meshes, .CSM_PACK is used to store the mount info.

These are some of the features of my format.

  • Support multi-texture
  • Support skinned animation
  • Decouple mesh and animation
  • Support animation configuration
  • Support mesh mounting

And the 2 figures below are the inside structure of CSM and AM. If you want to know more detail of them, later I will upload the docs.

I stored my animation data in .AM file, bone by bone, animation by animation, and frame by frame.

Export static mesh data

OK, so far, all the preparation work is done. We can finally start the programming process.

For the last 2 steps, I will not present very detailed info here. I will only talk on the difficulties needed to overcome when I programmed them. All the programming job is strictly based on the format we defined last step. For more detail, please turn to my source code.

Firstly we need to handle the static mesh data. In this step, some difficulties are.

1. Transformation between different types of coordinate system.
Basically 3DS Max uses right-hand coordinate system. But some common rendering system( i.e. DirectX ) uses left-hand coordinate system. They have totally different coordinate systems, see figure below. So, the coordinate data needs to be transformed before outputting. See the figure below, the left is a typical rendering system’s coordinate system, and the right is of 3DS Max.

About how to transform, you can refer to this paper – 《一种不同坐标系之间的变换矩阵的转换方法》by 杨卫东&刘玉树.

2. Handle mesh with multiple textures.
This is a tricky problem. We know that a single vertex could be shared by different faces, so if these faces are attached with different textures, the vertex should have multiple UV coordinates. This leads to the fact that we need to split the vertex into multiple vertices, so that we can ensure one vertex only maps to one UV coordinate. And the whole mesh needs to be reorganized.

From my CSM format definition, you may have found that a mesh is comprised of multiple submeshes. Actually the submeshes are split based on textures, that is, vertices using the same texture are categorized into one submesh. So if an original vertex is using 2 textures, this vertex is firstly split into 2 vertices and will exist under 2 submeshes respectively.

3. Vertex normal
This is another painful issue. About this topic, I once wrote an article in my blog before. Turn to it here.

Export skinned animation

Now, we have come to the most complex, most sticky part of the whole work. I can still remember the time, the pain and the effort when I tried to tackle this problem. Those memories are so unforgettable. But they all deserve.

Why I did it so painfully was that, firstly I was still not familiar with the mechanism of bone animation at that time, secondly the mechanism of 3DS Max skeletal system is hard to figure out, whether its bone system or biped system, lastly, decouple the vertex data and the animation data is another challenge.

About the computational process of bone animation, thanks to this blog, it is the key article that helped me accomplish the whole work.
http://www.cnblogs.com/neoragex2002/archive/2007/09/13/Bone_Animation.html

Screenshots

The figure below is my exporter plug-in UI. The upper part is used to include animations, which will later generate .CFG file. Ant the lower part is used to configure mesh mounting, which will later generate .CSM_PACK file.

And here is an example of exporting. On the back is the 3ds max software interface, rendering a mesh with or without some animation. And on the front is the mesh viewer developed by me, which renders the same mesh with the same animation.

This is another example, the Tiananmen Gate.

Posted in: Computer Graphics / Tagged: 3ds max, exporter, plug-in, 导出插件

Linux下DB2的问题总结

May 3, 2010 6:31 pm / Leave a Comment / Benny Chen

1. 使用db2cc命令启动db2 control center时出现 – Error: Can’t connect to X11 window server using ‘0.0’ as the value of the DISPLAY

解决方法:
– su root
– xhost +

关于xhost: http://linux.about.com/library/cmd/blcmdl_xhost.htm

2. 使用control center操控数据库时抛出SQL4414N错误

解决方法:
– su dasusr1
– db2admin start

3. 在用db2move导出数据库时出现如下的错误

Application code page not determined, using ANSI codepage 1208
Error opening list file. Terminating …
**Error occured while opening a file.
解决方法:
db2的用户对于当前文件夹没有写权限,使用chown改变owner或者chmod改变权限。
chown db2inst1:db2iadm1 $directory_name -R

4. 如何卸载db2

(1). 删除所有关联的instance
$DB2DIR/instance/db2idrop InstName
(2). 删除db2
$DB2DIR/install/db2_deinstall -a

5. $DB2DIR的include目录下为何只有一个asn.h

原因是在安装db2的时候,可能选用了typical模式,则不会安装这些头文件。使用custom 模式,并在select features to install中选中SDK

6. 如何查看db2的版本

db2level

Posted in: Some Experiences / Tagged: db2, linux

Faint!

March 29, 2010 10:08 pm / 1 Comment / Benny Chen

以后干什么事热血冲动之前,还是最好先做一下survey。前几天异常兴奋的开始翻译Google的C++风格指南,结果今天到Google上简单一搜搜出了一堆,这神圣的事情已经被无数人干过了。

Faint!

我也只是才翻译了一个chapter,但已经没有必要再继续下去了。

Google 开源项目风格指南 – 完整中文版在这里:http://code.google.com/p/zh-google-styleguide

Posted in: C++

[译]Google的C++风格参考指南(1)– 头文件

March 28, 2010 8:15 pm / Leave a Comment / Benny Chen

译者的话:

在一个开发团队中,很重要的一点是要遵循一个统一的风格,这样才能便于相互之间在代码上进行快速的交流和理解。反之,如果团队中的每个人只是按照自己的个性去写属于个人风格的代码,这必然会造成项目代码的五花八门,互相之间难以沟通,整个项目的代码质量低下,BUG率攀高,软件的可维护率也极差。长此以往,国将不国,团队将不团队,最终软件极有可能以一种极其混乱的状态收尾,这真是一个悲剧。

每一个C++程序员都有自己的编程风格,虽然大家的风格迥异,但也一定有一些共通的地方。我希望能找到一个对于C++风格的归纳和总结,这样在以后我的工作中,对我自己,甚至对我工作的团队,当然也对于正在阅读的你,能够有一个帮助和参考。所以,我决定翻译Google的这篇C++风格参考指南。当然,真正促使我去翻译这篇文章的原因是,我是一个C++技术和简洁代码风格的狂热追捧者。

注意,这只是一篇参考,它是Google的C++风格,你并不一定严格的遵循所有的规则,永远记住很重要的一点,风格永远跟着你的团队走。

本人能力有限,所以不一定每个地方都翻译的准确,另外一些不太好翻译的专业名词首次出现时,我会标注英文原文。

原文:Google C++ Style Guide

背景 (Background)

C++是很多Google开源项目所使用的开发语言。正如每一个C++程序员所熟知的,C++有很多强大的功 能,但功能强大也同时意味着复杂性(complexity),使得代码bug率更高且难以阅读和维护。

这篇参考指南 (guide)的目的在于,通过详细描述应该做的(dos)和不应该做的(don’ts),来控制我们撰写C++代码时的复杂性。这些规则令程序员在高效利用C++语言特性的同时,还能让手工写出的所有代码(code base)易于管理。

风格(style),或者可 读性(readability),我们把它称作为“用来管理我们C++代码的常用惯例(convention)”。不过用“风格”这个术语有些不当,因为这些惯例并不只是指代码源文件的格式。

让代码可管理,一种方式是加强代码的一致性(consistency)。令任何一个程序员能够快速的理解另一个程序员所写的代码是非常重要的。如果代码保持统一的风格并且遵循惯例,我们可以更加轻松的使用“模式匹配”(pattern- matching)推断出,不同的符号分别代表什么。创造这种代码公共性(common)需要习语(idiom)和模式(pattern),使得代码易于理解。在某些情况下,也许有好的理由来改变某些风格规定,但为了获得一致性,不管怎样我们还是得保持风格。

这篇参考指南所关注的另一点是C++的功能膨胀问题。C++是一个有很多高级特性的庞大语言。在某些情况下,我们限制甚至禁止使用一些特性。这样做是为了保持代码的简洁,并避免因使用这些特性而带来各种常见的错误和问题。指南里会列出这些特性,并解释为何限制使用它们。

Google 所开发的开源项目遵守这篇参考指南里所列出的规定。

注意,这篇指南不是C++教程,我们认为所有正在阅读的同志们都是熟悉C++的。

头文件(Header Files)

一般而言,每个.cc文件都有一个关联.h文件。但也有一些例外,比如用于单元测试(unit test)的.cc文件或者只包括一个main()函数的小型.cc文件。

正确的使用头文件可以让代码的可读性、规模(scale)和性能(performance)得到很大的改观。

接下来介绍的一些规则将会引导你越过使用头文件时的各种陷阱。

#define防护(The #define Guard)

所有的头文件都应该使用#define防护来防止多重包含(multiple inclusion),格式是<PROJECT>_<PATH>_<FILE>_H_

为了保证唯一性(uniqueness),该格式应该基于源文件在整个项目中的路径。比如说,项目foo中的头文件foo/src/bar/baz.h应该用如下的格式:
#ifndef FOO_BAR_BAZ_H_
#define FOO_BAR_BAZ_H_

…

#endif // FOO_BAR_BAZ_H_

头文件依赖(Header File Dependencies)

如果使用前置声明(forward declaration)就可以解决依赖,就不要用#include。

当你#include一个头文件的时候,你同时也引入了依赖关系————每当头文件有任何的修改,包含该头文件的代码也要重新编译。如果你的头文件还包含了其他的头文件,那任何对于这些文件的修改会导致任何包含了你的头文件的代码重新编译。因此,我们倾向于尽量减少使用#include,尤其避免在一个头文件中包含另一个头文件。

使用前置声明,可以显著的减少在你的头文件中#include其他头文件的次数。比如说,如果你的头文件要用到class File,但你不需要访问class File的详细定义,那你的头文件只需前置声明class File即可,而不是#include “file/base/file.h”。

那在哪些情况下,我们是只使用一个类(比如 class Foo),而无需访问它的定义呢?

  • 声明类数据成员类型为 Foo* 或者 Foo&
  • 声明(但不定义)参数或返回值为Foo的函数
  • 声明静态数据成员类型为Foo。这是因为静态数据成员在class之外定义

另 一方面,如果你的类继承自Foo或者你有一个类数据成员是Foo,那就必须包含Foo的头文件。

有些时候,使用指针 (scoped_ptr 更佳)成员而不是对象成员(object member)是明智的。然而这却降低了代码的可读性,并且导致性能损失。所以,如果这么做的目的只是减少头文件中的#include,那就别这么做。

当 然,.cc文件必须需要它所使用的类的定义,所以它会#include比较多的头文件。

内联函数(Inline Functions)

只当函数很小,比如只有10行或更少时,才定义它为内联函数。

解释:你可以以这种方式定义函数,该方式允许编译器将函数内联展开,而不是以传统的函数调用机制去调用它们。

优点:内联函数可以产生更加高效的目标代码(object code),但前提是内联函数要足够小。你可以随意的将这些函数设置为内联函数:访问函数(accessor),设置函数(mutator),以及其他的短小的并与性能紧密相关的函数。

缺点:过度使用内联函数会导致程序变慢。内联一个函数时,根据该函数的大小,内联会增加或降低代码的规模。内联一个很小的访问函数通常会降低代码规模,而内联一个非常庞大的函数则会大大增加代码的规模。在现代的处理器 (processor)上,因为使用指令缓存(instruction cache)的缘故,短小的代码通常跑的更快。

结论:

一个不错的经验法则是,不要内联超过10行的函数。要小心析构函数,它们通常要比表面上看到的要长很多,因为它们会隐式的调用成员或者基类的析构函数。

另一个有用的经验是,内联带有循环或者switch语句的函数是低效的(除非循环或switch语句从未被执行过)。

还有重要的一点是,就算函数被声明为内联,但它不一定就被内联。比如说,虚函数或者递归函数通常都不被内联。一般我们也不应该声明递归函数为内联函数。而通常我们把一个虚函数声明为内联函数的主要原因是,将它的定义放在类定义内,这样是为了图方便或者将它文档化(document)的行为,比如说在访问函数和设置函数上我们就是这么干的。

-inl.h文件(The -inl.h Files)

当你需要使用复杂的内联函数时,你可以使用文件名 后缀为-inl.h的文件来定义它。

内联函数的定义要放在头文件里,这样编译器在函数调用点才能访问到定义用于内联。然而,函数的实现代码通常放在.cc中,而且我 们也不喜欢把太多的实际代码放在.h文件中,除非这么做有利于可读性或性能。

如果一个内联函数的定义很短, 里面几乎没有任何逻辑,你应该把这样的定义代码放到.h文件中。比如说,访问函数和设置函数应毫无疑问地放在类定义中。稍复杂一些的内联函数定义也可能放 在.h文件中,这样能方便实现者(implementer)和调用者(caller)。尽管这样做可能使得.h文件有些笨重,但你可以把代码放到另一个 -inl.h文件中。这将实现(implementation)从类定义中分离,而且使得实现能够在需要的地方被包含。

-inl.h 文件还可以用于函数模板的定义,这使得你的模板定义易于阅读。

当然不要忘了,像其他的头文件一样,-inl.h文件 也需要#define防护。

函数参数顺序(Function Parameter Ordering)

当定义一个函数时,参数的顺序应该是:先输入参数,再输出参数。

C/C++函数的参数可以是函数的输入,函数输出,或者两者兼有。输入参数通常是值(values)或者常量引用(const reference),而输出参数和输入/输出参数是非常量指针(non-const pointer)。对于函数参数的排序,将纯输入参数放在任何输出参数之前。尤其不要因为一个参数是新添加的,就把它放在函数参数的最后,要将纯输入参数放在输出参数之前。

这不是个硬性规则。那种既是输入又是输出的参数(通常是class或者struct)会让情况变 得更复杂,还有一点,就像前面所说的一样,为了与相关函数保持一致性,也会需要违背这一规则。

文件包含的名字和顺序(Names and Order of Includes)

请使用以下标准顺序以保证可读性,并且避免隐藏的依赖:C库,C++库,其他库的.h,你的项目的.h。

所有项目的头文件应作为项目源文件目录的后代列出,不要使用UNIX中的目录快捷方式 . (当前目录)和 .. (父目录)。

举个例子,google-awesome-project/src/base/logging.h应该以#include “base/logging.h”的格式包含。

在 dir/foo.cc中,该文件的主要目的是实现或者测试在dir2/foo2.h中的东西,文件包含应按照如下排序:

  1. dir2/foo2.h (首选位置(preferred location) — 请见下面的详细说明).
  2. C system files.
  3. C++ system files.
  4. Other libraries’ .h files.
  5. Your project’s .h files.

首选的排序(preferred ordering,译者注:在以上例子中为dir2/foo2.h )降低了隐藏的依赖。我们希望每个头文件能够自我编译。最简单的方式就是确保它们中的每一个都在某个.cc文件中是第一个被#include的。

dir/foo.cc and dir2/foo2.h通常在同一目录下(比如base/basictypes_unittest.cc and base/basictypes.h),但也可能在不同的目录下。

在每个部分(section)中,根据字母顺序排列是不错的方式。

举个例子,google-awesome-project/src/foo/internal/fooserver.cc中的文件包含顺 序可能是这样:

#include “foo/public/fooserver.h” // 首选位置

#include <sys/types.h>
#include <unistd.h>

#include <hash_map>
#include <vector>

#include “base/basictypes.h”
#include “base/commandlineflags.h”
#include “foo/public/bar.h”

未完待续…

Posted in: C++ / Tagged: C++, Google, 参考指南, 头文件, 风格

Game Engine Framework

February 4, 2010 1:44 pm / 2 Comments / Benny Chen
FoC of Game

在这次找工作的过程中,有一道笔试题让我印象深刻,题目只有简单的一句话:please use pseudo code to write a game engine framework(请用伪代码写出一个游戏引擎的框架)

当时看到这道题,完全是一种头皮发麻的感觉,虽然我对游戏引擎还算比较熟悉,但它可是个“巨象”般的庞大结构,要在短短的90分钟的笔试时间内把它“摸”完(而且90分钟也不只这一道题),那可真是天方夜谭。所以我当时在试卷上留下的结果就是,草草的画了几个模块图了事。

之后我才意识到,我根本就完全没有理解这道题,或者说,是完全没有理解一个词的意义——framework。受游戏引擎庞大印象的牵连,我把framework想得太大了。

Framework对于IT人士来说貌似是个挺时髦的词,这个词也经常被我们挂在嘴边,我也如此。但问题是,我几乎从来没有去好好的留心过或者深究过,到底什么才是framework,什么样一个东西才能被称为framework,它的准确定义又是什么呢。

看一般英汉字典里对于framework的解释:

structure giving shape and support  框架; 结构

这只是给了我们framework的中文翻译而已,几乎还是没有给我们什么有用的信息。

记住一句话:有困难,找Wiki。当我看到Wiki上对software framework精确的定义时,突然间,一切都明白了。

原文URL:http://en.wikipedia.org/wiki/Software_framework

A software framework, in computer programming, is an abstraction in which common code providing generic functionality can be selectively overridden or specialized by user code providing specific functionality. Frameworks are a special case of software libraries in that they are reusable abstractions of code wrapped in a well-defined API, yet they contain some key distinguishing features that separate them from normal libraries.

Software frameworks have these distinguishing features that separate them from libraries or normal user applications:

1. inversion of control – In a framework, unlike in libraries or normal user applications, the overall program’s flow of control is not dictated by the caller, but by the framework.
2. default behavior – A framework has a default behavior. This default behavior must actually be some useful behavior and not a series of no-ops.
3. extensibility – A framework can be extended by the user usually by selective overriding or specialized by user code providing specific functionality
4. non-modifiable framework code – The framework code, in general, is not allowed to be modified. Users can extend the framework, but not modify its code.

这段话的大概意思是这样,首先表明了software framework是一种抽象体,这个抽象体通过一些公共代码提供了通用的系统功能,使用framework的用户可以在此基础上进行编码,以覆盖或者特化该系统中的相关功能。接着,这段话还说明framework也是一种库,但跟传统的软件库又不同。framework的特点被总结为以下4点:

  1. framework完全决定了程序的控制流,该控制流绝对不会被调用framework的用户(callee)改变,这就是framework跟传统的软件库不同的地方;
  2. framework提供默认行为,且这些默认行为绝对不是无意义的行为;
  3. 用户可以扩展framework的行为;
  4. framework本身的代码不允许被用户改变。

看到这里,我几乎是只有抱头痛哭的份了。因为,从开始学最基础的DirectX技术起,那每一个Demo中,不管多小的Demo,几乎就包含了这样的一种框架,抑或是说,一种游戏引擎框架。只要理解了framework的意思,这道题就根本不是什么问题。我是完全被“游戏引擎”这4个字吓住了,这道题的目的根本不是要你真正的写一个正规的游戏引擎(那是扯蛋),只要弄出一个基本架子即可,达到“麻雀虽小五脏俱全”的效果。

几乎所有的游戏或者说所有的图形渲染的程序,都是遵循下面这样一个过程,一个再熟悉不过的控制流。

FoC of Game

游戏的整个控制流就可以到这么简单,除了图上所描述的东西之外,再加上一系列的消息处理,就可以组成一个基本的游戏框架了。按照wiki上所描述的基本软件framework的4大特点,再来看看游戏引擎framework是如何体现的。

  1. 很显然,游戏的控制流如上图所示,使用framework的用户无法更改;
  2. 游戏引擎framework当然提供默认的行为,比如Initialize部分封装默认的窗口初始化和一系列的设备初始化等过程;
  3. Update和Render,是framework使用者真正进行艺术创造的地方,是游戏开发者的画板。
  4. 游戏引擎framework的代码同样不允许用户修改。

用代码来表现framework是很简单的,在面向对象的语言中一般用抽象类,既提供一些默认的实现,又提供抽象接口供用户扩展。而用户要使用这个framework,只需自定义一个类继承该抽象类,并提供各个抽象方法的实现。用户所需做的只是做一系列的填空题,当然填的如何精彩这就完全取决于用户了。

写这篇文章的真正目的不是为了说明一个游戏引擎的framework是怎么样的,只是借此来强化framework的概念,这样当我们下次再说出这个词的时候,能够知其所以然,而不是自欺欺人。

如果对游戏引擎framework感兴趣,这里有一个简单的游戏引擎framework的代码可供参考: http://gpwiki.org/index.php/SDL:Tutorials:Simple_Engine_Framework

Posted in: Game Programming, Some Experiences / Tagged: framework, game engine, 框架, 游戏引擎

Quotes from “Clean Code: A Handbook of Agile Software Craftsmanship” (1)

January 20, 2010 5:57 pm / 2 Comments / Benny Chen

最近在读这本Robert Martin的Clean Code,中文译本是《代码简洁之道》,我觉得非常好看。我一直比较注重写出简洁出色的代码,并且一直以为自己在这方面做的还不错,但随着这本书的阅读,虽然我的一些观点和想法被得到验证,但另外一些却完全被颠覆。也让我意识到离一个出色的专业程序员还有多大的差距。

We’ve all looked at the mess we’ve just made and then have chosen to leave it for another day. We’ve all felt the relief of seeing our messy program work and deciding that a working mess is better than nothing. We’ve all said we’d go back and clean it up later. Of course, in those days we didn’t know LeBlanc’s law: Later equals never.

这样的经历真是太普遍了,我们写了一堆烂代码并且实现了某个功能,颇有成就感,但同时我们也意识到这是一堆垃圾代码,并且提醒自己等未来有空了一定要回来收拾这堆垃圾。但——Later equals never,我们再也没回来过。
所以,垃圾要及时处理,不然就这样遗臭下去了。

Replace the temptation to create noise with the determination to clean your code. You’ll find it makes you a better and happier programmer.

程序员避免忧郁症的好方法就是遵循简洁之道。

And now note the word that Bjarne uses to describe the consequence of that inelegance. He uses the word “tempt.” There is a deep truth here. Bad code tempts the mess to grow! When others change bad code, they tend to make it worse.

当面对一堆烂代码时,很少有人愿意去收拾这堆烂摊子,结果就是让它们烂到极致.(让我想起之前做过的一个flash项目,接手时是噩梦般的代码,我只能东补西凑,到最后功能加的越多,越是恐怖的混乱,几乎完全无法维护)

You get the drift. Indeed, the ratio of time spent reading vs. writing is well over 10:1. We are constantly reading old code as part of the effort to write new code. Because this ratio is so high, we want the reading of code to be easy, even if it makes the writing harder. Of course there’s no way to write code without reading it, so making it easy to read actually makes it easier to write.

这和我以前一直所想的观点正好契合,写只有1次,而读会有N次(N会随着不同的项目有着巨大的差别),所以为了那更多更多次的读,多花时间在写上是绝对值得的。而Martin给出了读写的比例至少在10:1的样子,他还提出了“要想写容易,得先容易读“。

It was the bad code that brought the company down.

烂代码让这家公司歇菜了!烂代码的威力如此恐怖,对于这点,我坚信不疑。

Clean code is simple and direct. Clean code reads like well-written prose.

简洁的代码读起来应该像优美的散文。

One difference between a smart programmer and a professional programmer is that the professional understands that clarity is king. Professionals use their powers for good and write code that others can understand.

聪明的程序员不等于专业的程序员。

In short, a programmer who writes clean code is an artist who can take a blank screen through a series of transformations until it is an elegantly coded system.

我热爱艺术,所以我热爱简洁漂亮的代码。我梦想成为一个艺术家,从程序员入手是个不错的选择。

Posted in: Some Experiences / Tagged: clean code, 代码简洁之道

My Footsteps of Programming 3DS Max Exporter Plug-in

November 20, 2009 9:03 pm / 4 Comments / Benny Chen

Several months ago, I achieved to develop a 3DS Max exporter plug-in. But until recently, I didn’t get the time to jot down something about my experience while developing it. Finally, thank God, I am starting to write this article, in case that several years later I totally forget how I did it at that time.

As you know, 3DS Max is a powerful tool for creating 3D models, especially widely-used in games. But generally we cannot directly get our model from 3DS Max, otherwise, we need a sort of middleware to retrieve specified model data we need from 3DS Max and export them into a specific type of file. This middleware is right the exporter plug-in.

OK, with this concept in mind, let’s get started.

Step by step, I will roughly present my footsteps of programming a 3ds max exporter plug-in based on 3DS Max 9 SDK.

Get familiar with 3DS Max and SDK

As we all know, there are a series of 3DS Max versions. Firstly you need to choose one to program based on. At the time of my embarking on this plug-in, the latest version is 3DS Max 2009. However, I chose Max 9 because it is already a widely-used and popular version. Most importantly, my laboratory was using Max 9.

Before doing some real programming jobs, it is pretty important to gain some basic concepts of 3ds max. We needn’t to be a perfect 3D designer ourselves, but these are the essential concepts we must know.

  • what is a Node in 3ds max
  • the hierarchical chart of nodes
  • how materials are attached to a node
  • what is a node’s Modifier
  • key-frame animation and how bone nodes are attached to normal nodes
  • the Biped technique

The fact is, the more we want to export from 3DS Max, the more we need to concern about it. For example, if we also want to export the data of camera or light from Max(meshes for games usually don’t export these data), we must apprehend what is a camera or light node in Max and how they work with other nodes.

But how the hell could we get the detail of these knowledge? Turn to the Max SDK Document, whenever you have any confusion with Max. Besides, we can also log on to Autodesk’s official Web site for more help.

BTW, the assumption here is that you are already familiar with some 3D concepts, like vertex, triangle, mesh, texture, space transformation, skinned animation, etc. If you are still a 3D newbie, you’d better figure them out firstly.

To be continued…

Predefine mesh file format

Export static mesh data

Export skinned animation

Posted in: Computer Graphics / Tagged: 3ds max, exporter, plug-in, 导出插件

Post Navigation

← Older Posts
Newer Posts →

LinkedIn

Milan Petrovic

Categories

  • In My Life (25)
    • A Day in the Life (8)
    • English Learning (2)
    • Learn a Word (7)
    • Something In The Way (8)
  • Music Heaven (8)
    • Guitar (1)
    • In Concert (1)
    • Lyrics (3)
  • OK Computer (54)
    • 3D (3)
    • C++ (10)
    • Computer Graphics (15)
    • Game Programming (23)
    • iOS (6)
    • Linux (1)
    • Lua (9)
    • My Projects (3)
    • Some Experiences (9)
    • Talking in Code (2)
    • Unity (2)
  • Quotations (2)
  • Uncategorized (1)
  • Visca Barça (24)
    • FCB BJ (5)

Recent Posts

  • [译]优化你的手机游戏(没有延迟的,才是健康的)- 一篇给游戏美术设计师读的文章
  • 新浪微博API for MOAI
  • 稍后继续
  • Unity Developer ++
  • Another Thread @ Moai Forum
  • 1st Day of Golden Week
  • 为SyntaxHighlighter添加新语言
  • 基于Lua的State Pattern
  • Class Diagram of Pacman
  • 基于Moai的Pacman

Recent Comments

  • 约修亚_RK on 为SyntaxHighlighter添加新语言
  • 爱装的小男孩 on 小心DLL链接静态库时的内存错误
  • happyfire on Game Loop的几种实现方式
  • William on 新浪微博API for MOAI
  • Benny Chen on 新浪微博API for MOAI
  • your man on 新浪微博API for MOAI
  • 你家男人 on 稍后继续
  • 逍遥 on 关于对std::vector的遍历
  • papa on Unity Developer ++
  • T客网 ︱ Techpot » Blog Archive » iOS开发与OpenGL ES相关问题整理(1) on iOS开发与OpenGL ES相关问题整理(1)

Tags

2d 3D 3dsmax 3ds max air Apply architecture Asia tour barca Beijing bilbao binary search blocked bob boost bruce springsteen C++ capo CGContextDrawImage Champions League Change DLL DX10 eval exporter flash framework frustum culling game game engine iniesta ios linux lua Moai opengles pacman plug-in plugin 北京 导出插件 崩溃 巴萨 游戏引擎 踢球
© Copyright 2026 - A Game Developer
Infinity Theme by DesignCoral / WordPress