Skip to content

Commit

Permalink
[FIX] travis/ubuntu16.04: gcc-7.2 bug
Browse files Browse the repository at this point in the history
  • Loading branch information
marehr committed Feb 23, 2018
1 parent 99b6357 commit 0d98402
Show file tree
Hide file tree
Showing 3 changed files with 281 additions and 15 deletions.
8 changes: 3 additions & 5 deletions include/seqan3/range/container/concatenated_sequences.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,9 @@ template <typename inner_type,
//!\endcond
class concatenated_sequences
{
static_assert(!detail::sequence_concept_modified_by_const_iterator_bug<inner_type>,
"KNOWN BUG: inner_type = std::basic_string<> is not working "
"for the ubuntu::ppa version of gcc7, because of a faulty STL version. ");
protected:
//!\privatesection
//!\brief Where the concatenation is stored.
Expand Down Expand Up @@ -1319,8 +1322,3 @@ class concatenated_sequences
};

} // namespace seqan3

#ifndef NDEBUG
static_assert(seqan3::reservable_sequence_concept<seqan3::concatenated_sequences<std::string>>);
static_assert(seqan3::forward_range_concept<seqan3::concatenated_sequences<std::string>>);
#endif
111 changes: 102 additions & 9 deletions include/seqan3/range/container/concept.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,92 @@

#include <initializer_list>

// remove if sequence_concept_modified_by_const_iterator_bug vanished from travis
#include <string>

// TODO:
// * merge sequence_concept_modified_by_const_iterator back into
// sequence_concept
// * remove is_basic_string
// * fix test cases
// * remove #include <string> in this file
// once the ubuntu::ppa [1] of g++-7 has a newer update than
// 7.2.0-1ubuntu1~16.04 (2017-08-20)
//
// [1] https://launchpad.net/~ubuntu-toolchain-r/+archive/ubuntu/test?field.series_filter=xenial
namespace seqan3::detail
{
//!\privatesection

//!\brief Returns whether `basic_string_t` is of type `std::basic_string<value_t, traits_t, allocator_t>`.
//!\attention Will be deleted once seqan3::detail::sequence_concept_modified_by_const_iterator_bug is fixed.
template <typename basic_string_t>
struct is_basic_string : std::false_type
{};

//!\brief Returns whether `basic_string_t` is of type `std::basic_string<value_t, traits_t, allocator_t>`.
//!\attention Will be deleted once seqan3::detail::sequence_concept_modified_by_const_iterator_bug is fixed.
template <typename value_t, typename traits_t, typename allocator_t>
struct is_basic_string<std::basic_string<value_t, traits_t, allocator_t>> : std::true_type
{};

//!\brief Shorthand of seqan3::detail::is_basic_string
//!\attention Will be deleted once seqan3::detail::sequence_concept_modified_by_const_iterator_bug is fixed.
template <typename basic_string_t>
constexpr bool is_basic_string_v = is_basic_string<basic_string_t>::value;

static_assert(is_basic_string_v<std::basic_string<char>>, "");
static_assert(is_basic_string_v<std::string>, "");
static_assert(is_basic_string_v<std::wstring>, "");
static_assert(!is_basic_string_v<char>, "");

/*!\interface seqan3::detail::sequence_concept_modified_by_const_iterator <>
* \brief Checks whether insert and erase can be used with const_iterator
*
* \attention This will be merged back into sequence_concept once
* seqan3::detail::sequence_concept_modified_by_const_iterator_bug is fixed.
*/
//!\cond
template <typename type>
concept bool sequence_concept_modified_by_const_iterator = requires (type val, type val2)
{
{ val.insert(val.cbegin(), val2.front()) } -> typename type::iterator;
{ val.insert(val.cbegin(), typename type::value_type{}) } -> typename type::iterator;
{ val.insert(val.cbegin(), typename type::size_type{}, typename type::value_type{})} -> typename type::iterator;
{ val.insert(val.cbegin(), val2.begin(), val2.end()) } -> typename type::iterator;
requires is_basic_string_v<type> || requires(type val)
{
// TODO this function is not defined on strings (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=83328)
{ val.insert(val.cbegin(), std::initializer_list<typename type::value_type>{}) } -> typename type::iterator;
};
{ val.erase(val.cbegin()) } -> typename type::iterator;
{ val.erase(val.cbegin(), val.cend()) } -> typename type::iterator;

{ val.insert(val.begin(), typename type::size_type{}, typename type::value_type{}) } -> typename type::iterator;
{ val.insert(val.begin(), val2.begin(), val2.end()) } -> typename type::iterator;
};
//!\endcond

/*!\brief Workaround for a ubuntu/travis-ci exclusive bug with g++-7.2.
*
* seqan3::detail::sequence_concept_modified_by_const_iterator <std::string> is
* known to work, but ubuntu::ppa (<18.04)/travis-ci has a version of g++-7.2
* where a bug in the STL prevents this concept to be true.
*
* \attention This workaround can be removed if
* `/test/range/container/container_concept_test.cpp` is not failing on
* ubuntu::ppa (<18.04)/travis-ci anymore. \n
* Probably when the ppa version of gcc7 is newer than `7.2.0-1ubuntu1~16.04` (2017-08-20)
* \sa https://launchpad.net/~ubuntu-toolchain-r/+archive/ubuntu/test?field.series_filter=xenial
*/
template<typename string_t = std::string>
constexpr bool sequence_concept_modified_by_const_iterator_bug
= is_basic_string_v<string_t> && !sequence_concept_modified_by_const_iterator<string_t>;

//!\publicsection

} // seqan3::detail

