# 移植接口开发指导

### 基本架构

一个标准的移植接口模块的目录架构如下：

```
项目根路径/
|--include/
|----模块名/
|------interface/
|------具体实现/
|------另一种具体实现/
|------更多具体实现..../
```

``include``目录的每一个子目录对应一个移植接口模块。每个模块的接口定义位于其下的``interface``目录，而具体实现则位于模块目录下。

特别的：
* ``include/pure``下存放着其本身不需要任何移植就能使用（或者完全依赖于别的移植接口模块）的库，这些库通常提供了一些基本定义（例如``NULL``的宏定义）。
* 项目主体通过调用移植接口来实现功能，但移植接口不应该调用项目主体。即项目主体只能单向调用移植接口。

本项目的接口定义参考了``C#``的接口类设计，因此文件名和类名以字母``I``开头，这是``Interface``的缩写。

Stamon的移植接口类设计采用一种自创的方法——**CITP**（**Curiously Iterating Template Pattern，奇异递推模板模式**）。其详细原理参见``CITP设计文档.md``。利用CITP强大的接口约束表达力，开发者在按要求实现对应接口后，可以非常轻松的将自己的实现对接到项目中。

以经典的``ArrayList``模块为例。其代码位于``include/ArrayList``目录，接口定义在``include/ArrayList/interface``目录（即``include/ArrayList/interface/IArrayList.hpp``），而诸如``include/ArrayList/stdc``、``include/ArrayList/stdcpp``则分别是基于标准C语言、标准C++的接口实现。

### 在代码中导入移植接口模块

根据模块的文档等相关引导，直接使用C++的``#include``语法即可。

例如，导入模块``ArrayList``只需要以下代码：

```C++
#include "ArrayList.hpp"
```

其中，``ArrayList.hpp``由模块开发者提供，作为该模块的入口文件

一个模块可能会有若干个入口文件，具体情况因不同模块而异。

值得注意的是：
* 通常情况下，被导入的文件不应该是接口约束文件。例如应导入``ArrayList.hpp``而非``IArrayList.hpp``
* 通常情况下，请导入入口文件的文件名，而不是具体的绝对路径或相对路径，文件名所在路径应该在编译时指定。例如应导入``ArrayList.hpp``而非``D:/stamon2/include/ArrayList/stdc/ArrayList.hpp``

### 在构建系统中导入移植接口模块

Stamon采用**一体化编译**的方式来编译项目。因此可直接添加目标模块的接口约束文件所在目录和对应实现文件所在目录作为编译器的头文件搜索路径。

例如，在``G++``编译器中，使用``-I <dir>``即可将``<dir>``添加至头文件搜索路径中。若要导入``ArrayList``的``stdc``实现，则可添加以下参数：

```Makefile
-I include/ArrayList/interface  -I include/ArrayList/stdc
```

此时的工作目录应该是项目根目录。

### 开发一个移植接口模块

开发者在移植过程中，可以挑选并接入最佳的移植实现，也可以自己动手实现。

我们欢迎更多开发者为不同平台贡献移植实现。

接下来，笔者将带领大家从头开发一个``Example``模块，并把它接入到项目中。

笔者在``Windows 10``的系统环境下进行开发，编译器版本为``gcc version 15.1.0 (x86_64-posix-seh-rev0, Built by MinGW-Builds project)``。

##### 创建对应目录和文件

在Stamon源码的基础上创建如下的目录和文件结构：

```
项目根路径/
|--include/
|----Example/
|------interface/
|--------IExample.hpp
|------stdc/
|--------Example.hpp
|--src/
|----ExampleMain.cpp
```

##### 编写接口约束

``IExample.hpp``的代码如下所示：

```C++
#pragma once

#include "StamonLib.hpp"
#include "stdio.h"

namespace stamon::interface {

template<class Impl> class IExample : public Impl {
	// 示例类的接口约束
public:
	IExample(int val)
		: Impl(val) {
	}

	int getVal() {
		return Impl::getVal();
	}

	void example() {
		Impl::example();
	}
};

} // namespace stamon::interface
```

##### 编写对应实现

``Example.hpp``的代码如下所示：

```C++
#pragma once

#include "IExample.hpp"
#include "stdio.h"

namespace stamon::stdc {

class Example {
	// 示例类的标准C实现
public:
	int value;

	Example(int val)
		: value(val) {
	}

	int getVal() {
		return value;
	}

	void example() {
		printf("Hello, This is an example!\n");
		printf("value=%d\n", getVal());
	}
};

} // namespace stamon::stdc

namespace stamon {
using Example = interface::IExample<stdc::Example>;
}
```

##### 编写示例项目的入口代码

``ExampleMain.cpp``的代码如下所示：

```C++
#include "Example.hpp"

int main()
{
    stamon::Example e(114);
    e.example();
    return 0;
}
```

##### 构建示例项目

使用如下指令编译示例项目：

```
g++ src/ExampleMain.cpp -o bin/example.exe -O2 -std=c++17 -static -I include/pure -I include/Example/interface -I include/Example/stdc
```

生成的可执行文件路径为``bin/example.exe``

``-O2 -std=c++17 -static``用于优化项目、指定代码标准、生成静态的可执行文件。

值得关注的是``-I include/pure -I include/Example/interface -I include/Example/stdc``。通常情况下，任何项目都应该包含``include/pure``，``pure``目录中存放着整个项目所依赖的，平台无关的基础代码。除此之外开发者还要记得把自己开发的模块的接口约束目录和对应实现目录都添加进来。

##### 运行示例项目

运行``bin/example``即可得到输出。至此，我们成功开发了一个简单的模块。

```
>bin/example
Hello, This is an example!
value=114
>
```

##### 注意事项

* 接口约束代码只应导入必要的模块，并且除调用其他模块以外，本身不应该出现其他平台相关代码，否则会降低可移植性
* 在使用``CITP``编写接口约束代码时，应注意``CITP``的局限性。详情已在``CITP设计文档.md``中指出