Modifiying Julia tuple, Dict and object from C

Hello,

I haven’t seen anything in the documentation so I’m asking here; is there a way to manipulate tuple, Dict and object from C ?

I create the Julia type in C using jl_eval function - I haven’t found another way to do it, it seems there is no documented API for this - but then I would like to be able to modify Julia object from C (ideally without using eval) and also having a way to convert them back the objects to a C type.
Is this possible or is it a non-supported feature from the C API and I’m wasting my time :smiley: ?

I don’t think this is possible. First your C code would have to be accessing the Dict structure, which I don’t think is a “public interface” i.e. it could change between Julia revisions. Second adding a value could involve a memory allocation, which needs to be done by Julia for a object held in Julia. Otherwise the Dict ends up referencing memory allocated by the julia code AND by the C code, which is no, just no.

It’s a bit sad if it’s not possible at all.

I don’t see why something like void insert_dict(jl_value_t* dict, jl_value_t* key, jl_value_t* value); would be such an issue for the GC. Document that the Dict API can only deal with Julia owned memory and let it segfault otherwise.

It’s perfectly acceptable to have limitation on an API when embedding a language. On the other hand, it is very limiting to not be able to extract values from a Dict or a Tuple.

Also, jl_arrays_t can either let Julia allocate memory of give a buffer allocated from C (and if you mis-manage your memory it’s gonna segfault as it should).

I think the issue is that arrays directly map to low level C objects, and since the memory layout of an array is basically the same between Julia and C and copying the memory would be an unnecessary expense they just provided library calls to get at the array’s data.

With a Dict object that is an object created with Julia code, there is no C equivalent of a Dict. If they where to provide a C API for the Dict, then why not IOBuffer or RegEx? Where would the “line” be?

IOBuffer is a buffer underneath and could easily be mapped (but C already has buffer). Regex is not a datastructure.

Dict and Tuples are standard, non-trivial data structure. Not being able to use standard data structure is crippling for any who wish to use Julia as a scripting language inside a compiled application.

Here is a code example where I access a Tuple element and modify a Dict from C without using jl_eval_string (except for initialisation because I am lazy).

#include <stdio.h>
#include <julia.h>

JULIA_DEFINE_FAST_TLS() // only define this once, in an executable (not in a shared library) if you want fast code.

int main(int argc, char *argv[])
{
    jl_init();

    // Accessing a `Tuple` element

    jl_value_t **args;
    JL_GC_PUSHARGS(args, 3);
    jl_value_t *ret = jl_eval_string("(1.0, 2.0, 3.0)");
    args[0] = ret;
    jl_function_t *func = jl_get_function(jl_base_module, "getindex");
    jl_value_t *idx = jl_box_int32(3);
    args[1] = idx;
    jl_value_t *ret1 = jl_call2(func, ret, idx);
    args[2] = ret1;
    double ret_unboxed = jl_unbox_float64(ret1);
    JL_GC_POP();

    printf("The third element in the tuple was %f.\n", ret_unboxed);

    // Modifying a `Dict`

    JL_GC_PUSHARGS(args, 5);
    jl_value_t *dict = jl_eval_string("Dict{Int,Float64}()");
    args[0] = dict;
    func = jl_get_function(jl_base_module, "Pair");
    jl_value_t *key = jl_box_int32(1);
    args[1] = key;
    jl_value_t *val = jl_box_float64(3.0);
    args[2] = val;
    jl_value_t *pair = jl_call2(func, key, val);
    args[3] = key;
    func = jl_get_function(jl_base_module, "push!");
    jl_call2(func, dict, pair);
    func = jl_get_function(jl_base_module, "length");
    jl_value_t *len = jl_call1(func, dict);
    args[4] = len;
    int len_unboxed = jl_unbox_int32(len);
    printf("The length of the dict was %d.\n", len_unboxed);
    JL_GC_POP();

    jl_atexit_hook(0);
    return 0;
}

The program returns

embedding-test ❯ ./embedding-test
The third element in the tuple was 3.000000.
The length of the dict was 1.

Is this the direction that you want to achieve?

Disclaimer: This is my first time playing around with embedding Julia so I do not know if this is good style.

Your definition of “data structure” seems to be a bit narrow in my opinion. The user-facing part of a regular expression may be just a string (another data structure :wink:) but once it gets compiled by the engine (PCRE2 in Julia’s case and I am quoting from their documentation) it exists as

[… ] a pointer to a private data structure that contains the compiled pattern, or NULL if an error was detected.

See here, emphasis mine.

2 Likes

This is indeed close to what I’m trying to do. I found getindex for read and setindex / setindex! for modification (push! looks interesting) but it requires knowing the keys.

So I’m playing around using keys = keys(dict) and trying to work with the Tuple of symbol it returns from C.
I think I’m on the right track ! I’ll post a working code when I get there :slight_smile:, thanks.

Fair point. But arguing if something that holds a pointer is a data structure is a bit of a sterile debate :slight_smile: (since almost everything can be expressed as a pointer ; also REGEX is not the topic).

Appreciate the help !

1 Like

(I am arguing that any composite type can be considered a data structure. But as you say it’s OT :smile:)

2 Likes

You could also do collect(keys(dict)) which should give you a Vector{Symbol} which might be easier to access from C.

1 Like

Using collect(keys(a)) and getindex to iterate through it worked :slight_smile: Thanks !