Introduction to

CLIF

左 遥(laike9m@)

What is CLIF?

C++ Language Interface Foundation

CLIF provides a common foundation for creating C++ wrapper generators for various languages.

Other options

Lib When to use
ctypes call functions in shared libraries (DLL, .so)
C-Extension low level, replace the part you need
Cython A new language
SWIG wrap existing C/C++ libraries
CLIF wrap existing C++ libraries

cpp_lib.h

void hello() {
  std::cout << "hello world!";
}

cpp_lib.clif

from "experimental/users/yaoz/CLIF_test/cpp_lib.h":
  def hello()

test.py

from google3.experimental.users.yaoz.CLIF_test.python import cpp_lib

cpp_lib.hello()  # hello world!

cpp_lib.h

std::string return_hello() {
  return "hello world!";
}

cpp_lib.clif

from "experimental/users/yaoz/CLIF_test/cpp_lib.h":
  def return_hello()

test.py

from google3.experimental.users.yaoz.CLIF_test.python import cpp_lib

print(cpp_lib.return_hello())  # None

cpp_lib.h

std::string return_hello() {
  return "hello world!";
}

cpp_lib.clif

from "experimental/users/yaoz/CLIF_test/cpp_lib.h":
  def return_hello() -> str

test.py

from google3.experimental.users.yaoz.CLIF_test.python import cpp_lib

print(cpp_lib.return_hello())  # hello world!

return value

cpp_lib.h

int add(int x, int y) {
  return x + y;
}

cpp_lib.clif

from "experimental/users/yaoz/CLIF_test/cpp_lib.h":
    def add(x: int, y: int) -> int

test.py

from google3.experimental.users.yaoz.CLIF_test.python import cpp_lib

print(cpp_lib.add(1, 2))  # 3

parameters

default C++ type CLIF type
int int
string bytes or str
bool bool
double float
vector<> list<>
pair<> tuple<>
unordered_set<> set<>
unordered_map<> dict<>
PyObject* object

Pointer ?

int F(string input, string* output) {
  *output = input;
  return 0;
}

Parameter order

int F(input_param1, input_param2, ..., output_param1, output_param2) {
  ...
}

all input parameters before any output parameter(s)

- Google C++ convention

cpp_lib.h

int F(string input, string* output) {
  *output = input;
  return 0;
}

cpp_lib.clif

from "experimental/users/yaoz/CLIF_test/cpp_lib.h":
  def F(input: str) -> (code: int, output: str)

test.py

from google3.experimental.users.yaoz.CLIF_test.python import cpp_lib

print(cpp_lib.F('aaa'))  # (0, 'aaa')

Output param —> return value

cpp_lib.h

string* F() {
  static string s = "AAA";
  std::cout << "s is: " << s;
  return &s;
}

cpp_lib.clif

from "experimental/users/yaoz/CLIF_test/cpp_lib.h":
  def F() -> str

test.py

from google3.experimental.users.yaoz.CLIF_test.python import cpp_lib

print(cpp_lib.F())  # 'AAA'

But it's not perfect

cpp_lib.h

string& F() {
  static string s = "AAA";
  std::cout << "s is: " << s;
  return s;
}

cpp_lib.clif

from "experimental/users/yaoz/CLIF_test/cpp_lib.h":
  def F() -> str

test.py

from google3.experimental.users.yaoz.CLIF_test.python import cpp_lib

s = cpp_lib.F()  # s is: AAA
s = "BBB"
cpp_lib.F()      # s is: AAA

Same with reference

cpp_lib.h

namespace foo {

std::string return_hello() {
  return "hello world!";
}

}

cpp_lib.clif

from "experimental/users/yaoz/CLIF_test/cpp_lib.h":
  namespace `foo`:
    def return_hello() -> str

test.py

from google3.experimental.users.yaoz.CLIF_test.python import cpp_lib

print(cpp_lib.return_hello())  # hello world!

namespace

Class

cpp_lib.clif

class MyClass:
  def __init__(self, param: int, another_param: float)

  def Method(self) -> dict<int, int>

  @classmethod
  def StaticMethod(cls, param: int)

# or better written as
staticmethods from `MyClass`:
  def StaticMethod(param: int)

Multiple constructors?

cpp_lib.h

class Foo {
 public:
  Foo(){};
  Foo(int special) : var(special) {}
  int var;
};

cpp_lib.clif

class Foo:
  def __init__(self, special: int)
  @add__init__
  def Factory(self)

test.py

inst_a = cpp_lib.Foo(1)
inst_b = cpp_lib.Foo.Factory()

Function renaming

cpp_lib.h

class Vector {
 public:
  int Size();
};

cpp_lib.clif

class Vector:

  def `Size` as __len__() -> int

Attribute access

cpp_lib.h

class A:
  public:
    v: list<int>
    File get_file();
    void set_file(File);
  private:
    File f;

cpp_lib.clif

class A:
  v: list<int>
  file: File = property(`get_file`, `set_file`)

Inheritance

cpp_lib.h

class Parent {
 public:
  void SomethingInteresting();
};

class Child : public Parent {
 public:
  void Useful();
};

cpp_lib.clif

class Child:
  def SomethingInteresting(self)
  def Useful(self)

Inheritance

cpp_lib.h

class Parent {
...
};

class Child : public Parent {
 public:
  void Useful();
};

cpp_lib.clif

from full.path.to.another.python.wrapper import Parent

from "c/include/path/to/child.h":
  class Parent:
    # wraps Parent class

  class Child(Parent):

GIL

CLIF will release the GIL on every function call,

except for:

  • Default constructors
  • Properties and variable access
  • Functions marked with @do_not_release_gil
  • Functions that take or return the Python object type.

总结

  • CLIF 包装已有的 C++ lib 给 Python 提供 API
  • .clif 文件描述接口
  • output parameter -> return value
  • 指针、引用无法完全兼容
  • 函数重命名
  • @add__init__ 提供 Factory 支持多个构造函数

 

https://github.com/google/clif