spirit/classic/test/position_iterator_tests.cpp
Nikita Kniazev d3e14ccf04 Classic: Fixed position_iterator forming reference to local
When underlying iterator returns by value the corresponding position_iterator
was providing iterator_adaptor with a reference type what lead to returning
a reference to a local variable in dereference operator.

Made a custom trait because it is a way more elegant than:
```cpp
    typedef typename boost::mpl::if_c<
        boost::is_reference<reference>::value,
        typename boost::add_reference<
            typename boost::add_const<
                typename boost::remove_reference<reference>::type
            >::type
        >::type,
        typename boost::add_const<reference>::type
    >::type const_reference_type;
```

Fixes https://svn.boost.org/trac10/ticket/9737.
2018-11-23 03:19:03 +03:00

584 lines
16 KiB
C++

/*=============================================================================
Copyright (c) 2003 Giovanni Bajo
http://spirit.sourceforge.net/
Use, modification and distribution is subject to the Boost Software
License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
http://www.boost.org/LICENSE_1_0.txt)
=============================================================================*/
#include <boost/detail/lightweight_test.hpp>
#include <iostream>
#include <vector>
#include <string>
#include <list>
#include <algorithm>
#include <iterator>
#include <cstddef>
#include <boost/config.hpp>
#include <boost/concept_check.hpp>
#include <boost/mpl/if.hpp>
#include <boost/mpl/list.hpp>
#include <boost/mpl/for_each.hpp>
// Our baby
#include <boost/spirit/include/classic_position_iterator.hpp>
using namespace std;
using namespace BOOST_SPIRIT_CLASSIC_NS;
namespace mpl = boost::mpl;
///////////////////////////////////////////////////////////////////////////////
namespace test_impl {
template <typename IterT>
void InstanciateTestOne(void)
{
IterT();
// Check that the iterator is a full non-mutable forward iterator
typedef boost::ForwardIteratorConcept<IterT> concept_t;
boost::function_requires<concept_t>();
}
struct InstanciateTest
{
template <typename BaseIterT>
void operator()(BaseIterT )
{
InstanciateTestOne<position_iterator<BaseIterT> >();
InstanciateTestOne<position_iterator2<BaseIterT> >();
InstanciateTestOne<position_iterator<BaseIterT, file_position_without_column> >();
InstanciateTestOne<position_iterator2<BaseIterT, file_position_without_column> >();
}
};
///////////////////////////////////////////////////////////////////////////////
} /* namespace test_impl */
// These tests are defined after main() to be absolutely sure that the
// instantiation test will happen before any other (since it's mainly
// a compile-time test).
void CheckInstantiation(void);
void CheckConstructors(void);
void CheckBasicFunctionality(void);
void CheckColumnCounting(void);
void CheckLineExtraction(void);
void CheckDistance(void);
void CheckSingular();
void CheckInstantiation(void)
{
typedef mpl::list
<
char*
,const char*
,string::iterator
,string::const_iterator
> iter_list_t;
mpl::for_each<iter_list_t>(test_impl::InstanciateTest());
}
int main(void)
{
CheckInstantiation();
CheckConstructors();
CheckBasicFunctionality();
CheckColumnCounting();
CheckLineExtraction();
CheckDistance();
CheckSingular();
return boost::report_errors();
}
///////////////////////////////////////////////////////////////////////////////
namespace test_impl {
template <typename IterT>
void CheckIncrement(IterT iter)
{
IterT end;
// Check also that copy construction and assignment do not
// interfere with increment
IterT iter2(iter);
IterT iter3 = iter;
BOOST_TEST(iter != end);
BOOST_TEST(iter2 != end);
BOOST_TEST(iter3 != end);
BOOST_TEST(*iter == '0');
++iter;
++iter2;
++iter3;
BOOST_TEST(iter == iter2);
BOOST_TEST(iter == iter3);
BOOST_TEST(*iter == *iter2);
BOOST_TEST(*iter == *iter3);
BOOST_TEST(iter.get_position() == iter2.get_position());
BOOST_TEST(iter.get_position() == iter3.get_position());
BOOST_TEST(*iter == '1');
BOOST_TEST(*iter++ == '1');
BOOST_TEST(*iter2++ == '1');
BOOST_TEST(*iter3++ == '1');
BOOST_TEST(*iter == *iter2);
BOOST_TEST(*iter == *iter3);
BOOST_TEST(iter.get_position() == iter2.get_position());
BOOST_TEST(iter.get_position() == iter3.get_position());
BOOST_TEST(*iter == '2');
++iter; ++iter; ++iter; ++iter; ++iter; ++iter; ++iter;
BOOST_TEST(*iter == '9');
++iter;
BOOST_TEST(iter == end);
// Check that one after end is no more end
++iter;
BOOST_TEST(iter != end);
}
template <typename IterT>
void CheckLineCounting(IterT iter)
{
IterT end;
BOOST_TEST(*iter == '\n');
BOOST_TEST(iter.get_position().line == 1);
++iter; // 0
BOOST_TEST(iter.get_position().line == 2);
++iter; // 1
++iter; // 2
++iter; // 3
++iter; // \r
BOOST_TEST(*iter == '\r');
BOOST_TEST(iter.get_position().line == 2);
++iter; // \n
BOOST_TEST(*iter == '\n');
BOOST_TEST(iter.get_position().line == 2);
++iter; // 4
BOOST_TEST(iter.get_position().line == 3);
++iter; // 5
++iter; // 6
++iter; // 7
++iter; // \n
BOOST_TEST(*iter == '\n');
BOOST_TEST(iter.get_position().line == 3);
++iter; // 8
BOOST_TEST(iter.get_position().line == 4);
++iter; // 9
++iter; // \n
BOOST_TEST(iter.get_position().line == 4);
BOOST_TEST(*iter == '\n');
++iter; // \r
BOOST_TEST(iter.get_position().line == 5);
BOOST_TEST(*iter == '\r');
++iter; // end
BOOST_TEST(iter.get_position().line == 6);
BOOST_TEST(iter == end);
}
template <typename IterT>
void CheckColumnCounting_Tab4(IterT iter)
{
IterT end;
// Don't call set_tabchars() here because
// default must be 3.
BOOST_TEST(*iter == '\t');
BOOST_TEST(iter.get_position().column == 1);
++iter; // 0
BOOST_TEST(iter.get_position().column == 5);
++iter; // 1
BOOST_TEST(iter.get_position().column == 6);
++iter; // 2
BOOST_TEST(iter.get_position().column == 7);
++iter; // 3
BOOST_TEST(iter.get_position().column == 8);
++iter; // tab
BOOST_TEST(*iter == '\t');
BOOST_TEST(iter.get_position().column == 9);
++iter; // 4
BOOST_TEST(iter.get_position().column == 13);
++iter; // tab
BOOST_TEST(*iter == '\t');
BOOST_TEST(iter.get_position().column == 14);
++iter; // 5
BOOST_TEST(iter.get_position().column == 17);
++iter; // tab
BOOST_TEST(*iter == '\t');
BOOST_TEST(iter.get_position().column == 18);
++iter; // end
BOOST_TEST(iter == end);
}
template <typename IterT>
void CheckColumnCounting_Tab3(IterT iter)
{
IterT end;
iter.set_tabchars(3);
// Check also that tab settings propagates through
// assignment and copy construction
IterT iter2(iter);
IterT iter3; iter3 = iter2;
BOOST_TEST(*iter == '\t');
BOOST_TEST(iter.get_position().column == 1);
++iter; // 0
++iter2; ++iter3;
BOOST_TEST(iter.get_position().column == 4);
BOOST_TEST(iter2.get_position().column == 4);
BOOST_TEST(iter3.get_position().column == 4);
++iter; // 1
BOOST_TEST(iter.get_position().column == 5);
++iter; // 2
BOOST_TEST(iter.get_position().column == 6);
++iter; // 3
BOOST_TEST(iter.get_position().column == 7);
++iter; // tab
BOOST_TEST(*iter == '\t');
BOOST_TEST(iter.get_position().column == 8);
++iter; // 4
BOOST_TEST(iter.get_position().column == 10);
++iter; // tab
BOOST_TEST(*iter == '\t');
BOOST_TEST(iter.get_position().column == 11);
++iter; // 5
BOOST_TEST(iter.get_position().column == 13);
++iter; // tab
BOOST_TEST(*iter == '\t');
BOOST_TEST(iter.get_position().column == 14);
++iter; // end
BOOST_TEST(iter == end);
}
const string line1 = "abcd";
const string line2 = "efgh";
const string linebuf = "\n" + line1 + "\n" + line2 + "\n";
template <typename IterT>
void AssertIterString(IterT begin, IterT end, string s)
{
BOOST_TEST(string(begin, end) == s);
}
template <typename IterT>
void CheckLineExtractionOne(IterT iter)
{
IterT end;
// At the start, we are on a newline, which is an empty
// string
BOOST_TEST(iter.get_currentline() == string());
BOOST_TEST(
string(iter.get_currentline_begin(), iter.get_currentline_end())
== string());
++iter; // a
++iter; // b
++iter; // c
BOOST_TEST(iter.get_currentline() == line1);
AssertIterString(
iter.get_currentline_begin(),
iter.get_currentline_end(),
line1);
++iter; // d
++iter; // newline
++iter; // e
// check that copy construction and assignment do
// not interfere with get_currentline
IterT iter2(iter);
IterT iter3; iter3 = iter;
BOOST_TEST(iter2.get_currentline() == line2);
BOOST_TEST(iter3.get_currentline() == line2);
AssertIterString(
iter2.get_currentline_begin(),
iter2.get_currentline_end(),
line2);
AssertIterString(
iter3.get_currentline_begin(),
iter3.get_currentline_end(),
line2);
++iter; // f
++iter; // g
++iter; // h
++iter; // newline
// Check when the iterator is on a newline
BOOST_TEST(iter.get_currentline() == line2);
AssertIterString(
iter.get_currentline_begin(),
iter.get_currentline_end(),
line2);
++iter;
BOOST_TEST(iter == end);
}
void CheckLineExtraction(void)
{
typedef string::const_iterator iter_t;
CheckLineExtractionOne(
position_iterator2<iter_t, file_position>
(linebuf.begin(), linebuf.end(), ""));
CheckLineExtractionOne(
position_iterator2<iter_t, file_position_without_column>
(linebuf.begin(), linebuf.end(), ""));
}
template <typename IterT>
void CheckEmptySequence(void)
{
typedef IterT iter_t;
char a[10];
// Check construction with empty sequence, and
// correct propagation of the information
iter_t iter(a,a, "");
iter_t iter2(iter);
iter_t iter3; iter3 = iter;
BOOST_TEST(iter == iter_t());
BOOST_TEST(iter2 == iter_t());
BOOST_TEST(iter3 == iter_t());
}
template <typename IterC, typename Iter>
void CheckConstructors(void)
{
char a[20];
std::string name = "abc";
file_position_without_column pos(name,1);
file_position posc(name,1,1);
typedef IterC iterc_t;
typedef Iter iter_t;
BOOST_TEST(iterc_t(a,a+20,name).get_position() == posc);
BOOST_TEST(iterc_t(a,a+20,name,1).get_position() == posc);
BOOST_TEST(iterc_t(a,a+20,name,1,1).get_position() == posc);
BOOST_TEST(iterc_t(a,a+20,posc).get_position() == posc);
BOOST_TEST(iter_t(a,a+20,name).get_position() == pos);
BOOST_TEST(iter_t(a,a+20,name,1).get_position() == pos);
BOOST_TEST(iter_t(a,a+20,pos).get_position() == pos);
// Check copy constructor and assignment. Notice that we want
// an implicit copy constructor.
iterc_t ic1(a,a+20,name);
iterc_t ic2 = ic1;
iterc_t ic3; ic3 = ic1;
BOOST_TEST(ic1 == ic2);
BOOST_TEST(ic1 == ic3);
BOOST_TEST(ic1.get_position() == ic2.get_position());
BOOST_TEST(ic1.get_position() == ic3.get_position());
iter_t i1(a,a+20,name);
iter_t i2 = i1;
iter_t i3; i3 = i1;
BOOST_TEST(i1 == i2);
BOOST_TEST(i1 == i3);
BOOST_TEST(i1.get_position() == i2.get_position());
BOOST_TEST(i1.get_position() == i3.get_position());
// Check construction with an empty sequence
CheckEmptySequence<iter_t>();
CheckEmptySequence<iterc_t>();
}
template <typename IterT>
void CheckDistance(IterT begin)
{
IterT end;
std::size_t std_distance = std::distance(begin, end);
std::size_t manual_count = 0;
for(IterT it = begin; it != end; ++it)
++manual_count;
BOOST_TEST(std_distance == manual_count);
}
///////////////////////////////////////////////////////////////////////////////
} /* namespace test_impl */
void CheckConstructors(void)
{
test_impl::CheckConstructors
<
position_iterator<char*, file_position>,
position_iterator<char*, file_position_without_column>
>();
test_impl::CheckConstructors
<
position_iterator2<char*, file_position>,
position_iterator2<char*, file_position_without_column>
>();
}
void CheckBasicFunctionality(void)
{
const char* a = "0123456789";
typedef const char* iter_t;
test_impl::CheckIncrement(position_iterator<iter_t>(a, a+10, ""));
test_impl::CheckIncrement(position_iterator2<iter_t>(a, a+10, ""));
test_impl::CheckIncrement(position_iterator<iter_t, file_position_without_column>(a, a+10, ""));
test_impl::CheckIncrement(position_iterator2<iter_t, file_position_without_column>(a, a+10, ""));
const char* b = "\n0123\r\n4567\n89\n\r";
test_impl::CheckLineCounting(position_iterator<iter_t>(b, b+16, ""));
test_impl::CheckLineCounting(position_iterator2<iter_t>(b, b+16, ""));
test_impl::CheckLineCounting(position_iterator<iter_t, file_position_without_column>(b, b+16, ""));
test_impl::CheckLineCounting(position_iterator2<iter_t, file_position_without_column>(b, b+16, ""));
}
void CheckColumnCounting(void)
{
const char* a = "\t0123\t4\t5\t";
typedef const char* iter_t;
test_impl::CheckColumnCounting_Tab4(position_iterator<iter_t>(a, a+10, ""));
test_impl::CheckColumnCounting_Tab4(position_iterator2<iter_t>(a, a+10, ""));
test_impl::CheckColumnCounting_Tab3(position_iterator<iter_t>(a, a+10, ""));
test_impl::CheckColumnCounting_Tab3(position_iterator2<iter_t>(a, a+10, ""));
}
void CheckLineExtraction(void)
{
test_impl::CheckLineExtraction();
}
void CheckDistance(void)
{
const char* b = "\n0123\r\n4567\n89\n\r";
typedef const char* iter_t;
test_impl::CheckDistance(position_iterator<iter_t>(b, b+15, ""));
test_impl::CheckDistance(position_iterator2<iter_t>(b, b+15, ""));
test_impl::CheckDistance(position_iterator<iter_t, file_position_without_column>(b, b+15, ""));
test_impl::CheckDistance(position_iterator2<iter_t, file_position_without_column>(b, b+15, ""));
}
///////////////////////////////////////////////////////////////////////////////
namespace test_impl {
template <bool AsValue = false>
class check_singular_iterator
{
bool singular_;
int count_;
public:
typedef std::forward_iterator_tag iterator_category;
typedef int value_type;
typedef std::ptrdiff_t difference_type;
typedef int const* pointer;
typedef typename boost::mpl::if_c<AsValue, int, int const&>::type reference;
check_singular_iterator() : singular_(true), count_(0) {}
explicit check_singular_iterator(int x) : singular_(false), count_(x) {}
reference operator*() const {
BOOST_TEST(!singular_);
return count_;
}
pointer operator->() const {
BOOST_TEST(!singular_);
return &count_;
}
check_singular_iterator& operator++() {
BOOST_TEST(count_ > 0);
--count_;
return *this;
}
check_singular_iterator operator++(int) {
check_singular_iterator tmp(*this);
++(*this);
return tmp;
}
bool operator==(check_singular_iterator const& other) const {
BOOST_TEST(!singular_ && !other.singular_);
return count_ == other.count_;
}
bool operator!=(check_singular_iterator const& other) const {
return !(*this == other);
}
};
template <typename CountIterator, typename Iterator>
void CheckSingularImpl()
{
CountIterator begin(Iterator(5), Iterator(0));
CountIterator end1(Iterator(0), Iterator(0));
CountIterator end2;
BOOST_TEST(begin == begin);
BOOST_TEST(begin != end1);
BOOST_TEST(begin != end2);
BOOST_TEST(end1 != begin);
BOOST_TEST(end1 == end1);
BOOST_TEST(end1 == end2);
BOOST_TEST(end2 != begin);
BOOST_TEST(end2 == end1);
BOOST_TEST(end2 == end2);
BOOST_TEST(std::distance(begin, begin) == 0);
BOOST_TEST(std::distance(begin, end1) == 5);
BOOST_TEST(std::distance(begin, end2) == 5);
BOOST_TEST(std::distance(end1, end1) == 0);
BOOST_TEST(std::distance(end1, end2) == 0);
BOOST_TEST(std::distance(end2, end1) == 0);
BOOST_TEST(std::distance(end2, end2) == 0);
BOOST_TEST(*begin == 5);
}
template <typename PositionT>
void CheckSingular()
{
{
typedef check_singular_iterator<false> interator_type;
CheckSingularImpl<position_iterator<interator_type, PositionT>, interator_type>();
CheckSingularImpl<position_iterator2<interator_type, PositionT>, interator_type>();
}
{
typedef check_singular_iterator<true> interator_type;
CheckSingularImpl<position_iterator<interator_type, PositionT>, interator_type>();
CheckSingularImpl<position_iterator2<interator_type, PositionT>, interator_type>();
}
}
}
void CheckSingular()
{
test_impl::CheckSingular<file_position>();
test_impl::CheckSingular<file_position_without_column>();
}