perl swig

Mapa de STL en Perl usando SWIG



(2)

Este es un duplicado de mi pregunta en la lista de correo de SWIG .

Estoy tratando de usar contenedores stl en mis enlaces SWIG. Todo funciona a la perfección, excepto para el manejo de mapas stl en Perl. En el lado de C ++, tengo

std::map<std::string, std::string> TryMap(const std::map<std::string, std::string> &map) { std::map<std::string, std::string> modified(map); modified["7"] = "!"; return modified; }

SWIG config se ve así

%module stl %include "std_string.i" %include "std_map.i" %template(StringStringMap) std::map<std::string, std::string>; %{ #include "stl.h" %} %include "stl.h"

En mi script de Python puedo llamar a TryMap de esta manera

print dict(stl.TryMap({''a'': ''4''}))

y obtener una salida hermosa

{''a'': ''4'', ''7'': ''!''}

pero en Perl llamo

print Dumper stl::TryMap({''a'' => ''4''});

y recibe un error

TypeError in method ''TryMap'', argument 1 of type ''std::map< std::string,std::string > const &'' at perl.pl line 7.

De hecho, puedo hacer algo como

my $map = stl::TryMap(stl::StringStringMap->new()); print $map->get(''7'');

y obtener ''!'', pero esta no es una opción porque hay un montón de código heredado que usa "TryMap" que espera que el resultado sea el hash Perl normal.

Creo que hay una forma de resolver esto porque SWIG resuelve este problema en particular en Python e incluso en Perl si uso stl vectores y cadenas pero no mapas.

¿Hay alguna forma de manejar el mapa stl con Perl en SWIG? Estoy usando el último SWIG 2.0.7

ACTUALIZAR Quizás haya algo mal con perl5/std_map.i . Es muy corto =)

$ wc -l perl5/std_map.i python/std_map.i 74 perl5/std_map.i 305 python/std_map.i


Has probado:

print Dumper stl::TryMap((''a'' => ''4''));


Puse su función C ++ en el archivo de encabezado como una función en línea para la prueba.

Luego pude construir una interfaz SWIG que hace lo que estás buscando. Tiene dos partes clave. En primer lugar, escribí un mapa de tipos que permitirá que se proporcione un std::map o un hash de Perl como entrada a las funciones de C ++ que esperan un std::map . En el caso de este último, construye un mapa temporal del hash de Perl para usar como argumento. (Que es conveniente pero potencialmente lento). El mapa de tipos elige el comportamiento correcto comprobando en qué se pasó realmente.

La segunda parte de la solución es asignar algunas de las funciones de miembros del mapa de C ++ a las funciones especiales que perl usa para sobrecargar operaciones en hashes. La mayoría de estos se implementan simplemente con %rename donde la función C ++ y las funciones perl son compatibles, sin embargo, FIRSTKEY y NEXTKEY no se mapean bien en los iteradores de C ++, por lo que se implementaron usando %extend y (internamente) otro std::map para almacenar el estado de iteración de los mapas que estamos envolviendo.

No hay mapas de tipos especiales implementados aquí para devolver los mapas; sin embargo, existe un comportamiento adicional a través de las operaciones especiales que ahora se implementan.

La interfaz SWIG se ve así:

