2011-08-02 6 views
6

Mam funkcję w bibliotece stron trzecich napisane w C: char* fix_filename_slashes(char* path). Ta funkcja oczekuje, że zmieniony łańcuch C zostanie przekazany do niej, aby mógł zmienić wszystkie ukośniki w ścieżce do poprawnego użycia w oparciu o system operacyjny. Wszystkie ciągi, których używam w mojej fasadzie, są zadeklarowane jako std::string s. Starałem się po prostu użyć foo.c_str() jak każdej innej funkcji, która oczekuje ciąg C nie ją zmienić i spodziewa się const char *, ale ta funkcja powoduje błąd: Error: Argument of type "const char *" is incompatible with parameter of type "char *"C++: Prawidłowa implementacja przekazywania std :: string do funkcji C, która chce zmienić ciąg znaków?

jest wynikiem wymyśliłem:

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

za "poprawne" lub czy istnieją inne (bardziej poprawne?) Sposoby wykonania tego zadania?

EDIT

Whups. Podobno funkcja zwraca KOPIĘ ciągu znaków. Nadal jest już kilka fajnych odpowiedzi.

+5

ma niezdefiniowane zachowanie napisane nad nim –

+0

@Gene Bushuyev: Czy mógłbyś wyjaśnić UB, więc wiem, co jest nie tak? – Casey

+2

21.3.6 ... const charT * c_str() const; ...."Program nie zmienia żadnej z wartości zapisanych w tablicy" – fizzer

Odpowiedz

7

Jeśli długość ciągu nie ulegnie zmianie, można użyć wskaźnika do pierwszego znaku ciągu. Jest to niezdefiniowane zachowanie w standardzie C++ 03, ale wszystkie znane implementacje działają poprawnie i są jawnie dozwolone w standardzie C++ 11.

fix_filename_slashes(&filename[0]); 

Jeśli rozmiar napisu może ulec zmianie, będziesz musiał wykonać trochę więcej pracy.

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

jaka jest poprawka w standardzie? – pm100

+0

Po kilku testach stwierdziłem, że funkcja nie zmienia długości. Więc, zobacz edytuj. Trochę głupi, by spodziewać się członka niezmiennego, jeśli zwróci się kopię. Nic jednak nie mogę z tym zrobić. – Casey

1

przekształcić go w sekwencji nul znaków przechowywanych w std::vector: przykład

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; 
} 

Zastosowanie:

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

Czy można użyć konstruktora wektora 'iterator, iterator' zamiast wstawki, nadal wykonując końcowe' push_back'? –

+0

byłoby krótsze, aby napisać 'vector v (filename.begin(), nazwapliku.end());' aby skopiować ciąg do wektora, a następnie 'push_back (0);' –

+0

@ Mark, @Gene: Tak , ale w ten sposób masz zagwarantowane, że tylko jedna alokacja ma miejsce (alokacja wykonana przez "rezerwę"). Jeśli użyjesz konstruktora zakresu, możesz otrzymać dwie alokacje (jedną przez konstruktor zakresu, jedną przez 'push_back'). –

0

Jeśli string stosowany oddzielny bufor do przechowywania ciąg c_str ten Wouldn Zmodyfikuj oryginalny ciąg.

Lepiej jest utworzyć bufor char na stosie lub sterty, skopiuj do niego znaki (zakończone znakiem null), wywołaj funkcję fix, a następnie przypisz bufor do łańcucha.

1

Ponieważ masz problemy, wystarczy spełnić wymagania funkcji C i skopiować ciąg znaków do tablicy znaków, a następnie po utworzeniu ciągu znaków z tablicy znaków lub wymusić przypisanie kopii do oryginału strunowy.

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

'delete [] temp;' i po wywołaniu 'strncpy' z' str.size() ',' temp' nie jest zakończone null. Również przeciek pamięci, jeśli 'fix_filename_slashes' zgłasza wyjątek (prawdopodobnie nie, ale co jeśli?); i po co zawracać głowę na "const char *"? (gosh, jestem wybredny) – tomasz

+0

Naprawiono kod, jednak funkcje C nie rzucają wyjątków i wiem, że powinno być o wiele więcej sprawdzania, dzieje się to tylko w celach ilustracyjnych. –

+0

prawda o wyjątkach, dlatego powiedziałem, że jestem wybredna ;-) – tomasz

0

Oto inne podejście, które wymaga niewielkiej konfiguracji, ale działa automatycznie po tym. Opiera się na tymczasowym obiekcie, który pobiera kopię oryginalnego łańcucha i kopiuje zmodyfikowany ciąg z powrotem do destruktora. Oczywiście całe to kopiowanie nie będzie zbyt wydajne, ale w większości przypadków sprawność nie będzie miała znaczenia.

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)); 
Powiązane problemy