1. 程式人生 > >LuaLaTeX呼叫外部Lua程式與C語言編寫的動態連結庫

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}%

效果如下:
lua呼叫c動態連結庫示例

  通過以上方式,我們就把LaTeX、Lua與C聯絡在了一起,這樣一來,對於程式設計師來說可以發揮的空間就很大了。

  這段時間我就在用Lua來編寫在LaTeX中繪製UML圖形的程式碼,原來是在XeLaTeX編譯方式下,用的辦法主要是pgf那一套(不過,通用性比Lua的強),但始終不怎麼方便,所以這幾天改用Lua來寫,正在編寫中。下面放一張圖片:
uml繪製示例
  專案已經放在碼雲中,等編得差不多了再發給大家看。Lua的文書處理功能不錯,暫時我在LaTeX的使用中還用不到C語言來編寫程式碼。