views:

140

answers:

7

Consider a class like this one:

class MyReferenceClass
{
public:
    MyReferenceClass();
    const double ImportantConstant1;
    const double ImportantConstant2;
    const double ImportantConstant3;
private:
    void ComputeImportantConstants(double *out_const1, double *out_const2, double *out_const3);
}

There is a routine (ComputeImportantConstants) that computes three constants at runtime. Suppose the computation is fairly complex, and inherently produces all three values at once. Moreover, the results depend on build configuration, so hardcoding the results isn't an option.

Is there a sensible way to store these computed values in the corresponding const double fields of the class?

If not, can you suggest a more natural way to declare such a class in C++?

In C# I would use a static class with a static constructor here, but that isn't an option in C++. I have also considered making ImportantConstant1..3 either non-const fields or function calls, but both seem inferior.

The only way to initialize const fields that I found is to use initializer lists, but it doesn't seem possible to pass the results of a multi-output computation in such a list.

+6  A: 

Why can't you do:

MyReferenceClass ComputeImportantConstants(){
    //stuff to compute
    return MyReferenceClass( const1, const2, const3 );
}

MyReferenceClass{
public:
    MyReferenceClass(double _1, double _2, double _3) 
        : m_Const1(_1),
        m_Const2(_2),
        m_Const3(_3){}

    double getImportantConst1() const { return m_Const1; }
    double getImportantConst2() const { return m_Const2; }
    double getImportantConst3() const { return m_Const3; }
private:
    const double m_Const1,
                 m_Const2,
                 m_Const3;
};

Like that and have the calculate function turn into a factory function?

wheaties
one improvement idea: make one static variable in ComputeImportantConstants() and return this variable once everything is computed. This way, subsequent calls of ComputeImportantConstants do not trigger an additional computation.
Tobias Langner
C++ nitpick: returning a `const double` from a function doesn't make much sense. It only makes life harder than necessary for the caller without improving security. The member variables are returned by value, after all.
Frerich Raabe
Good nitpick and you're right.
wheaties
I could do that; it's just that I was trying to avoid making them getter functions. Judging from other responses though that seems to be the preferred approach.
romkyns
There is nothing wrong with a struct having no methods and only holding data (POD types.) On the other hand, it's often far easier to affect change within a program if what is being accessed is hidden behind a function call. Most people are going to prefer encapsulation because of the latter.
wheaties
+5  A: 

first - you can do evil: cast away const in ComputeImportantConstants() and place the values there. Don't do it though, because then you lie to the compiler and it'll try to find the nastiest way to pay back.

second:

do something like this:

class A
private:
  double important1;
  double important2;
  double important3;
  A() { ComputeImportantConstants(); } //no need for parameters, it accesses the members
  void ComputeImportantConstants();
public:
  inline double GetImportant1() { return important1; }
  inline double GetImportant2() { return important2; }
  inline double GetImportant3() { return important3; }
};

you still can improve this class by making it some kind of singleton or so (since you want the computation to be done only once).

Tobias Langner
+2  A: 

You could move the const fields to a base class, and then pass an wrapper class to initialize them:

class MyBase
{
protected:
    const double ImportantConstant1;
    const double ImportantConstant2;
    const double ImportantConstant3;

    struct Initializer
    {
        double d1;
        double d2;
        double d3;
    };

    MyBase(Initializer const& i):
        ImportantConstant1(i.d1),ImportantConstant2(i.d2),ImportantConstant3(i.d3)
    {}
};

class MyReferenceClass:
    private MyBase
{
public:
    using MyBase::ImportantConstant1;
    using MyBase::ImportantConstant2;
    using MyBase::ImportantConstant3;
    MyReferenceClass():
        MyBase(makeInitializer())
    {}

private:
    MyBase::Initializer makeInitializer()
    {
        MyBase::Initializer i;
        ComputeImportantConstants(&i.d1,&i.d2,&i.d3);
        return i;
    }

    void ComputeImportantConstants(double *out_const1, double *out_const2, double *out_const3);
};
Anthony Williams
Works nicely; just needed to change to public MyBase inheritance and make the const double fields public. Also, in the real code the three values are already passed around in a struct so I don't need an extra Initializer struct. (I should really have written my question using that struct...)
romkyns
+1  A: 

