Daniel Colascione schrieb am So., 15. Feb. 2015 um 21:21 Uhr: > Here's a broad outline of what I have in mind. > Thanks, that looks really good. Just a few minor issues that I encountered over the last couple of weeks. > Thread-local environments > ------------------------- > > The `get_environment' member lets us do anything else interesting. As > in Java, environments are thread-local. We only support one thread for > the moment, so this constraint is easy to enforce. (Just abort if we > use an emacs_env off the main thread.) > Would you really abort, or rather use the error handling functions? We should be able to make the error values thread-local so that calling a function from the wrong thread would be the equivalent of raising a signal, giving the caller a chance to react. Otherwise the burden of remembering the correct thread would be on the caller's side. > typedef struct emacs_value_tag* emacs_value; > I think it's important that this is a pointer to a struct (for type safety and size correctness) rather than just an arbitrary type. > > typedef emacs_value (*emacs_subr)( > emacs_env* env, > int nargs, > emacs_value args[]); > Could we give this a void* data parameter for storing arbitrary user data? This is common for callbacks in C interfaces and allows C++ users to store C++ objects in the pointer. This can be implemented using another save pointer. > emacs_value (*make_function)( > emacs_env* env, > int min_arity, > int max_arity, > emacs_subr function); > Similary, here void* data should be passed to be retrieved later. > > emacs_value (*funcall)( > emacs_env* env, > emacs_value function, > int nargs, > emacs_value args[]); > Does function have to be a function object, or can it be a symbol? I.e. is the user supposed to call symbol-function first? > int64_t (*fixnum_to_int)( > emacs_env* env, > emacs_value value); > > emacs_value (*make_fixnum)( > emacs_env* env, > int64_t value); > > What's the behavior of these functions if the source would not fit into the result? Undefined behavior, abort(), raising a signal? > Modules can use make_global_reference to allocate a global reference > (i.e., a GC root) for any emacs_value; modules must then free these > references explicitly. > > All routines (except make_global_reference) that return emacs_value > values return local references. It's up to modules to register > long-lived references explicitly. > In which cases would global references be necessary? > Like JNI, we can just declare that it's illegal to call all but a few > specially-marked functions (like global reference deregistration) with > a pending error. > What's the behavior if other functions are called? abort()? > If Lisp signals or throws, `funcall' returns NULL. > Hmm, with the current implementation emacs_value is just the same as Lisp_Object, i.e. not a real pointer, so NULL doesn't have specific semantics. Should it return Qnil instead and force the user to use check_error? > 1) Do we need JNI-style local variable frames and functions that > release local references? > > 2) Maybe we want a separate, non-emacs_value type for global references? > No idea about these. > > 3) How exactly do we represent catch/throw values? > I've thought about this a bit, and I think it would be simplest to add a new function env->set_throw and have get_error and check_error return an enum { normal, signal, throw }. One could come up with something like creating a list (error-kind error-tag error-value), but it looks like the module implementation would create such lists only for the module code to convert them back, so it's simpler to represent the two kinds of non-local exits directly in the interface. > > 4) Do we need to use int64_t for integers? > Maybe just intmax_t and a static check that that is larger than an Elisp integer?