2011-10-01 13 views
8

¿Hay alguna manera segura de crear un string en D, usando información solo disponible en tiempo de ejecución, sin asignar memoria?¿Crear cadenas en D sin asignar memoria?

Un simple ejemplo de lo que podría querer hacer:

void renderText(string text) { ... } 

void renderScore(int score) 
{ 
    char[16] text; 
    int n = sprintf(text.ptr, "Score: %d", score); 
    renderText(text[0..n]); // ERROR 
} 

El uso de este, se obtendría un error porque el trozo de text no es inmutable, y por lo tanto no es un string (es decir immutable(char)[])

sólo puedo pensar en tres maneras de evitar esto:

  1. moldeada del sector a un string. Funciona, pero es feo.
  2. Asigne una nueva cadena utilizando la división. Esto funciona, pero prefiero no tener que asignar memoria.
  3. Cambie renderText para tomar const(char)[]. Esto funciona aquí, pero (a) es feo, y (b) muchas funciones en Phobos requieren string, así que si quiero usarlas de la misma manera, entonces esto no funciona.

Ninguno de estos es particularmente agradable. ¿Me estoy perdiendo de algo? ¿Cómo todos los demás solucionan este problema?

+1

¡Herejía, herejía! Use 'snprintf'! : D – BCS

+2

Creo que el # 3 es realmente la solución correcta. string -> inmutable (char) [] significa que los datos no pueden cambiar (siempre que sigan siendo referenciados en alguna parte, de lo contrario, el GC puede recogerlo, AFAIK). No creo que puedas hacer una garantía así para los datos asignados por la pila. Pero a menos que renderText realmente necesite almacenar texto en alguna parte, debería usar const (char) []. Como inmutable es más fuerte que const, solo se debe usar cuando sea necesario. Estoy de acuerdo, sin embargo, que muchas funciones en phobos innecesariamente toman cadenas en lugar de const (char) [], que deberían ser arregladas en phobos. – jpf

+0

@jpf: Estoy de acuerdo 100%. –

Respuesta

2

Echa un vistazo assumeUnique de std.exception Respuesta de Jonathan.

+0

¿Cómo va a interactuar eso con assumeUnique tomando un argumento ref (en este caso a una expresión slice que IIRC no es un valor r)? – BCS

+0

No creo que usar assumeUnique con stack asignado sea válido. El espacio de pila no es exclusivo, cuando ha regresado la renderScore y se llama a otra función, puede reutilizar el mismo espacio en la pila. -> no único y también __no inmutable__. Recuerde que lo inmutable también significa implícitamente compartido: ¿Realmente quiere que el código pase la pila de datos asignados a otro hilo? – jpf

+0

Tendría que hacer: 'auto text2 = text [0..n]; renderText (assumeUnique (text2)); 'aunque como dice jpf, es muy desagradable ya que todavía tienes acceso de escritura a' text', lo que frustra el propósito de 'inmutable'. –

0

No, no se puede crear una cadena sin asignación. Quizás quiso decir acceso? Para evitar la asignación, debe usar slice o puntero para acceder a una cadena creada anteriormente. Sin embargo, no estoy seguro sobre el lanzamiento, puede o no asignar nuevo espacio de memoria para la nueva cadena.

+0

El OP parece dispuesto a usar la asignación de la pila, así que supongo que solo está tratando de evitar la asignación del montón. – BCS

0

Una forma de evitar esto sería copiar los caracteres mutables en una nueva versión inmutable continuación, el tramo que:

void renderScore(int score) 
{ 
    char[16] text; 
    int n = sprintf(text.ptr, "Score: %d", score); 
    immutable(char)[16] itext = text; 
    renderText(itext[0..n]); 
} 

Sin embargo:

  1. DMD actualmente no permite esto debido a un insecto.
  2. Está creando una copia innecesaria (mejor que una asignación de GC, pero aún no es excelente).
+0

¿Estás seguro de que esto debería funcionar? Cite TDPL: "Se emite un valor inmutable en Stone: ... Nunca cambiará durante la ejecución del programa". Todavía diría que este requisito se cumple __nunca__ para los datos asignados por la pila.Puede funcionar para variables locales, pero solo si se inicializan en tiempo de compilación para que puedan colocarse en el segmento de datos estáticos en lugar de en la pila. Pero su ejemplo aún tiene que establecer itext en tiempo de ejecución -> stack space. – jpf

