Ticket #6126: test_signed_integer_output_with_karma_minimal.cpp

File test_signed_integer_output_with_karma_minimal.cpp, 5.2 KB (added by t0rt1e@…, 11 years ago)

Minimal test case making not use of Boost.unit_test features to compile cleanly against Boost svn trunk

Line 
1/*
2 * test_signed_integer_output_with_karma_minimal -- Minimal test case
3 * exposing problem in Boost.Spirit.Karma's signed integer output
4 * generators from Boost.Fusion adapted ADTs.
5 *
6 * Copyright (C) 2011 Torsten Maehne
7 *
8 * Distributed under the Boost Software License, Version 1.0.
9 * (See accompanying file LICENSE_1_0.txt or copy at
10 * http://www.boost.org/LICENSE_1_0.txt)
11 */
12
13/*!
14 * \file test_signed_integer_output_with_karma_minimal.cpp
15 *
16 * \brief Minimal test of signed integer output from Boost.Fusion
17 * adapted ADTs and structs with Boost.Spirit.Karma rules.
18 *
19 * Boost.Spirit.Karma output rules should show the same behavior for
20 * Boost.Fusion-adapted structs (direct access of the public member
21 * variables) and ADTs (access to the private member variables via
22 * getters and setters). This has been the case in Boost 1.44 and is
23 * demonstrated in this test case for rational number ADTs and
24 * structs, which use signed integer member variables.
25 *
26 * Compilation of this test case against Boost 1.45.0 and 1.46.1 will
27 * fail. Compilation against Boost 1.47.0 and 1.48.0 will succeed, but
28 * the output of negative values of signed integer members of
29 * Boost.Fusion-adapted ADTs will be wrong. The minus sign is output
30 * correctly, but it is followed by a wrong value, which seems to be
31 * yielded by a cast from a signed to an unsigned integer value
32 * instead of taking the absolute value of the signed integer
33 * value. This has been observed on Mac OS X 10.7.2 (x86_64) with
34 * Xcode 4.2 using Apple's g++ 4.2.1, Apple's clang++
35 * 3.0. Interestingly, compiling the test case against Boost 1.47.0 or
36 * Boost 1.48.0 using MacPorts g++ 4.5.3, will yield a different wrong
37 * output.
38 *
39 * Compilation against Boost from the Subversion trunk (rev. 76203)
40 * succeeds, but yields the same wrong and compiler-dependent output.
41 *
42 * \author Torsten Maehne
43 * \date 2011-12-28
44 * \version $Id$
45 */
46#include <iostream>
47#include <sstream>
48#include <boost/rational.hpp>
49#include <boost/version.hpp>
50#if BOOST_VERSION < 104500
51// Until Boost 1.44 use BOOST_FUSION_ADAPT_CLASS
52#include <boost/fusion/include/adapt_class.hpp>
53#else
54// Since Boost 1.45 BOOST_FUSION_ADAPT_CLASS is named BOOST_FUSION_ADAPT_ADT
55#include <boost/fusion/include/adapt_adt.hpp>
56#include <boost/spirit/include/support_adapt_adt_attributes.hpp>
57#endif
58
59#include <boost/fusion/include/adapt_struct.hpp>
60// Include only the strictly necessary parts of Boost.Spirit.Karma to reduce compile time.
61#include <boost/spirit/include/karma_char.hpp>
62#include <boost/spirit/include/karma_nonterminal.hpp>
63#include <boost/spirit/include/karma_numeric.hpp>
64#include <boost/spirit/include/karma_operator.hpp>
65#include <boost/spirit/include/karma_generate.hpp>
66
67
68namespace {
69
70//! Rational number ADT with signed integer members.
71typedef boost::rational<int> rational_int_adt;
72
73//! Rational number struct with signed integer members.
74struct rational_int_struct {
75 int numerator, denominator;
76
77 explicit rational_int_struct(int num = 0, int den = 1)
78 : numerator(num), denominator(den)
79 {}
80};
81
82} // anonymous namespace
83
84#if BOOST_VERSION < 104500
85// Adapt class boost::rational<int> to a Fusion sequence.
86BOOST_FUSION_ADAPT_CLASS(
87 ::rational_int_adt,
88 (int, int, obj.numerator(), obj.numerator(val))
89 (int, int, obj.denominator(), obj.denominator(val))
90)
91#else
92// Adapt ADT boost::rational<int> to a Fusion sequence.
93BOOST_FUSION_ADAPT_ADT(
94 ::rational_int_adt,
95 (int, int, obj.numerator(), obj.numerator(val))
96 (int, int, obj.denominator(), obj.denominator(val))
97)
98#endif
99
100// Adapt struct ::rational_int_struct to a Fusion sequence.
101BOOST_FUSION_ADAPT_STRUCT(
102 ::rational_int_struct,
103 (int, numerator)
104 (int, denominator)
105)
106
107
108int main(int argc, char* argv[]) {
109 // Namespace alias for Boost Spirit Karma.
110 namespace karma = boost::spirit::karma;
111 // Namespace alias for Boost Spirit Phoenix.
112 namespace phx = boost::phoenix;
113
114 using karma::int_;
115 using boost::spirit::karma::generate;
116 using std::cout;
117 using std::endl;
118 typedef std::ostream_iterator<char> ostr_iter_type;
119
120 // Output iterator type used as sink for the Boost.Spirit.Karma
121 // generator to the output test stream.
122 ostr_iter_type ostr_iter(cout);
123
124 //! Check the Karma generator rules for ::rational_int_adt.
125 karma::rule<ostr_iter_type, rational_int_adt()> rational_int_adt_;
126 rational_int_adt_.name("rational_int_adt_");
127 rational_int_adt_ = int_ << '/' << int_;
128 {
129 cout << "rational_int_adt(2, -6) = ";
130 generate(ostr_iter, rational_int_adt_, rational_int_adt(2, -6));
131 cout << " (expected: -1/3)" << endl;
132 }
133 {
134 cout << "rational_int_adt(-2, 4) = ";
135 // Maybe generate doesn't like temporaries?
136 rational_int_adt rat(-2, 4);
137 generate(ostr_iter, rational_int_adt_, rat);
138 cout << " (expected: -1/2)" << endl;
139 }
140
141 //! Check the Karma generator rules for ::rational_int_struct.
142 karma::rule<ostr_iter_type, rational_int_struct()> rational_int_struct_;
143 rational_int_struct_.name("rational_int_struct_");
144 rational_int_struct_ = int_ << '/' << int_;
145 {
146 cout << "rational_int_struct(2, -6) = ";
147 generate(ostr_iter, rational_int_struct_, rational_int_struct(2, -6));
148 cout << " (expected: 2/-6)" << endl;
149 }
150 return 0;
151}