1

At class instantiation, I would like to read data from a file and process it into a number of class objects. What I did so far (and works well) is

myData::myData(const std::string & file):
  data1_(this->read(file)),
  processedData1_(this->createProcessedData1_(data1_)),
  processedData2_(this->createProcessedData2_(data1_)),
  processedData3_(this->createProcessedData3_(data1_))
{
}

In a different class, the read() method creates more than one raw data object. In this case, I don't know how to pack things into the initializer list, so I'm doing something along the lines of

myData::myData(const std::string & file):
  data1_(),
  data2_(),
  processedData1_(),
  processedData2_(),
  processedData3_()
{
  this->read(file); // fills data1_, data2_
  processedData1_ = this->createProcessedData1_(data1_, data2_);
  processedData2_ = this->createProcessedData2_(data1_, data2_);
  processedData3_ = this->createProcessedData3_(data1_, data2_);
}

What I don't like about this approach is that

  • the data is initalized twice: once (void) in the initializer list, once filled with actual content in the constructor; and that
  • I cannot mark any of the (processed) data objects as const.

Is there a way to organize the object creation such that it all happens in the initialization list?

Nico Schlömer
  • 53,797
  • 27
  • 201
  • 249
  • In the code processedData is initialised twice. – ellimilial Jan 30 '13 at 17:42
  • const assures the compiler that the value of whatever is labeled const will not change (i.e. the value is set at compile time). How could your processed data members be const when they are set at runtime based on the contents of a file? – Aeluned Jan 30 '13 at 17:45
  • @ellimilial Same as `data*_`, indeed: the first time with something void in the initializer list, then actually filled in the body of the constructor. I'd like to avoid that, but don't know how. – Nico Schlömer Jan 30 '13 at 17:46
  • @Aeluned The content of the data objects doesn't need to be known at compile time, `const` just enforces that the contents aren't changed after object construction. – Nico Schlömer Jan 30 '13 at 17:50
  • 2
    How about refactoring `data1_` and `data2_` into a contained `class` object? – aschepler Jan 30 '13 at 17:57
  • @aschepler Fitting all of `data*_` into an `allData_` `struct` indeed seems like a possibility. – Nico Schlömer Jan 30 '13 at 18:02
  • @Nico yes, you're right. I missed what you were doing. aschepler's got the answer. – Aeluned Jan 30 '13 at 18:04

2 Answers2

1

The only way to supply a value to a const member variable (without const cast or using mutable keyword) would be to provide it in the constructor initializer list. See this: How to initialize a const field in constructor.

If you prefer not to refrain to the 'ugly' casts or keywords and need your data to be be const (for example for use in const functions), I would go for a small myDataInitialiser class that would first read the data. Once data is read, you could pass the whole instance of initialiser to the constructor of your original myData class.

Community
  • 1
  • 1
ellimilial
  • 1,246
  • 11
  • 20
1

You may think about splitting the data loading / processing in a factory-like static method that in turn constructs a myData instance (passing the processedData*_ values as constructor params).

This way you can keep the loading and processing separate from the class that may end up just storing the results and possibly provide further processing or accessors to parts of the data.

Could something like

class MyData {
public:
    MyData(DataType processedData1, ...) : processedData1_(processedData1) ... { }
private:
    const DataType processedData1_;
}

struct DataContainer {
    DataType data1;
    DataType data2;
}

DataContainer read(const std::string& file) { ... }

DataType createProcessedData1(DataType data) { ... }
...

// hands ownership to caller
MyData* LoadData(const std::string & file) {
    DataContainer d = read(file);
    return new MyData(createProcessedData1(d.data1), createProcessedData2(d.data2), ..)
}

work for you?

I'm assuming you don't need to keep state when loading and processing data. If this isn't the case you can make read and createProcessedData* members of a MyDataLoader class.

Utaal
  • 8,484
  • 4
  • 28
  • 37
  • The key point is really stuffing all the read data into one struct. – Nico Schlömer Jan 31 '13 at 16:37
  • That can certainly help but my point was more about separating the concerns of loading/processing from that of storing the results. You can think of defining a `void read(const std::string& file, /*out*/ DataType& processedData1, ...)` or something similar and avoid the container altogether. – Utaal Feb 01 '13 at 15:17