Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Pattern to share data between objects in C++

I have started a migration of a high energy physics algorithm written in FORTRAN to an object oriented approach in C++. The FORTRAN code uses a lot of global variables all across a lot of functions.

I have simplified the global variables into a set of input variables, and a set of invariants (variables calculated once at the beginning of the algorithm and then used by all the functions).

Also, I have divided the full algorithm into three logical steps, represented by three different classes. So, in a very simple way, I have something like this:

double calculateFactor(double x, double y, double z)
{
    InvariantsTypeA invA();
    InvariantsTypeB invB();

    // they need x, y and z
    invA.CalculateValues();
    invB.CalculateValues();

    Step1 s1();
    Step2 s2();
    Step3 s3();

    // they need x, y, z, invA and invB
    return s1.Eval() + s2.Eval() + s3.Eval();
}

My problem is:

  • for doing the calculations all the InvariantsTypeX and StepX objects need the input parameters (and these are not just three).
  • the three objects s1, s2 and s3 need the data of the invA and invB objects.
  • all the classes use several other classes through composition to do their job, and all those classes also need the input and the invariants (by example, s1 has a member object theta of class ThetaMatrix that needs x, z and invB to get constructed).
  • I cannot rewrite the algorithm to reduce the global values, because it follows several high energy physics formulas, and those formulas are just like that.

Is there a good pattern to share the input parameters and the invariants to all the objects used to calculate the result?

Should I use singletons? (but the calculateFactor function is evaluated around a million of times)

Or should I pass all the required data as arguments to the objects when they are created?(but if I do that then the data will be passed everywhere in every member object of every class, creating a mess)

Thanks.

like image 391
smancill Avatar asked Jan 25 '11 16:01

smancill


1 Answers

Well, in C++ the most suitable solution, given your constraints and conditions, is represented by pointers. Many developers told you to use boost::shared_ptr. Well it is not necessary, although it provides a better performance especially when considering portability and robustness to system faults.

It is not necessary for you to bind to boost. It is true that they are not compiled and that now standardization processes will lead to c++ with boost directly integrated as a standard library, but if you do not want to use an external library you obviously can.

So let's go and try to solve your problem using just C++ and what it provides actually.

You'll probably have a main method and there, you told before, initialize all invariants elements... so you basically have constants and they can be every possible type. no need to make them constant if you want, however, in main you instantiate your invariant elements and point them for all those components requiring their usage. First in a separate file called "common_components.hpp" consider the following (I assume that you need some types for your invariant variables):

typedef struct {
   Type1 invariant_var1;
   Type2 invariant_var2;
   ...
   TypeN invariant_varN;
} InvariantType; // Contains the variables I need, it is a type, instantiating it will generate a set of global variables.
typedef InvariantType* InvariantPtr; // Will point to a set of invariants

In your "main.cpp" file you'll have:

#include "common_components.hpp"
// Functions declaration
int main(int, char**);
MyType1 CalculateValues1(InvariantPtr); /* Your functions have as imput param the pointer to globals */
MyType2 CalculateValues2(InvariantPtr); /* Your functions have as imput param the pointer to globals */
...
MyType3 CalculateValuesN(InvariantPtr); /* Your functions have as imput param the pointer to globals */
// Main implementation
int main(int argc, char** argv) {
   InvariantType invariants = {
      value1,
      value2,
      ...
      valueN
   }; // Instantiating all invariants I need.
   InvariantPtr global = &invariants;
   // Now I have my variable global being a pointer to global.
   // Here I have to call the functions
   CalculateValue1(global);
   CalculateValue2(global);
   ...
   CalculateValueN(global);
}

If you have functions returning or using the global variable use the pointer to the struct modifying you methods' interface. By doing so all changes will be flooded to all using thoss variables.

like image 125
Andry Avatar answered Sep 28 '22 07:09

Andry