# C-API 模型推断实现文档

本文档描述Paddle C-API的实现细节。Paddle C-API是多语言API的基础部分。Paddle需要暴露的API很多。先实现模型推断的API，通过模型推断API的实现作为一个样例，来进行讨论。至于为什么需要C-API，请参考[Why Plain C](./00.why_plain_c.md)。

## Table of Contents
   * [C-API 模型推断实现文档](#c-api-模型推断实现文档)
      * [暴露接口原则](#暴露接口原则)
      * [目录结构](#目录结构)
      * [实现方式](#实现方式)
         * [capi.h](#capih)
         * [具体某种类型的头文件](#具体某种类型的头文件)
         * [capi_private.h](#capi_privateh)
         * [具体某种类型的实现文件](#具体某种类型的实现文件)
         * [libpaddle_capi_shared.{so, dylib}](#libpaddle_capi_sharedso-dylib)
         * [libpaddle_capi_whole.a](#libpaddle_capi_wholea)
         * [examples](#examples)
      * [编译选项](#编译选项)


## 暴露接口原则

1. 所有的接口均为C接口。即使用`extern "C"`
2. 除构造某种类型的函数(`paddle_matrix_create`等)，其他函数均返回`paddle_error`。且调用时不能抛出异常或出现运行时错误。
3. 所有类型名为`paddle_类型名`，所有与类型相关的函数，函数名为`paddle_类型名_函数名`
4. 如果某一个Paddle Core概念(GradientMachine/Matrix)需要被暴露到其他语言，那么
	* 为了暴露的接口尽量简单。只暴露概念的接口，而不暴露概念的实现。即暴露`GradientMachine`或者`Matrix`但不暴露`RecurrentGradientMachine`和`CpuSparseMatrix`。
	* 暴露这个概念必要函数。`必要`是指，即完成某一个任务的最少函数。
5. 不在`capi`接口层做过多封装。
	* 如果某一个Paddle概念必须要暴露，但是又过于琐碎。不在`capi`这一层进行封装，而是直接修改Paddle Core。让Paddle核心中，这一概念不再琐碎。


## 目录结构

```text
Paddle
  `-- paddle
        `-- capi
              `-- examples  # The example project for C-API.
              `-- tests  # unittests for C-API
              `-- capi.h  # C-API header file.
              `-- capi_private.h  # The shared header file between implementation sources.
              `-- matrix.{h, cpp}
              `-- gradient_machine.{h, cpp}
              `-- ...
```


Paddle的C-API目录结构如上图表所示。这个目录中除了`capi_private.h`之外的所有头文件，均会被安装到include/paddle路径下。C-API生成的二进制文件会被安装到`lib`目录下。即，安装后的目录结构为

```text
`-- include
      `-- paddle
             `-- capi.h
             `-- matrix.h
             `-- gradient_machine.h
             `-- ...
`-- lib
     `-- libpaddle_capi_shared.{so, dylib}  # In mac, dynamic libary's file name extention is `dylib`
     `-- libpaddle_capi_whole.a  # static library for all symbols of Paddle.
```

## 实现方式

下面分别介绍某一类文件的实现方式。

### capi.h

`capi.h`是用户使用C-API时所唯一需要引入的头文件。在`capi.h`中，引入了类型的头文件，`matrix.h`, `gradient_machine.h`。在引入其他类型的头文件时，使用相对路径的引用方式。即`#include "matrix.h"`

### 具体某种类型的头文件

具体某种类型的头文件，即例如`matrix.h`，`gradient_machine.h`等。在这些头文件中，包含了某种类型的类型定义和暴露的全部函数。

这个头文件不假设其他文件的引用顺序，即使用户直接引用某种类型的头文件，也不应该报错(虽然不鼓励这样)。如果某一个类型需要引用另一个类型，例如`gradient_machine`需要引用`matrix`，则直接引入另一种类型的头文件，即`#include "matrix.h"`。

### capi_private.h

`capi_prviate.h`是各个实现中共享的头文件，他主要包含了实际暴露的类型结构。在用户使用C-API时，Paddle的类型全部退化成`void *`，即`typedef paddle_matrix void*`。但，对于每种C-API暴露的类型，均是在`capi_private.h`中实现的结构体。

```cpp
struct CMatrix {
   int type = MatrixType;
   std::shared_ptr<paddle::Matrix> mat;
};
```

通常，这个结构体包含两个项目。

* `type`是一个类型的标志。对于每种类型，type字段均不尽相同。这样，即使C-API接受的类型全是`void *`，我们也可以确定每一个参数的类型。

  ```cpp
  void some_c_api_function(void* some_instance) {
     int* type = (int *) some_instance;
     switch (*type) {
       case MatrixType:
         CMatrix* mat = (CMatrix *) some_instance;
         ...
       ...
     }
  }
  ```
* 这个结构体中的另一个项目是，Paddle Core中这一类型接口的智能指针(shared_ptr)。
	* 使用智能指针的原因是: 用户可以安全的释放某个C-API的实例，而不必在意Paddle Core是否还在使用这个实例。
	* 例如，用户通过C-API获得了神经网络的参数实例。当用户使用完这个参数后，直接删除这个参数即可。即便Paddle Core中的模型还在使用这个参数，这个参数也不会一并删除。

### 具体某种类型的实现文件

具体某种类型的实现文件，即`matrix.cpp`, `gradient_machine.cpp`等文件。在这些文件中，使用C++ 11实现了C-API的接口，并且使用`extern "C"`导出这些接口。在实现过程中，对输入参数的安全性进行了必要的判断，并将C-API接口的参数转发给`Paddle Core`。

### libpaddle\_capi_shared.{so, dylib}

`libpaddle_capi_shared`是C-API导出的动态库。这个动态库的连接参数与Paddle的其他二进制(例如`paddle_trainer`)类似。用户可以直接使用这个动态库来引入Paddle C-API。具体使用方法为`-lpaddle_capi_shared`。

### libpaddle\_capi_whole.a

`libpaddle_capi_whole`是C-API导出的静态库。这个静态库包含了Paddle的全部符号。他是将`libpaddle_gserver.a`, `libpaddle_math.a`, `libpaddle_capi.a`等全部静态库中的目标文件全部打包后产生的文件。具体使用方法为`--whole-archive -lpaddle_capi_whole --no-whole-archive`。


### examples

在样例中，使用`C99`开发了模型预测的样例代码。具体请参考[example/README.md](../../../paddle/capi/examples/README.md)。

## 编译选项

C-API的编译选项默认关闭，打开这个编译选项，需要在cmake的时候，设置

```bash
cmake ${YOUR_SOURCE_ROOT} -DWITH_C_API=ON -DWITH_PYTHON=OFF -DWITH_SWIG_PY=OFF
```

编译C-API的时候推荐Paddle不嵌入Python解释器，也不生成`SWIG`接口，具体原因参考[Why Plain C](./00.why_plain_c.md)。