The only way to initialize const fields that I found is to use initializer lists, but it doesn't seem possible to pass the results of a multi-output computation in such a list.

That's true; however, you could initialize a single member - which is a struct of constants. See below.

I have also considered making ImportantConstant1..3 either non-const fields or function calls, but both seem inferior.

I don't think that getter functions would be inferior. The compiler would most likely inline them. Consider this:

class MyReferenceClass
{
public:
    MyReferenceClass() : m_constants( ComputeImportantConstants() ) { }

    inline double ImportantConstant1() const { return m_constants.c1; }
    inline double ImportantConstant2() const { return m_constants.c2; }
    inline double ImportantConstant3() const { return m_constants.c3; }

private:
    struct Constants {
        Constants( double c1_, double c2_, double c3_ ) : c1( c1_ ), c2( c2_ ), c3( c3_ ) { }

        const double c1;
        const double c2;
        const double c3;
    };

    Constants ComputeImportantConstants() {
        return Constants( 1.0, 2.0, 3.0 );
    }

    const Constants m_constants;
};

Since m_constants as well as all its fields are constant, the values cannot be changed by other member methods - just in the code you sketched in your question. An initialize can be used here since we initialize a single value: a struct.

Access to the constants is (most likely) to be as efficient as before: the suggest to inline the functions and the compiler is quite likely to do so given how small the getters are.

Frerich Raabe
+1  A: 

Just split up the thing into the part that is simple to initialize and the complex part, and initialize the complex part via copy constructor:

// here's the part with the consts: 
struct ComplexPart
{
    const double a,b,c; 
    ComplexPart(double _a, double _b, double _c) {}
};
// here's the expensive calc function:
void calc(double *a,double *b,double *c);

// and this is a helper which returns an initialized ComplexPart from the computation:
ComplexPart calc2()
{
    double *a,*b,*c;
    calc(&a,&b,&b);
    return ComplexPart(a,b,c);
}
// put everything together:    
struct MyReferenceClass : public ComplexPart
{
    MyReferenceClass() : ComplexPart(calc2()) {}
};
Luther Blissett
Using inheritance here looks painfully wrong to me.
Frerich Raabe
+1  A: 

What about something like that:

class A
{
  private:
    static void calc(double &d1, double &d2, double &d3)
    {
      d1 = 1.0;
      d2 = 2.0;
      d3 = 3.0;
    }
    class D
    {
      public:
        operator double() const
        {
          return(x);
        }
      private:
        friend class A;
        double x;
    };
  public:
    A()
    {
      calc(d1.x, d2.x, d3.x);
    }
    D d1, d2, d3;
};

#include <iostream>

int main()
{
  A a;
  std::cout << a.d1 << std::endl;
  std::cout << a.d2 << std::endl;
  std::cout << a.d3 << std::endl;
  // the following lines will not compile so you can't change the value
  // std::cout << a.d3.x << std::endl;
  // a.d2.x = 0.0;
  return(0);
}
Tomek
+1  A: 

None of the answer above seemed to pay attention to a detail: static is mentioned here, so these constants seem to be independent of the actual instance of the class.

In other words: those are global constants. As you guessed, the presence of the const keyword is important here, because of the optimizations the compiler will apply.

Anyway, the idea is to use a helper structure.

// foo.h
class Foo
{
public:
  static double const m1;
  static double const m2;
  static double const m3;
};

// foo.cpp
struct Helper
{
  double m1, m2, m3;
  Helper() { complexInit(m1, m2, m3); }
} gHelper;

double const Foo::m1 = gHelper.m1;
double const Foo::m2 = gHelper.m2;
double const Foo::m3 = gHelper.m3;

Of course, in a real program, i would encourage you to actually wrap the constants behind some kind of interface, it's really bad practice to expose them this way, because it makes changing them (using another type) very difficult.

Also note that you don't need pointers for output parameters, plain references do.

Matthieu M.