Click here to Skip to main content
15,886,026 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
See more:
I am trying to write a library using expression templates, and I need to find a way to assign the expression in an variable without evaluating it (which is what I've found in every resource, eg Wikipedia ). Also, since I want to do this for different types of expressions, I need some kind of type erasure.

I tried to do this in a simple example, using lambdas as members, but I often get Segfault.

What I have tried:

In the code below, I have defined Number and Addition class, as the basis for what I am trying to do. Also, I define an Expression class that allows me to use Expression variables to store combinations of Number and Addition expressions.

C++
#include<iostream>
#include<functional>
#include<vector>


// message to print during evaluation, in order to track the evaluation path.
#define msg std::cout<<typeid(*this).name()<<std::endl


class Expression{
    public:
    std::function<double(void)> evaluate;
    
    template<typename T>
    Expression(const T &RH){evaluate = RH.evaluate;}

    template<typename T>
    Expression& operator=(const T &RH)
   {evaluate = RH.evaluate; return *this;}
};




class Number{ 
    double val;
    public:
    Number()=default;

    std::function<double()> evaluate;

    Number(const double &x):val(x)
    {evaluate = [this](){msg; return this->val;};  }
    
   Number(const Number &x):val(x.evaluate())
  {evaluate = [this](){msg; return this->val;}; }
};

template<typename leftHand,typename rightHand>
class Addition{ 
    const leftHand &LH;
    const rightHand &RH;

    public:
    std::function<double()> evaluate;

    Addition(const leftHand &LH, const rightHand &RH):LH(LH),RH(RH){
          evaluate = [this]()
          {msg; return this->LH.evaluate() + this->RH.evaluate();};
    }
};

template<typename leftHand,typename rightHand>
auto operator+(const leftHand &LH, const rightHand &RH)
{return Addition<leftHand,rightHand>(LH,RH); }



using std::cout;
using std::endl;


inline Expression func (std::vector<Expression> x, int i){
    cout<<i<<endl;

    if (i==0){return static_cast<Expression>(x[0]);}    

    return static_cast<Expression>( x[i] + func(x,i-1) ) ;
};


int main(){
    Number y(-2.);
    Number x(1.33);

    Expression z(y+x);
    Expression w(x+y+x);
    
    // works
    z =x;
    cout<<z.evaluate()<<endl;
    cout<<(z+z+z+z).evaluate()<<endl;

    // Segfault due to recusion 
    // z =z+x;
    // cout<<z.evaluate()<<endl;

    // Unkown Segfault 
    // z = x+y ;
    // cout<<(z+z).evaluate()<<endl;
    // cout<<typeid(z+z).name()<<endl;

    // Unkown Segfault 
    // z = w+y+x+x;
    // cout<<z.evaluate()<<endl;

   
    // Unkown Segfault 
    // std::vector<Expression> X={x,y,x,y,x,y,x,y};

    // cout << typeid(func(X,X.size()-1)).name()  << endl;
    // cout << (func(X,X.size()-1)).evaluate()  << endl;
    return 0;
}



I also tried doing something similar using CRTP + Polymorphic cloning, but I get similar behavior:


C++
#include<iostream>
#include<memory>


// message to print during evaluation, in order to track the evaluation path.
#define msg std::cout<<typeid(*this).name()<<std::endl;


struct BaseExpression{
    BaseExpression()=default;
    virtual double evaluate()const =0 ;
};

template<typename subExpr>
struct GenericExpression:BaseExpression{
    const subExpr& self() const {return static_cast<const subExpr&>(*this);}
    subExpr& self() {return static_cast<subExpr&>(*this);}
    
    double evaluate() const { msg; return self().evaluate(); };
};


class Number: public GenericExpression<Number>{
    double val;
    public:
    Number()=default;

    Number(const double &x):val(x){}
    Number(const Number &x):val(x.evaluate()){}
    Number(Number *x):val(x->evaluate()){}
    
    double evaluate()const  { msg; return val;}
    double& evaluate() { msg; return val;}
};

template<typename leftHand,typename rightHand>
class Addition:public GenericExpression<Addition<leftHand,rightHand>>{
    const leftHand &LH;
    const rightHand &RH;

    public:
    Addition(const leftHand &LH, const rightHand &RH):LH(LH),RH(RH){}
    Addition(Addition *add):LH(add->LH),RH(add->RH){}

    double evaluate() const {msg; return LH.evaluate() + RH.evaluate();}
};

template<typename leftHand,typename rightHand>
Addition<leftHand,rightHand> 
operator+(const GenericExpression<leftHand> &LH, const GenericExpression<rightHand> &RH){
    return Addition<leftHand,rightHand>(LH.self(),RH.self()); 
}


class Expression: public GenericExpression<Expression>{
    public:
    std::unique_ptr<BaseExpression> baseExpr;

    Expression()=default;
    // Expression(const Expression &E){baseExpr = (E.baseExpr ;};
    // Expression(Expression *E){baseExpr = E->baseExpr;};

    double evaluate() const {msg;  return baseExpr->evaluate();}

    // I could use operator= here, but I wanted to try this first 
    template<typename subExpr>
    void assign(const GenericExpression<subExpr> &RH){
        baseExpr = std::make_unique<subExpr>(new subExpr(RH.self()));
    }

};


using std::cout;
using std::endl;

int main(){
    Number x(3.2);
    Number y(-2.3);
    Expression z,w;

    // works fine!
    z.assign(x);
    cout<<z.evaluate()<<endl;

    // works fine!
    z.assign(x+y);
    cout<<z.evaluate()<<endl;

    // works fine!
    // z.assign(y+x+x+x+x);
    // cout<<z.evaluate()<<endl;

    
    // works fine!
    // w.assign(z+y);
    // cout<<w.evaluate()<<endl;

    // Segmentation fault of z.evaluate() infinite recursion in between
    // LH.evaluate() (in Addition<Expression,Number>::evaluate()) and 
    // baseExpr->evaluate() (in Expression::evaluate())
    // z.assign(z+x);
    // cout<<z.evaluate()<<endl;

    // Uknown Segmentation fault!
    // z.assign(y+x+x);
    // w.assign(z+x+y);
    // cout<<w.evaluate()<<endl;



    return 0;
}


Generally, what I am trying to achieve is the following:
C++
Number x(5.),y(3.);
Expression z;

z=x;
z=z+y;

z.evaluate();


That is, assignment of an expression and possible reassignment without evaluation. I know it's too much to ask of a strongly typed language, but it feels like something C++ can do at the right hands.
Posted
Updated 20-Jun-21 23:11pm
v2
Comments
BernardIE5317 19-Dec-21 23:29pm    
I just noticed your post is a few months old but in case you are still interested the site below may or may not be useful All I know is it refers to expression templates It is a boost library page
https://www.boost.org/doc/libs/1_78_0/doc/html/yap.html

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900