战魂小筑

讨论群:309800774 知乎关注:http://zhihu.com/people/sunicdavy 开源项目:https://github.com/davyxu

   :: 首页 :: 新随笔 :: 联系 :: 聚合  :: 管理 ::
  257 随笔 :: 0 文章 :: 506 评论 :: 0 Trackbacks

#

使用ProtocolBuffer的编译程序protoc可以将.proto生成*.pb.cc的C++代码文件. 但是在带有预编译头的工程里, 就经常需要手动添加类似stdafx.h的包含. 这里使用批处理来完成这一过程:

BuildProtoCC.bat

@echo off

set protofile=%1%.proto
set output_cc=%1%.pb.cc

"protoc.exe" %protofile% --cpp_out .
@echo 使用%protofile%生成%output_cc%

if exist "pchheader.txt" goto 合并预编译头 else echo pchheader.txt NOT EXIST

:合并预编译头
@echo 尝试删除以前的中间文件
del *.pched
@echo 合并编译头文件pchheader.txt到生成%output_cc%
copy pchheader.txt+"%output_cc%" "%output_cc%.pched" /y
@echo 等待protoc.exe结束
@ping -n 2 127.1>nul
echo 清理文件
del "%output_cc%"
ren "%output_cc%.pched" "%output_cc%"

 

这段批处理这样使用:

假设你有一个叫login.proto文件和批处理放在一起

在同一目录再放置一个pchheader.txt文件,并在里面写入#include "stdafx.h”

调用一下批处理

call BuildProtoCC.bat login

注意login不能带后缀

将使用protoc.exe生成login.pb.cc和login.pb.h 然后使用批处理合并文件功能自动合并预编译头

posted @ 2011-11-02 17:32 战魂小筑 阅读(2172) | 评论 (1)编辑 收藏

帮朋友转招聘

 

应用软件工程师

职位描述:

   使用C/C++,MFC和数据库等工具构建用户需求的应用软件。

职位薪水:
   4000~8000 + 项目奖金

职位需求:
1、熟练掌握C语言或C++语言。
2、熟悉MFC的使用,有Windows GUI编程经验。
3、熟悉数据库的使用并有相关应用经验。
4、具备良好的编程思想。
5、具有一年以上应用软件开发经验。

工作地点:成都, 有意者请密我细谈

posted @ 2011-10-25 14:09 战魂小筑 阅读(318) | 评论 (0)编辑 收藏

我在工程里使用以下函数给线程设置一个方便调试的名称

    typedef struct tagTHREADNAME_INFO
    {
        DWORD dwType;        // must be 0x1000
        LPCSTR szName;        // pointer to name (in user addr space)
        DWORD dwThreadID;    // thread ID (-1=caller thread)
        DWORD dwFlags;        // reserved for future use, must be zero
    } THREADNAME_INFO;
 
    void SetThreadName( DWORD dwThreadID, LPCSTR szThreadName )
    {
        THREADNAME_INFO info;
        info.dwType = 0x1000;
        info.szName = szThreadName;
        info.dwThreadID = dwThreadID;
        info.dwFlags = 0;
 
          // Visit http://www.cppblog.com/sunicdavy for original article
        __try
        {
            RaiseException( 0x406D1388, 0, sizeof(info)/sizeof(DWORD), (DWORD*)&info );
        }
        __except(EXCEPTION_CONTINUE_EXECUTION)
        {
            int a =1;
        }
    }

使用期间一直正常. 随着工程需求变化,我需要频繁的将一个被命名的线程创建工作一段时间后马上结束释放资源并重新创建,出现了死锁问题

死锁总是发生在主线程的Sleep或者是SetEvent两个WindowsAPI函数. 而且仅仅在Visual Studio 2008里调试时会发生死锁,脱离调试环境一切正常.

我开始打日志调试, 结果发现死锁前能够正常创建线程,但是线程函数根本没有调用进入.之后就在主线程Sleep处死锁了.

由于SetThreadName没有使用正规的API接口,而是使用抛异常方式, 而且线程名称应该只在Visual Studio IDE中才能显示, MSDN对RaiseException的描述里也有说明可以使用WaitForDebugEvent接收应用程序抛得异常.因此可以推断是SetThreadName造成的问题. 屏蔽SetThreadName,测试通过

posted @ 2011-10-12 09:50 战魂小筑 阅读(2974) | 评论 (1)编辑 收藏

   在龚敏敏的 《关于D3D11,你必须了解的几件事情系列》文章里看到DX11支持DX9特性的细节,特别在DX11的例子中试了一下。

