Parser Generator 2 编译C/C++库

背景

Parser Generator 2 最糟糕的设计,莫过于要使用LibBuilder来构建C/C++库文件。

下载后其默认的配置是为Visual Studio 6.0(以及Borland C)配置的,是32位。但后来Visual Studio(VS)的发展速度很快,C/C++用户使用Parser Generator 2遇到的第一个问题就是为最新的VS做配置。但往往最新的VS会改变编译参数和编译配置,另外我们还有可能要编译64位版本,这就需要用户修改相关的脚本。这个脚本在Cpp\Scripts下,其内容看起来是一种脚本语言。那就意味着开发这还需搞懂这种脚本语言。在这一步很多开发者就放弃了,非常可惜。

代码分析

今天本文要脱离LibBuilder,直接编译库文件。通过观察,我们发现所有的C/C++源代码来自Cpp\Source目录下。那么这就意味着我们可以直接从这些源代码入手进行编译。

首先看Cpp\Lib\msvc32下面的库文件。这里有一篇文章:

https://blog.csdn.net/xujianlane/article/details/2801204

看里面列的两个表格,基本上说清楚了。yl.lib是只支持单线程静态库,ylmt.lib是支持多线程静态库,ylmtr.lib是支持多线程run time类的静态库,ylmtri.dll是支持多线程run time类的动态库。

由于最初发布该工具的时候,PC机的处理能力并没有现在那么强劲,运行效率非常重要,所以设计者提供了多种模式的库来适应用户的性能要求,显然静态单线程的库会比动态多线程的类库快很多。但是从今天计算机处理能力来看,这种差别已经可以忽略了。使用类开发已经是主流,所以我们今天的目标只要编出ylmtri.dll即可。如果有特殊需求,需要在性能低的设备上运行,可以根据本文的内容类推。

在Cpp\Source下,有300多个文件。但是仔细看前缀,是非常有规律的。忽略开头的yy,可以看到有ac, am, as, wc, wm, ws,六种。

如果你选择查看Visual C++ (32-bit)编译配置的属性页,其中有一个wchar_t的支持。

所以可以猜到 a 就是支持ascii, w就是支持wchar_t。从编译出库的模式看,s就是single,单线程;m就是multiple,多线程;c就是class,即支持类。

a

只支持8bit字符

w

支持宽字符

s

只支持单线程,不支持类

m

支持多线程,不支持类

c

支持多线程,类

从这里可以看到,a和m的选择其实是在用LibBuilder编类库的时候确定下来的。如果Treat wchar_t选项是True,那么yya*那些源代码根本不会被编译。显然我们只需要编yyw*的代码。另外既然我们的目标是使用类的版本,那么我们需要编译的代码就缩小到wc的代码。即只需要编译所有代码的1/6,大概是50多个文件就够了。

另外我们还看到一些不带特殊前缀的文件:

我们打开yydllm.c看一下:

#ifdef YYPROTOTYPE
BOOL WINAPI DllMain(HANDLE hDLL, DWORD dwReason, LPVOID lpReserved)
#else
BOOL WINAPI DllMain(hDLL, dwReason, lpReserved)
HANDLE hDLL;
DWORD dwReason;
LPVOID lpReserved;
#endif
#endif
{
    switch (dwReason) {
    case DLL_PROCESS_ATTACH:
        yyinit();
        break;
    case DLL_PROCESS_DETACH:
        yydelete();
        break;
    case DLL_THREAD_ATTACH:
        break;
    case DLL_THREAD_DETACH:
        break;
    default:
        break;
    }
    return TRUE;
}
#endif

哦,原来是dll入口函数,yyinit()不就是yyinit.c提供的,yydelete()不就是yydelete.c提供的。看来这几个文件也要编进去。

编译

现在我们编译的目标如下:

  1. 支持多线程

  1. 支持宽字节

  1. 支持Unicode

  1. 使用类

  1. 生成动态链接库

我们使用最新版本的Visual Sutidio 2022 Community来做这个事情。

环境脚本

我们创建一个目录PG2Libs作为我们的工作目录(<work_dir>)。在这个目录下创建一个批处理文件env.bat(我的系列都用这个模式),内容如下:

set PGDIR=D:\Program Files (x86)\Parser Generator 2
set VSINSTALLDIR=D:\Program Files\Microsoft Visual Studio\2022\Community
set PATH=%VSINSTALLDIR%\Common7\IDE;%PGDIR%\Bin;%PATH%
start cmd /k devenv

前面的set是用来设置环境的,包括路径和变量。第一行是设置PGDIR为Parser Generator 2的安装目录。第二行是设置VS的安装目录。第三行是将相关的可执行文件位置加入到PATH里。读者可以根据自己安装的目录来写。

这样最后一行启动的VS,并使用上面设置的环境。

创建项目

我们还是使用ylmtri来命名项目,解决方案的名字写GP2Libs。这样还可以在这个解决方案里加别的项目来编别的库:

注意,位置要填写GP2Libs所在的目录

选择空项目:

在属性页里,配置类型选动态库。注意,调试版在目标文件名后加一个d

Include目录配置:

这里使用了%PGDIR%,这个就是env.bat里定义的

预处理宏:

预处理宏可以查看Cpp\Source\yytdefs.h的内容,通过总结:

Debug版本

Release版本

_WIN32

_DLL

_MT

YYDEBUG

YYBUILDDLL

YYNUNICODE

_WIN32

_DLL

_MT

YYBUILDDLL

YYNUNICODE

接下来就可以添加代码了,添加所有的yywc开头的文件,另外添加不带特殊前缀的c文件就可以了。

另外创建完解决方案,读者可以修改env.bat最后一行:

start cmd /k devenv PG2Libs.sln

这样运行这个批处理就可以直接打开解决方案。

编译项目

上面的配置都好了以后,就可以开始编译了。编译过程主要是处理出错.

错误

PG2Libs\ylmtri\yywcdop.cpp(37,3): error C4996: 'wcscpy': This function or variable may be unsafe. Consider using wcscpy_s instead. To disable deprecation, use _CRT_SECURE_NO_WARNINGS. See online help for details.

这错误是说wscpy已经不安全了,建议使用wscpy_s或者定义_CRT_SECURE_NO_WARNINGS关闭这个错误。

解决一

如果你选择改wscpy_s,那么调用时要增加一个参数,就是目标字符串的可用长度。比如:

void yywlexer::yydebugoutput(yywint_t ch) const
{
    wchar_t string[32];
    switch (ch) {
    case EOF:
        // wcscpy(string, L"EOF"); //原来的写法
        wcscpy_s(string, 32, L"EOF"); //修改后的写法
        break;
    ...

同样的方法来修改swprintf:

        // output stack contents
        if (yygetglobaldebugstack() || yydebugstack) {
            yydebugoutput(L"\nstack");
            int n = swprintf_s(string, 128, L"\n     +"); // 修改后
            int i;
            for (i = 0; i < 10; i++) {
                n += swprintf_s(&string[n], 128 - n, L" %5d", (int)i); //修改后
            }
            yydebugoutput(string);
            ...

注意,向string[n]拷贝,长度要去掉n。

解决二

在yydefs.h文件底部添加宏定义:

#define _CRT_SECURE_NO_WARNINGS

比较建议解决方案一,既然已经使用了最新的编译工具,就彻底排除隐患。

通过修改就可以顺利编译了,在Debug配置下最终得到ylmtrid.dll

如此编译Relase版和64位版本都不是问题了。

总结

本文总结了不使用LibBuilder的方法来构建Parser Generator 2的类库。站在现在的开发工具角度看,编译多线程支持类的动态库是最好的选择,在这一选择下通过分析源代码的类型,构建该库并不困难。当彻底摆脱了LibBuilder的限制,在任何VS版本下使用Parser Generator 2都不是问题了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值