namespace seqan3
{

Expand Down Expand Up @@ -127,15 +213,22 @@ concept bool sequence_concept = requires (type val, type val2)

// modify container
//TODO: how do you model this?
// { val.emplace(typename type::const_iterator{}, ? } -> typename type::iterator;
{ val.insert(val.cbegin(), val2.front()) } -> typename type::iterator;
{ val.insert(val.cbegin(), typename type::value_type{}) } -> typename type::iterator;
{ val.insert(val.cbegin(), typename type::size_type{}, typename type::value_type{})} -> typename type::iterator;
{ val.insert(val.cbegin(), val2.begin(), val2.end()) } -> typename type::iterator;
//TODO this fails on std::string, although it should work
// { val.insert(val.cbegin(), std::initializer_list<typename type::value_type>{}) } -> typename type::iterator;
{ val.erase(val.cbegin()) } -> typename type::iterator;
{ val.erase(val.cbegin(), val.cend()) } -> typename type::iterator;
// { val.emplace(typename type::const_iterator{}, ? } -> typename type::iterator;

{ val.insert(val.begin(), val2.front()) } -> typename type::iterator;
{ val.insert(val.begin(), typename type::value_type{}) } -> typename type::iterator;
// because of a travis bug we can't assume typename type::iterator as return type
{ val.insert(val.begin(), typename type::size_type{}, typename type::value_type{}) };
{ val.insert(val.begin(), val2.begin(), val2.end()) };
//TODO should return type::iterator on strings (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=83328)
{ val.insert(val.begin(), std::initializer_list<typename type::value_type>{}) };
{ val.erase(val.begin()) } -> typename type::iterator;
{ val.erase(val.begin(), val.end()) } -> typename type::iterator;

// workaround a travis bug where insert/erase can't take a const iterator, e.g. cbegin()
requires detail::sequence_concept_modified_by_const_iterator_bug<type> ||
detail::sequence_concept_modified_by_const_iterator<type>;

{ val.push_back(val.front()) } -> void;
{ val.push_back(typename type::value_type{}) } -> void;
{ val.pop_back() } -> void;
Expand Down
177 changes: 176 additions & 1 deletion test/range/container/container_concept_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,34 @@
#include <deque>
#include <string>

#include <seqan3/range/container/all.hpp>
#include <seqan3/range/container/concept.hpp>
#include <seqan3/range/container/concatenated_sequences.hpp>

using namespace seqan3;

// if detail::sequence_concept_modified_by_const_iterator_bug<> is false
// test seqan3::concatenated_sequences<std::string>, otherwise
// test seqan3::concatenated_sequences<std::vector<char>>
using concatenated_sequences_string_t = seqan3::concatenated_sequences<
std::conditional_t<
detail::sequence_concept_modified_by_const_iterator_bug<>,
std::vector<char>,
std::string
>>;

TEST(range_concept, forward_range_concept)
{
EXPECT_TRUE((seqan3::forward_range_concept<std::array<char, 2>>));
EXPECT_TRUE((seqan3::forward_range_concept<std::list<char>>));
EXPECT_TRUE((seqan3::forward_range_concept<std::forward_list<char>>)); // `.size()` missing
EXPECT_TRUE((seqan3::forward_range_concept<std::vector<char>>));
EXPECT_TRUE((seqan3::forward_range_concept<std::deque<char>>));
EXPECT_TRUE((seqan3::forward_range_concept<std::string>));

EXPECT_TRUE((seqan3::forward_range_concept<concatenated_sequences_string_t>));
EXPECT_TRUE((seqan3::forward_range_concept<seqan3::concatenated_sequences<std::vector<char>>>));
}

TEST(container_concept, container_concept)
{
EXPECT_TRUE((seqan3::container_concept<std::array<char, 2>>));
Expand All @@ -53,6 +77,132 @@ TEST(container_concept, container_concept)
EXPECT_TRUE((seqan3::container_concept<std::vector<char>>));
EXPECT_TRUE((seqan3::container_concept<std::deque<char>>));
EXPECT_TRUE((seqan3::container_concept<std::string>));

EXPECT_TRUE((seqan3::container_concept<concatenated_sequences_string_t>));
EXPECT_TRUE((seqan3::container_concept<seqan3::concatenated_sequences<std::vector<char>>>));
}

template <typename string_t>
void container_concept_travis_bug_test()
{
// non const version of container_concept_const_travis_bug_test
using namespace std::string_literals;

// example code from http://en.cppreference.com/w/cpp/string/basic_string/insert
string_t s = "xmplr";
string_t r = "";
typename string_t::iterator it;

// insert(size_type index, size_type count, char ch)
r = s.insert(0, 1, 'E');
EXPECT_EQ("Exmplr", s);

// insert(size_type index, const char* s)
r = s.insert(2, "e");
EXPECT_EQ("Exemplr", s);

// insert(size_type index, string const& str)
r = s.insert(6, "a"s);
EXPECT_EQ("Exemplar", s);

// insert(size_type index, string const& str,
// size_type index_str, size_type count)
r = s.insert(8, " is an example string."s, 0, 14);
EXPECT_EQ("Exemplar is an example", s);

// insert(const_iterator pos, char ch)
it = s.insert(s.begin() + s.find_first_of('n') + 1, ':');
EXPECT_EQ("Exemplar is an: example", s);

// insert(const_iterator pos, size_type count, char ch)
//TODO should return type::iterator on strings, remove if
// sequence_concept_modified_by_const_iterator_bug is no issue anymore
// it =
s.insert(s.begin() + s.find_first_of(':') + 1, 2, '=');
EXPECT_EQ("Exemplar is an:== example", s);

// insert(const_iterator pos, InputIt first, InputIt last)
{
string_t seq = " string";
//TODO should return type::iterator on strings, remove if
// sequence_concept_modified_by_const_iterator_bug is no issue anymore
// it =
s.insert(s.begin() + s.find_last_of('e') + 1,
std::begin(seq), std::end(seq));
EXPECT_EQ("Exemplar is an:== example string", s);
}

// insert(const_iterator pos, std::initializer_list<char>)
//TODO should return type::iterator on strings (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=83328)
// it =
s.insert(s.begin() + s.find_first_of('g') + 1, { '.' });
EXPECT_EQ("Exemplar is an:== example string.", s);
}

template <typename string_t>
void container_concept_const_travis_bug_test()
{
// travis failed on this statement
// concept bool sequence_concept = requires (type val, type val2)
// ^~~~~~~~~~~~~~~~
// /include/seqan3/range/container/concept.hpp:113:14: note: with ‘std::basic_string<char> val’
// /include/seqan3/range/container/concept.hpp:113:14: note: with ‘std::basic_string<char> val2’
// [...]
// /include/seqan3/range/container/concept.hpp:113:14: note: the required expression ‘val.erase(val.cbegin(), val.cend())’ would be ill-formed

using namespace std::string_literals;

static_assert(detail::sequence_concept_modified_by_const_iterator<string_t>);
static_assert(!detail::sequence_concept_modified_by_const_iterator_bug<string_t> && std::is_same_v<string_t, std::string>);
static_assert(seqan3::container_concept<string_t>);

// example code from http://en.cppreference.com/w/cpp/string/basic_string/insert
string_t s = "xmplr";

// insert(size_type index, size_type count, char ch)
s.insert(0, 1, 'E');
EXPECT_EQ("Exmplr", s);

// insert(size_type index, const char* s)
s.insert(2, "e");
EXPECT_EQ("Exemplr", s);

// insert(size_type index, string const& str)
s.insert(6, "a"s);
EXPECT_EQ("Exemplar", s);

// insert(size_type index, string const& str,
// size_type index_str, size_type count)
s.insert(8, " is an example string."s, 0, 14);
EXPECT_EQ("Exemplar is an example", s);

// insert(const_iterator pos, char ch)
s.insert(s.cbegin() + s.find_first_of('n') + 1, ':');
EXPECT_EQ("Exemplar is an: example", s);

// insert(const_iterator pos, size_type count, char ch)
s.insert(s.cbegin() + s.find_first_of(':') + 1, 2, '=');
EXPECT_EQ("Exemplar is an:== example", s);

// insert(const_iterator pos, InputIt first, InputIt last)
{
string_t seq = " string";
s.insert(s.cbegin() + s.find_last_of('e') + 1,
std::begin(seq), std::end(seq));
EXPECT_EQ("Exemplar is an:== example string", s);
}

// insert(const_iterator pos, std::initializer_list<char>)
s.insert(s.cbegin() + s.find_first_of('g') + 1, { '.' });
EXPECT_EQ("Exemplar is an:== example string.", s);
}

TEST(container_concept, container_concept_travis_bug)
{
container_concept_travis_bug_test<std::string>();

if constexpr(!detail::sequence_concept_modified_by_const_iterator_bug<>)
container_concept_const_travis_bug_test<std::string>();
}

TEST(container_concept, sequence_concept)
Expand All @@ -63,6 +213,25 @@ TEST(container_concept, sequence_concept)
EXPECT_TRUE((seqan3::sequence_concept<std::vector<char>>));
EXPECT_TRUE((seqan3::sequence_concept<std::deque<char>>));
EXPECT_TRUE((seqan3::sequence_concept<std::string>));

EXPECT_TRUE((seqan3::sequence_concept<concatenated_sequences_string_t>));
EXPECT_TRUE((seqan3::sequence_concept<seqan3::concatenated_sequences<std::vector<char>>>));
}