我的显卡是Geforce GT440,跑DX11的例子没有大问题,修改了一下DXSDK里的最简单Shader例子,跑出来的结论是,DX9_3可以创建设备,但是在CreateVertexShader地方失败了,尝试将Shader编译版本降低到VS_2_0也是持续失败,于是乎证明了DX11的接口及引擎不能简单通过调节FEATURE_LEVEL来变成DX9级别跑, 最关键的是,DX11不支持XP,XP下跑DX11的例子直接就报找不到DLL。

因此,我觉得DX11里的DX9的兼容级别很蛋疼,毫无用处的东西。

龚敏敏的KlayGE引擎在某此升级时直接删除了DX11以前的所有插件,只使用OpenGL来兼容XP, 可喜的是OpenGL 4.0没有微软那么霸道及商业化,可以为XP提供DX11级别的特性,这才是趋势。

posted @ 2011-09-29 17:52 战魂小筑 阅读(2134) | 评论 (1)编辑 收藏

使用protobuf的生成器可以对proto文件进行解析后生成指定的目标语言代码.随着项目的不断扩大, 协议修改变的非常频繁, 因此每次重编变的异常耗时. 模仿C/C++编译器的时间戳检查生成机制,我给protobuf生成器添加了时间戳检查功能. 下面说下原理:

此功能不是必须的, 可以通过命令行指定—timestampfile FILE 来指定要生成的proto文件对应的时间戳

每次运行解析器时, 解析器通过命令行获取proto文件, 我们可以先读取之前保存的时间戳, 然后与这个文件的当前修改时间进行对比

如果时间戳不相等,说明文件已经被修改, 需要重新生成.之后更新时间戳文件即可.

下面是在protobuf的protoc工程里添加的代码:

command_line_interface.cc

void CommandLineInterface::ReadTimeStampFile( const string& timestampfile )
{
    FILE* f = fopen( timestampfile.c_str(), "rb" );

    if ( f == NULL )
        return;

    time_stamp_map_.clear( );

    while( !feof( f ) )
    {
        size_t read = 0;

        int strlen = 0;
        read = fread( &strlen, 1 , sizeof strlen,  f );

        if ( read == 0 )
            break;

        std::string filename;
        filename.resize( strlen );

        read = fread( (void*)filename.data(), 1, sizeof(char) * strlen, f );

        if ( read == 0 )
            break;


        __time64_t timestamp;
        read = fread( &timestamp, 1, sizeof(timestamp), f );

        if ( read == 0 )
            break;

        time_stamp_map_[filename] = timestamp;
    }

    fclose( f );
}

void CommandLineInterface::WriteTimeStampFile( const string& timestampfile )
{
    FILE* f = fopen( timestampfile.c_str(), "wb" );

    if ( f == NULL )
        return;

    for ( time_stamp_map::iterator it = time_stamp_map_.begin();
        it != time_stamp_map_.end();
        it++)
    {
        const std::string& filename = it->first;
        __time64_t timestamp = it->second;

        int strlen = filename.length();
        fwrite( &strlen, 1, sizeof(strlen), f );
        fwrite( (void*)filename.data(), 1, sizeof(char)*strlen, f );
        fwrite( &timestamp, 1, sizeof( timestamp ), f );
    }
    
    fclose( f );
}

bool CommandLineInterface::NeedGenerate( const std::string& filename )
{
    struct _stat buf;

    if ( _stat( filename.c_str(), &buf ) != 0 )
    {
        // file error
        return true;
    }

    time_stamp_map::iterator it = time_stamp_map_.find( filename );

    
    if ( it != time_stamp_map_.end() )
    {
        __time64_t& timestamp = it->second;

        if ( timestamp == buf.st_mtime )
            return false;
    }

    // don't hold the time stamp or time stamp not match, generate it!
    
    // save to map , then write time stamp
    time_stamp_map_[filename] = buf.st_mtime;

    return true;
}


void CommandLineInterface::CheckFileStamp( const string& timestampfile )
{
    
    ReadTimeStampFile( timestampfile );

    

    for (vector<string>::iterator it = input_files_.begin();
        it != input_files_.end();) {
        
        if ( !NeedGenerate( *it) )
        {
            it = input_files_.erase( it );
        }
        else
        {
            ++it;
        }
    }


    WriteTimeStampFile( timestampfile );
}
 

以上代码为核心逻辑, 之后在int CommandLineInterface::Run(int argc, const char* const argv[]) 函数中添加代码:

