Lua:面向对象/C之间的交互

前段时间对平台的任务感兴趣,其要求是一周内12篇博文,尝试了之后发现还是太敷衍了,之后还是回归到内容本身上来,尽量保证一篇博文的内容能涵盖足够多的知识点或者足够深的思考成分。

面向对象

面向对象主要有三个方面:封装、继承和多态。Lua若做到了这三点,则认为是也具有面向对象的特征。Lua可以通过表来实现上面三个特征。

类本身的概念就是创建对象的模板。然而Lua本身不存在类的概念,但是可以创建一个原型(prototype)对象,当调用不属于对象的某些操作时,会最先到prototype中查找这些操作。在lua中若想要对象b作为对象a的prototype只需要以下代码:

setmetatable(a, {__index = b}) 

继承

有了index后,在a域找不到的相关属性就会选择在b域查找。如若有更多的对象选择继承b,则覆写以上代码即可,此时的b就被用来当作是prototype了。比如如下实例代码,可选择将Account作为prototype对象:

Account = {}
Account.balance = 0
function Account.new (o)o = o or {} -- create object if user does not provide one setmetatable(o, Account)Account.__index = Accountreturn o
endfunction Account:deposit (v)self.balance = self.balance + v
enda = Account.new{balance = 100}
a:deposit(100)
print(a.balance) --200

lua面向对象有一个有趣的方式就是不需要创建新类去指定新的对象行为,可以直接在对象中实现相关操作即可,比如上面的a对象,可以直接赋予其新的函数:

function a:NewBalance()self.balance = 0
end
print(a.balance) -- 200
a:NewBalance()
print(a.balance) -- 0

也可以用相同方式实现多重继承,即实现index metamethod,使其在多个父类中查找子类不存在的域:

function search (k, plist) for i=1, #plist dolocal v = plist[i][k] -- try 'i'-th superclass if v then return v endend
endfunction createClass(...)local arg = {...}local c = {}setmetatable(c, {__index = function (t, k)return search(k, arg)end})
end

privacy封装

lua本身不具有私有性访问机制。lua设计初衷是小型到中型的程序设计,避免太冗余和太多的人为限制。但是我们依然可以有方案实现私有封装操作:创建一个函数工厂,内设状态和闭包,外部只能获取到return的函数table而没有状态:

function newAccount (initialBalance) local self = {balance = initialBalance} local withdraw = function (v) self.balance = self.balance - v end local deposit = function (v) self.balance = self.balance + v end local getBalance = function () return self.balance endreturn { withdraw = withdraw, deposit = deposit, getBalance = getBalance } 
end

如上所示,除了函数newAccount内部可控制balance变量以外,都无法再对balance进行操作,外部只能调用函数接口。

C API

C API是一个C代码与Lua进行交互的函数集,分为以下几个部分:读写Lua全局变量的函数,调用Lua函数的函数,运行Lua代码片段的函数,注册C函数然后可在Lua中被调用的函数等。

在 C 和 Lua 之间通信关键内容在于一个虚拟的栈。几乎所有的 API 调用都是对栈上的值进行操作,所有 C 与 Lua 之间的数据交换也都通过这个栈来完成。另外,你也可以使用栈来保存临时变量。栈的使用解决了 C 和 Lua 之间两个不协调的问题:第一,Lua 会自动进行垃圾收集,而 C 要求显式的分配存储单元,两者引起的矛盾。第二,Lua 中的动态类型和 C 中的静态类型不一致引起的混乱。

堆栈

Lua和C之间交互主要面临两个问题,一个是内存管理,一个是静态类型和动态类型的不一致性。比如a[k] = v这一段代码,k和v很可能是好几种不同的类型,而且由于metatable的存在,a也可能会有不同的类型。如果不同类型之间的赋值函数组合在一起,很有可能需要几十个不同的函数来完成这一个操作。

当然我们可以在C侧声明union类型解决该问题,倘若我们采用settable函数来实现a[k] = v这段代码:

void lua_settable (lua_Value a, lua_Value k, lua_Value v);

但是这会面临另一个问题:内存管理。如果把lua值保存在C侧,lua引擎便不清楚这个值是否还在使用,很可能会误认为某个值是垃圾从而收集它。并且,lua的这个动态类型是非常复杂的,映射到其他语言会非常困难,lua设计初衷并非只是C/C++,还要和其他主流语言比如Java进行交互,不可能每一个语言都要完成类似这种的转换。

于是Lua API实现了一个抽象的栈在Lua和C之间交换值。无论你何时想要从 Lua 请求一个值(比如一个全局变量的值),调用 Lua,被请求的值将会被压入栈。无论你何时想要传递一个值给 Lua,首先将这个值压入栈,然后调用 Lua(这个值将被弹出)。虽然我们需要函数将C类型压入栈和函数从栈上取值,但是我们至少是避免了组合爆炸。而且这个栈由lua管理,垃圾回收器知道哪个值在被C使用。

