Nim如何與C/C++/Objc互動

header pragma(頭文件指示):html

type
  PFile {.importc: "FILE*", header: "<stdio.h>".} = distinct pointer
  # 導入c語言的 FILE* 指針類型到Nim裏用PFile新類型來代替使用.

Compile pragma(編譯指示):
直接讓nim文件使用c/c++代碼文件, 編譯的時候會先編譯.c文件成.o而後連接讓nim也能使用其內容.node

#test.nim
{.compile: "testc.c".}
proc csum(n: cint): cint {.importc.}
echo csum(100)

//testc.c
int csum(int n) {
    return n + 10;
}

Link pragma(鏈接指示):
直接連接obj文件.c++

{.link: "test.o".}

PassC pragma(編譯參數指示):
相似makefile compile flag: -g -Wallgit

{.passC: "-Wall -Werror".}
#也可以使用外部程序指令
{.passC: gorge("pkg-config --cflags sdl").}

PassL pragma(鏈接參數指示):
相似makefile link flag: -L/xxx/sdl -lsdlapp

{.passL: "-lSDLmain -lSDL".}
#一樣可以使用外部程序指令
{.passL: gorge("pkg-config --libs sdl").}

Emit pragma(發表指示, 這翻起來還真彆扭): 這個指示能夠直接在nim代碼裏運行c/c++/objc等代碼, 強大如斯.
(註釋: '"""'3個引號是nim的語法, 在裏面的全部內容都是字符串.)tcp

{.emit: """
static int cvariable = 420;
""".}

{.push stackTrace:off.}
proc embedsC() =
  var nimVar = 89
  # emit裏用"`"號(就是~號的按鍵不要加shift)之間使用nim代碼的內容.
  {.emit: """fprintf(stdout, "%d\n", cvariable + (int)`nimVar`);""".}
{.pop.}

embedsC()

寫類和結構開頭要寫上/*TYPESECTION*/ 或 /*VARSECTION*/.ide

{.emit: """
/*TYPESECTION*/
struct Vector3 {
public:
  Vector3(): x(5) {}
  Vector3(float x_): x(x_) {}
  float x;
};
""".}

type Vector3 {.importcpp: "Vector3", nodecl} = object
  x: cfloat

proc constructVector3(a: cfloat): Vector3 {.importcpp: "Vector3(@)", nodecl}

ImportCpp pragma(C++導入指示): 可用c2nim工具把C/C++庫或代碼文件轉成nim代碼.
和使用importc方法相似, 這些符號會在後面詳解, 這裏有小教如何在nim裏連上鬼火引擎呢.函數

# Horrible example of how to interface with a C++ engine ... ;-)

{.link: "/usr/lib/libIrrlicht.so".}

#命名空間都打上, 之後能夠不用寫這些空間名.
{.emit: """
using namespace irr;
using namespace core;
using namespace scene;
using namespace video;
using namespace io;
using namespace gui;
""".}

const
  irr = "<irrlicht/irrlicht.h>"

type
  IrrlichtDeviceObj {.final, header: irr,
                      importcpp: "IrrlichtDevice".} = object
  IrrlichtDevice = ptr IrrlichtDeviceObj

proc createDevice(): IrrlichtDevice {.
  header: irr, importcpp: "createDevice(@)".}
proc run(device: IrrlichtDevice): bool {.
  header: irr, importcpp: "#.run(@)".}

固然也能夠不emit命名空間, 能夠寫成這樣.
(嘛~這些都屬於C++的內容了, 想必你們都知道, 但仍是多句嘴吧.)工具

type
  IrrlichtDeviceObj {.final, header: irr,
                      importcpp: "irr::IrrlichtDevice".} = object

Importcpp for procs(c++導入之函數使用):
單獨一個"#"號表示代替第一個或下一個參數.
"#"號接"."表示使用c++點操做或指針"->"操做.
"@"號表示代替剩餘的參數, 使用時用逗號來分隔.post

proc cppMethod(this: CppObj, a, b, c: cint) {.importcpp: "#.CppMethod(@)".}
var x: ptr CppObj
cppMethod(x[], 1, 2, 3)

產生的效果等價於

x->CppMethod(1, 2, 3)

C++的"."或"->"同上面的例子也能夠寫成:

proc cppMethod(this: CppObj, a, b, c: cint) {.importcpp: "CppMethod".}

