Jump to content
  • Advertisement
Sign in to follow this  
Concentrate

This is stupid

This topic is 2810 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Why isn't this polymorphic (C++). I feel stupid.


#include <iostream>
#include <cmath>

using namespace std;

struct IMathCommand{
virtual double operator()(const double& value)const{ return value;} ;
virtual ~IMathCommand(){};
};

class ZeroMathCommand : public IMathCommand{
double operator()(const double& value)const{ return 0.0; }
};
class SquareMathCommand : public IMathCommand{
double operator()(const double& value)const{ return value * value; }
};
class SinusMathCommand : public IMathCommand{
double operator()(const double& value)const{ return std::sin(value); }
};
//Add more MathCommand if needed

class MathSolver{
private:
IMathCommand &mathFunc_;
public:
explicit MathSolver(IMathCommand& cmd = IMathCommand())
: mathFunc_(cmd){}

void setMathCommand(const IMathCommand& cmd){
mathFunc_ = cmd;
}
const IMathCommand& getMathCommand(){
return mathFunc_;
}
double execute(const double val = 0.0)const{
return mathFunc_(val);
}
};


int main(){

MathSolver solver = MathSolver();
cout << solver.execute() << endl; //default prints 0

solver.setMathCommand( SquareMathCommand() );
cout << solver.execute(10) << endl; //print
return 0;
}

Share this post


Link to post
Share on other sites
Advertisement
[font="Courier New"]MathSolver::MathSolver [/font]stores a reference to a temporary value, which is then immediately destructed and deallocated.

[font="Courier New"]MathSolver::setMathCommand[/font] then tries to copy a a new value value over the top of the above-mentioned, already-deallocated temporary. Even if that did work, you'd be copying by-value, and thus slicing the polymorphic type down to it's [font="Courier New"]IMathCommand[/font] base.

Edit:
Also "[font="Courier New"]const double& value[/font]" and "[font="Courier New"]const double value[/font]" should just be "[font="Courier New"]double value[/font]".

Edit 2:
To illustrate problem 1 - storing a reference to a temporary:int* pInt = new int;//allocate some memory
int& rInt = *pInt;//create a reference (aliased name) to that memory
*pInt = 42;//the pointer points to the same bit of memory that the reference aliases
assert( rInt == 42 );
delete pInt;//now we've deallocated the memory, but the reference is still aliasing it!
rInt = 12;//undefined behaviour!!!
Problem 2 - slicing:struct Base { int x;
Base& Base::operator=( const Base& other ) { x = other.x; }
};
struct Derived : public Base { int y; }

Base only4Bytes;
Derived thisIs8Bytes;

only4Bytes = thisIs8Bytes;//invokes Base& Base::operator=( const Base& ), which just copies 'x'

Share this post


Link to post
Share on other sites
[color="#000000"]This much in itself is wrong:IMathCommand& cmd = IMathCommand()You are not allowed to assign a temporary to a non-const reference. You may however assign a temporary to a const-reference:const IMathCommand& cmd = IMathCommand()

Share this post


Link to post
Share on other sites


struct IMathCommand{
virtual double operator()(const double& value)const{ return value;} ;
virtual ~IMathCommand(){};
};

This is not an interface, as such, prefixing it with I is silly. Interfaces do not contain implementation and should be pure abstract.

class SinusMathCommand : public IMathCommand{
double operator()(const double& value)const{ return std::sin(value); }
};
[/quote]
This is not a nose. Sin or Sine. Pick one.