%module stl %include <std_string.i> %include <exception.i> %rename(FETCH) std::map<std::string, std::string>::get; %rename(STORE) std::map<std::string, std::string>::set; %rename(EXISTS) std::map<std::string, std::string>::has_key; %rename(DELETE) std::map<std::string, std::string>::del; %rename(SCALAR) std::map<std::string, std::string>::size; %rename(CLEAR) std::map<std::string, std::string>::clear; %{ #include <map> #include <string> // For iteration support, will leak if iteration stops before the end ever. static std::map<void*, std::map<std::string, std::string>::const_iterator> iterstate; const char *current(std::map<std::string, std::string>& map) { std::map<void*, std::map<std::string, std::string>::const_iterator>::iterator it = iterstate.find(&map); if (it != iterstate.end() && map.end() == it->second) { // clean up entry in the global map iterstate.erase(it); it = iterstate.end(); } if (it == iterstate.end()) return NULL; else return it->second->first.c_str(); } %} %extend std::map<std::string, std::string> { std::map<std::string, std::string> *TIEHASH() { return $self; } const char *FIRSTKEY() { iterstate[$self] = $self->begin(); return current(*$self); } const char *NEXTKEY(const std::string&) { ++iterstate[$self]; return current(*$self); } } %include <std_map.i> %typemap(in,noblock=1) const std::map<std::string, std::string>& (void *argp=0, int res=0, $1_ltype tempmap=0) { res = SWIG_ConvertPtr($input, &argp, $descriptor, %convertptr_flags); if (!SWIG_IsOK(res)) { if (SvROK($input) && SvTYPE(SvRV($input)) == SVt_PVHV) { fprintf(stderr, "Convert HV to map/n"); tempmap = new $1_basetype; HV *hv = (HV*)SvRV($input); HE *hentry; hv_iterinit(hv); while ((hentry = hv_iternext(hv))) { std::string *val=0; // TODO: handle errors here SWIG_AsPtr_std_string SWIG_PERL_CALL_ARGS_2(HeVAL(hentry), &val); fprintf(stderr, "%s => %s/n", HeKEY(hentry), val->c_str()); (*tempmap)[HeKEY(hentry)] = *val; delete val; } argp = tempmap; } else { %argument_fail(res, "$type", $symname, $argnum); } } if (!argp) { %argument_nullref("$type", $symname, $argnum); } $1 = %reinterpret_cast(argp, $ltype); } %typemap(freearg,noblock=1) const std::map<std::string, std::string>& { delete tempmap$argnum; } %template(StringStringMap) std::map<std::string, std::string>; %{ #include "stl.h" %} %include "stl.h"

Luego adapté tu muestra perl para probar:

use Data::Dumper; use stl; my $v = stl::TryMap(stl::StringStringMap->new()); $v->{''a''} = ''1''; print Dumper $v; print Dumper stl::TryMap({''a'' => ''4''}); print Dumper stl::TryMap($v); foreach my $key (keys %{$v}) { print "$key => $v->{$key}/n"; } print $v->{''7''}."/n";

Que pude ejecutar con éxito:

Got map: 0x22bfb80 $VAR1 = bless( { ''7'' => ''!'', ''a'' => ''1'' }, ''stl::StringStringMap'' ); Convert HV to map a => 4 Got map: 0x22af710 In C++ map: a => 4 $VAR1 = bless( { ''7'' => ''!'', ''a'' => ''4'' }, ''stl::StringStringMap'' ); Got map: 0x22bfb20 In C++ map: 7 => ! In C++ map: a => 1 $VAR1 = bless( { ''7'' => ''!'', ''a'' => ''1'' }, ''stl::StringStringMap'' ); 7 => ! a => 1 !

También puede vincular este objeto a un hash, por ejemplo:

use stl; my $v = stl::TryMap(stl::StringStringMap->new()); print "$v/n"; tie %foo, "stl::StringStringMap", $v; print $foo{''a''}."/n"; print tied(%foo)."/n";

En teoría, puede escribir un mapa de tipos para establecer este enlace automáticamente al regresar de cada llamada de función, pero hasta ahora no he logrado escribir un mapa de tipos que funcione tanto con el sistema de vinculación como con el tipo de tiempo de ejecución de SWIG.

Cabe señalar que este no es el código de producción preparada. Hay un problema de seguridad de subprocesos para el mapa interno y también falta algo de manejo de errores que yo sepa. Tampoco he probado completamente todas las operaciones de hash desde el lado perl más allá de lo que ves arriba. También sería bueno hacerlo más genérico interactuando con la macro swig_map_common . Finalmente, no soy un gurú perl de ninguna manera y no he utilizado mucho la API C, por lo que sería aconsejable tener precaución en esa área.