Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

More Symbol Exports and C-API Example #4997

Merged
merged 2 commits into from
Jan 30, 2014
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 48 additions & 0 deletions examples/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
JULIAHOME = $(abspath ..)
include $(JULIAHOME)/Make.inc

override CFLAGS += $(JCFLAGS)
override CXXFLAGS += $(JCXXFLAGS)

FLAGS = -Wall -Wno-strict-aliasing -fno-omit-frame-pointer \
-I$(JULIAHOME)/src -I$(JULIAHOME)/src/support -I$(BUILD)/include $(CFLAGS)

DEBUGFLAGS += $(FLAGS)
SHIPFLAGS += $(FLAGS)
JLDFLAGS += $(LDFLAGS) $(NO_WHOLE_ARCHIVE) $(call exec,$(LLVM_CONFIG) --ldflags) $(OSLIBS) $(RPATH)

ifeq ($(USE_SYSTEM_LIBM),0)
ifneq ($(UNTRUSTED_SYSTEM_LIBM),0)
ifeq ($(OS),WINNT)
JLDFLAGS += $(WHOLE_ARCHIVE) $(BUILD)/lib/libopenlibm.a $(NO_WHOLE_ARCHIVE)
else
JLDFLAGS += $(WHOLE_ARCHIVE) $(BUILD)/$(JL_LIBDIR)/libopenlibm.a $(NO_WHOLE_ARCHIVE)
endif
endif
endif

embedding-release: embedding

release debug:
$(MAKE) embedding-$@

%.o: %.c
@$(call PRINT_CC, $(CC) $(CPPFLAGS) $(CFLAGS) $(SHIPFLAGS) -c $< -o $@)
%.do: %.c
@$(call PRINT_CC, $(CC) $(CPPFLAGS) $(CFLAGS) $(DEBUGFLAGS) -c $< -o $@)

embedding: $(BUILD)/bin/embedding$(EXE)
embedding-debug: $(BUILD)/bin/embedding-debug$(EXE)

$(BUILD)/bin/embedding$(EXE): embedding.o
@$(call PRINT_LINK, $(CXX) $(LINK_FLAGS) $(SHIPFLAGS) $^ -o $@ -L$(BUILD)/$(JL_PRIVATE_LIBDIR) -L$(BUILD)/$(JL_LIBDIR) -ljulia $(JLDFLAGS))
$(BUILD)/bin/embedding-debug$(EXE): embedding.do
@$(call PRINT_LINK, $(CXX) $(LINK_FLAGS) $(DEBUGFLAGS) $^ -o $@ -L$(BUILD)/$(JL_PRIVATE_LIBDIR) -L$(BUILD)/$(JL_LIBDIR) -ljulia-debug $(JLDFLAGS))


clean: | $(CLEAN_TARGETS)
rm -f *.o *.do
rm -f $(BUILD)/bin/embedding-debug $(BUILD)/bin/embedding

.PHONY: clean release debug

100 changes: 100 additions & 0 deletions examples/embedding.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
#include <julia.h>
#include <stdio.h>
#include <math.h>

double my_c_sqrt(double x)
{
return sqrt(x);
}