+0

@jpf: la inmutabilidad y el tiempo de vida son cuestiones ortogonales. La inmutabilidad garantiza que no cambiará durante su vida útil, pero no garantiza una vida útil infinita. Si hace referencia a algo con una vida útil finita, depende de usted asegurarse de no utilizarlo más allá de la destrucción. –

+0

Parece que tienes razón. TDPL es un poco inconsistente allí, en la página dice claramente "tan pronto como se inicializa, puede considerar que ha sido grabado para siempre en la memoria que lo almacena. Nunca cambiará durante la ejecución del __programa__", que imho también significa vida infinita. Pero en la página 401 dice "se garantiza que un valor inmutable nunca cambiará a lo largo de __its__ lifetime" – jpf

6

Tiene una matriz estática de char. Desea pasarlo a una función que toma immutable(char)[]. La forma solo de hacer eso sin ninguna asignación es emitir. Piénsalo. Lo que quieres es un tipo para actuar como si fuera otro. Eso es lo que hace el casting. Puedes elegir usar assumeUnique para hacerlo, ya que eso hace exactamente el elenco que estás buscando, pero si eso realmente te gana algo es discutible. Su objetivo principal es documentar que lo que está haciendo el elenco es hacer que el valor que se está emitiendo se trate como immutable y que no haya otras referencias a él. En cuanto a su ejemplo, eso es esencialmente cierto, ya que es lo último en la función, pero si usted quiere hacer eso en general depende de usted.Dado que es una matriz estática que arriesga los problemas de memoria si la arruinas y la pasas a una función que permite hacer una fuga, no estoy seguro de que assumeUnique sea la mejor opción. Pero, de nuevo, depende de usted.

Independientemente de si está haciendo un cast (explícitamente o con assumeUnique), necesita estar seguro de que la función a la que lo está transfiriendo no va a filtrar las referencias a los datos que está pasando a él. Si lo hace, entonces estás pidiendo problemas.

La otra solución, por supuesto, es cambiar la función para que tome const(char)[], pero eso aún corre el riesgo de perder referencias a los datos que está pasando. Por lo tanto, aún necesita estar seguro de qué función realmente va a hacer la función. Si es pure, no devuelve const(char)[] (o cualquier cosa que pueda contener const(char)[]), y no hay forma de que pueda filtrarse a través de cualquiera de los otros argumentos de la función, entonces está seguro, pero si alguno de ellos no es cierto , entonces vas a tener que tener cuidado. Por lo tanto, en última instancia, creo que todo lo que realmente usa para usar const(char)[] en lugar de convertirlo en string es que no tiene que emitir. Eso es aún mejor, ya que evita el riesgo de arruinar el elenco (y es mejor, en general, evitar el casting cuando puedes), pero aún tienes las mismas cosas de las que preocuparte cuando se trata de escapar de las referencias.

Por supuesto, eso también requiere que usted pueda cambiar la función para que tenga la firma que desea. Si no puedes hacer eso, entonces vas a tener que lanzar. Creo que en este punto, más de las funciones de Phobos basadas en cadenas han sido modificadas para que sean plantillas en el tipo de cadena. Por lo tanto, esto debería ser menos problemático ahora con Fobos de lo que solía ser. Algunas funciones (en particular las de archivo std.) aún necesitan plantillas, pero, en última instancia, las funciones en Phobos que requieren string específicamente deberían ser bastante raras y tendrán un buen motivo para requerirlas.

En última instancia, sin embargo, el problema es que está tratando de tratar una matriz estática como si fuera una matriz dinámica, y mientras que D definitivamente le permite hacer eso, está asumiendo un riesgo definitivo al hacerlo, y debe asegurarse de que las funciones que está utilizando no pierdan ninguna referencia a los datos locales que les está pasando.

+0

¿Qué tal si renderText toma un "scope const (char) []"? "La clase de almacenamiento del alcance significa que las referencias en el parámetro no pueden ser escapadas (por ejemplo, asignadas a una variable global)". – jpf

+0

@jpf: ¿Estás seguro de eso? DMD lo permite. –

+0

Esa fue una cita de la referencia de lenguaje D2: http://www.digitalmars.com/d/2.0/function.html Sección "Parámetros de función". Sin embargo, no puedo encontrar algo similar en TDPL. Pero no me sorprendería si aún no se implementa correctamente ;-) – jpf

Cuestiones relacionadas