Обычно
static int torch_Tensor_(elementSize)(lua_State *L)
означает, что torch_Tensor_
- это функция, которая принимает один параметр с именем elementSize
, который не имеет типа (?! - синтаксическая ошибка), и возвращает функцию, которая принимает указатель на lua_State
и возвращает int
. Это явно неверно (функции не могут возвращать другие функции).
Но на самом деле здесь происходит то, что torch_Tensor_
определяется как функционально-подобный макрос, поэтому, прежде чем компилятор увидит это объявление, torch_Tensor_(elementSize)
заменяется чем-то другим.
В https://github.com/torch/torch7/blob/master/Tensor.c есть
#include "general.h"
#define torch_Storage_(NAME) TH_CONCAT_4(torch_,Real,Storage_,NAME)
#define torch_Storage TH_CONCAT_STRING_3(torch.,Real,Storage)
#define torch_Tensor_(NAME) TH_CONCAT_4(torch_,Real,Tensor_,NAME)
#define torch_Tensor TH_CONCAT_STRING_3(torch.,Real,Tensor)
#include "generic/Tensor.c"
#include "THGenerateAllTypes.h"
#include "generic/Tensor.c"
#include "THGenerateHalfType.h"
с TH_CONCAT_...
, определенным в lib/TH/THGeneral.h.in
:
#define TH_CONCAT_STRING_3(x,y,z) TH_CONCAT_STRING_3_EXPAND(x,y,z)
#define TH_CONCAT_STRING_3_EXPAND(x,y,z) #x #y #z
#define TH_CONCAT_4_EXPAND(x,y,z,w) x ## y ## z ## w
#define TH_CONCAT_4(x,y,z,w) TH_CONCAT_4_EXPAND(x,y,z,w)
Таким образом, torch_Tensor_
определяется как макрос до включения generic/Tensor.c
.
torch_Tensor_(elementSize)
расширяется до
TH_CONCAT_4(torch_,Real,Tensor_,elementSize)
, который расширяется до
TH_CONCAT_4_EXPAND(torch_,...,Tensor_,elementSize)
...
является заполнителем, а не реальным кодом. Real
определяется как макрос в различных THGenerate*Type.h
файлах, поэтому эта строка на самом деле становится
TH_CONCAT_4_EXPAND(torch_,char,Tensor_,elementSize)
TH_CONCAT_4_EXPAND(torch_,int,Tensor_,elementSize)
TH_CONCAT_4_EXPAND(torch_,float,Tensor_,elementSize)
...
в зависимости от контекста. В любом случае, конечным результатом является один идентификатор вида
torch_charTensor_elementSize
torch_intTensor_elementSize
torch_floatTensor_elementSize
...
(один жетон).
Полученное определение функции выглядит, например, как
static int torch_charTensor_elementSize(lua_State *L)
{
...
}
в зависимости от того, в какой контекст generic/Tensor.c
был включен.
Причина, по которой все делается таким образом, состоит в том, чтобы иметь то, что равняется одному и тому же коду, но для нескольких разных типов. В C ++ вы бы написали шаблон функции:
namespace torch {
template<typename Real>
static int Tensor_elementSize(lua_State *L) { ... }
}
Но в C нет шаблонов (и пространств имен), поэтому единственный способ получить «общий» код, подобный этому, - это сделать это вручную с помощью макросов и трюков предварительной обработки (и вручную «декорировать» имена; например, функция elementSize
для поплавки действительно называются torch_floatTensor_elementSize
).
Все, что мы действительно пытаемся сделать, это абстрагироваться от параметра типа, который здесь называется Real
.