编程实战:类C语法的编译型脚本解释器(三)插件(自定义函数)接口

本文介绍了如何在类C编译型脚本解释器中实现自定义函数的插件接口,包括CPlugin结构和其成员函数,以及CPluginMap插件管理器的使用。通过CMax插件实例展示了插件的创建、检查参数和执行过程。
摘要由CSDN通过智能技术生成

初级代码游戏的专栏介绍与文章目录-CSDN博客

我的github:codetoys,所有代码都将会位于ctfc库中。已经放入库中我会指出在库中的位置。

这些代码大部分以Linux为目标但部分代码是纯C++的,可以在任何平台上使用。


系列入口:

编程实战:类C语法的编译型脚本解释器(系列)-CSDN博客

        本文讲解插件(自定义函数)的接口。

        下文中的“插件”和“自定义函数”是两个概念:

  • “插件” 提供自定义函数功能的类,具有特殊的接口,一个插件实现一个自定义函数
  • “自定义函数” 脚本里的函数调用,形如“fun(a,b,c)”,脚本解释器负责将脚本里的函数调用与插件对应

目录

一、插件接口

二、插件管理器


一、插件接口

	//插件
	struct CPlugin
	{
		string plugin_name;
		Variable::types plugin_return_type;
		CPlugin() :plugin_name(""), plugin_return_type(Variable::NULLVARIABLE) {}
		CPlugin(char const* _name, Variable::types _type) :plugin_name(_name), plugin_return_type(_type) {}
		virtual string& help(string& ret)
		{
			ret = plugin_name + " : 返回值 " + Variable::TypeStr(plugin_return_type) + "\r\n";
			return ret;
		}
		virtual bool CheckPlugin(vector<Variable >& params, void*& pc, string& msg) = 0;
		virtual bool ExecFunction(vector<Variable >& params, void* const& pc, Variable& ret, string& msg, void* pe) = 0;
	};

        这是个关键的接口,只有两个成员变量:

类型名称说明
stringplugin_name自定义函数名称,也就是函数名
Variable::typesplugin_return_type自定义函数的返回值类型,返回值放在一个Variable中,必须明确指定返回类型,编译时会检查返回值类型是否正确

        两个构造函数无关紧要,只是提供了设置自定义函数名称和返回值类型而已。

        三个虚函数很重要:

  1. help() 返回自定义函数说明,已经提供了一个示例实现,这种参数传递方式是用来避免不必要的对象创建的。
  2. CheckPlugin() 编译时检查,如果返回false则编译不通过。后面详细介绍。
  3. ExecFunction() 执行,执行脚本时通过此接口获得返回值。 后面详细介绍。

        CheckPlugin()和ExecFunction()有很多共同的参数,一起介绍:

类型名称说明
vector<Variable>params函数的参数列表,也就是fun(a,b,c)的“a,b,c”,编译时提供的值是无意义的,但可以检查类型是否符合预期
void * &pc

编译时用户提供的指针的引用,用户可以在编译时修改,意即:用户可以在CheckPlugin里面申请内存,保存在pc变量里供执行时使用

此变量在执行时是只读的,但是指向的内容仍然是可以修改的(我感觉在运行时不断修改这个指针是难以理解的)(大BUG,这个值其实根本没设置,编译和运行都没有设置,没有这个参数

Variable &ret存放函数的返回值
string &msg如果执行出错,可以在这里放返回值
void *pe运行时由用户提供的指针

         CheckPlugin()和ExecFunction()本身的返回值为bool,表达插件本身执行成功或失败,具体区别借助实例来理解:

	struct CMax : public CPlugin
	{
		CMax() :CPlugin("max", Variable::DOUBLE) {}
		virtual string& help(string& ret)
		{
			ret = CPlugin::help(ret);
			ret += "取最大值,1-N个参数,参数必须是数值\r\n";
			return ret;
		}
		virtual bool CheckPlugin(vector<Variable >& params, void*& pc, string& msg)
		{
			msg = "";
			if (params.size() < 1)msg += "参数不足\r\n";
			for (size_t i = 0; i < params.size(); ++i)
			{
				if (!params[i].isNumber())msg += "参数必须是数值\r\n";
			}
			return 0 == msg.size();
		}
		virtual bool ExecFunction(vector<Variable >& params, void* const& pc, Variable& ret, string& msg, void* pe)
		{
			size_t _max = 0;
			for (size_t i = 1; i < params.size(); ++i)
			{
				if (params[i].GetDouble() > params[_max].GetDouble())_max = i;
			}
			ret = params[_max];
			return true;
		}
	};

        这是内置函数max的插件,CheckPlugin检查参数的个数和类型,ExecFunction则把参数转换为double然后把最大的放在ret中返回。

        这个插件没有用到pc和pe这两个参数,目前可以无视,因为这两个参数是客户代码使用的,插件解释器只是传递而并不操作,到需要用的时候你自然知道怎么用。 

二、插件管理器

	//插件表
	class CPluginMap
	{
	public:
		struct HANDLE
		{
			string plugin_name;

			bool isNULL()const { return 0 == plugin_name.size(); }
		};
	private:
		static map<string, CPlugin*>& GetPluginMap();
	public:
		template<typename T>
		static void addplugin(map<string, CPlugin*>& mapPlugins);
		static bool AddPlugin(char const* name, Variable::types type, CPlugin* p);
		static CPlugin* GetPlugin(string const& fun_name);
		static CPlugin* GetPlugin(HANDLE const& h);
		static string& PluginHelp(string& ret);
	};

        插件管理器管理所有的插件,也就是管理所有的自定义函数。

        子类型HANDLE是内部使用的,提供类似指针的快速访问,不过目前并没有性能优势,因为内部藏的还是自定义函数名。

        addPlugin往插件管理器里面添加插件,是静态方法。插件管理器的实现相当简单,也很随意,所以就不展开解释了。

        用户代码只需要在执行脚本前用addPlugin或AddPlugin添加插件即可。


(这里是结束,但是不是整个系列的结束)

  • 11
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值