跳转到内容

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 并使用断言来验证您的条件。
华夏公益教科书