跳轉到內容

Io 程式設計/編寫外掛

來自華夏公益教科書,自由的教科書,為自由的世界

為 Io 編寫 C 外掛

[編輯 | 編輯原始碼]

此示例將向您展示如何建立一個用 C 編寫的新的外掛,該外掛可以包含在 Io 原始碼包中並與之一起構建。這裡展示的示例外掛是一個非常小的物件,它只有一個返回自身的方法。我們將它稱為TrivialObject

有關建立 C++ 繫結的介紹,請參閱 將 Io 繫結到 C++

此框架基於 Steve Dekorte 的 Zlib 外掛原始碼。

外掛結構

[編輯 | 編輯原始碼]

Io 外掛由幾個部分組成

  • build.io,透過克隆 AddonBuilder 並指定系統依賴項或執行其他預構建操作來定義外掛。
  • depends,一個文字檔案,包含一個單行列表,列出了此外掛所依賴的其他 Io 外掛(如果有)。
  • "protos",一個文字檔案,包含一個單行列表,列出了此外掛提供的原型名稱。
  • io/,一個目錄,包含您要與外掛一起包含的 Io 指令碼(名為YourAddon.io)。如果您的所有程式碼都在 C 中,並且您沒有其他 Io 方法要定義(如本例所示),您可能不需要在這裡放任何東西。
  • source/,一個目錄,包含要構建到外掛庫中的 C 和/或 C++ 原始碼和標頭檔案。如上所述,如果您的所有程式碼都是用 Io 編寫的,您可能不需要在這裡放任何東西。
  • tests/,一個目錄,包含一組要執行的測試,用於驗證此外掛。

建立外掛

[編輯 | 編輯原始碼]

外掛結構

[編輯 | 編輯原始碼]

在 Io 原始碼樹下使用以下結構建立這些檔案和資料夾

  • io/
    • addons/
      • TrivialObject/
        • build.io
        • depends
        • io/
        • source/
          • IoTrivialObject.h
          • IoTrivialObject.c
        • tests/

現在編寫檔案內容

AddonBuilder clone do(
)

對於如此簡單的物件,我們不需要任何依賴項,但是可以使用以下方法指定它們

  • dependsOnBinding(name)
  • dependsOnHeader(name)
  • dependsOnLib(name)
  • dependsOnFramework(name)
  • dependsOnInclude(name)
  • dependsOnLinkOption(name)
  • dependsOnSysLib(name)
  • dependsOnFrameworkOrLib(frameworkName, libName)

IoTrivialObject.h

[編輯 | 編輯原始碼]
//metadoc copyright Your Name Here 2008

// don't forget the macro guard
#ifndef IOTrivialObject_DEFINED
#define IOTrivialObject_DEFINED 1

#include "IoObject.h"
#include "IoSeq.h"

// define a macro that can check whether an IoObject is of our type by checking whether it holds a pointer to our clone function
#define ISTrivialObject(self) IoObject_hasCloneFunc_(self, (IoTagCloneFunc *)IoTrivialObject_rawClone)

// declare a C type for ourselves
typedef IoObject IoTrivialObject;

// define the requisite functions
IoTag *IoTrivialObject_newTag(void *state);
IoObject *IoTrivialObject_proto(void *state);
IoObject *IoTrivialObject_rawClone(IoTrivialObject *self);
IoObject *IoTrivialObject_mark(IoTrivialObject *self);
void IoTrivialObject_free(IoTrivialObject *self);

// define our custom C functions
IoObject *IoTrivialObject_returnSelf(IoTrivialObject *self, IoObject *locals, IoMessage *m);

#endif

IoTrivialObject.c

[編輯 | 編輯原始碼]
//metadoc TrivalObject copyright Your Name Here
//metadoc TrivalObject license BSD revised
//metadoc TrivalObject category Example
/*metadoc TrivalObject description
Describe your addon here.
*/

#include "IoState.h"
#include "IoTrivialObject.h"

// _tag makes an IoTag for the bookkeeping of names and methods for this proto
IoTag *IoTrivialObject_newTag(void *state)
{
	// first allocate a new IoTag
	IoTag *tag = IoTag_newWithName_("TrivialObject");

	// record this tag as belonging to this VM
	IoTag_state_(tag, state);

	// give the tag pointers to the _free, _mark and _rawClone functions we'll need to use
	IoTag_freeFunc_(tag, (IoTagFreeFunc *)IoTrivialObject_free);
	IoTag_markFunc_(tag, (IoTagMarkFunc *)IoTrivialObject_mark);
	IoTag_cloneFunc_(tag, (IoTagCloneFunc *)IoTrivialObject_rawClone);
	return tag;
}