Lua侧严格采用先进后出的方式操作栈,它只会改变栈顶部分;C侧却可以查询、删除、插入栈上的任何元素。

压入元素

可以将每种可用C来描述的Lua类型压栈:

void lua_pushnil (lua_State *L); 
void lua_pushboolean (lua_State *L, int bool); 
void lua_pushnumber (lua_State *L, double n); 
void lua_pushlstring (lua_State *L, const char *s, size_t length); 
void lua_pushstring (lua_State *L, const char *s); 

任意的字符串(char*类型,允许包含'\0'字符)用 lua_pushlstring,它要求一个明确的长度作为参数。以'\0'结束的字符串(const char*)用 lua_pushstring。

无论何时压入一个元素到栈上,都需要确保在栈上有空间来做这件事情。可以调用下面函数来检测栈上是否有足够需要的空间。

int lua_checkstack (lua_State *L, int sz);

查询元素

API用索引访问栈内元素。第一个入栈的索引是1,栈顶是-1,-2指的是栈顶下方的那个元素。通过lua_isnumber/lua_isstring等等函数来判断栈内某个元素是否是指定类型:

int lua_is... (lua_State *L, int index);

lua_isnumber 和 lua_isstring 函数不检查这个值是否是指定的类型,而是看它是否能被转换成指定的那种类型。例如,任何数字类型都满足 lua_isstring。

另外,通过lua_tonumber/lua_tostring等函数实现对类型的转换:

int lua_toboolean (lua_State *L, int index); 
double lua_tonumber (lua_State *L, int index); 
const char * lua_tostring (lua_State *L, int index); 
size_t lua_strlen (lua_State *L, int index); 

注意const修饰符,包括上方lua_pushstring函数,都意味着lua不允许将指向字符串的指针保存到访问他们的外部函数中。

其他堆栈操作

通过以下函数完成普通的堆栈操作:

int lua_gettop (lua_State *L); 
void lua_settop (lua_State *L, int index); 
void lua_pushvalue (lua_State *L, int index); 
void lua_remove (lua_State *L, int index); 
void lua_insert (lua_State *L, int index); 
void lua_replace (lua_State *L, int index);

顾名思义,gettop获取堆栈元素个数,settop设置栈大小,pushvalue复制索引index位置上的元素到栈顶,remove移除指定index位置上的元素,insert将栈顶元素插入到index位置上,replace将栈顶元素弹出并设置到指定index位置上。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/685520.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【JavaScript】Cookies

文章目录 1. 什么是Cookies2. Cookies的基本属性3. JavaScript中的Cookies操作设置Cookies读取Cookies获取特定Cookies的值删除Cookies 4. Cookies的应用场景记住用户登录状态存储用户偏好设置跨页面数据传递 5. 安全性注意事项6. 总结 在 Web 开发中, Cookies 是一…

索引失效场景

在数据库系统中,索引用于加速查询处理,但在某些情况下,即使存在索引,数据库查询优化器(Query Optimizer)可能选择不使用它们。这称之为“索引失效”。以下列出了常见的索引失效场景,并进行了解析…

GPU独显下ubuntu屏幕亮度不能调节解决方法

GPU独显下屏幕亮度不能调节(假设你已经安装了合适的nvidia显卡驱动),我试过修改 /etc/default/grub 的 GRUB_CMDLINE_LINUX_DEFAULT"quiet splash acpi_backlightvendor" ,没用。修改和xorg.conf相关的文件,…

不花一分钱,在 Mac 上跑 Windows(M1/M2 版)

这是在 MacOS M1 上体验最新 Windows11 的效果: VMware Fusion,可以运行 Windows、Linux 系统,个人使用 licence 免费 安装流程见 👉 https://zhuanlan.zhihu.com/p/452412091 从申请 Fusion licence 到下载镜像,再到…

MySQL性能调优篇(10)-数据库备份与恢复策略

MySQL数据库备份与恢复策略 数据库备份与恢复是数据库管理中非常重要的一环,对于保障数据的安全性和可靠性起着至关重要的作用。本文将介绍MySQL数据库备份与恢复的策略,包括备份类型、备份方法以及恢复策略。 1. 备份类型 1.1 完整备份 完整备份是备…

安装 Windows Server 2019

1.镜像安装 镜像安装:Windows Server 2019 2.安装过程(直接以图的形式呈现) 先选择""我没有产品密钥"",选择桌面体验 选择自定义 设置密码后继续 安装成功

SpringUtils 工具类,方便在非spring管理环境中获取bean

应用场景: 1 可用在工具类中, 2 spring【Controller,service】环境中, 3 其中的一个方法getAopProxy可获得代理对象,需要将 EnableAspectJAutoProxy(exposeProxy true) 允许获取代理对象 import org.springframework.aop.framew…

