2011-08-02 20 views
6

Tengo una función en una biblioteca de terceros escrita en C: char* fix_filename_slashes(char* path). Esta función espera que se le pase un C-string mutable para que pueda cambiar todas las barras en el camino al uso correcto basado en el sistema operativo. Todas las cadenas que estoy usando en mi Fachada están declaradas como std::string s. Me trató de utilizar simplemente foo.c_str() como cualquier otra función que espera una cadena C no cambiarlo y espera una const char *, pero esta función provoca un error: Error: Argument of type "const char *" is incompatible with parameter of type "char *"C++: ¿Implementación correcta para pasar una cadena std :: a una función C que quiere cambiar la cadena?

Es el resultado que se me ocurrió:

char* tempf = const_cast<char*>(filename.c_str()); 
filename = std::string(fix_filename_slashes(tempf)); 
tempf = NULL; 

considerado "correcto" o hay otras formas (¿más correctas?) Para realizar la tarea.

EDITAR

Whups. Aparentemente, la función devuelve una COPIA de la cadena. Todavía hay algunas buenas respuestas ya dadas.

+5

tiene un comportamiento indefinido escrito por todas partes –

+0

@Gene Bushuyev: ¿Podría explicar el UB, así que sé lo que está mal? – Casey

+2

21.3.6 ... const charT * c_str() const; ...."El programa no debe alterar ninguno de los valores almacenados en la matriz" – fizzer

Respuesta

7

Si la longitud de la cadena no cambia, puede utilizar un puntero al primer carácter de la cadena. Este es un comportamiento indefinido en el estándar C++ 03, pero todas las implementaciones conocidas funcionan correctamente y está explícitamente permitido bajo el estándar C++ 11.

fix_filename_slashes(&filename[0]); 

Si el tamaño de la cuerda puede cambiar, tendrá que hacer un poco más de trabajo.

filename.resize(max_size, 0); 
append_filename_suffix(&filename[0]); 
filename.resize(strlen(filename.c_str())); 
+0

¿cuál es la solución en el estándar? – pm100

+0

Después de algunas pruebas, he determinado que la función no cambia la longitud. Entonces sí, mira editar. Es un poco estúpido esperar un miembro no const si se devuelve una copia. Sin embargo, no puedo hacer nada al respecto. – Casey

1

convertirlo en una secuencia terminada en nulo de caracteres almacenados en un std::vector: ejemplo

template <typename Character> 
std::vector<Character> to_vector(std::basic_string<Character> const& s) 
{ 
    std::vector<Character> v; 
    v.reserve(s.size() + 1); 
    v.insert(v.end(), s.begin(), s.end()); 
    v.push_back(0); 
    return v; 
} 

Uso:

std::string filename = get_filename(); 
std::vector<char> filename_cstr = to_vector(filename); 
filename = std::string(fix_filename_slashes(&filename_cstr[0])); 
+1

¿Se puede usar el constructor vector iterator, iterator en lugar de la inserción, todavía se está realizando el 'push_back' final? –

+0

hubiera sido más corto escribir 'vector v (filename.begin(), filename.end());' para copiar cadena en vector, seguido de 'push_back (0);' –

+0

@Mark, @Gene: Sí , pero de esta manera se le garantiza que solo se realiza una asignación (la asignación realizada por 'reserva'). Si usa el constructor de rango, puede terminar con dos asignaciones (una por el constructor de rango, una por 'push_back'). –

0

Si el string utilizó un tampón separada para almacenar la cadena c_str este wouldn modifique la cadena original.

Mejor es crear un búfer char en la pila o en el montón, copiar los caracteres en él (nulo terminado), llamar a la función de reparación y luego asignar el búfer a la cadena.

1

Como va a hacer todos los problemas, puede cumplir con los requisitos de la función C y copiar la cadena en una matriz char, luego crear una cadena desde la matriz char o forzar una copia en su original cuerda.

char* temp = new char[str.size() + 1] 
    // Force a copy of the result into another string 
    str = (const char*)fix_filename_slashes(strncpy(temp, str.c_str(), str.size() + 1)); 
    delete [] temp; 
+1

'eliminar [] temp;' y después de llamar a 'strncpy' con' str.size() ',' temp' no termina en nulo. También pérdida de memoria si 'fix_filename_slashes' arroja una excepción (probablemente no, pero ¿y si?); y ¿por qué molestarse en lanzar '' const char * '? (Dios mío, soy quisquilloso) – tomasz

+0

Se corrigió el código, sin embargo, las funciones C no arrojan excepciones y sé que debería haber muchas más comprobaciones, solo para fines ilustrativos. –

+0

true sobre excepciones, es por eso que dije que era exigente ;-) – tomasz

0

Aquí hay otro enfoque que requiere un poco de configuración, pero funciona automáticamente después de eso. Se basa en un objeto temporal, que toma una copia de la cadena original y copia la cadena modificada en el destructor. Obviamente, toda esta copia no será demasiado eficiente, pero en la mayoría de los casos la eficiencia no tendrá importancia.

class mutable_string 
{ 
public: 
    mutable_string(std::string & str, int maxlen = 0) : m_str(str) 
    { 
     m_buffer.resize(max(maxlen, str.length()) + 1); 
     memcpy(&m_buffer[0], str.c_str(), str.length()+1); 
    } 
    ~mutable_string() 
    { 
     m_str = m_buffer; 
    } 
    operator char*() 
    { 
     return &m_buffer[0]; 
    } 
private: 
    std::string &  m_str; 
    std::vector<char> m_buffer; 
}; 

fix_filename_slashes(mutable_string(filename)); 
Cuestiones relacionadas