int CommandLineInterface::Run(int argc, const char* const argv[]) {
  Clear();
  if (!ParseArguments(argc, argv)) return 1;

  // Set up the source tree.
  DiskSourceTree source_tree;
  for (int i = 0; i < proto_path_.size(); i++) {
    source_tree.MapPath(proto_path_[i].first, proto_path_[i].second);
  }

  // Map input files to virtual paths if necessary.
  if (!inputs_are_proto_path_relative_) {
    if (!MakeInputsBeProtoPathRelative(&source_tree)) {
      return 1;
    }
  }

  // Allocate the Importer.
  ErrorPrinter error_collector(error_format_, &source_tree);
  Importer importer(&source_tree, &error_collector);

  vector<const FileDescriptor*> parsed_files;


  if ( time_stamp_filename_ != "" )
    CheckFileStamp( time_stamp_filename_ );

加黑部分为添加的代码, 这里是检查时间戳的入口

同时,我们还需要添加命令行解析开关, 这里在

bool CommandLineInterface::InterpretArgument(const string& name,
                                             const string& value) {

函数中,找到:

  } else if (name == "--error_format") {
    if (value == "gcc") {
      error_format_ = ERROR_FORMAT_GCC;
    } else if (value == "msvs") {
      error_format_ = ERROR_FORMAT_MSVS;
    } else {
      cerr << "Unknown error format: " << value << endl;
      return false;
    }

  } 
  else if ( name == "--timestampfile" ){
        time_stamp_filename_ = value;
  }
  else if (name == "--plugin") {
    if (plugin_prefix_.empty()) {
      cerr << "This compiler does not support plugins." << endl;
      return false;
    }

加黑部分为解析开关

 

之后在头文件中添加声明代码:

  // Time stamp function
  void ReadTimeStampFile( const string& timestampfile );

  void WriteTimeStampFile( const string& timestampfile );

  bool NeedGenerate( const std::string& filename );

  void CheckFileStamp( const string& timestampfile );

  typedef std::map<std::string, __time64_t> time_stamp_map;    // proto file name as key, timestamp as value
  time_stamp_map time_stamp_map_;
  string time_stamp_filename_;
转载请注明: 战魂小筑http://www.cppblog.com/sunicdavy
posted @ 2011-08-16 11:38 战魂小筑 阅读(3809) | 评论 (1)编辑 收藏

protocolbuffer默认支持java, python 和c++,其他的语言需要自己编写代码生成器他语言的代码.

不过官网也提到过ActionScript3的支持,点击看这里

protobuf-actionscript3包括ActionScript3的序列号及反序列化的as源码.以及protoc的as3代码生成器的C++代码

另外, protobuf-actionscript3还需要一个加密库as3crypto支持,需要一并下载

当然, protobuf 库也是最重要的.

下面说明如何编译出支持as3的protoc代码生成器:

 

1. 将protobuf-actionscript3库中compiler\as3目录拷贝到protobuf库的src\google\protobuf\compiler下.目录结构如下:

as3\
    cpp\
    java\
    python\
    code_generator.cc
    command_line_interface.cc

...

 

2.打开protobuf工程中的sln, 在libprotoc添加刚才添加的as3目录下的所有文件

3. 在protoc工程的main.cc中添加

  google::protobuf::compiler::as3::As3Generator as3_generator;
  cli.RegisterGenerator("--as3_out", &as3_generator,
      "Generate ActionScript source file."); 

4. 打开as3_file.h

注释掉以下代码

namespace protobuf {
  class FileDescriptor;        // descriptor.h
  namespace io {
    class Printer;             // printer.h
  }
  namespace compiler {
    class OutputDirectory;     // code_generator.h
  }
}

包含内添加

#include <google/protobuf/stubs/common.h>
#include <google/protobuf/compiler/code_generator.h>
#include <google/protobuf/io/printer.h>
5. 编译出protoc.exe
6. 准备proto文件, 使用protoc.exe使用类似CPP生成的方式进行生成
7. 将protobuf-actionscript里的as3-lib及下载好的加密的swc整合到自己的flash工程即可开始使用
posted @ 2011-08-10 22:00 战魂小筑 阅读(2861) | 评论 (0)编辑 收藏

LNK4099号警告发生于 静态库没有提供pdb文件给之后使用静态库的工程链接中

解决方法如下:

 

找到静态库的工程属性:C/C++->Output Files

将Program Database File Name改为与静态库同名的pdb文件

清理工程, 重新编译,将lib及pdb提供给链接工程即可

 

P.S. 默认配置中的pdb是vcXX.pdb XX为VC版本, vs2008对应VC90.pdb

 

转载请注明: 战魂小筑http://www.cppblog.com/sunicdavy

posted @ 2011-08-09 10:53 战魂小筑 阅读(6637) | 评论 (2)编辑 收藏

对于习惯于SVN的集中式代码管来说,Windows下的TortoiseHg需要一些规范来避免代码合并错误及丢失,下面我具体说下TortoiseHg操作中的一些注意要点.

这里使用的TortoiseHg版本是2.0.4,对应的Mercurial版本是1.8.3,操作环境为局域网

1. 克隆代码

image

局域网获取代码时,代码源(Source)的格式是 http://ip-address:8000

而代码源的机器需要打开WorkBench->Repository->Web Server...

默认情况下,不打开Web Server,别人是无法获取你电脑上的代码库的.

2. 代码changeset的辨别

image 

默认的,Graph里左边一支是自己的版本,右边的一支代表其他人提交过来的changeset. 带圈的代表工作目录基于的版本修改

3. 同步代码

默认情况下,Push是被禁用的,这也能避免别的不必要的版本被推送到自己的代码库.

需要同步代码时点击面板的Synchronize,弹出Remote Repository.

image

在文本框里可以输入http://ip-address:8000之类的地址,点击右边的软盘图标可以保存该地址以便下次再同步

点击Preview可以预览对方代码库的修改,Accept后被获取到本地

 

4. 获取别人代码,合并本地正在修改的代码

image

出现以上提示时,选择Merge来合并本地修改.

不过使用TortoiseHg时, 不建议使用这种方式,比较好的方法应该是在合并前提交自己代码进库,参考下面一条.

5. 获取别人代码, 合并已经提交的本地代码,本地没有挂起修改的代码

  使用这种方法时, Pull过代码后,不Update,而是直接Merge with Local

image

碰到有冲突时,需要解决冲突

image

请使用Tool Resolve, TortoiseHg会使用你从设定中选择的合并工具来进行代码合并,其他功能很类似于SVN. 如果合并不爽,也可以重新进行合并

 

6. 重新恢复冲突解决

如果由于误操作关闭合并对话框,或者是需要提交本地代码而关闭对话框, 可以在Repository->Resolve…重新打开对话框

 

以上为本人的一些心得,欢迎板砖伺候

转载请注明 战魂小筑 及本文地址

posted @ 2011-05-25 19:22 战魂小筑 阅读(3619) | 评论 (0)编辑 收藏

等了大半年,松鼠脚本终于释出了3.0稳定版本

松鼠脚本可以说是Lua的超级增强版,作者在Crysis项目使用Lua多年,深知Lua的优缺点.之后自己编写了松鼠脚本来解决诸如class,attribute,delegation,更强大的thread,exception等等功能.

松鼠脚本本身对Windows开发人员极为照顾,第三方库可谓丰富,远程调试,代码加色及语法检查都可以直接在VS2008的IDE中进行.

简单的远程调试功能需要以下步骤:

1. 下载松鼠脚本3.0版本

2. 在松鼠脚本Wiki中下载SQDBG远程调试库

3. 将sqdbg工程放到SQUIRREL3之下,打开SQUIRREL3\squirrel.sln,将sqdbg添加到工程,编译所有

4. 在这个Solution下,创建一个Squirrel松鼠工程

image

5. 在新创建的松鼠工程属性中如下图设置

image 

Interpreter其实就是启动调试器(不是sq.exe)

WorkingDirectory 就是main.nut所在的目录(官网的路径设置有点误导)

Command Line Options 就是传入sqdbg.exe的命令行参数

修改下main.nut文件,例如:

for(local i = 1;i<10;i++)
{
    print( i );
}

在print语句前打上断点,调试松鼠工程,即可看到调试结果

image

 

有关于SQDBG多文件调试:

SQDBG默认只能调试1个文件,显然是个玩具,不能应对游戏和其他领域的多文件调试. 分析了下SQDBG的代码

   1:  int main(int argc, char *argv[])
   2:  {
   3:      if(argc < 2){
   4:          scprintf(_SC("SQDBG error : no file specified"));
   5:          return -1;
   6:      }
   7:          
   8:      HSQUIRRELVM v = sq_open(1024);
   9:      sqstd_seterrorhandlers(v);
  10:   
  11:      //!! INITIALIZES THE DEBUGGER ON THE TCP PORT 1234
  12:      //!! ENABLES AUTOUPDATE
  13:      HSQREMOTEDBG rdbg = sq_rdbg_init(v,1234,SQTrue);
  14:      if(rdbg) {
  15:   
  16:          //!! ENABLES DEBUG INFO GENERATION(for the compiler)
  17:          sq_enabledebuginfo(v,SQTrue);
  18:   
  19:          sq_setprintfunc(v,printfunc,errorfunc);
  20:   
  21:          //!! SUSPENDS THE APP UNTIL THE DEBUGGER CLIENT CONNECTS
  22:          if(SQ_SUCCEEDED(sq_rdbg_waitforconnections(rdbg))) {
  23:              scprintf(_SC("connected\n"));
  24:   
  25:              const SQChar *fname=NULL;
  26:  #ifdef _UNICODE
  27:              SQChar sTemp[256];
  28:              mbstowcs(sTemp,argv[1],(int)strlen(argv[1])+1);
  29:              fname=sTemp;
  30:  #else
  31:              fname=argv[1];
  32:  #endif 
  33:              //!!REGISTERS STANDARDS LIBS
  34:              sq_pushroottable(v);
  35:              sqstd_register_bloblib(v);
  36:              sqstd_register_iolib(v);
  37:              //!!EXECUTE A SCTIPT
  38:              if(SQ_FAILED(sqstd_dofile(v,fname,SQFalse,SQTrue))) {
  39:                  PrintError(v);
  40:                  _getch();
  41:              }
  42:          }
  43:          //!! CLEANUP
  44:          sq_rdbg_shutdown(rdbg);
  45:      }
  46:      else {
  47:          PrintError(v);
  48:      }
  49:      sq_close(v);

发现写这个库的老外还是很认真的,使用了sq_rdbg作为debugger的api前缀,意味着这个库代码是可以复用的.

注意第38行:

sqstd_dofile(v,fname,SQFalse,SQTrue)

这就是SQDBG只能debug 1个文件的原因.

如果需要在项目中做远程调试,只需要将这句代码换为工程中加载所有脚本的代码和注册系统API代码即可

 

当然,最简单的方法,直接在代码顶端添加dofile引用另外的代码即可

posted @ 2011-05-14 17:42 战魂小筑 阅读(5200) | 评论 (5)编辑 收藏

最近正为游戏引擎选择一款较好的逻辑脚本而头疼不已

1. Lua

Lua用了5,6年的样子,从LuaPlus到lua-tinker再到lua_bind. 虽然lua本身很小巧方便,但是其不支持面向对象特性,让很多人在使用时模拟了很多面向对象的特性,但是这种做法在调试时简直是噩梦.

我觉得游戏脚本最好是能预编译成byte code,让编译器在编译期将很多的bug暴露出来,而不是像lua那样.很多空访问错误必须要等到runtime才能暴露.让调试效率大大降低.这点,mono C#就是极好的选择.

2. Mono

由于Unity的流行,Mono被带入游戏开发者的视线内.其内置的很多系统脚本都是用C#写成,而且逻辑脚本用C#编写也是非常清晰严谨的.

据说Mono在Windows下编译异常麻烦,而且嵌入方式也没有太多例子,现在这块严重缺乏资料.

3. V8

Google V8正是由于Chrome而风靡起来.直接JIT方式就是Chrome速度飞快的原因.

最近down了一个下来试了下,果然名不虚传,API也非常的易用.不过由于不是很熟悉JavaScript的原因, 自己用scons编译出一个v8的shell,跑了一个带类继承的例子,居然不识别class关键字.这才感叹还不如用小巧的lua呢.在我看来,JavaScript与Lua比较,也就是个能new,但是没class的语言.也被别人称为半吊子面向对象,都是使用prototype方式来实现类功能.

4. JavaScriptCore

在Google V8的类测试碰壁后,马上研究了下Unity下的JavaScript到底用了什么库,没错就是Apple Safari里的JavaScriptCore. 这东西资料比Mono还少,都是高龄库了.想必整合不是一般的麻烦.但是还是不确定这个脚本引擎是否原生支持类

5. Python

Python? 没想过这种格式诡异的语言,正如Unity里支持Boo(一种很类似Python的语言)很少有人用一样, 上次用过Python是在一个build系统里, 游戏里是不会考虑这种脚本系统的,效率也是很大的原因.

6. AngleScript, GameMonkey

这两种语言都是很老的游戏脚本语言,不过都是类c++方式的,不过由于项目使用不多,或许是有一定的bug,资料也相对较少,所以可能最后考虑

posted @ 2011-05-14 15:13 战魂小筑 阅读(11455) | 评论 (24)编辑 收藏

仅列出标题
共26页: First 8 9 10 11 12 13 14 15 16 Last