int main()
{
jl_init(NULL);

{
// Simple running Julia code

jl_eval_string("println(sqrt(2.0))");
}

{
// Accessing the return value

jl_value_t *ret = jl_eval_string("sqrt(2.0)");

if(jl_is_float64(ret))
{
double retDouble = jl_unbox_float64(ret);
printf("sqrt(2.0) in C: %e\n", retDouble);
}
}

{
// Same as above but with function handle (more flexible)

jl_function_t *func = jl_get_function(jl_base_module, "sqrt");
jl_value_t* argument = jl_box_float64(2.0);
jl_value_t* ret = jl_call1(func, argument);

if(jl_is_float64(ret))
{
double retDouble = jl_unbox_float64(ret);
printf("sqrt(2.0) in C: %e\n", retDouble);
}
}

{
// 1D arrays

jl_value_t* array_type = jl_apply_array_type( jl_float64_type, 1 );
jl_array_t* x = jl_alloc_array_1d(array_type , 10);
JL_GC_PUSH1(&x);

double* xData = jl_array_data(x);

for(size_t i=0; i<jl_array_len(x); i++)
xData[i] = i;

jl_function_t *func = jl_get_function(jl_base_module, "reverse!");
jl_call1(func, (jl_value_t*) x);

printf("x = [");
for(size_t i=0; i<jl_array_len(x); i++)
printf("%e ", xData[i]);
printf("]\n");

JL_GC_POP();
}

{
// define julia function and call it

jl_eval_string("my_func(x) = 2*x");

jl_function_t *func = jl_get_function(jl_current_module, "my_func");
jl_value_t* arg = jl_box_float64(5.0);
double ret = jl_unbox_float64(jl_call1(func, arg));

printf("my_func(5.0) = %f\n", ret);
}

{
// call c function

jl_eval_string("println( ccall( :my_c_sqrt, Float64, (Float64,), 2.0 ) )");
}

{
// check for exceptions

jl_eval_string("this_function_does_not_exist()");

if (jl_exception_occurred())
printf("%s \n", jl_get_exception_str( jl_exception_occurred() ) );
}


return 0;
}


47 changes: 47 additions & 0 deletions src/jlapi.c
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,38 @@ DLLEXPORT const char *jl_bytestring_ptr(jl_value_t *s)
return jl_string_data(s);
}

DLLEXPORT jl_value_t *jl_call(jl_function_t *f, jl_value_t **args, int32_t nargs)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These functions are convenient, but there are not many cases where you can use them correctly, since as new arguments to f are evaluated, the previous arguments already have to be rooted, so the extra roots allocated here are usually redundant.

For example a call like jl_call2(f, new_thing1(), new_thing2()) is buggy. I know you didn't introduce these functions, but we need to think about whether it's a good idea to keep them.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@JeffBezanson: Not totally sure about this. In the case jl_call2(f, new_thing1(), new_thing2()) the arguments of the new_thing1/2 functions have to be rooted before. But the returned values not, or? How can they when they are part of the jl_call2 call...

But anyway. I can change this if you want. I just completed the jl_call functions that were already there. In the documentation there is currently no mention that jl_call will root the arguments

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's exactly the problem --- if you pass computed values directly via a C function call, you're probably missing a root. The only safe way to do this call is

jl_value_t **argv;
JL_GC_PUSHARGS(argv, 2);
argv[0] = new_thing1();
argv[1] = new_thing2();
jl_apply(f, argv, 2);
JL_GC_POP();

This code pattern cannot be wrapped in a C function that simply accepts the values of new_thing1() and new_thing2().

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not fully get that. Is the issue that JL_PUSH123 take the address of a jl_value_t*? I don't see a difference of your version to

jl_value_t **argv;
argv[0] = new_thing1();
argv[1] = new_thing2();
jl_call(f, argv, 2);

internally jl_call just copies the argv array into a rooted array.

If the issue is only with JL_PUSH123 we could use JL_PUSH_ARGS in jl_call123

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

