Casey
Casey

Reputation: 10936

C++: Correct implementation for passing a std::string to a C function that wants to change the string?

I have a function in a third-party library written in C: char* fix_filename_slashes(char* path). This function expects a mutable C-string passed to it so it can change all the slashes in the path to the correct use based on the operating system. All the strings I'm using in my Facade are declared as std::strings. I attempted to simply use foo.c_str() as every other function that expects a C string doesn't change it and expects a const char *, but this function causes an error: Error: Argument of type "const char *" is incompatible with parameter of type "char *"

Is the result I came up with:

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

considered "correct" or are there other (more correct?) ways to accomplish the task?

EDIT

Whups. Apparently the function returns a COPY of the string. Still there are some nice answers already given.

Upvotes: 8

Views: 4681

Answers (5)

Mark Ransom
Mark Ransom

Reputation: 308206

If the string length does not change, you can use a pointer to the first character of the string. This is undefined behavior in the C++03 standard, but all known implementations work properly and it is explicitly allowed under the C++11 standard.

fix_filename_slashes(&filename[0]);

If the size of the string may change, you'll have to do a little more work.

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

Upvotes: 7

Mark Ransom
Mark Ransom

Reputation: 308206

Here's another approach that takes a little setup, but works automatically after that. It relies on a temporary object, which takes a copy of the original string and copies the modified string back in the destructor. Obviously all this copying won't be too efficient, but in most cases the efficiency won't matter.

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

Upvotes: 0

Adrian Regan
Adrian Regan

Reputation: 2250

Since you are going to all the trouble you could just comply with the requirements of the C function and copy your string to a char array then after the function create a string from the char array or force a copy assignment on your original string.

    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;

Upvotes: 1

Mark B
Mark B

Reputation: 96251

If the string used a separate buffer to store the c_str string this wouldn't modify the original string.

Better is to create a char buffer on the stack or heap, copy the characters into it (null terminated), call the fix function, and then assign the buffer back into the string.

Upvotes: 0

James McNellis
James McNellis

Reputation: 355079

Convert it into a null-terminated sequence of characters stored in a std::vector:

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

Usage example:

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

Upvotes: 1

Related Questions