Opened 10 years ago

Closed 10 years ago

Last modified 10 years ago

#7704 closed Bugs (fixed)

lexical_cast to filesystem::path fails for string::length > 22

Reported by: Braden McDaniel <braden@…> Owned by: Antony Polukhin
Milestone: Boost 1.54.0 Component: lexical_cast
Version: Boost 1.52.0 Severity: Problem
Keywords: Cc:

Description

When building with clang on Darwin using -stdlib=libc++, a lexical_cast to a filesystem::path will fail if the input string is longer than 22 elements.

Consider this test program:

#include <boost/lexical_cast.hpp>
#include <boost/filesystem/path.hpp>

int main()
{
  try {
    boost::filesystem::path p;
    std::string s1 = "aaaaaaaaaaaaaaaaaaaaaaa";
    p = boost::lexical_cast<boost::filesystem::path>(s1);
  } catch (std::exception & ex) {
    std::cerr << ex.what() << std::endl;
    return EXIT_FAILURE;
  }
}

…compiled thusly:

clang++ -stdlib=libc++ -I /Users/bradenmcdaniel/Source/boost_1_52_0 -L /Users/bradenmcdaniel/Source/boost_1_52_0/stage/lib -lboost_filesystem -lboost_system -o lexical-cast-path lexical_cast_path.cpp

When run, it produces this output:

$ ./lexical-cast-path 
bad lexical cast: source type value could not be interpreted as target

If the length of s1 is reduced by 1 (from 23 to 22), the test program will succeed.

Change History (12)

comment:1 by Braden McDaniel <braden@…>, 10 years ago

Component: Nonelexical_cast
Owner: set to Antony Polukhin

comment:2 by Antony Polukhin, 10 years ago

Could not reproduce it on clang version 3.0-6ubuntu3 with default stl library.

It looks more like a libc++ bug, but I`m not sure.

Could you attach a backtrace? ( Have not found a .deb pakage for libc++ and don`t have enough time to install libc++ from sources)

comment:3 by Braden McDaniel <braden@…>, 10 years ago

That's consistent with what I've seen: I was not able to reproduce the problem when building without the -stdlib=libc++ flag. Unfortunately, the version of GNU libstdc++ available on Darwin is rather dated and thus not perceived as a path forward for new C++ development.

Here is a backtrace from the throw point:

#0  0x00007fff8457254d in __cxa_throw ()
#1  0x000000010000658c in boost::throw_exception<boost::bad_lexical_cast> (e=@0x7fff5fbff9a0) at throw_exception.hpp:66
#2  0x0000000100006249 in boost::detail::lexical_cast_do_cast<boost::filesystem::path, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >::lexical_cast_impl (arg=@0x7fff5fbffaa0) at lexical_cast.hpp:2141
#3  0x0000000100005b7c in boost::lexical_cast<boost::filesystem::path, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > > (arg=@0x7fff5fbffaa0) at lexical_cast.hpp:2300
#4  0x0000000100005162 in main () at lexical_cast_path.cpp:10

If you'd like me to try to get one from some other point in the code, let me know.

comment:4 by Antony Polukhin, 10 years ago

Try this code:

#include <sstream>
#include <boost/filesystem/path.hpp>

int main()
{
    boost::filesystem::path p;
    std::istringstream s1("aaaaaaaaaaaaaaaaaaaaaaa");
    s1 >> p;
    if (s1.get() != std::char_traits<char>::eof())
        return EXIT_FAILURE;

    return 0;
}

If it exits with EXIT_FAILURE, then it is a bug in libc++

in reply to:  4 comment:5 by Braden McDaniel <braden@…>, 10 years ago

That test program succeeds (that is, it returns 0).

comment:6 by Antony Polukhin, 10 years ago

Please, try this one:

#include <sstream>
#include <boost/filesystem/path.hpp>
#include <boost/lexical_cast.hpp>

template< class BufferType >
class stl_buf_unlocker: public BufferType{
public:
    typedef BufferType base_class;
    using base_class::pptr;
    using base_class::pbase;
    using base_class::setg;
    using base_class::setp;
};

typedef stl_buf_unlocker<std::basic_stringbuf<char> > unlocked_but_t;

int main()
{
    char data[] = "aaaaaaaaaaaaaaaaaaaaaaa";
    char* start = data;
    char* finish = start + sizeof(data);

    std::basic_istringstream<char> stream;
    static_cast<unlocked_but_t*>(stream.rdbuf())->setg(start, start, finish);
    
    stream.unsetf(std::ios::skipws);
    
    std::string output;
    // boost::filesystem::path output;
    
    const bool b1 = (stream >> output);
    const bool b2 = (stream.get() == std::char_traits<char>::eof());

    BOOST_ASSERT(b1);
    BOOST_ASSERT(b2);
    
    return 0;
}

If it does not trigger asserts, then comment std::string output; and uncomment boost::filesystem::path output; and try it once more.

comment:7 by Braden McDaniel <braden@…>, 10 years ago

In both cases no assert is triggered and the program returns 0.

comment:8 by Antony Polukhin, 10 years ago

(In [83543]) Reimplement STL string buffer unlocker (refs #8267 and refs #7704)

comment:9 by Antony Polukhin, 10 years ago

(In [83642]) Fix streams and buffers usage (refs #8267 and refs #7704). Now conversions the use STL streams shall work faster

comment:10 by Antony Polukhin, 10 years ago

(In [83644]) Add tests for filesystem::path conversion (refs #7704)

comment:11 by Antony Polukhin, 10 years ago

Resolution: fixed
Status: newclosed

(In [83689]) Merge from trunk:

  • Fix stream related issues with libc++ and clang (fixes #7704, fixes #8267)
  • Change runtime assert to compile time when converting to pointer (fixes #8334)
Last edited 10 years ago by Antony Polukhin (previous) (diff)

comment:12 by Antony Polukhin, 10 years ago

Milestone: To Be DeterminedBoost 1.54.0
Note: See TracTickets for help on using tickets.