Отвечая на мой собственный вопрос, я думаю, что использование стратегии 1, как указано в вопросе, является самым простым и надежным способом решения этой проблемы. Стратегия 2 возможна, но она включает в себя зеркальное отображение AST в C и Julia, что усложняет решение. Стратегия 3, если это возможно, требует глубоких знаний о том, как работает система типов Julia, и, по сути, сводится к дублированию функций из исходного кода Julia C.
Единственное (незначительное) неудобство с моим решением заключается в том, что я не смог получить указатель на vararg Expr
, используя @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