Found a really cool use of private inheritance in C++ in this answer on stackoverflow: http://stackoverflow.com/a/676725/422131.
More details to follow.
More details to follow.
Read more!
std::make_unique
- a factory function for unique_ptr (which should be the most popular
smart pointer) akin to std::make_shared for std::shared_ptr (which shouldn't be the most
popular smart pointer).
#include <memory>
class Foo {
public:
Foo(int n) { ... };
...
};
auto fooPtr = std::make_unique<Foo>(10);
std::cbegin and std::cend, applied to STL containers give you const_iterators.std::shared_timed_mutex, a la boost::shared_mutex - a very important addition if you use a fair bit of synchronization. This provides the multiple-reader single-writer (MRSW) type of abstractions.std::tuple<int, double, std::string> threeElems = std::make_tuple(1, 2.0, "Foo"); auto strFoo = std::get<std::string>(threeElems);
std::make_shared deduces the type of
the returned tuple using the types of the passed arguments. If you
wanted to use a string literal whose type would be deduced as
std::string, you must use an s suffix like this:auto threeElems = std::make_tuple(1, 2.0, "Foo"s); auto strFoo = std::get<std::string>(threeElems);
std::vector<foo> vec;
std::for_each(vec.begin(), vec.end(), [](auto& elem) { std::cout << elem << '\n'; });
auto keyword for the parameter types. You could
use multiple parameters, all declared auto, whose types are
independently deduced. How does this really help? First, you don't have to write:
[](Foo& elem) { std::cout << elem << '\n'; }
Also, you could cache a lambda in a generic context with minimal syntactic noise and reuse it for multiple types. Consider this:
auto elemPrint = [](const auto& elem) { std::cout << elem << '\n'; };
std::for_each(vecOfInts.begin(), vecOfInts.end(), elemPrint);
std::for_each(vecOfStrs.begin(), vecOfStrs.end(), elemPrint);
where vecOfInts, vecOfStrs, etc. all contain elements of different, unrelated type.
auto foo(int x, double y) -> decltype(x + y)
{
return x + y;
}
You can now simply write:
auto foo(int x, double y)
{
return x + y;
}
#include <iostream>
#include <cstring>
#include <algorithm>
class MyString
{
public:
// constructor
explicit MyString(const char *str) : buffer(NULL)
{
if (str && str[0] != '\0') {
auto ln = strlen(str); // C++11: auto -
// compiler determines correct type for ln
buffer = new char[ln + 1];
std::copy(str, str + ln, buffer); // more general than strncpy
}
}
// destructor
~MyString()
{
delete []buffer;
}
size_t len() const
{
if (buffer) {
return strlen(buffer);
} else {
return 0;
}
}
std::ostream& print(std::ostream& os)
{
return (os << buffer);
}
private:
char *buffer;
};
MyString en("Hello");
MyString es(en);
MyString en("Hello");
MyString es("Hola");
en = es;
class MyString
{
public:
// constructor
// destructor
// copy constructor
MyString(const MyString& that)
{
auto ln = that.len();
if (ln) {
buffer = new char[ln + 1];
std::copy(that.buffer, that.buffer + ln, buffer);
}
}
// copy assignment
MyString& operator = (const MyString& that)
{
auto ln = that.len();
if (this != &that) {
// release earlier content
delete [] buffer;
// and mimic copy construction
buffer = new char[ln + 1];
std::copy(that.buffer, that.buffer + ln, buffer);
}
return *this;
}
// rest of the class
};
class MyString
{
...
MyString& operator = (const MyString& that)
{
auto ln = that.len();
if (this != &that) {
// allocate and set aside
char *new_buffer = new char[ln + 1];
std::copy(that.buffer, that.buffer + ln, new_buffer);
// cache the old
char *old_buf = buffer;
// assign the new
buffer = new_buffer;
// delete the old
delete [] old_buf;
}
return *this;
}
...
};
But problems still abound. If copy threw, we'd be left with a leak. Besides, we are still dealing with a single member and this scheme quickly gets out of hand if you deal with two or more members with similar requirements. We can make a small improvement here.
class MyString
{
...
MyString& operator = (const MyString& that)
{
if (this != &that) {
// Use RAII
MyString tmpStr(that.buffer);
// swap the two pointers
std::swap(buffer, tmpStr.buffer);
// et voila!
}
return *this;
}
...
};
If an exception is thrown before line 8, nothing changes. If one is thrown after line 8, tmpStr.buffer is deallocated by a call to its destructor. The call to swap cannot throw. Once that call is complete, ownership of buffers have been exchanged and the destructor of tmpStr takes care of deallocating the older buffer of the current object (this). If we are dealing with multiple members, extending this logic requires a little extra effort. Define a swap member function, or specialize std::swap for MyString, and implement it with no-throw guarantees. A set of pointer swaps for one should be able to provide that guarantee. Your code would then look like:
namespace std
{
void swap(MyString& lhs, MyString& rhs)
{
if (&lhs != &rhs) {
char *tmp = lhs.buffer;
lhs.buffer = rhs.buffer;
rhs.buffer = tmp;
}
}
}
class MyString
{
...
MyString& operator = (const MyString& that)
{
if (this != &that) {
// Use RAII
MyString tmpStr(that.buffer);
// swap the two objects
std::swap(*this, tmpStr); // or swap(tmpStr) if swap were a member
// et voila!
}
return *this;
}
...
};
This is the standard idiom for writing copy assignments using no-throw swaps and on another day I would have happily concluded this article here. Alas! We still have a problem. If you've been attentive you may have already noticed it. What if the MyString constructor threw at line 20? It mighty well can, if say the call to std::copy threw. Ok, I hear you - it won't in the case of this example. But we are performing two operations in the constructor - allocation and assignment of values to the cells of the allocated buffer. If the latter operation throws, the destructor of MyString won't get called and we'd be leaking the memory allocated for buffer. The fool-proof way to deal with the lack of atomicity of this kind of resource allocation plus initialization issues is to harness RAII in some form to protect the smallest units of allocation. We'll use a C++11 smart pointer to do the trick for us. Here is the full listing.
#include <iostream>
#include <cstring>
#include <algorithm>
#include <memory>
class MyString
{
public:
// constructor
explicit MyString(const char *str)
{
if (str && str[0] != '\0') {
auto ln = strlen(str);
buffer.reset(new char[ln + 1]);
std::copy(str, str + ln, buffer.get());
}
}
// copy constructor
MyString(const MyString& that)
{
auto ln = that.len();
if (ln) {
buffer.reset(new char[ln + 1]);
std::copy(that.buffer.get(), that.buffer.get() + ln, buffer.get());
}
}
// destructor
~MyString()
{}
size_t len() const
{
if (buffer) {
return strlen(buffer.get());
} else {
return 0;
}
}
// copy assignment
MyString& operator = (const MyString& that)
{
if (this != &that) {
// copy the right side
MyString tmp(that);
// relinquish our data's ownership
// to tmp, and acquire tmp's data
swap(tmp);
}
return *this;
// let tmp go out of scope and release
// our older data in its destructor
}
// nothrow swap
void swap(MyString& rhs)
{
buffer.swap(rhs.buffer);
}
std::ostream& print(std::ostream& os)
{
return (os << buffer.get());
}
private:
// C++11 smart pointer to make resource
// management of buffer exception-safe
std::unique_ptr<char[]> buffer;
};
int main()
{
MyString m1("Hello"), m2("Hola");
MyString m3(m1);
m1 = m2;
m1.print(std::cout) << std::endl;
m2.print(std::cout) << std::endl;
m3.print(std::cout) << std::endl;
}
$ astyle proj/src/lib/mysource.cpp
Fud foo(int i) // Fud is a copiable class
{
return Fud(i); // Temporary created
}
void bar(Fud);
...
bar(foo(1)); // Temporary passed to bar
Fud f = foo(2);
...
Fud foo(int i) // Fud is a copiable class
{
return Fud(i); // Temporary created
}
void bar(const Fud&);
...
bar(foo(1)); // const-reference to temporary, no copying
Fud f = foo(2);
...
void foo(int i, Fud *&f) // Fud is a copiable class
{
f = new Fud(i);
}
void bar(const Fud&);
...
Fud *pfud = NULL;
foo(2, pfud);
bar(*pfud); // const-reference to temporary, no copying
...
Fud obj = Fud(1);
Fud obj(1);
Fud obj = Fud(1);
Fud obj(1);
#include <vector>
#include <string>
#include <iostream>
using std::vector;
using std::string;
using std::cout;
using std::endl;
struct TestClass
{
TestClass(int i) : i_(i)
{}
TestClass(const TestClass& tc) : i_(tc.i_)
{
cout << "Copied." << endl;
}
private:
int i_;
};
vector<TestClass> make_vec()
{
vector<TestClass> vec;
vec.reserve(20);
cout << "Starting populating vector." << endl;
for (int i = 0; i < 10; i++) {
vec.push_back(TestClass(i));
}
cout << "Vector populated." << endl;
return vec;
}
int main()
{
vector<TestClass> vec = make_vec();
}
Starting populating vector.
Copied.
Copied.
Copied.
Copied.
Copied.
Copied.
Copied.
Copied.
Copied.
Copied.
Vector populated.
return vec;
}
int main()
{
vector<TestClass> vec;
vec = make_vec();
}
Starting populating vector.
Copied.
Copied.
Copied.
Copied.
Copied.
Copied.
Copied.
Copied.
Copied.
Copied.
Vector populated.
Copied.
Copied.
Copied.
Copied.
Copied.
Copied.
Copied.
Copied.
Copied.
Copied.
1 double number = 0.0;
2 number = 2.0;
3 double square_root = ::sqrt(number);
1 double number = 0.0;
2 number = 2.0;
3 double anotherNumber = number;
template<int size>
struct CheckedIntArray {
int& operator[](int index) {
if (index >=0 && index < size) {
return array_[index];
}
throw IndexOutOfBoundsException; // some exception
}
private:
int array_[size];
};
1 CheckedIntArray<16> my_array;
2 my_array[0] = 15;
1 int i = 0;
2 ++i; // r-value expression
1 int arr[32] = {0};
2 int arr2[32] = {1};
3 arr[0] = 5; // arr[0] is an l-value
4 // the following is illegal
5 // arr = arr2; // arr is an r-value
6
7
1 int m = 4;
2 int n = 5 + 8/m;
1 const int& r = 5;
2 const double& s = 2.0;
1 using std::string;
2 using std::stringstream;
3 using std::cout;
4 using std::endl;
5
6 ...
7
8 int x = 0;
9 double f = 1.6;
10 stringstream sout;
11 sout << "Some data values streamed: " << x << "|" << f;
12 const char *str = sout.str().c_str();
13 cout << str << endl; // this will likely print garbage
#include <iostream>
#include <boost/shared_ptr.hpp>
using std::cout;
using std::endl;
using boost::shared_ptr;
struct Node {
int n_;
shared_ptr<Node> next;
Node(int n = 0) : n_(n) {
}
~Node() {
cout << "~Node() invoked for " << n_ << endl;
}
};
shared_ptr<Node> getList() {
shared_ptr<Node> head( new Node(0) );
shared_ptr<Node> next1( new Node(1) );
shared_ptr<Node> next2( new Node(2) );
head->next = next1;
next1->next = next2;
return head;
}
int main() {
shared_ptr<Node> head = getList();
if ( head )
cout << "Got head" << endl;
return 0;
}
#include <iostream>
#include <boost/shared_ptr.hpp>
using std::cout;
using std::endl;
using boost::shared_ptr;
struct Node {
int n_;
shared_ptr<Node> next;
shared_ptr<Node> prev;
Node(int n = 0) : n_(n) {
}
~Node() {
cout << "~Node() invoked for " << n_ << endl;
}
};
shared_ptr<Node> getList() {
shared_ptr<Node> head( new Node(0) );
shared_ptr<Node> next1( new Node(1) );
shared_ptr<Node> next2( new Node(2) );
head->next = next1;
next1->next = next2;
next1->prev = head;
next2->prev = next1;
return head;
}
int main() {
shared_ptr<Node> head = getList();
if ( head )
cout << "Got head" << endl;
return 0;
}
#include <iostream>
#include <boost/shared_ptr.hpp>
using std::cout;
using std::endl;
using boost::shared_ptr;
struct Node {
int n_;
shared_ptr<Node> next;
shared_ptr<Node> *prev;
Node(int n = 0) : n_(n) {
}
~Node() {
cout << "~Node() invoked for " << n_ << endl;
}
};
shared_ptr<Node> getList() {
shared_ptr<Node> head( new Node(0) );
shared_ptr<Node> next1( new Node(1) );
shared_ptr<Node> next2( new Node(2) );
head->next = next1;
next1->next = next2;
next1->prev = &head;
next2->prev = &next1;
return head;
}
int main() {
shared_ptr<Node> head = getList();
if ( head )
cout << "Got head" << endl;
return 0;
}
int main() {
shared_ptr<Node> new_head;
{
shared_ptr<Node> head = getList();
new_head = head->next;
new_head->prev = 0;
}
return 0;
}
#include <boost/shared_ptr.hpp>
#include <boost/weak_ptr.hpp>
using std::cout;
using std::endl;
using boost::shared_ptr;
using boost::weak_ptr;
struct Node {
int n_;
shared_ptr<Node> next;
weak_ptr<Node> prev;
Node(int n = 0) : n_(n) {
}
~Node() {
cout << "~Node() invoked for " << n_ << endl;
}
};
shared_ptr<Node> getList() {
shared_ptr<Node> head( new Node(0) );
shared_ptr<Node> next1( new Node(1) );
shared_ptr<Node> next2( new Node(2) );
head->next = next1;
next1->next = next2;
next1->prev = head;
next2->prev = next1;
return head;
}
int main() {
shared_ptr<Node> head = getList();
if ( head )
cout << "Got head" << endl;
return 0;
}
#include <iostream>
#include <boost/shared_ptr.hpp>
#include <boost/weak_ptr.hpp>
using std::cout;
using std::endl;
using boost::shared_ptr;
using boost::weak_ptr;
struct Node {
int n_;
shared_ptr<Node> next;
weak_ptr<Node> prev;
Node(int n = 0) : n_(n) {
}
~Node() {
cout << "~Node() invoked for " << n_ << endl;
}
};
shared_ptr<Node> getList() {
shared_ptr<Node> head( new Node(0) );
shared_ptr<Node> next1( new Node(1) );
shared_ptr<Node> next2( new Node(2) );
head->next = next1;
next1->next = next2;
next1->prev = head;
next2->prev = next1;
return head;
}
int main() {
shared_ptr<Node> new_head;
weak_ptr<Node> alias;
{
shared_ptr<Node> head = getList();
new_head = head->next;
alias = head;
}
assert(alias.expired());
alias = new_head;
shared_ptr<Node> new_head2 = alias.lock();
assert(new_head2.use_count() == 2); // because new_head and new_head2 share ownership
return 0;
}
1 struct Foo {
2 void bar() {
3 // implementation
4 }
5 };
6
7 struct Foo2 {
8 void bar() {
9 // implementation
10 }
11 };
1 template<class F>
2 struct AllFoo {
3 AllFoo(F& f) : f_(f) {}
4
5 void bar() {
6 f_.bar();
7 }
8 };
1 template<class E>
2 struct Expr {
3 Expr(E& e) : e_(e) {
4 }
5
6 double operator() (double d) {
7 return e_(d);
8 }
9
10 E e_;
11 };
1 struct Constant {
2 Constant(double d) : d_(d) { }
3 Constant(int d) : d_(d) { }
4 double operator() (double) {
5 return d_;
6 }
7
8 double d_;
9 };
10
11
12 struct Variable {
13 double operator() (double d) {
14 return d;
15 }
16 };
1 template<class E1, class E2, class Op>
2 struct ComplexExpr {
3 ComplexExpr(Expr<E1> l, Expr<E2> r) : l_(l), r_(r) {
4 }
5
6 double operator() (double d) {
7 return Op::apply(l_(d), r_(d));
8 }
9
10 Expr<E1> l_;
11 Expr<E2> r_;
12 };
Variable x;
cout << (x+3)(2); // prints 5
cout << ((x*x+3)*(x+3))(2); // prints 35
1 template<class E1, class E2>
2 Expr<ComplexExpr<Expr<E1>, Expr<E2>, Add> > operator+ (E1 e1, E2 e2) {
3 typedef ComplexExpr<Expr<E1>, Expr<E2>, Add> ExprType;
4 return Expr<ExprType>( ExprType(Expr<E1>(e1), Expr<E2>(e2)) );
5 }
1 template<class E1>
2 Expr<ComplexExpr<Expr<E1>, Expr<Constant>, Multiply> > operator* (E1 e1, double d) {
3 typedef ComplexExpr<Expr<E1>, Expr<Constant>, Multiply> ExprType;
4 return Expr<ExprType>( ExprType(Expr<E1>(e1), Expr<Constant>(Constant(d))) );
5 }
6
7 template<class E1>
8 Expr<ComplexExpr<Expr<Constant>, Expr<E1>, Multiply> > operator* (double d, E1 e1) {
9 typedef ComplexExpr<Expr<Constant>, Expr<E1>, Multiply> ExprType;
10 return Expr<ExprType>( ExprType(Expr<Constant>(Constant(d)), Expr<E1>(e1)) );
11 }
Variable x;
cout << (x+3.0)(2); // prints 5
cout << ((x*x+3.0)*(x+3.0))(2); // prints 35
Variable x;
cout << (x+3)(2); // prints 5
cout << ((x*x+3)*(x+3))(2); // prints 35
1 template<class E1>As it should be already apparent - there are no objects allocated on free store, no reference counted proxy wrappers, and a fair bit of compile-time type computation and call dispatching - this results in significant savings in the runtime costs of the program, apart from the obvious cut in the number of lines of code. The basic philosophy of the template based program has not changed from the non-template version - it uses functional composition involving the function call operator, and operator overloading to support naturally combining simple expressions into arbitrarily complex expressions. However, the use of templates allows a fair bit of this work to be done at compile time. This is all that is there to Expression templates. You can put together the expression framework using code given here and use the following function to test your code.
2 Expr<ComplexExpr<Expr<E1>, Expr<Constant>, Multiply> > operator* (E1 e1, int d) {
3 typedef ComplexExpr<Expr<E1>, Expr<Constant>, Multiply> ExprType;
4 return Expr<ExprType>( ExprType(Expr<E1>(e1), Expr<Constant>(Constant(d))) );
5 }
6
7 template<class E1>
8 Expr<ComplexExpr<Expr<Constant>, Expr<E1>, Multiply> > operator* (int d, E1 e1) {
9 typedef ComplexExpr<Expr<Constant>, Expr<E1>, Multiply> ExprType;
10 return Expr<ExprType>( ExprType(Expr<Constant>(Constant(d)), Expr<E1>(e1)) );
11 }
1 int main()
2 {
3 Variable x;
4
5 cout << ((2.0*x*x + 3.0*x + 3.0)*(2.0*x*x + 3.0*x + 3.0))(2) << endl;
6 cout << integrate((2.0*x*x + 3.0*x + 3.0), 0, 1) << endl;
7 cout << integrate((2.0*x*x + 3.0*x + 3.0)*(2.0*x*x + 3.0*x + 3.0), 0, 1) << endl;
8 cout << integrate((x/(1.0+x)), 0, 1) << endl;
9 cout << integrate(x*x, 0, 7) << endl;
10
11 return 0;
12 }
13
template<class InputIterator, class Predicate>
InputIterator find_if (InputIterator first, InputIterator last, Predicate pred)
1 Struct FuncG {
2 double operator(double x) {
3 return (x*sin(x) + 6) ;
4 }
5 }
1 template<class Func>
2 double integrate(Func f, double low, double high, double epsilon = 0.001) {
3 assert(low <= high );
4 double auc1 = 0, auc2 = 0;
5 double running_x = low;
6 while ( running_x < high ) {
7 auc1 += f(running_x) * epsilon;
8 running_x += epsilon;
9 auc2 += f(running_x) * epsilon;
10 }
11
12 return (auc1 + auc2)/2;
13 }
1 MathFunction f = x*x + 2*x + 3;
2 double d = integrate(f, 0, 2);
3 f = x*x*sin*sin + 2*x*cos*sin + cos*cos;
4 const double PI = 3.1415926535897;
5 d = f(PI/2);
1 double f(double x) {
2 return 3;
3 }
1 struct C3 {
2 double operator() (double) {
3 return 3;
4 }
5 };
1 struct Constant {
2 Constant(const double& d) : d_(d) {
3 }
4
5 double operator() (double) {
6 return d_;
7 }
8
9 const double d_;
10 };
1 struct Variable {
2 double operator() (double x) {
3 return x;
4 }
5 };
1 struct Expression {
2 virtual double operator() (double) = 0;
3 virtual Expression* clone() = 0;
4 virtual ~Expression() {
5 }
6 };
1 struct Constant : Expression {
2 ...
3 Expression* clone() {
4 return new Constant(*this);
5 }
6 };
7
8 struct Variable : Expression {
9 ...
10 Expression* clone() {
11 return new Variable(*this);
12 }
13 };
1 Variable x;
2 Expression& e = x*x + 2*x + 1;
3 double d = e(5); // d == 36
4 double d2 = integrate(e, 0, 1); // d2 == 2.33
1 template<class Op>
2 struct ComplexExpression : Expression {
3 Expression* l_;
4 Expression* r_;
5
6 ComplexExpression(Expression& l, Expression& r) : l_(l.clone()), r_(r.clone()) {
7 }
8
9 ~ComplexExpression() {
10 delete l_;
11 delete r_;
12 }
13
14 double operator() (double d) {
15 return Op::apply( (*l_)(d), (*r_)(d) );
16 }
17
18 Expression* clone() {
19 return new ComplexExpression(*l_, *r_);
20 }
21 };
1 struct Add {
2 static double apply(double l, double r) {
3 return l+r;
4 }
5 };
6
7 struct Subtract {
8 static double apply(double l, double r) {
9 return l-r;
10 }
11 };
12
13 struct Multiply {
14 static double apply(double l, double r) {
15 return l*r;
16 }
17 };
18
19 struct Divide {
20 static double apply(double l, double r) {
21 return l/r;
22 }
23 };
1 Expression* operator * (Expression& l, Expression& r) {
2 return new ComplexExpression<Multiply>(l, r);
3 }
1 Expression* operator * (Expression& l, Expression& r) {
2 return new ComplexExpression<Multiply>(l, r);
3 }
1 Expression& operator * (Expression& l, Expression& r) {
2 return ComplexExpression<Multiply>(l, r);
3 }
1 struct ExpressionRef {
2 ExpressionRef(Expression* ptr) : sp_(ptr), ref_cnt_(1) {
3 }
4
5 ~ExpressionRef() {
6 if (--ref_cnt_ == 0) {
7 delete sp_;
8 sp_ = 0;
9 }
10 }
11
12 ExpressionRef(const ExpressionRef& source) : sp_(source.sp_) {
13 ++ref_cnt_;
14 }
15
16 inline ExpressionRef& operator = (const ExpressionRef& rhs) {
17 if ( this != &rhs ) {
18 if ( --ref_cnt_ <= 0 ) {
19 delete sp_;
20 sp_ = 0;
21 }
22
23 sp_ = rhs.sp_;
24 ++ref_cnt_;
25 }
26
27 return *this;
28 }
29
30 double operator() (double d) {
31 return (*sp_)(d);
32 }
33
34 ExpressionRef clone() {
35 ExpressionRef copy(sp_->clone());
36 return copy;
37 }
38
39 inline Expression* get() {
40 return sp_;
41 }
42
43 inline Expression& getref() {
44 return *sp_;
45 }
46
47 inline operator void*() {
48 return sp_;
49 }
50
51 private:
52 Expression *sp_;
53 int ref_cnt_;
54 };
1 ExpressionRef operator * (Expression& l, Expression& r) {
2 return ExpressionRef(new ComplexExpression<Multiply>(l, r));
3 }
4
1 ExpressionRef operator + (Expression& l, Expression& r) {
2 return ExpressionRef(new ComplexExpression<Add>(l, r));
3 }
4
5 ExpressionRef operator + (ExpressionRef& l, ExpressionRef& r) {
6 return l.getref() + r.getref();
7 }
8
9 ///////////////
10
11 ExpressionRef operator + (Expression& l, const double d) {
12 return l + Constant(d);
13 }
14
15 ExpressionRef operator + (const double d, Expression& l) {
16 return Constant(d) + l;
17 }
18
19 //////////////
20
21 ExpressionRef operator + (ExpressionRef& l, Expression& r) {
22 return l.getref() + r;
23 }
24
25 ExpressionRef operator + (Expression& l, ExpressionRef& r) {
26 return l + r.getref();
27 }
28
29 //////////////
30
31 ExpressionRef operator + (ExpressionRef& l, double d) {
32 return l.getref() + Constant(d);
33 }
34
35 ExpressionRef operator + (double d, ExpressionRef& r) {
36 return Constant(d) + r.getref();
37 }
1 double integrate(Expression& e, double low=0, double high = 1, double epsilon = 0.001) {
2 assert(low <= high );
3 double auc1 = 0, auc2 = 0;
4 double running_x = low;
5 while ( running_x < high ) {
6 auc1 += e(running_x) * epsilon;
7 running_x += epsilon;
8 auc2 += e(running_x) * epsilon;
9 }
10
11 return (auc1 + auc2)/2;
12 }
13
14 double integrate(ExpressionRef& er, double low=0, double high = 1, double epsilon = 0.001) {
15 return integrate(er.getref(), low, high, epsilon);
16 }
17
18 int main()
19 {
20 Variable x;
21
22 cout << ((2*x*x + 3*x + 3)*(2*x*x + 3*x + 3))(2) << endl;
23 cout << integrate((2*x*x + 3*x + 3), 0, 1) << endl;
24 cout << integrate((2*x*x + 3*x + 3)*(2*x*x + 3*x + 3), 0, 1) << endl;
25 cout << integrate((x/(1+x)), 0, 1) << endl;
26 cout << integrate(Exp(), 0, 1) << endl;
27 cout << integrate(x*x, 0, 7) << endl;
28
29 return 0;
30 }