前言
功能上的增增补补其实相对容易,而之所以想叫万能平台,最核心的多亏了配置文件的引入。够用和好用也不仅仅是对于使用者,难道对于开发者就不能提升自己的效率吗?
大佬之所以能成为大佬,想必除了要有过硬的基础实力,更要有站得更高的眼界和经验。一日,在沟通怎么能让各个平台或者目标客户都能方便移植的讨论上,想起了配置文件。之前考虑这个问题还停留在条件编译上,想着总得遇见其他平台再重新定义或者注释完在编译烧写呗,又或者运行时传参,可是那才能有几种模式啊。好像都只是为了升级而升级,意义并不是很大。
大佬提到,那能不能像用过的一些开源项目一样,用python去加载配置文件呢,里面有每个部分的配置项参数使能等等,非常快捷方便。
python确实上手快,但是c语言就不能也按照这样的加载方式,将代码整体框架全部写好编译完,根据用户需求调整配置文件做到不重新编译就能直接使用吗?
Linux下是没有专门的供给C语言使用的配置文件函数的,以前见过xilinx或者海思的项目也是python来加载的,没有现成的葫芦可以照着画瓢现学一点py肯定是一条路,就怕不熟有超出认知外的bug不好改。不过好歹思路有了先拿c硬写一写,最开始的做法就是按照之前rtsp的方法准备造轮子,但偶然间,发现了iniparser开源项目,可以像那些面向对象语言一样,使用ini文件进行参数配置,就可以避免每次有细微的改动或者功能的裁剪都要打开整个SDK加载编译烧写,直接修改配置文件加载就可以了,加载文件,传参这些基本功能封装的也非常简单好上手,瞬间欣喜万分。虽然需要慢慢完善的地方很多,但是好歹有了雏形
iniparser移植
下载
Iniparser顾名思义,是针对ini文件的解析器,可对init文件进行解析、设置、删除等操作。ini文件则是我们要写的一些系统或软件的配置文件。源码路径如下
https://github.com/ndevilla/iniparser
测试可用版本为3.1
修改iniparser库的Makefile
CC = aarch64-himix100-linux-gcc AR = aarch64-himix100-linux-ar
直接make生成静态库即可
修改海思sample的Makefile
INIPARSER_LIB ?= $(PWD)/../../iniparser-3.1/src PLATFORM_LIB ?= $(PWD)/../venc INC_FLAGS += -I$(INIPARSER_LIB) INC_FLAGS += -I$(PLATFORM_LIB) SENSOR_LIBS += $(INIPARSER_LIB)/libiniparser.a
基本语法
Iniparser库的API可以对ini文件(配置文件)进行解析、设置、删除等操作。
ini文件的最基本组成单元就是key或者叫property,每个key都有一个名称(name)和对应的值(value):
name=value
而许多个Key可以被归类为一组,即section。组名定义要独立一行,并用中括号括起来:
[section] name=value
在section声明下的keys都会和该section关联起来。一个section的作用域会在下一个section声明的地方结束,如果没有下一个section的声明,那么该section的结束地方就是该文件末尾。section是不可以嵌套的。
定位一个key是用section:key来表示的,所以不同section下的key的名称是可以相同的。
iniparser库处理名称的时候,会统一换成小写,所以section和property的名称命名是大小写无关的。
注释要以分号开头:
;comment
示例:
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include "iniparser.h" void create_example_ini_file(void); int parse_ini_file(char * ini_name); int main(int argc, char * argv[]) { int status ; if (argc<2) { create_example_ini_file(); status = parse_ini_file("example.ini"); } else { status = parse_ini_file(argv[1]); } return status ; } void create_example_ini_file(void) { FILE * ini ; ini = fopen("example.ini", "w"); fprintf(ini, "#\n" "# This is an example of ini file\n" "#\n" "\n" "[Pizza]\n" "\n" "Ham = yes ;\n" "Mushrooms = TRUE ;\n" "Capres = 0 ;\n" "Cheese = Non ;\n" "\n" "\n" "[Wine]\n" "\n" "Grape = Cabernet Sauvignon ;\n" "Year = 1989 ;\n" "Country = Spain ;\n" "Alcohol = 12.5 ;\n" "\n"); fclose(ini); } int parse_ini_file(char * ini_name) { dictionary * ini ; /* Some temporary variables to hold query results */ int b ; int i ; double d ; char * s ; ini = iniparser_load(ini_name); if (ini==NULL) { fprintf(stderr, "cannot parse file: %s\n", ini_name); return -1 ; } iniparser_dump(ini, stderr); /* Get pizza attributes */ printf("Pizza:\n"); b = iniparser_getboolean(ini, "pizza:ham", -1); printf("Ham: [%d]\n", b); b = iniparser_getboolean(ini, "pizza:mushrooms", -1); printf("Mushrooms: [%d]\n", b); b = iniparser_getboolean(ini, "pizza:capres", -1); printf("Capres: [%d]\n", b); b = iniparser_getboolean(ini, "pizza:cheese", -1); printf("Cheese: [%d]\n", b); /* Get wine attributes */ printf("Wine:\n"); s = iniparser_getstring(ini, "wine:grape", NULL); printf("Grape: [%s]\n", s ? s : "UNDEF"); i = iniparser_getint(ini, "wine:year", -1); printf("Year: [%d]\n", i); s = iniparser_getstring(ini, "wine:country", NULL); printf("Country: [%s]\n", s ? s : "UNDEF"); d = iniparser_getdouble(ini, "wine:alcohol", -1.0); printf("Alcohol: [%g]\n", d); iniparser_freedict(ini); return 0 ; }
一目了然,还是相对非常简单的
注意
iniparser_getboolean着重提醒一下,单拎出来因为虽然有些情况我们都习惯1是使能0是关闭,但yes/no,true/false,尤其是在没有代码的配置文件里,表现得还是更加直观的,所以看代码就明白在配置文件里我们该怎么填写vlaue了
int iniparser_getboolean(dictionary * d, const char * key, int notfound) { char * c ; int ret ; c = iniparser_getstring(d, key, INI_INVALID_KEY); if (c==INI_INVALID_KEY) return notfound ; if (c[0]=='y' || c[0]=='Y' || c[0]=='1' || c[0]=='t' || c[0]=='T') { ret = 1 ; } else if (c[0]=='n' || c[0]=='N' || c[0]=='0' || c[0]=='f' || c[0]=='F') { ret = 0 ; } else { ret = notfound ; } return ret; }