07-k8s中secret资源02-玩转secret

一、回顾secret资源的简单实用 第一步:将想要的数据信息【key:value】中的value值,使用base64编码后,写入secret资源清单中; 第二步:创建secret资源; 第三步:pod资源引用secret资源&…

第2讲springsecurity+vue通用权限系统

阿里云 maven阿里云镜像 <?xml version"1.0" encoding"UTF-8"?><!-- Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with this work for addition…

MySQL的配置文件my.cnf正常的配置项目

my.cnf&#xff08;或my.ini&#xff09;是MySQL的配置文件&#xff0c;其中包含了多种设置&#xff0c;用于控制MySQL服务器的运行方式。以下是my.cnf中一些常见的配置项目&#xff1a; 服务器设置 - [mysqld]&#xff1a;服务器的配置部分。 - user&#xff1a;指定M…

C++,stl,常用排序算法,常用拷贝和替换算法

目录 1.常用排序算法 sort random_shuffle merge reverse 2.常用拷贝和替换算法 copy replace replace_if swap 1.常用排序算法 sort 默认从小到大排序 #include<bits/stdc.h> using namespace std;int main() {vector<int> v;v.push_back(1);v.push_ba…

速盾网络:cdn加速服务器代理分销

CDN&#xff08;Content Delivery Network&#xff09;是一种分布式网络架构&#xff0c;旨在提供快速、安全和高效的内容传输和分发服务。CDN加速服务器代理分销是指将CDN网络资源转售给其他企业或个人&#xff0c;以帮助他们实现内容加速和分发的目标。 CDN加速服务器代理分…

基于Web技术的家居室内温湿度监测系统

设计一个基于Web技术的家居室内温湿度监测系统涉及前端和后端开发&#xff0c;以及与硬件传感器的集成。以下是一个简单的设计概述&#xff1a; ### 1. 系统架构 - **前端**: 用户界面&#xff0c;用于显示实时数据和历史记录&#xff0c;可通过Web浏览器访问。 - **后端**: 服…

Leetcode 647. 回文子串

题意理解&#xff1a; 给你一个字符串 s &#xff0c;请你统计并返回这个字符串中 回文子串 的数目。 回文字符串 是正着读和倒过来读一样的字符串。 子字符串 是字符串中的由连续字符组成的一个序列。 具有不同开始位置或结束位置的子串&#xff0c;即使是由相同的字符组成&am…

【友塔笔试面试复盘】八边形取反问题

问题&#xff1a;一个八边形每条边都是0&#xff0c;现在有取反操作&#xff0c;选择一条边取反会同时把当前边和2个邻边取反&#xff08;如果是0变为1&#xff0c;如果是1变为0&#xff09; 现在问你怎么取反能使得八条边都变为1. 当时陷入了暴力递归漩涡&#xff0c;给出一个…

问题:内存时序参数 CASLatency 是() #学习方法#微信#微信

问题&#xff1a;内存时序参数 CASLatency 是&#xff08;&#xff09; A&#xff0e;行地址控制器延迟时间 B&#xff0e;列地址至行地址延迟时间 C&#xff0e;列地址控制器预充电时间 D&#xff0e;列动态时间 参考答案如图所示

[职场] 求职如何设置预期 #笔记#经验分享

求职如何设置预期 在求职的道路上&#xff0c;无论处于哪个年龄阶段&#xff0c;合理的就业期望值才能使我们的愿望与社会的需求相吻合&#xff0c;才能让自己在今后的工作中发挥出最大的实力与能力。 一、结合测评软件&#xff0c;明确求职目标 根据霍兰德职业兴趣测试结果&a…

题目:3.神奇的数组(蓝桥OJ 3000)

问题描述&#xff1a; 解题思路&#xff1a; 官方&#xff1a; 我的总结&#xff1a; 利用双指针遍历每个区间并判断是否符合条件&#xff1a;若一个区间符合条件则该区间在其左端点不变的情况下的每一个子区间都符合条件&#xff0c;相反若一个区间内左端点固定情况下有一个以…

asp.net web api 用户身份验证

前后端分离的开发中&#xff0c;应用服务需要进行用户身份的验证才允许访问数据。实现的方法很简单。创建一个webapi项目。在App_Start目录下找到WebApiConfig.cs&#xff0c; 在里面增加一个实现类。 public static class WebApiConfig{public static void Register(HttpConfi…

javax.servlet 和 jakarta.servlet的关系和使用tomcat部署 jakarta.servlet

1&#xff0c;javax.servlet 和 jakarta.servlet的关系 javax.servlet 和 jakarta.servlet 是 Java Servlet API 的两个版本。 Java Servlet API 是由 Sun Microsystems&#xff08;现在是 Oracle&#xff09;开发和维护的&#xff0c;其包名以 javax.servlet 开头。从 Java …