1. pass map to shared library
- Posted by raseu Jul 12, 2012
- 1467 views
Euphoria Interpreter v4.1.0 development
32-bit Windows, Using System Memory
Revision Date: unknown, Id: 0:unknown
is it possible to pass a euphoria map to a shared library .so/.dll?
if so, it would be a great bonus when developing eui shared libraries
i have the following code compiled as a dll
--// maplib.e --// euc -dll maplib.e include std/map.e export function mapfunc(map params) if map(params) then printf(1, "map\n") end if if object(params) then printf(1, "object\n") end if if sequence(params) then printf(1, "sequence\n") end if if integer(params) then printf(1, "integer\n") end if if atom(params) then printf(1, "atom\n") end if ? map:get(params, "integer") ? map:get(params, "atom") puts(1, map:get(params, "sequence") & "\n") map:put(params,"retval", "return value") return params end function
and the following code to load and test the library
--// testmap.e --// eui testmap.e include std/dll.e include std/map.e include lib/maplib.e map testmap = map:new() map:put(testmap, "integer", 1) map:put(testmap, "atom", 2.0) map:put(testmap, "sequence", "string") --// call directly mapfunc(testmap) --// call via dll constant libhandle = open_dll("maplib.dll") constant __mapfunc = define_c_proc(libhandle, "mapfunc", { E_OBJECT }) if (libhandle > 0) then ? { libhandle, __mapfunc } c_proc(__mapfunc, { testmap }) end if
provides the following output
map
object
integer
atom
1
2
string
{50921472,39}
object
integer
atom
the program fails when attempting to read from the map
via the dll function call
any help appreciated
ras
2. Re: pass map to shared library
- Posted by mattlewis (admin) Jul 12, 2012
- 1520 views
Euphoria Interpreter v4.1.0 development
32-bit Windows, Using System Memory
Revision Date: unknown, Id: 0:unknown
is it possible to pass a euphoria map to a shared library .so/.dll?
if so, it would be a great bonus when developing eui shared libraries
The problem is that the map data isn't easily accessible to the shared library. I think the simplest is to use a fairly low level hack, plus some editing of your translated shared library C code. In 4.1, you can write your own debuggers, which requires some low level access to euphoria data. We can use this to communicate between the main program and the library.
The following code will find the location in memory where a particular symbol is stored:
include euphoria/symstruct.e include euphoria/debug/debug.e debug:initialize_debugger( machine_func( M_INIT_DEBUGGER, {} ) ) public function find_sym( sequence name ) atom symtab = debug:get_symbol_table() integer size = peek_pointer( symtab ) for i = 1 to size - 1 do atom sym = symtab + ST_ENTRY_SIZE * i atom name_ptr = peek_pointer( sym + ST_NAME ) if name_ptr then if equal( name, peek_string( name_ptr ) ) then return sym end if end if end for return 0 end function
FYI...I just noticed that euphoria/debug/debug.e gets installed to euphoria/debug.e, which is incorrect. You'll need to either move the file manually or modify the include statement.
The rest of my main program:
include std/console.e include std/dll.e include std/eumem.e include std/map.e constant RAS = open_dll( "./ras.so" ), SET_RAM = define_c_proc( RAS, "set_ram", {E_ATOM} ), DISPLAY_MAP = define_c_proc( RAS, "display_map", {E_ATOM}), MAP_PUT = define_c_proc( RAS, "put", { E_ATOM, E_OBJECT, E_OBJECT } ) map foo = map:new() map:put( foo, 1, "one" ) c_proc( SET_RAM, { find_sym( "ram_space" ) } ) c_proc( DISPLAY_MAP, { foo } ) c_proc( MAP_PUT, { foo, 2, "two" } ) c_proc( DISPLAY_MAP, { foo } ) display( map:pairs( foo ) )
That's all pretty straightforward, even though we haven't looked at the library code:
include std/eumem.e include std/map.e include std/console.e sequence local_ram = {} sequence remote_ram = {} public procedure display_map( map foo ) use_remote() display( map:pairs( foo ) ) use_local() end procedure public procedure put( map the_map, object key, object value ) use_remote() map:put( the_map, key, value ) use_local() end procedure public procedure set_ram( sequence ram ) remote_ram = ram local_ram = eumem:ram_space end procedure without inline procedure use_remote() eumem:ram_space = remote_ram end procedure procedure use_local() eumem:ram_space = local_ram end procedure
If you look at display_map() and put(), you'll notice that we have to swap out eumem:ram_space in order to operate on the map passed from the main program. If you just wanted to read the map, this might work. In order to be able to read and write, however, we need to modify the C code for set_ram(), use_remote() and use_local().
In particular, we have to comment out some reference counting code, and change some of the assignments. Here's what my locally modified version looks like:
void _1set_ram(object _ram_14280) { object _0, _1, _2; /** ras.e:21 remote_ram = ram*/ // RefDS(_ram_14280); // DeRef(_1remote_ram_14261); _1remote_ram_14261 = get_pos_int( "get sym pointer", _ram_14280 ); /** ras.e:22 local_ram = eumem:ram_space*/ // RefDS(_2ram_space_171); // DeRef(_1local_ram_14260); _1local_ram_14260 = _2ram_space_171; /** ras.e:23 end procedure*/ DeRef(_ram_14280); return; ; } void set_ram() __attribute__ ((alias ("_1set_ram"))); void _1use_remote() { object _0, _1, _2; /** ras.e:27 eumem:ram_space = remote_ram*/ // RefDS(_1remote_ram_14261); // DeRef(_2ram_space_171); _2ram_space_171 = *(intptr_t*)_1remote_ram_14261; /** ras.e:28 end procedure*/ return; ; } void _1use_local() { object _0, _1, _2; /** ras.e:31 eumem:ram_space = local_ram*/ // RefDS(_1local_ram_14260); // DeRef(_2ram_space_171); *(intptr_t*)_1remote_ram_14261 = _2ram_space_171; _2ram_space_171 = _1local_ram_14260; /** ras.e:32 end procedure*/ return; ; }
The actual variable names will probably be different for your library, of course.
Matt
3. Re: pass map to shared library
- Posted by raseu Jul 15, 2012
- 1378 views
thanks for the informative reply.
i resolved the issue by converting and returning
the map as a sequence via map:pairs and map:new_from_kvpairs
ras