class MathSolver{
private:
IMathCommand &mathFunc_;
public:
explicit MathSolver(IMathCommand& cmd = IMathCommand()) //
: mathFunc_(cmd){}
[/quote]
Bad. Allocating a temporary and then assigning that to a reference which you then assign to a member. Even changing it to [font="Courier New"][color="#000080"]const&[/font] is the wrong thing to do.

void setMathCommand(const IMathCommand& cmd){
mathFunc_ = cmd;
}
[/quote]
This does not do what you think it does. Specifically, it does not change the reference. Just the referenced value. As such you slice any type you put in.

Share this post


Link to post
Share on other sites
Oh, and here you go. A compile time based equation formulator! Including operator overloading!
#include <iostream>
#include <cmath>

template<class LeftOp, class RightOp>
struct binary_op {
binary_op(LeftOp const& left, RightOp const& right) : left(left), right(right) {}

LeftOp left;
RightOp right;
};

template<class Op>
struct unary_op {
unary_op(Op const& op) : op(op) {}
Op op;
};

struct constant {
constant(double val) : val(val) {}

double operator()() const {
return val;
}

double val;
};

struct variable {
variable(double& val) : val(val) {}
double operator()() const {
return val;
}

void set_value(double newval) {
val = newval;
}

double& val;
};

template<class LeftOp, class RightOp>
struct addition_op : binary_op<LeftOp, RightOp> {
addition_op(LeftOp left, RightOp right) : binary_op<LeftOp, RightOp>(left, right) {}

double operator()() const {
return binary_op::left() + binary_op::right();
}
};

template<class Op>
struct sin_op : unary_op<Op> {
sin_op(Op op) : unary_op<Op>(op) {}
double operator()() const {
return std::sin(unary_op::op());
}
};

template<class Op>
struct sqrt_op : unary_op<Op> {
sqrt_op(Op op) : unary_op<Op>(op) {}
double operator()() const {
return std::sqrt(unary_op::op());
}
};

template<class LeftOp, class RightOp>
struct pow_op : binary_op<LeftOp, RightOp> {
pow_op(LeftOp left, RightOp right) : binary_op<LeftOp, RightOp>(left, right) {}

double operator()() const {
return std::pow(binary_op::left(), binary_op::right());
}
};

template<class Op>
struct summation_op : unary_op<Op> {
summation_op(Op op, variable v, double start, double stop, double step) : unary_op<Op>(op), var(v), start(start), stop(stop), step(step) {}
double operator()() {
double sum = 0;
for(double curr = start; curr <= stop; curr += step) {
var.set_value(curr);
sum += unary_op::op();
}

return sum;
}

variable var;
double start;
double stop;
double step;
};

template<class LeftOp, class RightOp>
addition_op<LeftOp, RightOp> operator+(LeftOp left, RightOp right) {
return addition_op<LeftOp, RightOp>(left, right);
}

template<class LeftOp>
addition_op<LeftOp, constant> operator+(LeftOp left, double right) {
return addition_op<LeftOp, constant>(left, constant(right));
}

template<class RightOp>
addition_op<constant, RightOp> operator+(double left, RightOp right) {
return addition_op<constant, RightOp>(constant(left), right);
}

template<class LeftOp, class RightOp>
pow_op<LeftOp, RightOp> do_pow(LeftOp left, RightOp right) {
return addition_op<LeftOp, RightOp>(left, right);
}

template<class LeftOp>
pow_op<LeftOp, constant> do_pow(LeftOp left, double right) {
return pow_op<LeftOp, constant>(left, constant(right));
}

template<class RightOp>
pow_op<constant, RightOp> do_pow(double left, RightOp right) {
return pow_op<constant, RightOp>(constant(left), right);
}

template<class Op>
sin_op<Op> do_sin(Op op) {
return sin_op<Op>(op);
}

sin_op<constant> do_sin(double op) {
return sin_op<constant>(constant(op));
}

template<class Op>
sqrt_op<Op> do_sqrt(Op op) {
return sqrt_op<Op>(op);
}

sqrt_op<constant> do_sqrt(double op) {
return sqrt_op<constant>(constant(op));
}

template<class Op>
summation_op<Op> do_sum(Op op, variable var, double first, double last, double step = 1) {
return summation_op<Op>(op, var, first, last, step);
}

summation_op<constant> do_sum(double op, variable var, double first, double last, double step = 1) {
return summation_op<constant>(constant(op), var, first, last, step);
}

int main(){
double xval;
double yval;
variable x(xval);
variable y(yval);
auto exec = do_sum(do_sqrt(do_sin(2.3 + y) + do_pow(x, 2.) + 4.), x, 1, 10, 1);

std::cout<<"Enter y-val: ";
std::cin>>yval;
double val = exec();
std::cout<<val<<std::endl;
return 0;
}

And if you're curious and wondering what that compiles down to, here's what it compiles down to:
000000013F691084 addsd xmm6,mmword ptr [yval]
double val = exec();
000000013F69108D movsd xmm9,mmword ptr [__real@3ff0000000000000 (13F692298h)]
000000013F691096 xorpd xmm8,xmm8
000000013F69109B movapd xmm0,xmm6
000000013F69109F movapd xmm7,xmm9
000000013F6910A4 call sin (13F691B3Eh)
000000013F6910A9 movsd xmm10,mmword ptr [rsp+28h]
000000013F6910B0 movsd xmm11,mmword ptr [rsp+50h]
000000013F6910B7 movsd xmm12,mmword ptr [__real@4024000000000000 (13F692290h)]
000000013F6910C0 movapd xmm6,xmm0
000000013F6910C4 movapd xmm1,xmm10
000000013F6910C9 movapd xmm0,xmm7
000000013F6910CD movsd mmword ptr [xval],xmm7
000000013F6910D6 call pow (13F691B38h)
000000013F6910DB addsd xmm0,xmm6
000000013F6910DF addsd xmm0,xmm11
000000013F6910E4 call sqrt (13F691B44h)
000000013F6910E9 addsd xmm7,xmm9
000000013F6910EE comisd xmm12,xmm7
000000013F6910F3 addsd xmm8,xmm0
000000013F6910F8 jae main+0C4h (13F6910C4h)

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!