LuaLaTeX呼叫外部Lua程式與C語言編寫的動態連結庫
在LuaLaTeX編譯方式下,雖然可以直接在tex檔案中編寫Lua程式碼,但會受到LaTeX的影響,編寫中並不是很便利。所以我在實際使用中一般都把Lua程式碼的主體部分放在LaTeX的外部,這樣可以完全按照以往的程式設計習慣去編寫。我以前所在的公司,曾經在機器人運動控制系統中使用過Lua,但因為我沒有負責那一部分,所以只是知道有Lua這麼個指令碼語言,並沒有實際編寫過。這幾天才開始學習編寫Lua程式碼,所以還有很多不足,請多多體諒。下面介紹一下我的用法。
1、開發環境
-
作業系統:Ubuntu 16.04 64位桌面版
-
LaTeX工具:TeXstudio 2.10.8
-
編譯方式:LuaLaTeX
-
Lua編輯工具:Visual Studio Code 1.30.2
-
Lua外掛:vscode-lua 0.12.4
2、LaTeX中使用Lua
我使用的巨集包是luacode。在安裝TeXstudio後,我順手就安裝了texlive-all,這個巨集包包含在裡面。在TeXstudio中,右鍵點選\uspackage{luacode}彈出的選單中,可以開啟luacode的說明文件。
luacode中提供了四種使用Lua程式碼的命令,他們的差別如下表:
\luadirect | \luaexec | \luacode | \luacode* | |
---|---|---|---|---|
巨集 | Yes | Yes | Yes | No |
單反斜槓 | \string\ | \string\ | \string\ | Just \ |
雙反斜槓 | \string\\ | \\ | \\ | \\ |
波浪號 | \string | ~ | ~ | ~ |
# | \string# | \# | #(or \#) | # |
百分號 | No easy way | \% | %(or \%) | % |
TEX註釋 | Yes | Yes | No | No |
Lua的行註釋 | No | NO | Yes | Yes |
從表中可以看到不同命令的差異。我通常使用的是\luaexec命令。呼叫方式就是在\luaexec{}的大括號內編寫Lua程式碼。通過以上命令來編寫Lua程式碼,一方面因為受到LaTeX格式的影響,會有很多不便;另一方面也不符合我們的軟體設計原則。所以需要把Lua的歸給Lua,不要全都放在這裡,這裡僅保留呼叫程式碼。
3、呼叫外部Lua程式碼
呼叫方式很簡單,一般有兩種。一種是用require,另一種是用dofile。但要注意的是package.path在LaTeX中好像並沒有用,我的程式碼在Lua命令列模式下呼叫沒有問題,在LaTeX中就不行,具體原因還不清楚。現在我是在require中直接加上路徑。例如:
% ------ 類圖 ------
\newcommand{\umlClass}[2][]{%
\luaexec{%
require "lua/uml_class"
local umlClass = UmlClass:new(nil, "#1", "#2")
tex.sprint(umlClass:Draw())
}%
}%
其中,uml_class.lua(呼叫時不用加副檔名)檔案放在tex所在資料夾的子目錄lua中。傳入的引數#1與#2一定要放在雙引號裡面。輸出方式就是tex.sprint(…)。
4、呼叫C編寫的動態連結庫
這方面與單純Lua下呼叫差不多,網上有很多例子,我就不多說了,直接貼上程式碼。
**c 原始碼:test.c **
// CSDN 陸巍的部落格 https://blog.csdn.net/weixin_44420912/article/details/86352615
#include <stdio.h>
#include "/usr/local/include/luajit-2.0/lua.h"
#include "/usr/local/include/luajit-2.0/lauxlib.h"
#include "/usr/local/include/luajit-2.0/lualib.h"
// 加上宣告,以免編譯時出現警告
void luaL_setfuncs (lua_State* L, const luaL_Reg* l, int nup);
static int Area(lua_State *L) {
double radius = luaL_checknumber(L, 1);
lua_pushnumber(L, 3.1416 * radius * radius);
return 1;
}
static int Hello(lua_State *L) {
char* str1= {"Hello, it's me."};
lua_pushstring(L, str1);
return 1;
}
static luaL_Reg RegLibs[] = {
{"Area", Area},
{"Hello", Hello},
{NULL, NULL}
};
// 這裡的函式名luaopen_test是有固定格式的,格式為:luaopen_你的庫名
int luaopen_test(lua_State* L) {
lua_newtable(L);
luaL_setfuncs(L, RegLibs, 0);
return 1;
}
我的Lua是使用sudo apt install命令安裝的,版本是5.2.4。在不同版本下,以上程式碼還有些不同,可參考網上文章介紹,比較多,不說了。這裡只是把我實際驗證過的程式碼發上來。
把test.c檔案編譯為動態連結庫
gcc -fPIC -shared -o test.so test.c
我的gcc版本是5.4.0。
LaTeX中的呼叫程式碼
// CSDN 陸巍的部落格 https://blog.csdn.net/weixin_44420912/article/details/86352615
\documentclass{article}%
%
\usepackage{ctex}%
\usepackage{luacode}%
%
\setlength{\parindent}{0pt}%
%
\begin{document}%
%
LuaLaTeX使用Lua程式碼,Lua呼叫C動態連結庫示例:\\\\%
%
\luaexec{%
local test = require("test")
str = "字串示例:" .. test.Hello() .. "\\\\"
tex.sprint(str)
str = "數值計算示例,面積: " .. test.Area(6)
tex.sprint(str)
}%
%
\end{document}%
效果如下:
通過以上方式,我們就把LaTeX、Lua與C聯絡在了一起,這樣一來,對於程式設計師來說可以發揮的空間就很大了。
這段時間我就在用Lua來編寫在LaTeX中繪製UML圖形的程式碼,原來是在XeLaTeX編譯方式下,用的辦法主要是pgf那一套(不過,通用性比Lua的強),但始終不怎麼方便,所以這幾天改用Lua來寫,正在編寫中。下面放一張圖片:
專案已經放在碼雲中,等編得差不多了再發給大家看。Lua的文書處理功能不錯,暫時我在LaTeX的使用中還用不到C語言來編寫程式碼。