template<size_t N>
struct IFunction {
virtual ~IFunction() {}
virtual double eval(const double* vars) = 0;
};
template<>
struct IFunction<3> {
virtual ~IFunction() {}
virtual double eval(const double* vars) = 0;
// Should I do this
double eval(double x, double y, double z) {
// Is this safe, and if not how easily it's going to break ?
return this->eval(&x);
}
// Or this ?
//double eval(double x, double y, double z) {
// double vars[3] = {x,y,z};
// return this->eval(vars);
//}
};
struct MyFunctionImpl3 : public IFunction<3> {
virtual double eval(const double* vars) {
// compute something...
}
}
🔒 Locked
[c++] function parameter stack order
Advertisement
Original Post
Question relating to calling conventions (parameter order on stack). Code example and embedded question: Compiled on g++ 3.4.5 (mingw) and worked as I expected. Haven't tested on linux/g++ or msvc yet but I believe it should work even then. If I restrict this on x86 and "standard" calling conventions, I assume it should work. But then again, is this still a case of disaster in waiting or simply very bad practice ?
I suppose it's both something that'll bite you back some day and bad practice...
What's your goal exactly: "optimizing" the call by doing a single push instead of 3 or just avoid creating that double[3] locally?
Note that if you know it'll be 3 doubles, you might as well do it the other way around: have the method that takes a double const * pass vars[0], vars[1] and vars[2] to the one that takes 3 arguments.
And thiscall with msvc is right to left, if I remember properly (and this goes via ecx).
What's your goal exactly: "optimizing" the call by doing a single push instead of 3 or just avoid creating that double[3] locally?
Note that if you know it'll be 3 doubles, you might as well do it the other way around: have the method that takes a double const * pass vars[0], vars[1] and vars[2] to the one that takes 3 arguments.
And thiscall with msvc is right to left, if I remember properly (and this goes via ecx).
Passing the parameters as 'double* vars' at all is a bad idea, to be honest. If you intend that the function always takes an array of 3 doubles, then pass an array by reference (like double (&)vars[3] - the parentheses around the & are important, since otherwise you are trying to declare an array of references, which is illegal) or a struct (or better yet, use boost::array). If not, you need some kind of length information (using "null termination" for an array of float data is a quite bad idea for a variety of reasons).
Oh, I think I see what you want to do: you want to pass N doubles to eval() for a IFunction, in general.
Umm... just use boost::function or related tools. Please. Here be many, large dragons.
Oh, I think I see what you want to do: you want to pass N doubles to eval() for a IFunction
Umm... just use boost::function or related tools. Please. Here be many, large dragons.
Quote:
What's your goal exactly: "optimizing" the call by doing a single push instead of 3 or just avoid creating that double[3] locally?
Yes basically a slight optimization for 1,2,3 variable IFunction convenience method. Initially I thought it wouldn't work at all because cdecl or thiscall pushes arguments right-to-left but then afterwards remembered push actually decreases stack...
Quote:
Note that if you know it'll be 3 doubles, you might as well do it the other way around:
have the method that takes a double const * pass vars[0], vars[1] and vars[2] to the one that takes 3 arguments.
Nice, I could declare that 3 argument method as virtual instead of the array one and then let the derived class implement that. Only drawback I see would be that each specialized IFunction would need matching specialized implementing class. Currently only one implementing class is enought (if being lazy). Thanks for the idea!
One implementing class is enough but you're not really sure how many double's you'll get passed... As for the (micro-)optimization, you'd better leave that to the compiler, it's usually quite good at it. The whole virtual table lookup will probably be more of a hit than pushing the arguments on the stack anyway.
One of the "features" of the C language is that the order of
evaluation of arguments is not guaranteed. For example, "test"
might return either 1 or 2. Most compilers on most machines
will do it one way or the other, but you'll be unpleasantly
surprised when "portable" code is ported.
int bar(int *x)
{ *x=1;
}
int mumble(int *x)
{ *x=2;
}
int foo(int x,int y)
{ return(x+y);
}
int test()
{
int a=0;
foo(bar(&a),mumble(&a));
return(a);
}
see http://www.andromeda.com/people/ddyer/topten.html for more.
evaluation of arguments is not guaranteed. For example, "test"
might return either 1 or 2. Most compilers on most machines
will do it one way or the other, but you'll be unpleasantly
surprised when "portable" code is ported.
int bar(int *x)
{ *x=1;
}
int mumble(int *x)
{ *x=2;
}
int foo(int x,int y)
{ return(x+y);
}
int test()
{
int a=0;
foo(bar(&a),mumble(&a));
return(a);
}
see http://www.andromeda.com/people/ddyer/topten.html for more.
Quote:
Original post by jkv
Haven't tested on linux/g++ or msvc yet but I believe it should work even then.
MSVC doesn't like it too much. If you output the arguments in eval() like so:
virtual double eval(const double * vars) { std::cout << vars[0] << "\n" << vars[1] << "\n" << vars[2] << "\n"; return 0.0; }And call base version with foo.eval(1.0, 2.0, 3.0); it spits out:
12.00214e-3072.13097e-314during one run, which happens to be quite different from the inputs.
Quote:
Original post by SiCrane
And call base version with foo.eval(1.0, 2.0, 3.0); it spits out:during one run, which happens to be quite different from the inputs.12.00214e-3072.13097e-314
Heh. I'm guessing the wrong values are from somewhere else on the stack? That doesn't seem like what the results would look like from an endianness or alignment issue....
Topic Locked
This topic has been locked by a moderator. New replies are not allowed.
Sign in to reply to this topic.
Advertisement