functions and classes in C++

Discussion in 'Computer Science & Culture' started by prometheus, Nov 26, 2011.

Thread Status:
Not open for further replies.
  1. prometheus viva voce! Registered Senior Member

    Messages:
    2,045
    I'm on the job market now, so I've been brushing up on my C++ (I'm a physicist, not a computer scientist so the knowledge I have of C++ and programming in general is pretty patchy.). I learned a bit about classes a few years ago and I've been trying to relearn them by going back over my code library and adapting some basic programmes I've written to use them, in this case an implementation of Simpson's method for numerical integration.

    Here is the code, int main() is in main.cpp:

    Code:
    #include <iostream>
    #include <cmath>
    #include "functions.h"
    #include "simpson.h"
    
    using namespace std;
    
    int main()
    {
    
    // Integration limits
    double bot = 0; double top = 1;
    
    double step; 
    int no_steps = 10; // number of steps 
    step = (top - bot)/ ((double)no_steps);
    
    simpson simp(bot, step, top);
    
    cout << simp.integral() << endl;
    cout << simp.error() << endl;
    
    return 0;
    
    }
    The contents of simpson.h is:
    Code:
    class simpson
    {
    
    double x; double h; double up_lim; 
    double sum; double err;
    
    public:
    
    simpson(double, double, double); 
    
    	double integral()
    	{
    	sum = 0;
    		do
    		{
    		sum = sum +  h/6 * (func(x) + 4 * func(x + h/2) + func(x + h));
    		x = x + h;
    		}
    		while(x < up_lim - h/10);
    	return sum;
    	}
    
    	double error()
    	{
    	err = 0;
    		do
    		{
    		err = err + std::pow(h/2, 5) * func(x + h/2)/90 ;
    		x = x + h;
    		}
    		while(x < up_lim - h/10);
    	return err;
    	}
    };
    simpson::simpson(double bot, double step, double top) //constructor
    {
    h = step;
    x = bot;
    up_lim = top;
    }
    The functions func(x) and deriv(x) are defined in functions.h (as you might expect). Clearly the Simpson algorithm depends on the integration limits and the step size, but it (obv.) also depends on the integrand func(x). I'd like to be able to call simp.integral() with a function as an argument so I could easily modify the program to integrate any number of functions in one go. What is the best way to do this?

    Thanks.

    Please Register or Log in to view the hidden image!

     
  2. Google AdSense Guest Advertisement



    to hide all adverts.
  3. przyk squishy Valued Senior Member

    Messages:
    3,203
    You can pass functions around as arguments via function pointers. So if I recall correctly, you can define something like:
    typedef double (*real_function)(double);
    Then you'd give your integral() a definition that starts
    double integral(real_function func) {...}
    Within the definition of integral(), the local variable func is a pointer to a function, and you can call that function either with syntax like x = (*func)(y) or just x = func(y) (both are acceptable). To call the integral() function with a function pointer argument, use either result = simp.integral(&func); or just result = simp.integral(func);.

    A few other comments:
    • Do you really need a class here? Why not just define a single funtion that computes both the integral and the error of an arbitrary function? This would be more efficient as you'd only need one function call and one loop to get both jobs done. If you want to do many integrations with the same limit and step parameters and just don't want to specify these every time, you can use namespaces and anonymous namespaces to implement a module.
    • Normally in C++ you wouldn't define a function in a header file. You only put the declaration (the function prototype) there, and put the actual definition in another source file (eg. in "functions.cpp" or "simpson.cpp"). Otherwise you can get into situations where the compiler tries to compile the same function multiple times and gets confused because of this. The exception in C++ is really trivial member functions in a class declaration, which get compiled inline.
    • Minor point: if you're looping between a start and an end value, for is more natural than do ... while.
    • Even more minor point: you might want to give your header files more C++-specific names, such as "function.hpp" or "function.hh" rather than just "function.h". The reason is that some general purpose text editors (including Vim and Emacs) will use this to distinguish between C++ and C header files, and apply the respective syntax highlighting and formatting rules (eg. if you open "functions.hpp" in Emacs, it'll know that it should highlight the keyword class, which doesn't exist in C).
     
  4. Google AdSense Guest Advertisement



    to hide all adverts.
  5. prometheus viva voce! Registered Senior Member

    Messages:
    2,045
    Thanks a lot przyk.

    Please Register or Log in to view the hidden image!



    The answer is that I don't strictly need to use a class here, and the original implementation of this algorithm was as you describe - a function with one loop that computed the integral and the error in one go. The point of the exercise was to actually do something with classes - normally the tutorials on classes have you writing things like class rectangle and class shape etc. which I was getting pretty fed up with. I feel I learn programming better when I am actually doing something.
    I didn't know that. Thanks for the tip.

    Please Register or Log in to view the hidden image!



    You're quite right of course. I'm struggling to remember why I used a do - while loop now...

    Not that it really matters but I'm using gedit which does provide the correct highlighting. I will do as you suggest though.

    Thanks again.

    Please Register or Log in to view the hidden image!

     
  6. Google AdSense Guest Advertisement



    to hide all adverts.
  7. przyk squishy Valued Senior Member

    Messages:
    3,203
    Ok, I'm just pointing out that for the exercise you're doing, classes don't really seem like the right tool for the job. The idea of a class is that you're defining a new type of variable or object that you're going to create many instances of. So in a program you might want to create and manipulate lots of circles or rectangles, but you're not likely to want to create many instances of a class for doing numerical integration. How likely is it that you'd want to do something like
    Code:
    simpson simp1(bot1, step1, top1);
    simpson simp2(bot2, step2, top2);
    simpson simp3(bot3, step3, top3);
    
    or maybe even create a whole array of simpsons? If you just want a collection of related algorithms that share some private variables, you can just use namespaces. So an example "simpson.cpp" implementing a module for Simpson integration might look something like:
    Code:
    #include "simpson.hpp"    // Put the typedef for real_function in here.
    
    namespace simpson
    {
            // Anonymous namespace. This keeps the variables bot, step,
            // and top private to the simpson.cpp file.
            namespace
            {
                    double bot, step, top;
            }
    
            double integral(real_function f)
            {
                    /* ... */
            }
    
            double error(real_function f)
            {
                    /* ... */
            }
    
            void set_limits(double b, double s, double t)
            {
                    bot = b;
                    step = s;
                    top = t;
            }
    }
    
    A good exercise if you haven't tried it already might be to create a vector or quaternion class. I think technically the "correct" way to do this in C++ is to extend the valarray class in the standard template library, so this forces you to learn about class inheritance in C++.

    I suppose I should add another point about header files. In the C and C++ world I think it is standard practice to add include guards in them, so your "functions.h" header might look something like this:
    Code:
    #ifndef FUNCTIONS_HEADER
    #define FUNCTIONS_HEADER
    
    double func(double);
    double deriv(double);
    
    #endif
    
    For a small project this makes no difference, but in larger projects where you might have a heirarchy of header files including other header files, the include guards ensure no more than one copy of the header gets included into a source file. So it's probably another good habit to pick up early. I'm sure I've even seen some IDEs put them in automatically.

    OK. I should warn that this is just a personal habit of mine because I use Emacs and Vim. So in case you are ever employed to do C++ coding, I have no idea if this is actually standard practice.
     
  8. Chipz Banned Banned

    Messages:
    838
    I've been meaning to get into this thread but have only had time to look at it when I was on an archaic compiler. The modern way of doing this is using lambda functions, not functors. I'll lift some code samples from stuff I've previously done and put them here when I have time.
     
  9. Crunchy Cat F-in' *meow* baby!!! Valued Senior Member

    Messages:
    8,423
    If I am understanding you correctly then you want to have a list of functions that take in (x) as a parameter (like func(x)). Then, a single call to integral() would invoke each function in the list and add the result of each call to the total sum that integral() returns. Is my interpretation correct?
     
  10. Chipz Banned Banned

    Messages:
    838
    Code:
    #include <iostream>
    #include <functional>
    #include <math.h>
    
    template <class T>
      class funx_variations
      {
      public:
        static T f_type1(const T& input)
        {
          return input*3 - 1;
        }
        static T f_type2(const T& input)
        {
          return input*3 + 1;
        }
      };
    //
    double 
    simpsons_integral(const double& step, const double& top, double bot,
                      std::function<double (const double&)> f)
    {
      double sum=0.0;
      while (bot < (top - (step/10)))
        {
          sum+= step/6.0 * (f(bot) + 4 + f(bot+ step/2) + f(bot+step));
          bot+= step;
        }
      return sum;   
    }
    
    double 
    error(const double& step, const double& top, double bot,
          std::function<double (const double&)> f)
    {
      double err=0.0;
      while (bot < (top-(step/10.0)))
        {
          err+= pow(step/2,5)*f(bot + step/2)/90;
          bot+= step;
        }
    }
    
    int 
    main(int argc, char* argv[])
    {
      std::function<double (const double&)> f1,f2;
      f1= funx_variations<double>::f_type1;
      f2= funx_variations<double>::f_type2;
      
      if (f1 && f2)
        {
          double f1_integ=simpsons_integral(3,4,5,f1);
          double f1_error=error(3,4,5,f1);
          double f2_integ=simpsons_integral(3,4,5,f2);
          double f2_error=error(3,4,5,f2);
    
          std::cerr<<f1_integ<<","<<f1_error<<std::endl;
          std::cerr<<f2_integ<<","<<f2_error<<std::endl;
        }
      else
        std::cerr<<"Oops..."<<std::endl;
    
      return 0;
    }
    
    This is closer to how things are done in C++0x and is the modern standard. It's useful to note this isn't logistically different than przyk's approach. In fact, I'd be willing to guess that many of the STL templates have implementations close to his functors. It's just that functors can get very very ugly and are often difficult to maintain.
     
  11. prometheus viva voce! Registered Senior Member

    Messages:
    2,045
    Thanks to everyone for the responses (particularly przyk). As I said before, my goal was to learn about the basic use of classes, not to write a simpson's method program.

    No. The point was to be able to have a list of functions somewhere
    Code:
    double f(double x)
    {
    /* ... */
    }
    double g(double x)
    {
    /* ... */
    }
    double h(double x)
    {
    /* ... */
    }
    and to be able to pass the function to the algorithm as an argument:
    Code:
    cout << simp.integral(f(x)) << endl;
    cout << simp.integral(h(x)) << endl;
    Przyk explained how to do this. Without doing it you'd be forced to have a separate loop for the algorithm for each function you wanted to integrate, which is obviously a stupid way of doing it.
     
  12. Crunchy Cat F-in' *meow* baby!!! Valued Senior Member

    Messages:
    8,423
    Ahhh, ok.
     
  13. Aladdin Registered Senior Member

    Messages:
    125
    May I then suggest you try writing some code for a game? Something simple such as Sudoku, Reversi or similar, yet complex enough to give you a grasp on using most OOP features. It should be more fun than dealing with academic subjects and still produce the same end result (i.e. learnig OOP / C++).

    For learning purposes you may also try to inspect/read code written by others. There are lots of open-source projects available for such a task. The tricky part is to pick something basic enough so that you have a chance of understanding its overall design/implementation.
     
    Last edited: Dec 8, 2011
  14. prometheus viva voce! Registered Senior Member

    Messages:
    2,045
    Thanks for the tip. Wouldn't that mean learning about GUI's, or at least enough to get by. I have no experience of them at all and they scare me.
     
  15. Aladdin Registered Senior Member

    Messages:
    125
    Well, I guess it would. If you want to write an app start-to-finish there's no way getting 'round GUI issues.

    Not sure what kind of a job you're aiming at. You mentioned you're a physicist, not a programmer, so if you want to stick to a scientist position then I guess you may not need bother with all aspects of programming. Pick whatever you think it'll help you and learn it employing the funniest methods you can find.

    But if you're thinking career shift (I have an acquaintance who trained as a physicist but soon after finishing University chose to immerse himself in computer programming) then I guess sooner or later you'll have to face your fears and learn about all sort of aspects of software development (GUI, databases, setting up a build environment, test-driven development,...).

    StackOverflow is one great resource for asking questions when you get stuck.


    ps/ And may I also suggest reading (or at least browsing through) Extreme Programming Explained -- it's mainly about a particular style of software development (XP, or eXtreme Programming), but I found its advice applicable in other domains, too. Check-it out at your local library (though I'm pretty sure one can find it as a pirated copy through torrents or other such file-sharing apps).
     
    Last edited: Dec 9, 2011
  16. quadraphonics Bloodthirsty Barbarian Valued Senior Member

    Messages:
    9,391
    Are you sure that it's C++ and OOP that you should be brushing up on, or maybe plain old C or Fortran? Not sure exactly what types of jobs you're looking for, but in some places C++ is not used so much. I personally avoid it unless absolutely necessary.

    Even if you do want to learn C++, the best way to go may be to first master C, and only then worry about classes and generic types. C++ is very easy to grok if you already have a firm grip on C, but can get confusing and time-consuming if you don't.

    Also:

    Yeah, any time you're thinking of making a major life mistake like programming in C++, you first need to ask yourself that question. If you aren't convinced that you really need to define a class, then just stick to regular C.

    I'd go so far as to say that while loops (and do-while loops), like classes, should be avoided unless absolutely required. They have a habit of running forever when things don't go as planned.
     
  17. quadraphonics Bloodthirsty Barbarian Valued Senior Member

    Messages:
    9,391
    Yes, that is also very good advice. Everyone should get into that habit early on. Otherwise, it will eventually come back to bite you in the ass - you'll end up wasting hours trying to sort out the header heirarchy, and probably at a time when you're struggling to meet a deadline

    Please Register or Log in to view the hidden image!

     
  18. Chipz Banned Banned

    Messages:
    838
    Ech...

    Please Register or Log in to view the hidden image!



    You could master C and have no idea how to program C++. You could master C++ and have (practically) no idea how to do a lick of C. The only problem with C++ is all of the Java-educated programmers and NT developers working in it. The only problem with C is usually you don't have that much time.

    Bottom line; they're very different arts. I started learning programming through Lisp and then C, learning these languages are how you learn to develop correctly. That said...when I started doing C++, I had to (temporarily) UNLEARN C. If you start programming C in C++ you will be hated, you will have errors, you will be an idiot. That's not saying the C++ way is the "best" way, but it is the "C++ way" and its best in context.

    Today... yes, I use Fortran in scientific applications...I use C in kernel drivers and low-level processes. But when I need to make a non-time-critical application which works and is developed quickly; I write it in C++ and don't give Java/NT programmers write permissions

    Please Register or Log in to view the hidden image!

    . That way, it continues to work.
     
Thread Status:
Not open for further replies.

Share This Page