Open ben-albrecht opened 4 years ago
I don't know of a better way at the moment. This looks fairly close to what I would expect the compiler to do for you when we support exporting records
[Repost from https://github.com/Cray/chapel-private/issues/651]
Some users have expressed the desire to be able to export records and their methods when compiling a library (by way of the export
keyword).
export record foo {
var x: int = 0;
proc init() { x = 42; writeln("Hello!"); }
proc deinit() { writeln("Goodbye!"); }
proc doSomething() { writeln("Something"); }
}
And be able to interact with it from Python as though it were a native Python object:
# This is code generated for Cygwin...
class foo:
def __init__(self):
# Somehow, heap allocate memory for the record instance on the Chapel heap.
self.handle <*foo> = library.chpl_foo_heap_allocate()
# Call the appropriate initializer in user code.
library.chpl_init_foo_0(self.handle)
# Any other setup...
# This is called at some point by the Python GC when this object has a refcount of 0.
def __del__(self):
# Call the appropriate deinitializer in user code.
library.chpl_deinit_foo(self.handle)
# Somehow, deallocate memory on the Chapel heap.
library.chpl_foo_heap_deallocate(self.handle <*void>)
# Any other last minute accounting.
# Still need to work out how getters/setters will work?
def getField(self, field): pass
def setField(self, field, value): pass
def doSomething(self): pass
There are some design questions that need to be ironed out first, though:
1) When you export a record, what methods are exported? All of them? None of them? Initializers/deinitializers?
public
/private
step in to save the day, but unfortunately those keywords are not functional yet for methods.2) How should fields be accessed? Getters/setters? Should they be accessed directly in languages that support it?
If getters/setters should be used, what should the naming convention for getter/setter symbols look like? IE, getField_x()
, or in languages with dynamic typing, getField('x')
?
If getter/setters should be used, how should we deal with symbol collisions?
3) What should our memory strategy look like?
chpl_malloc
/chpl_free
the memory required for the object directly using the compiler calculated size.new
routine for the wrapper.
- What should our memory strategy look like?
Allocate a "list" of objects internally on the Chapel side, and have Python wrappers access their memory via that object index. This is the approach used by @ben-albrecht for HPO.
@dlongnecke-cray - here is an example of using pointers as integers to represent a Chapel object, which can be passed between Chapel and Python to represent the Chapel-side objects:
https://github.com/chapel-lang/chapel/issues/14477#issuecomment-556090454
This may be a better approach than the manual index counting approach used in HPO.
Note that we had a user ask for the ability to export a type today in chat
Feature request: Expose Chapel records as python objects.
Today, my process for exposing Chapel records (and classes) is very tedious. The effort required to wrap and object scales with
O(methods+fields)
. For some insight, my current process is shown below. Suggested improvements are welcome.Chapel side
Start with a record to wrap:
Store a list of objects created on the Chapel side:
Write wrappers for initializer (and deinitializer for classes), which takes an index the object being created/destroyed:
Write wrapper function for every public method, with an extra index field to specify which object:
Python side
Abstract away the index-tracking and any python <-> C type conversions on the python side: