0

So I'm trying to plot the output of this Euler integration function:

typedef double F(double,double);
using std::vector;

void euler(F f, double y0, double a, double b, double h,vector<POINT> Points)
{

POINT Pt;
double y_n = y0;
double t = a;
for (double t = a; t != b; t += h )

{
    y_n += h * f(t, y_n); 

    Pt.x = t; // assign the x value of the point to t.
    Pt.y = y_n; // assign the y value of the point to y_n.
    Points.push_back(Pt);

}


}


// Example: Newton's cooling law
double newtonCoolingLaw(double, double t) 
{
    return t; // return statement ends the function; here, it gives the time derivative y' = -0.07 * (t - 20)
}

I'm trying to use the Polyline() function in a Win32 application, so I do this under the case WM_PAINT:

case WM_PAINT:
    {

    hdc = BeginPaint(hWnd, &ps);

  //Draw lines to screen.

    hPen = CreatePen(PS_SOLID, 1, RGB(255, 25, 5));
    SelectObject(hdc, hPen);


    using std::vector;
    vector<POINT> Points(0);

    euler(newtonCoolingLaw, 1, 0, 20, 1,Points);

    POINT tmp = Points.at(0);
    const POINT* elementPoints[1] = { &tmp };

    int numberpoints = (int) Points.size() - 1 ;


    Polyline(hdc,elementPoints[1],numberpoints);

When I reroute my I/O to console, here are the outputs for the variables:

enter image description here

I'm able to draw the expected lines to the screen using MovetoEx(hdc,0,0,NULL) and LineTo(hdc,20,20), but for some reason none of these functions will work with my vector<POINT> Points. Any suggestions?

Robbie Capps
  • 417
  • 1
  • 5
  • 16

1 Answers1

2

There are multiple things that seem erroneous to me:

1) You should pass the vector by reference or as a return value:

void euler(/*...*/,vector<POINT>& Points)

Currently you are only passing a copy into the function, so the original vector will not be modified.

2) Don't compare doubles for (in-)equality in your for-loop header. Doubles have a limited precision, so if b is much bigger than h, your loop might never terminate, as t might never exactly match b. Compare for "smaller" instead:

for (double t = a; t < b; t += h )

3) Why are you declaring elementPoints as an array of pointers of size 1? Wouldn't a simple pointer do:

const POINT* elementPoints =  &tmp ; //EDIT: see point 5)

4) You have an of-by-one error when calling Polyline. If you want to stick with the array at all use.

Polyline(hdc,elementPoints[0],numberpoints);

EDIT: Sorry, I forgot an important one:

5) In your code, elementPoints[0] points to a single double (tmp) and not to the array inside of the vector. This would probably work, if you declared tmpas a reference:

POINT& tmp = Points.at(0); //I'm wondering why this doesn't throw an exception, as the vector should actually be empty here

However, I think what you actually want to do is to get rid of tmp and elementPoints altogether and write in the last line:

Polyline(hdc,&Points[0],(int) Points.size()-1);
//Or probably rather:
Polyline(hdc,&Points[0],(int) Points.size());

Btw.: What is the purpose of the -1?

