Modify

Opened 5 years ago

Closed 4 years ago

Last modified 4 years ago

#7704 closed Bugs (fixed)

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

Reported by: Braden McDaniel <braden@…> Owned by: apolukhin
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.

Attachments (0)

Change History (12)

comment:1 Changed 5 years ago by Braden McDaniel <braden@…>

  • Component changed from None to lexical_cast
  • Owner set to apolukhin

comment:2 Changed 5 years ago by apolukhin

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 Changed 5 years ago by Braden McDaniel <braden@…>

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 follow-up: Changed 5 years ago by apolukhin

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++

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

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

comment:6 Changed 5 years ago by apolukhin

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 Changed 5 years ago by Braden McDaniel <braden@…>

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

comment:8 Changed 4 years ago by apolukhin

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

comment:9 Changed 4 years ago by apolukhin

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

comment:10 Changed 4 years ago by apolukhin

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

comment:11 Changed 4 years ago by apolukhin

  • Resolution set to fixed
  • Status changed from new to closed

(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 4 years ago by apolukhin (previous) (diff)

comment:12 Changed 4 years ago by apolukhin

  • Milestone changed from To Be Determined to Boost 1.54.0

Add Comment

Modify Ticket

Change Properties
Set your email in Preferences
Action
as closed The owner will remain apolukhin.
The resolution will be deleted. Next status will be 'reopened'.
Author


E-mail address and user name can be saved in the Preferences.

 
Note: See TracTickets for help on using tickets.