Search code examples
c++clanglvaluestrerror

std::string constructor with lvalue throws with clang


I am making use of Matei David's handy C++ wrapper for zlib, but I get an error when compiling on macOs (clang-1100.0.33.

 include/strict_fstream.hpp:39:37: error: cannot initialize a parameter of type 'const char *' with an lvalue of type 'int'

The problem is here:

/// Overload of error-reporting function, to enable use with VS.
/// Ref: http://stackoverflow.com/a/901316/717706
static std::string strerror()
{
    std::string buff(80, '\0');

    // options for other environments omitted, where error message is set
    // if not Win32 or _POSIX_C_SOURCE >= 200112L, error message is left empty.

    auto p = strerror_r(errno, &buff[0], buff.size());

    // error next line
    std::string tmp(p,  std::strlen(p));
    std::swap(buff, tmp);
    buff.resize(buff.find('\0'));
    return buff;
}

(Which IIUC has nothing to do with zlib, just trying to report errors in a thread safe manner).

If I change to this:

static std::string strerror()
{
    std::string buff(80, '\0');

    auto p = strerror_r(errno, &buff[0], buff.size());

    // "fix" below
    size_t length = buff.size();
    std::string tmp(p,  length);
    std::swap(buff, tmp);

    buff.resize(buff.find('\0'));
    return buff;
}

My program compiles and runs fine.

I have two questions:

  1. Why does clang not like the constructor std::string tmp(p, std::strlen(p));?

  2. The buffer was declared at the beginning of the function as length 80. Why are we even bothering to look up the length?

  3. The answer to 2 may answer this, but is there something wrong with my version?

Thanks.


Solution

  • If you use int strerror_r(int errnum, char *buf, size_t buflen);, then there is no appropriate string constructor and the program is ill formed.

    If you use char *strerror_r(int errnum, char *buf, size_t buflen);, then the program is well-formed.

    The standard C/POSIX library implementation influences which function you get. Compiler is only involved as much as influencing what system library may be used by default.

    Former function is extension to POSIX specified in XSI (which is essentially an optional part of POSIX) and latter is a GNU extension.

    In case you use glibc (I don't know if that is an option on MacOS), you can control which version you get with macros, although the XSI compliant version is not available in older versions. Its documentation says:

    The XSI-compliant version of strerror_r() is provided if: (_POSIX_C_SOURCE >= 200112L || _XOPEN_SOURCE >= 600) && ! _GNU_SOURCE


    1. The buffer was declared at the beginning of the function as length 80. Why are we even bothering to look up the length?

    In the construction std::string tmp(p, std::strlen(p));, strlen seems entirely unnecessary to me. std::string tmp(p); is equivalent.


    If you don't need thread safety, then the most portable solution is to use std::strerror which is in standard C++:

    return std::strerror(errno); // yes, it's this simple
    

    If you do need thread safety, then you could wrap this in a critical section using a mutex.


    Note that strerror, the name of your function, is reserved to the language implementation in the global namespace when the standard library is used. The function should be in a namespace, or be renamed.