argv[0] is not rooted during the execution of new_thing2.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks Keno, sorry for not seeing this. So should I remove the rooting from the jl_call functions or not? Not sure if the double rooting really hurts a lot.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The python -> julia interop uses jl_call.
(https://github.com/jakebolewski/pyjulia/blob/master/julia/core.py#L267)

Does the rooting problem still still exist if the arguments have already been computed ouside the function call?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jakebolewski made a very good point. Unless we expose the rooting mechanism in another form as a macro (so that they can be used in language bindings), it is essential that jl_call does root its arguments

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The crucial point is that if a function takes 2 arguments, the first argument must be rooted while the second argument is computed. Therefore it would only help for jl_call2 to root its arguments if the prior roots were released for some reason. I suppose that could happen, but what I'm really worried about is people writing jl_call2(f, jl_call1(g,a), jl_call1(g,b)). This API makes that call seem possible.

{
jl_value_t *v;
JL_TRY {
jl_value_t **argv;
JL_GC_PUSHARGS(argv, nargs+1);
argv[0] = (jl_value_t*) f;
for(int i=1; i<nargs+1; i++)
argv[i] = args[i-1];
v = jl_apply(f, args, nargs);
JL_GC_POP();
}
JL_CATCH {
v = NULL;
}
return v;
}

DLLEXPORT jl_value_t *jl_call0(jl_function_t *f)
{
jl_value_t *v;
JL_TRY {
JL_GC_PUSH1(&f);
v = jl_apply(f, NULL, 0);
JL_GC_POP();
}
JL_CATCH {
v = NULL;
}
return v;
}

DLLEXPORT jl_value_t *jl_call1(jl_function_t *f, jl_value_t *a)
{
jl_value_t *v;
Expand Down Expand Up @@ -158,6 +190,21 @@ DLLEXPORT jl_value_t *jl_call2(jl_function_t *f, jl_value_t *a, jl_value_t *b)
return v;
}

DLLEXPORT jl_value_t *jl_call3(jl_function_t *f, jl_value_t *a, jl_value_t *b, jl_value_t *c)
{
jl_value_t *v;
JL_TRY {
JL_GC_PUSH4(&f,&a,&b,&c);
jl_value_t *args[3] = {a,b,c};
v = jl_apply(f, args, 3);
JL_GC_POP();
}
JL_CATCH {
v = NULL;
}
return v;
}

JL_CALLABLE(jl_f_get_field);
DLLEXPORT jl_value_t *jl_get_field(jl_value_t *o, char *fld)
{
Expand Down
76 changes: 50 additions & 26 deletions src/julia.h
Original file line number Diff line number Diff line change
Expand Up @@ -366,7 +366,7 @@ extern jl_datatype_t *jl_function_type;
extern jl_datatype_t *jl_abstractarray_type;
extern jl_datatype_t *jl_storedarray_type;
extern jl_datatype_t *jl_densearray_type;
extern jl_datatype_t *jl_array_type;
extern DLLEXPORT jl_datatype_t *jl_array_type;
extern jl_typename_t *jl_array_typename;
extern jl_datatype_t *jl_weakref_type;
extern DLLEXPORT jl_datatype_t *jl_ascii_string_type;
Expand All @@ -390,18 +390,18 @@ extern jl_datatype_t *jl_box_type;
extern jl_value_t *jl_box_any_type;
extern jl_typename_t *jl_box_typename;

extern jl_datatype_t *jl_bool_type;
extern jl_datatype_t *jl_char_type;
extern jl_datatype_t *jl_int8_type;
extern jl_datatype_t *jl_uint8_type;
extern jl_datatype_t *jl_int16_type;
extern jl_datatype_t *jl_uint16_type;
extern jl_datatype_t *jl_int32_type;
extern jl_datatype_t *jl_uint32_type;
extern jl_datatype_t *jl_int64_type;
extern jl_datatype_t *jl_uint64_type;
extern jl_datatype_t *jl_float32_type;
extern jl_datatype_t *jl_float64_type;
extern DLLEXPORT jl_datatype_t *jl_bool_type;
extern DLLEXPORT jl_datatype_t *jl_char_type;
extern DLLEXPORT jl_datatype_t *jl_int8_type;
extern DLLEXPORT jl_datatype_t *jl_uint8_type;
extern DLLEXPORT jl_datatype_t *jl_int16_type;
extern DLLEXPORT jl_datatype_t *jl_uint16_type;
extern DLLEXPORT jl_datatype_t *jl_int32_type;
extern DLLEXPORT jl_datatype_t *jl_uint32_type;
extern DLLEXPORT jl_datatype_t *jl_int64_type;
extern DLLEXPORT jl_datatype_t *jl_uint64_type;
extern DLLEXPORT jl_datatype_t *jl_float32_type;
extern DLLEXPORT jl_datatype_t *jl_float64_type;
extern jl_datatype_t *jl_floatingpoint_type;
extern jl_datatype_t *jl_voidpointer_type;
extern jl_datatype_t *jl_pointer_type;
Expand Down Expand Up @@ -684,7 +684,7 @@ int jl_args_morespecific(jl_value_t *a, jl_value_t *b);
jl_typename_t *jl_new_typename(jl_sym_t *name);
jl_tvar_t *jl_new_typevar(jl_sym_t *name,jl_value_t *lb,jl_value_t *ub);
jl_typector_t *jl_new_type_ctor(jl_tuple_t *params, jl_value_t *body);
jl_value_t *jl_apply_type(jl_value_t *tc, jl_tuple_t *params);
DLLEXPORT jl_value_t *jl_apply_type(jl_value_t *tc, jl_tuple_t *params);
jl_value_t *jl_apply_type_(jl_value_t *tc, jl_value_t **params, size_t n);
jl_value_t *jl_instantiate_type_with(jl_value_t *t, jl_value_t **env, size_t n);
jl_uniontype_t *jl_new_uniontype(jl_tuple_t *types);
Expand All @@ -710,12 +710,12 @@ DLLEXPORT jl_function_t *jl_new_closure(jl_fptr_t proc, jl_value_t *env,
jl_lambda_info_t *li);
DLLEXPORT jl_lambda_info_t *jl_new_lambda_info(jl_value_t *ast, jl_tuple_t *sparams);
DLLEXPORT jl_tuple_t *jl_tuple(size_t n, ...);
jl_tuple_t *jl_tuple1(void *a);
jl_tuple_t *jl_tuple2(void *a, void *b);
DLLEXPORT jl_tuple_t *jl_tuple1(void *a);
DLLEXPORT jl_tuple_t *jl_tuple2(void *a, void *b);
DLLEXPORT jl_tuple_t *jl_alloc_tuple(size_t n);
jl_tuple_t *jl_alloc_tuple_uninit(size_t n);
jl_tuple_t *jl_tuple_append(jl_tuple_t *a, jl_tuple_t *b);
jl_tuple_t *jl_tuple_fill(size_t n, jl_value_t *v);
DLLEXPORT jl_tuple_t *jl_tuple_append(jl_tuple_t *a, jl_tuple_t *b);
DLLEXPORT jl_tuple_t *jl_tuple_fill(size_t n, jl_value_t *v);
DLLEXPORT jl_sym_t *jl_symbol(const char *str);
DLLEXPORT jl_sym_t *jl_symbol_lookup(const char *str);
DLLEXPORT jl_sym_t *jl_symbol_n(const char *str, int32_t len);
Expand Down Expand Up @@ -815,6 +815,10 @@ DLLEXPORT void jl_array_del_beg(jl_array_t *a, size_t dec);
DLLEXPORT void jl_array_sizehint(jl_array_t *a, size_t sz);
DLLEXPORT void *jl_value_ptr(jl_value_t *a);
DLLEXPORT void jl_cell_1d_push(jl_array_t *a, jl_value_t *item);
STATIC_INLINE jl_value_t *jl_apply_array_type(jl_datatype_t *type, size_t dim)
{
return jl_apply_type((jl_value_t*)jl_array_type, jl_tuple2(type, jl_box_long(dim)));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here the result of jl_box_long is not GC rooted while the new tuple is allocated.

}

// eq hash tables
DLLEXPORT jl_array_t *jl_eqtable_put(jl_array_t *h, void *key, void *val);
Expand Down Expand Up @@ -876,17 +880,26 @@ DLLEXPORT struct tm* localtime_r(const time_t *t, struct tm *tm);
// exceptions
DLLEXPORT void NORETURN jl_error(const char *str);
void NORETURN jl_errorf(const char *fmt, ...);
void jl_too_few_args(const char *fname, int min);
void jl_too_many_args(const char *fname, int max);
void jl_type_error(const char *fname, jl_value_t *expected, jl_value_t *got);
DLLEXPORT void jl_too_few_args(const char *fname, int min);
DLLEXPORT void jl_too_many_args(const char *fname, int max);
DLLEXPORT void jl_type_error(const char *fname, jl_value_t *expected, jl_value_t *got);
DLLEXPORT void jl_type_error_rt(const char *fname, const char *context,
jl_value_t *ty, jl_value_t *got);
jl_value_t *jl_no_method_error(jl_function_t *f, jl_value_t **args, size_t na);
void jl_check_type_tuple(jl_tuple_t *t, jl_sym_t *name, const char *ctx);
DLLEXPORT jl_value_t *jl_exception_occurred(void);
DLLEXPORT void jl_exception_clear(void);
STATIC_INLINE char* jl_get_exception_str(jl_value_t* exception)
{
return jl_string_data( jl_fieldref( exception ,0 ) );
}


// initialization functions

DLLEXPORT void julia_init(char *imageFile, int build_mode);
DLLEXPORT int julia_trampoline(int argc, char *argv[], int (*pmain)(int ac,char *av[]), char* build_mode);
DLLEXPORT void jl_init(char *julia_home_dir);
void jl_init_types(void);
void jl_init_box_caches(void);
DLLEXPORT void jl_init_frontend(void);
Expand All @@ -911,6 +924,7 @@ jl_value_t *jl_parse_next(void);
DLLEXPORT jl_value_t *jl_load_file_string(const char *text, char *filename);
DLLEXPORT jl_value_t *jl_expand(jl_value_t *expr);
jl_lambda_info_t *jl_wrap_expr(jl_value_t *expr);
DLLEXPORT void *jl_eval_string(char *str);

// some useful functions
DLLEXPORT void jl_show(jl_value_t *stream, jl_value_t *v);
Expand All @@ -926,7 +940,7 @@ extern DLLEXPORT jl_module_t *jl_main_module;
extern DLLEXPORT jl_module_t *jl_core_module;
extern DLLEXPORT jl_module_t *jl_base_module;
extern DLLEXPORT jl_module_t *jl_current_module;
jl_module_t *jl_new_module(jl_sym_t *name);
DLLEXPORT jl_module_t *jl_new_module(jl_sym_t *name);
// get binding for reading
DLLEXPORT jl_binding_t *jl_get_binding(jl_module_t *m, jl_sym_t *var);
// get binding for assignment
Expand All @@ -941,12 +955,16 @@ DLLEXPORT void jl_set_global(jl_module_t *m, jl_sym_t *var, jl_value_t *val);
DLLEXPORT void jl_set_const(jl_module_t *m, jl_sym_t *var, jl_value_t *val);
DLLEXPORT void jl_checked_assignment(jl_binding_t *b, jl_value_t *rhs);
DLLEXPORT void jl_declare_constant(jl_binding_t *b);
void jl_module_using(jl_module_t *to, jl_module_t *from);
void jl_module_use(jl_module_t *to, jl_module_t *from, jl_sym_t *s);
void jl_module_import(jl_module_t *to, jl_module_t *from, jl_sym_t *s);
void jl_module_importall(jl_module_t *to, jl_module_t *from);
DLLEXPORT void jl_module_using(jl_module_t *to, jl_module_t *from);
DLLEXPORT void jl_module_use(jl_module_t *to, jl_module_t *from, jl_sym_t *s);
DLLEXPORT void jl_module_import(jl_module_t *to, jl_module_t *from, jl_sym_t *s);
DLLEXPORT void jl_module_importall(jl_module_t *to, jl_module_t *from);
DLLEXPORT void jl_module_export(jl_module_t *from, jl_sym_t *s);
void jl_add_standard_imports(jl_module_t *m);
STATIC_INLINE jl_function_t *jl_get_function(jl_module_t *m, const char *name)
{
return (jl_function_t*) jl_get_global(m, jl_symbol(name));
}

// external libraries
enum JL_RTLD_CONSTANT {
Expand Down Expand Up @@ -1045,6 +1063,12 @@ jl_value_t *jl_apply(jl_function_t *f, jl_value_t **args, uint32_t nargs)
return f->fptr((jl_value_t*)f, args, nargs);
}

DLLEXPORT jl_value_t *jl_call(jl_function_t *f, jl_value_t **args, int32_t nargs);
DLLEXPORT jl_value_t *jl_call0(jl_function_t *f);
DLLEXPORT jl_value_t *jl_call1(jl_function_t *f, jl_value_t *a);
DLLEXPORT jl_value_t *jl_call2(jl_function_t *f, jl_value_t *a, jl_value_t *b);
DLLEXPORT jl_value_t *jl_call3(jl_function_t *f, jl_value_t *a, jl_value_t *b, jl_value_t *c);

#define JL_NARGS(fname, min, max) \
if (nargs < min) jl_too_few_args(#fname, min); \
else if (nargs > max) jl_too_many_args(#fname, max);
Expand Down