在C中构造和返回Julia类型

时间:2018-09-14 14:18:33

标签: c julia

您应该如何从Julia的Expr调用的C函数中构造并返回Julia :(x + 2),例如ccall,以便使其能够被Julia进行垃圾回收? / p>

编辑:

建议进行编辑,并附上一些其他详细信息。首先,问题不是一口气生成Expr,而是评估者能够逐步构造任意Expr等,请参见下文。

我需要一个Julia的项目解析器。解析器输入的内容部分是代数表达式,最后我想使用等效的Julia Expr。由于似乎没有带有Julia目标的解析器生成器,我在考虑使用C ++生成解析器(这很方便,因为无论如何我都需要在同一项目中为C ++代码使用相同的解析器语法),或者使用flex / bison或ANTLR。因此,问题实际上是如何从C ++解析器在Julia中构建AST。我有一些解决方案:

  1. Julia中有一堆回调,C ++代码可以调用这些回调(例如,对代数表达式进行推式文字/加法运算等),而Julia负责管理AST节点的分配。或者也许只是调用Symbol()Expr()等,再加上针对该问题的特定结构的更专业的回调。即使我对朱莉娅(Julia)非常陌生,我也认为可以实现目标。
  2. 让C ++解析器生成普通结构的AST,Julia代码可以遍历以构造等效的Julia结构。有点混乱的解决方案,但是也许。
  3. 让C ++代码直接分配并返回Julia AST,我认为,如果可能的话,它相当于AST为jl_type_t和朋友。最大的问题是如何构造这样的结构。除了.h文件,我没有找到任何文档,它们的注释不那么好。而且我也怀疑在Julia中是否可以释放C ++代码中分配的内存(收集垃圾),我想知道如何解决这个问题。

1 个答案:

答案 0 :(得分:0)

回答我自己的问题,我认为按照问题中所述的策略1进行解决是最简单,最有效的方法。策略2是可行的,但是涉及在C和Julia中镜像AST,这使解决方案更难维护。策略3,即使有可能,也需要深入了解Julia类型系统在幕后的工作方式,本质上等同于从Julia C源代码复制功能。

我的解决方案唯一(次要)的麻烦是,我无法使用Expr来获取指向vararg @cfunction的指针。因此,在代数表达式的上下文中,例如,您将需要单独的函数指针来构造一元和二进制运算。如果有人在此方面获得更多成功,请告诉我。

代码

expr.c

// struct to gather neccessary callbacks
typedef struct {
  void* (*symbol_callback)(char const *); // Symbol(Cstring)
  void* (*int_callback)(int); // Int(Int) - boxing an int
  void* (*expr4_callback)(void *, void*, void*, void*); // Expr for binary op
} callbacks;

void *expr(callbacks *c) {
  return c->expr4_callback(
    c->symbol_callback("call"),
    c->symbol_callback("+"),
    c->symbol_callback("x"),
    c->int_callback(2)
  );
}

Makefile

CC=gcc

CFLAGS=-c -Wall -fPIC

SOURCES=expr.c
OBJECTS=$(SOURCES:.c=.o)

.c.o:
    $(CC) $(CFLAGS) $< -o $@

lib: $(OBJECTS)
    $(CC) -shared -fPIC -o libexpr.so $(OBJECTS)

clean:
    rm *.o *.so

Test.jl

# define a proxy function for Symbol(::String) that does the conversion from
# a C byte string
symbol_proxy(name::Ptr{UInt8})::Symbol = Symbol(unsafe_string(name))

struct Callbacks
    # pointer to Symbol(::String) proxy
    symbol::Ptr{Nothing}
    # pointer to Int(::Int) function
    int64::Ptr{Nothing}
    # pointer to Expr() function with four arguments (for binary op)
    expr4::Ptr{Nothing}
end

# get callbacks
c = Callbacks(
    @cfunction(symbol_proxy, Ref{Symbol}, (Ptr{UInt8},)),
    @cfunction(Int, Ref{Int64}, (Cint,)),
    @cfunction(Expr, Ref{Expr}, (Any,Any,Any,Any))
)

# call shared library to construct :(x+2)
e = ccall((:expr, "./libexpr"), Ref{Expr}, (Ref{Callbacks},), c)
dump(e)

输出

使用make lib编译共享库并运行Test.jl会产生输出

Expr
  head: Symbol call
  args: Array{Any}((3,))
    1: Symbol +
    2: Symbol x
    3: Int64 2