MikeMB
  • 20,029
  • 9
  • 57
  • 102
  • Thanks so much man! I'm a complete beginner, and you just taught me a lot. I've been working on this one issue for a while now. – Robbie Capps Feb 19 '15 at 16:42
  • Also, to answer some of your questions about my terrible code: `tmp` as it was defined didn't throw an exception because I would typically define an arbitrary size of the `Points` vector to keep it from doing so. The `-1` was my bad, as I was misinterpreting the issues that I was having before, which as you've pointed out were probably from using `elementPoints` incorrectly. Just for clarification, `&Points[0]` is a reference to element 0 in `Points`? – Robbie Capps Feb 19 '15 at 16:47
  • 1
    @Robbie: No worries, the combination of pointers, references, arrays, and vectors all in a few lines of code can become quite confusing at the beginning. In this contex `&` means "take the address", so `&Points[0]` basically returns the address of the first element ("element 0") in the vector. As vectors store their elements internally as an array, this is equivalent to the start address of the internal array of `POINT`s, which is what `Polyline` expects. I have to admit though, that I'm not sure whether this is absolutely guaranteed by the standard. – MikeMB Feb 19 '15 at 17:10
  • 1
    @Robbie: I also corrected a syntax error in my suggestion for `tmp`. The `&` has to be put after the type and before the variable name (same as with `*` for pointers). – MikeMB Feb 19 '15 at 17:14
  • Well, it certainly produces the results that I wanted, at least for a demonstration that it can work. Thanks again. You really helped me understand some of this stuff in more depth. – Robbie Capps Feb 19 '15 at 17:14
  • The last code block is the important one. But get rid of the `(int)` cast. – Ben Voigt Feb 19 '15 at 17:34
  • @Ben Voigt: Why? The interface expects an `int`, `size()` returns an `size_t`. I tend to make such casts explicit (and then look how I can avoid them by changing th interface if possible). Is this bad practice? – MikeMB Feb 19 '15 at 17:58
  • Strike the comment in the parenthesis. Usually I'd try to change the type of the argument, but in this case I'd prefer the interface to accept an unsigned int, which would seem more natural to me. – MikeMB Feb 19 '15 at 18:13
  • @MikeMB: It's not good to use casts where they aren't needed, because they hide errors. C-style casts are especially bad. See http://rextester.com/UQAR98460 for example. – Ben Voigt Feb 19 '15 at 19:03
  • @Ben Voigt: While I agree that one should avoid casts where possible (by making them uneccessary), an explicit cast is - in my opinion - better than an implicit one. You can't avoid a cast here, because the parameters of the two library functions are different. Without the explicit cast, the compiler will generate a warning, but there is nothing you can do about it. Do that with a big project, and you will get swamped with warnings and overlook new (important) ones. – MikeMB Feb 19 '15 at 19:45
  • @Ben: In general, I also prefer c++-style casts, but on the other side, they often make the code less readable. Thats why I use them for conversions of builtin types inside parameter list, but thats probably a matter of taste or the Coding guideline, you want/have to stick to. – MikeMB Feb 19 '15 at 19:49
  • @MikeMB: There's no such thing as an implicit cast. There are implicit conversions, and they are safer than casts. In cases where the implicit conversion can cause dataloss (and a compile warning), better to use an inline helper function that wraps the implicit conversion (and implement THAT without casts also, to prevent hiding serious type errors). That way the warning only occurs in the helper function, and can be selectively ignored using pragmas. E.g. `Polyline(hdc,&Points[0],narrowing(Points.size()));` may be preferred to `Polyline(hdc,&Points[0], Points.size());` – Ben Voigt Feb 19 '15 at 20:25
  • and the implicit conversion in `Polyline(hdc,&Points[0],Points.size());` should be preferred to any variant with a cast. – Ben Voigt Feb 19 '15 at 20:26
  • @Ben: Yes, I used a wrong expression here. However, everything I've read so far indicates, that both versions `Polyline(hdc,&Points[0],Points.size());` and `Polyline(hdc,&Points[0],(int)Points.size());` will produce exactly the same code. If thats a wrong assumption on my side, could you direct me to more reading material on that topic, because I'd really like to understand, whats going on under the hood. – MikeMB Feb 19 '15 at 21:27
  • @MikeMB: All the variations we're discussing will generate the same code. However, the version with explicit cast may generate no diagnostic even when there is a severe problem (false negative), and the version with implicit conversion may generate noise diagnostics (false positive). Actually these latter diagnostics are a good thing because they draw the programmer's attention, but as you say remain a distraction after the programmer has analyzed the narrowing conversion. What's wanted is a syntax to hush the warning over narrowing, while still diagnosing severe type errors that arise. – Ben Voigt Feb 19 '15 at 21:54
  • Such a syntax can be provided with zero runtime cost via `template inline To narrowing(const From& val) { return val; }` (one can consider enabling perfect forwarding, but is it really worthwhile when the primary usage is with primitive types?) Now `size_t f(); void g(int); g(narrowing(f())));` will have no warnings, while `size_t f(); void g(int); g(narrowing(f));` still diagnoses the mistake, as does `size_t* f(); void g(int); g(narrowing(f()));` – Ben Voigt Feb 19 '15 at 21:58