1 /*
2     Copyright (C) 2013 Tom Bachmann
3 
4     This file is part of FLINT.
5 
6     FLINT is free software: you can redistribute it and/or modify it under
7     the terms of the GNU Lesser General Public License (LGPL) as published
8     by the Free Software Foundation; either version 2.1 of the License, or
9     (at your option) any later version.  See <http://www.gnu.org/licenses/>.
10 */
11 
12 /*
13      Demo FLINTXX header to illustrate flintxx extension.
14 */
15 
16 ///////////////////////////////////////////////////////////////////////////////
17 // FAKE C DATA TYPE
18 // (This would normally reside in foo.h.)
19 ///////////////////////////////////////////////////////////////////////////////
20 
21 #ifndef FOO_H
22 #define FOO_H
23 #include <stdio.h>
24 
25 extern "C" { // usually only #ifdef __cplusplus etc
26 typedef slong foo;
27 typedef slong foo_t[1];
28 
foo_init(foo_t f)29 static __inline__ void foo_init(foo_t f)
30 {
31     *f = 0l;
32 }
33 
foo_clear(foo_t f)34 static __inline__ void foo_clear(foo_t f)
35 {
36 }
37 
foo_set(foo_t to,const foo_t from)38 static __inline__ void foo_set(foo_t to, const foo_t from)
39 {
40     *to = *from;
41 }
42 
foo_set_si(foo_t f,slong e)43 static __inline__ void foo_set_si(foo_t f, slong e)
44 {
45     *f = e;
46 }
47 
foo_add(foo_t to,const foo_t e1,const foo_t e2)48 static __inline__ void foo_add(foo_t to, const foo_t e1, const foo_t e2)
49 {
50     *to = *e1 + *e2;
51 }
52 
foo_add_si(foo_t to,const foo_t e1,slong e2)53 static __inline__ void foo_add_si(foo_t to, const foo_t e1, slong e2)
54 {
55     *to = *e1 + e2;
56 }
57 
foo_cmp(const foo_t e1,const foo_t e2)58 static __inline__ int foo_cmp(const foo_t e1, const foo_t e2)
59 {
60     if(*e1 == *e2)
61         return 0;
62     return *e1 > *e2 ? 1 : -1;
63 }
64 
foo_is_zero(const foo_t f)65 static __inline__ int foo_is_zero(const foo_t f)
66 {
67     return *f == 0;
68 }
69 
foo_magic(foo_t to,const foo_t from)70 static __inline__ void foo_magic(foo_t to, const foo_t from)
71 {
72     *to = 2 * (*from) + 1;
73 }
74 }
75 
76 #endif
77 
78 ///////////////////////////////////////////////////////////////////////////////
79 // C++ wrapper
80 // (This would normally reside in fooxx.h.)
81 ///////////////////////////////////////////////////////////////////////////////
82 
83 #ifndef FOOXX_H
84 #define FOOXX_H
85 
86 #include <iostream>
87 
88 #include "flintxx/expression.h"
89 #include "flintxx/flint_classes.h"
90 
91 namespace flint {
92 // fooxx_expression is an "all-purpose" expression template class. In
93 // principle, both Operation and Data can be arbitrary types (Data has to be
94 // copy constructible), but in this generality the objects will be not much
95 // use. In practice, Operation is an empty type, which is just used as a "tag",
96 // and Data is a rather primitive type holding essentially just some payload.
97 // Even more practically speaking, the only instantiations the FLINT developer
98 // should have have to make explicitly are when Operation is
99 // operations::immediate.
100 // The flintxx library will create other instantiations automatically, with
101 // more complicated Data arguments, and different Operation-s.
102 template<class Operation, class Data>
103 class fooxx_expression
104 
105 // In order for the flintxx library to do its work, your class must derive from
106 // flint::expression. If your class has just two template parameters Operation
107 // and Data, then the following line is sufficient.
108     : public expression<derived_wrapper<fooxx_expression>, Operation, Data>
109 {
110 public:
111 
112     // This line is formulaic, and just makes the base class available.
113     // The typedef is used by the FLINTXX_DEFINE_* macros below, and is
114     // necessary because of namespace injection bugs in gcc<4.5.
115     typedef expression<derived_wrapper< ::flint::fooxx_expression>,
116               Operation, Data> base_t;
117 
118     // The next two lines are formulaic, and most likely required in any
119     // concrete class.
120     FLINTXX_DEFINE_BASICS(fooxx_expression)
FLINTXX_DEFINE_CTORS(fooxx_expression)121     FLINTXX_DEFINE_CTORS(fooxx_expression)
122 
123     // This line enables reference types for your class. The second argument is
124     // the underlying C type (note this is foo, not foo_t). The third argument
125     // is the name under which to make the underlying C type available.
126     // All of fooxx, fooxx_ref and fooxx_srcref will have methods _foo() which
127     // can be used to manipulate the underlying C data type.
128     FLINTXX_DEFINE_C_REF(fooxx_expression, foo, _foo)
129 
130     // Now custom methods can be added. The typical pattern is to call a C
131     // function with argument this->evaluate()._foo(). The evaluate() method is
132     // inherited from the expression class (this is why it needs to be
133     // qualified by "this"). It obtains a reference to self if self is an
134     // immediate object, and otherwise evaluates self into a temporary
135     // immediate object.
136     // If you leave out the evaluate() step, then the method will only work
137     // on immediates (which may be desirable).
138     bool is_zero() const {return foo_is_zero(this->evaluate()._foo());}
139 };
140 
141 // This is formulaic. The class fooxx will be an instantiation of
142 // fooxx_expression, with Operation operations::immediate and Data
143 // detail::foo_data. We need to forward-declare this because of cyclic
144 // dependencies among the immediate types (e.g. fooxx_srcref can be
145 // constructed from fooxx, and vice versa).
146 namespace detail {
147 struct foo_data;
148 }
149 
150 // This line just carries out the plan of definition of fooxx explained above.
151 typedef fooxx_expression<operations::immediate, detail::foo_data> fooxx;
152 
153 // If you want reference types (i.e. if you had FLINTXX_DEFINE_C_REF above),
154 // these lines are again formulaic.
155 typedef fooxx_expression<operations::immediate,
156             flint_classes::ref_data<fooxx, foo> > fooxx_ref;
157 typedef fooxx_expression<operations::immediate,
158             flint_classes::srcref_data<fooxx, fooxx_ref, foo> > fooxx_srcref;
159 
160 namespace detail {
161 
162 // We now define the actual immediate Data type. This is not just foo_t (the
163 // underlying C data type), because want it to behave nicely "in a C++ world".
164 struct foo_data
165 {
166     // In general, your data type can contain members and member types in any
167     // way you want. However, to work with the automatic reference type system,
168     // the following three lines are necessary.
169     foo_t inner;
170     typedef foo_t& data_ref_t;
171     typedef const foo_t& data_srcref_t;
172 
173     // Default constructor. If this is not provided, fooxx will not be default
174     // constructible (this is OK but requires some additional care, see e.g.
175     // padicxx).
foo_dataflint::detail::foo_data176     foo_data() {foo_init(inner);}
177 
178     // Destructor. You most likely want this.
~foo_dataflint::detail::foo_data179     ~foo_data() {foo_clear(inner);}
180 
181     // Copy constructor. You must provide this.
foo_dataflint::detail::foo_data182     foo_data(const foo_data& o)
183     {
184         foo_init(inner);
185         foo_set(inner, o.inner);
186     }
187 
188     // Instantiation from srcref. This is basically the same as the copy,
189     // constructor, but unfortunately has to be repeated. This also takes care
190     // of instantiation from ref, since ref->srcref is an implicit conversion
191     // path.
foo_dataflint::detail::foo_data192     foo_data(fooxx_srcref r)
193     {
194         foo_init(inner);
195         foo_set(inner, r._foo());
196     }
197 
198     // Now you can add more constructors, or in fact any methods you like.
199     // This one allows constructing fooxx directly from long, int,
200     // unsigned short etc.
201     template<class T>
foo_dataflint::detail::foo_data202     foo_data(T t,
203             typename mp::enable_if<traits::fits_into_slong<T> >::type* = 0)
204     {
205         foo_init(inner);
206         foo_set_si(inner, t);
207     }
208 };
209 } // detail
210 
211 // By now our data type is instantiable, but nothing can be done with it.
212 // The flintxx library would be able to create expression templates involving
213 // fooxx, but will not do so because it has no way of evaluating them. We
214 // need to provides evaluation (and other) *rules* to the library. These
215 // (have to) live in namespace flint::rules.
216 //
217 // All possible rules are defined in flintxx/rules.h.
218 
219 namespace rules {
220 
221 // These two lines are convenient, are not formulaic except that they are used
222 // in all code below.
223 #define FOOXX_COND_S FLINTXX_COND_S(fooxx)
224 #define FOOXX_COND_T FLINTXX_COND_T(fooxx)
225 
226 // Define a conditional assignment rule. The general pattern is
227 //
228 //     FLINT_DEFINE_DOIT_COND2(name, cond1, cond2, eval).
229 //
230 // This will define a "doit" rule for "name", which takes one input and
231 // one output argument. The result looks something like
232 //
233 //     template<class T, class U>
234 //     struct assignment<T, U, enable if cond1<T> and cond2<U> are satisfied>
235 //     {
236 //         static void doit(T& to, const U& from)
237 //         eval;
238 //     };
239 //
240 // In our case, we are defining an assignment rule, i.e. an explanation on
241 // how to execute operator=. If the right hand side is an expression template,
242 // flintxx will automatically evaluate it first. Thus we need only treat the
243 // case where the LHS is fmpzxx or fmpzxx_ref, and the RHS is fmpzxx, fmpzxx_ref
244 // or fmpzxx_srcref. This is precisely what the conditions FOOXX_COND_T
245 // and FOOXX_COND_S (conditions "fooxx target" and "fooxx source") mean.
246 FLINT_DEFINE_DOIT_COND2(assignment, FOOXX_COND_T, FOOXX_COND_S,
247         foo_set(to._foo(), from._foo()))
248 
249 // This line defines assignment of integral PODs to fooxx. Since the underlying
250 // C library only defines fooxx_set_si, we can only safely allow this if the
251 // right hand side can always be losslessly converted into a signed long,
252 // so we use the condition traits::fits_into_slong. Traits are defined all
253 // throughout flintxx, but the most general purpose ones (like fits_into_slong,
254 // is_unsigned_integer etc) can be found in flintxx/traits.h
255 FLINT_DEFINE_DOIT_COND2(assignment, FOOXX_COND_T, traits::fits_into_slong,
256         foo_set_si(to._foo(), from, 1))
257 
258 // We now define evaluation rules. In full generality, the rule evaluation<...>
259 // can be used to define how to evaluate any kind of expression. But this is
260 // difficult to use. Moreover, much evaluation logic is shared among most
261 // data types. For example, to evaluate an expression like a + b + c,
262 // one typically first has to evaluate (say) a + b into a temporary t, and then
263 // evaluate t + c. The only step that is specific to fooxx here is how to
264 // add two immediates.
265 // For this reason, flintxx has special convenience forms of the evaluation
266 // rule, called binary and unary expressions. Defining a binary expression
267 // f(x, y) tells flintxx how to evaluate operation "f" on types "x" and "y",
268 // typically immediates. Then flintxx will figure out how to evaluate the
269 // arguments into temporaries first etc.
270 // There is a common special case, when f(x, y) is always the same as f(y, x),
271 // even though x and y may be of different types. Letting flintxx know of this
272 // avoids defining the rule both ways round.
273 //
274 // Here we define a commutative binary expression rule, for operation "plus",
275 // to be executed on to objects of types T and U, both satisfying FOOXX_COND_S.
276 // The result is to be of type foooxx (the second argument).
277 // In this case the types are fully symmetric, so we could have used
278 // FLINT_DEFINE_BINARY_EXPR_COND2 without adverse effects.
279 //
280 // The eval statement should have the effect of to = e1 + e2.
281 FLINT_DEFINE_CBINARY_EXPR_COND2(plus, fooxx, FOOXX_COND_S, FOOXX_COND_S,
282         foo_add(to._foo(), e1._foo(), e2._foo()))
283 
284 // Addation of fooxx and PODs. This time CBINARY instead of BINARY is vital.
285 FLINT_DEFINE_CBINARY_EXPR_COND2(plus, fooxx, FOOXX_COND_S,
286         traits::fits_into_slong,
287         foo_add_si(to._foo(), e1._foo(), e2))
288 
289 // Next we define relational operators. A convenient way of doing so is using
290 // a "cmp" function, which is handily provided by the underlying C library.
291 // This has a somewhat peculiar signature, so cannot be defined using one of
292 // the standard macros. However, it comes up with many actual FLINT data types,
293 // so we have a special FLINTXX macro just for defining cmp.
294 FLINTXX_DEFINE_CMP(fooxx, foo_cmp(e1._foo(), e2._foo()))
295 
296 // Now we define a rule how to print fooxx. There is no macro for this, because
297 // normally instead we define conversion to string, and flintxx takes care of
298 // printing. However, the C library for fooxx provides neither printing nor
299 // conversion to string, so we have to do our own implementation.
300 template<class T>
301 struct print<T, typename mp::enable_if<FOOXX_COND_S<T> >::type>
302 {
doitflint::rules::print303     static void doit(const T& i, std::ostream& o)
304     {
305         o << *i._foo();
306     }
307 };
308 } // rules
309 
310 // By now fooxx is a pretty passable wrapper type. In fact the only thing left
311 // to do is to expose foo_magic. This is a special function which can be
312 // executed on instances of foo, and yields another instance of foo. It is
313 // essentially just another unary expression, just with an unusual name, so
314 // this is how we treat it.
315 
316 // This line introduces a new type of unary operation, called "magic_op",
317 // together with a function flint::magic(T), which creates expression templates
318 // with this new operation. In principle, any expression template data type is
319 // now allowed to define rules how to performa magic on itself.
320 FLINT_DEFINE_UNOP(magic)
321 
322 // Finally, we need to explain how to perform magic on flintxx. This is again
323 // a rule.
324 namespace rules {
325 
326 // The pattern should be familiar by now.
327 FLINT_DEFINE_UNARY_EXPR_COND(magic_op, fooxx, FOOXX_COND_S,
328         foo_magic(to._foo(), from._foo()))
329 } // rules
330 } // flint
331 
332 #endif
333 
334 ///////////////////////////////////////////////////////////////////////////////
335 // Example program
336 ///////////////////////////////////////////////////////////////////////////////
337 
338 using namespace flint;
339 
340 int
main()341 main()
342 {
343     fooxx a, b(4);
344     fooxx_ref ar(a);
345     fooxx_srcref br(b);
346 
347     ar = 1 + br + 1; // a=6
348     std::cout << magic(a + (-1)) << '\n'; // 2*(6-1)+1 = 11
349 
350     return 0;
351 }
352