// _proto creates the first-ever instance of the prototype 
IoObject *IoTrivialObject_proto(void *state)
{
	// First we allocate a new IoObject
	IoTrivialObject *self = IoObject_new(state);

	// Then tag it
	IoObject_tag_(self, IoTrivialObject_newTag(state));

	// then register this proto generator
	IoState_registerProtoWithFunc_(state, self, IoTrivialObject_proto);

	// and finally, define the table of methods this proto supports
	// we just have one method here, returnSelf, then terminate the array
	// with NULLs
	{
		IoMethodTable methodTable[] = {
		{"returnSelf", IoTrivialObject_returnSelf},
		{NULL, NULL},
		};
		IoObject_addMethodTable_(self, methodTable);
	}

	return self;
}

// _rawClone clones the existing proto passed as the only argument
IoObject *IoTrivialObject_rawClone(IoTrivialObject *proto)
{
	IoObject *self = IoObject_rawClonePrimitive(proto);
	// This is where any object-specific data would be copied
	return self;
}

// _new creates a new object from this prototype
IoObject *IoTrivialObject_new(void *state)
{
	IoObject *proto = IoState_protoWithInitFunction_(state, IoTrivialObject_proto);
	return IOCLONE(proto);
}

// _mark is called when this proto is marked for garbage collection
// If this proto kept references to any other IoObjects, it should call their mark() methods as well.
IoObject *IoTrivialObject_mark(IoTrivialObject* self)
{
	return self;
}

// _free defines any cleanup or deallocation code to run when the object gets garbage collected
void IoTrivialObject_free(IoTrivialObject *self)
{
	// free dynamically allocated data and do any cleanup
}

// This is the one method we define, which does nothing but return self.
IoObject *IoTrivialObject_returnSelf(IoTrivialObject *self, IoObject *locals, IoMessage *m)
{
	// A method should always return an IoObject*
	// Per Io style guidelines, it's preferred to return self when possible.
	return self;
}

當執行make addon TrivialObject時,首先載入並執行build.io。在本例中它什麼也不做,但它可以指定依賴項,執行系統命令,甚至觸發外部 make 操作,如 SGML 的情況所示。

接下來,配置原始碼樹,AddonBuilder 在 source/ 目錄中建立一個名為 IoTrivialObjectInit.c 的新檔案,作為外掛庫的入口點。此檔案的具體內容取決於source/中找到的檔案數量和型別,如下所述。

IoTrivialObjectInit.c

#include "IoState.h"
#include "IoObject.h"

IoObject *IoTrivialObject_proto(void *state);

void IoTrivialObjectInit(IoObject *context)
{
	IoState *self = IoObject_state((IoObject *)context);

	IoObject_setSlot_to_(context, SIOSYMBOL("TrivialObject"), IoTrivialObject_proto(self));

}

如您所見,它定義了IoTrivialObject_proto(void* state),該函式透過向 VM 上下文新增一個名為外掛的槽位來初始化外掛,並將該槽位設定為由 IoTrivialobject_proto 建立的全新原型。

AddonBuilder 將外掛構建到外掛的 _build 目錄中。在這個目錄中,您可能會發現

  • _build/
    • dll/ — 動態庫在此處構建。
    • headers/ — source/ 目錄中的所有 *.h 檔案都會複製到這裡。
    • lib/ — 靜態庫在此處構建
    • objs/ — 構建生成的 .o/.obj 檔案放置在此處

構建完成後,Io 安裝過程會將整個 addons/ 目錄複製到其最終目的地(例如 /usr/local/lib/io/addons)。

使用附加源

[編輯 | 編輯原始碼]

在一個更復雜的外掛中,source/中可能存在多個原始檔,用於不同的目的。AddonBuilder 會根據這些檔案的檔名格式對它們進行不同的處理。

  • source 目錄中任何副檔名為 .h 的檔案都將複製到 _build/headers。
  • 任何以 Io*.c 或 Io*.cpp 為模式命名的原始檔,並且不包含下劃線,將被視為 Io 原型的原始碼。
    • 這些檔案可能包含一行包含docDependsOn(Name),以指定原始檔依賴於編譯名為IoName.c的檔案生成的 obj 檔案。這些原始檔將按照這些依賴關係指定的順序進行構建。
    • 每個原型原始檔將在外掛的 Init 檔案中生成一個相應的條目:IoObject *IoFileName_proto(void *state)
  • 以 Io*.c 或 Io*.cpp 為模式命名,並且包含下劃線的檔案,例如 IoExtra_source,被視為“額外”原始碼,並且將在外掛的 Init 檔案中生成一個相應的條目,以執行它們的初始化:void IoExtra_sourceInit(void *context)
  • 任何名稱中包含 "Init" 的原始檔都將被忽略,但此外掛的自動生成的 Init 檔案除外。
  • 目錄中的所有其他檔案都被 AddonBuilder 忽略。

可以使用make testaddons編寫並執行外掛的驗證測試。

要編寫一個測試

  • 在外掛樹的tests/correctness/目錄中建立一個名為run.io的檔案,以查詢並運行同一目錄中的任何單元測試。
    TestSuite clone setPath(System launchPath) run
  • tests/correctness下建立測試指令碼,這些指令碼克隆 UnitTest 並使用斷言來驗證您的條件。
華夏公益教科書