Wednesday, August 11, 2010

Interfacing RPython with C

The documentation on how to interface RPython with C is rather limited, the extending doc mentions that `MixedModules` using rffi is the most advanced method available. The rffi document only provides a quick glance at how to use rffi.llexternal. The best place to get started is looking at the source code in rlib/rsdl/ which binds RPython to SDL. What should be clear after reading is that PyPy provides a very direct and easy to understand interface for C; that should also provide the highest possible performance.

The source code in (found in pypy/rpython/tool) is very interesting, it generates dynamic C code that it compiles to get information about the C code we are trying to wrap, for example if you had used the wrong name in a struct it will generate an error. Using rffi_platform different C types can be defined, such as structs, constant integers; these are then put into a special container class called CConfig, which is then parsed by rffi_platform.configure(CConfig) and wrappers returned. After we have the wrappers we must tell any pointers we had previously used in the CConfig that they `become` those objects, MyPointer.TO.become(MyStruct).

RSDL that is included in PyPy is incomplete and lacks wrappers for Joystick, the code below wraps SDL Joystick. Full source code is available here.

eci = get_rsdl_compilation_info()
## wrapper for rffi.llexternal just to shorten the call
def external(name, args, result): return rffi.llexternal(name, args, result, compilation_info=eci)

JoystickPtr = lltype.Ptr(lltype.ForwardReference())
JoyAxisEventPtr = lltype.Ptr(lltype.ForwardReference())
JoyBallEventPtr = lltype.Ptr(lltype.ForwardReference())
JoyButtonEventPtr = lltype.Ptr(lltype.ForwardReference())
JoyHatEventPtr = lltype.Ptr(lltype.ForwardReference())

class CConfig:
_compilation_info_ = eci
Joystick = platform.Struct('SDL_JoyAxisEvent', []) # just and ID, struct contains nothing
# rsdl/ already defines SDL_JOYAXISMOTION, SDL_JOYBALLMOTION, etc..
JoyAxisEvent = platform.Struct('SDL_JoyAxisEvent',
[('type', rffi.INT),
('which', rffi.INT),
('axis', rffi.INT),
('value', rffi.INT)])

for name in CONSTS.split():
name = name.strip()
if name:
ci = platform.ConstantInteger('SDL_%s' %name)
setattr( CConfig, name, ci )


JoystickUpdate = external('SDL_JoystickUpdate', [], lltype.Void)
NumJoysticks = external('SDL_NumJoysticks', [], rffi.INT)
## CCHARP seems to stand for C char pointer ##
JoystickName = external('SDL_JoystickName', [rffi.INT], rffi.CCHARP)
JoystickOpen = external('SDL_JoystickOpen', [rffi.INT], JoystickPtr)
JoystickOpened = external('SDL_JoystickOpened', [rffi.INT], rffi.INT)

JoystickEventState = external('SDL_JoystickEventState', [rffi.INT], rffi.INT)

def handle_event( etype, event ):
p = rffi.cast( JoyAxisEventPtr, event )
axis = rffi.getintfield(p, 'c_axis')
value = rffi.getintfield(p, 'c_value')
print 'axis: %s value: %s' %(axis, value)

def poll(loops=1000):
event = lltype.malloc(RSDL.Event, flavor='raw')
i = 1
while i < ok =" RSDL.PollEvent(event);" ok =" rffi.cast(lltype.Signed,">= 0
if ok > 0: c_type = rffi.getintfield(event, 'c_type'); handle_event( c_type, event )
i += 1
finally:, flavor='raw')

def test():
num = NumJoysticks(); print 'number of joysticks/gamepads: %s' %num
JoystickEventState( RSDL.ENABLE )
if num:
joy = JoystickOpen( 0 )
numaxes = JoystickNumAxes( joy ); print 'number of axes: %s' %numaxes
numbut = JoystickNumButtons( joy ); print 'number of buttons: %s' %numbut

if __name__ == '__main__':
from pypy.translator.interactive import Translation
t = Translation( test )
t.annotate(); t.rtype()
entrypoint = t.compile_c()

No comments:

Post a Comment