在软件工程领域,有一句著名的话,叫做“Don’t Reinvent the Wheel”—–不要重复发明轮子。究其原因一方面是重复发明效率低下,另一方面是重新发明的轮子未必好用,也许发明之后发现轴承有问题,或者是有着各种瑕疵…….对于编程来说,“复用代码”有着更明确的好处。因此,如果有可能,我们希望更多秉承鲁迅先生提出的“拿来主义”在代码设计上。最近研究了一些C语言的库,得益于UEFI 的设计和 CLIB 的支持,大部分库都可以直接使用。今天介绍的是一个可以用于表达式计算的库:TinyExpr【参考1】。
首先是要将 TinyExpr Porting 到UEFI上,直接编译会出现一些 Error 和 Warning。经过研究,需要在 INF中加入下面的内容:
1.引入LibMath,否则一些 cos 之类的函数无法识别
[LibraryClasses] ShellCEntryLib UefiLib LibC LibStdio LibMath
2.关闭一些 Warning
[BuildOptions] MSFT:*_*_IA32_CC_FLAGS = /Ze /wd4201 /wd4152 /wd4090 /wd4204 /wd4055 /wd4244 MSFT:*_*_X64_CC_FLAGS = /Ze /wd4201 /wd4152 /wd4090 /wd4204 /wd4055 /wd4244
其中 C4201 Warning【参考2】,是 VS 编译器的扩展特性,比如下面这样的定义,在正经的 C 中是不允许的,但是 VC 中做了扩展是可以的:
struct S
{
float y;
struct
{
int a, b, c; // C4201
};
} z;
这样扩展之后,可以直接使用 z.a 和 z.b。
此外,tinyexpr.c 中有关于 NAN 的定义和StdLib中的 Math.h中的存在冲突。我的解决方法是先用 #undef NAN 取消之前的定义,再根据 VS 编译器中 Math.h 的定义重写一次,结果如下:
//#ifndef NAN
//#define NAN (0.0/0.0)
#undef NAN
#define NAN ((float)(INFINITY * 0.0F))
//#endif
最终测试代码如下:
#include <Library/BaseLib.h>
#include <Uefi.h>
#include <Library/UefiLib.h>
#include <Library/PrintLib.h>
#include <Library/ShellCEntryLib.h>
#include <Protocol/EfiShell.h>
#include <Library/ShellLib.h>
#include <stdio.h>
#include <math.h>
#include "tinyexpr.h"
/**
Set the socket options
@param [in] Argc The number of arguments
@param [in] Argv The argument value array
@retval 0 The application exited normally.
@retval Other An error occurred.
**/
int
main (
IN int Argc,
IN char **Argv
)
{
const char *c = "sqrt(5^2+7^2+11^2+(8-2)^2)";
double r = te_interp(c, 0);
printf("The expression:\n\t%s\nevaluates to:\n\t%f\n", c, r);
return 0;
}
上述代码计算表达式

结果如下:

完整的代码下载:
expr
参考:
1.https://github.com/codeplea/tinyexpr
2.https://msdn.microsoft.com/en-us/library/c89bw853.aspx