一樣也能應用於重載函數

//這裏應該是代替c++的operator重載操做符+和[]
proc vectorAddition(a, b: Vec3): Vec3 {.importcpp: "# + #".}
proc dictLookup(a: Dict, k: Key): Value {.importcpp: "#[#]".}

"'"單引號後面接0~9的數字來替換第i個參數, 第0位則做爲返回類型, 能夠用來傳遞c++函數模板. "'"單引號和數字之間加入"*"星號表示得到該類型的基類型(因此去掉星號T*就是T類型?), "**"雙星表示得到該元素類型的元素類型等. (這句不是很懂, 因此放上原文, E文不錯的朋友請告知, 感謝)

An apostrophe ' followed by an integer i in the range 0..9 is replaced by the i'th parameter type. The 0th position is the result type. This can be used to pass types to C++ function templates. Between the ' and the digit an asterisk can be used to get to the base type of the type. (So it "takes away a star" from the type; T* becomes T.) Two stars can be used to get to the element type of the element type etc.

type Input {.importcpp: "System::Input".} = object
proc getSubsystem*[T](): ptr T {.importcpp: "SystemManager::getSubsystem<'*0>()", nodecl.}

let x: ptr Input = getSubsystem[Input]()

產生的效果等價於

x = SystemManager::getSubsystem<System::Input>()

"#@"目前只知道應用於new操做符, 由於new操做符比較特殊吧? 其它功能暫不知. 代替c++的new操做符的示例:

#'*0#@拆開來就是:'*0爲該類型, #@指該操做行爲是new吧.
proc cnew*[T](x: T): ptr T {.importcpp: "(new '*0#@)", nodecl.}

# constructor of 'Foo':
proc constructFoo(a, b: cint): Foo {.importcpp: "Foo(@)".}

let x = cnew constructFoo(3, 4)

等價於

x = new Foo(3, 4)

然而, 依賴new的表示式也能用如下方法代替.

proc newFoo(a, b: cint): ptr Foo {.importcpp: "new Foo(@)".}

let x = newFoo(3, 4)

Wrapping constructors(封裝構造函數): 構造/析構函數是c++基礎內容, 這裏不在闡述, 不懂的話能夠翻閱相關資料瞭解一下.
加入constructor的pragma便可.

# a better constructor of 'Foo':
proc constructFoo(a, b: cint): Foo {.importcpp: "Foo(@)", constructor.}

Wrapping destructors(封裝析構函數):

proc destroyFoo(this: var Foo) {.importcpp: "#.~Foo()".}

Importcpp for objects(c++導入之對象):
這裏射映了一個C++的map模板類型並聲明使用對象

type
  StdMap {.importcpp: "std::map", header: "<map>".} [K, V] = object
proc `[]=`[K, V](this: var StdMap[K, V]; key: K; val: V) {.
  importcpp: "#[#] = #", header: "<map>".}

var x: StdMap[cint, cdouble]
x[6] = 91.4

等價於c++的

std::map<int, double> x;
x[6] = 91.4;

若是須要更詳細的操做, 可使用"'"加數字來射映(嗯, 那啥上面有解釋過).

type
  VectorIterator {.importcpp: "std::vector<'0>::iterator".} [T] = object

var x: VectorIterator[cint]

等價於

std::vector<int>::iterator x;

ImportObjC pragma(objc導入指示):
使用objc須要導入libobjc.a的靜態庫, passL就是這個做用.

# horrible example of how to interface with GNUStep ...

{.passL: "-lobjc".}
{.emit: """
#include <objc/Object.h>
@interface Greeter:Object
{
}

- (void)greet:(long)x y:(long)dummy;
@end

#include <stdio.h>
@implementation Greeter

- (void)greet:(long)x y:(long)dummy
{
  printf("Hello, World!\n");
}
@end

#include <stdlib.h>
""".}

type
  Id {.importc: "id", header: "<objc/Object.h>", final.} = distinct int

proc newGreeter: Id {.importobjc: "Greeter new", nodecl.}
proc greet(self: Id, x, y: int) {.importobjc: "greet", nodecl.}
proc free(self: Id) {.importobjc: "free", nodecl.}

var g = newGreeter()
g.greet(12, 34)
g.free()

這裏教你如何使用c2nim把c頭文件轉成nim:Nim Wrapping C

參考資料:importcpp-pragma

相關文章
相關標籤/搜索