TEST(container_concept, sequence_concept_modified_by_const_iterator)
{
EXPECT_FALSE((seqan3::detail::sequence_concept_modified_by_const_iterator<std::array<char, 2>>));
EXPECT_TRUE((seqan3::detail::sequence_concept_modified_by_const_iterator<std::list<char>>));
EXPECT_FALSE((seqan3::detail::sequence_concept_modified_by_const_iterator<std::forward_list<char>>));
EXPECT_TRUE((seqan3::detail::sequence_concept_modified_by_const_iterator<std::vector<char>>));
EXPECT_TRUE((seqan3::detail::sequence_concept_modified_by_const_iterator<std::deque<char>>));
if constexpr(!detail::sequence_concept_modified_by_const_iterator_bug<>)
{
EXPECT_TRUE((seqan3::detail::sequence_concept_modified_by_const_iterator<std::string>));
}

EXPECT_TRUE((seqan3::detail::sequence_concept_modified_by_const_iterator<concatenated_sequences_string_t>));
EXPECT_TRUE((seqan3::detail::sequence_concept_modified_by_const_iterator<seqan3::concatenated_sequences<std::vector<char>>>));
}

TEST(container_concept, random_access_sequence_concept)
Expand All @@ -73,6 +242,9 @@ TEST(container_concept, random_access_sequence_concept)
EXPECT_TRUE((seqan3::random_access_sequence_concept<std::vector<char>>));
EXPECT_TRUE((seqan3::random_access_sequence_concept<std::deque<char>>));
EXPECT_TRUE((seqan3::random_access_sequence_concept<std::string>));

EXPECT_TRUE((seqan3::random_access_sequence_concept<concatenated_sequences_string_t>));
EXPECT_TRUE((seqan3::random_access_sequence_concept<seqan3::concatenated_sequences<std::vector<char>>>));
}

TEST(container_concept, reservable_sequence_concept)
Expand All @@ -83,6 +255,9 @@ TEST(container_concept, reservable_sequence_concept)
EXPECT_TRUE((seqan3::reservable_sequence_concept<std::vector<char>>));
EXPECT_FALSE((seqan3::reservable_sequence_concept<std::deque<char>>));
EXPECT_TRUE((seqan3::reservable_sequence_concept<std::string>));

EXPECT_TRUE((seqan3::reservable_sequence_concept<concatenated_sequences_string_t>));
EXPECT_TRUE((seqan3::reservable_sequence_concept<seqan3::concatenated_sequences<std::vector<char>>>));
}

/* Check the SDSL containers */
Expand Down

0 comments on commit 0d98402

Please sign in to comment.