ovgolovin
ovgolovin

Reputation: 13410

Object initialization in constructor body

I have written a class to perform output of data stored in unordered_map according to the pattern. I expect this pattern to be passed to the object at the time of it creation and stay not changed during object lifetime.

This is the class I wrote first:

class Format {
public:
  Format(const string output_pattern);
  string interpolate(const boost::unordered_map<string, string> & field_values);
private:
  ...
};

I wanted to use Formatter in another class this way:

class A {
private:
  Format formatter;
public:
  A(const std::map<std::string, std::string> & info, ... other parameters) {
    std::map<std::string, std::string>::const_iterator it;
    it = info.find("format");
    if (it == info.end()) {
      throw std::runtime_error("Can't find 'format' in info");
    } else {
      formatter = Format(it->second);
    }
  }
};

As you see, there is a bit of work before I can call constructor. No surprise, it doesn't work as format is first initialized with default constructor (which is missing), and second it expects assignment operator defined for formatter = Format(it->second); line.

I can't initialize formatter like this:

A(const std::map<std::string, std::string> & info, ... other parameters):
  formatter(...) {
  ...
}

because I have first to extract parameter to pass as formatter initializer.

So, eventually I solved it this way:

class Format {
public:
  Format();
  void set_pattern(const string output_pattern);
  string interpolate(const boost::unordered_map<string, string> & field_values);
private:
  ...
};


class A {
private:
  Format formatter;
public:
  A(const std::map<std::string, std::string> & info, ... other parameters):
    formatter()
 {
    std::map<std::string, std::string>::const_iterator it;
    it = info.find("format");
    if (it == info.end()) {
      throw std::runtime_error("Can't find 'format' in info");
    } else {
      formatter.set_pattern(it->second);
    }
  }
};

What I really don't like about this is that this turned immutable formatter object into mutable. Here it is not a big issue. But I don't need it to be mutable and don't like it when I have to make it mutable just because I can't express it in code when it is immutable.

Is there any another good approach I can take to initialize it in constructor in my case?

Upvotes: 1

Views: 351

Answers (1)

Guilherme Bernal
Guilherme Bernal

Reputation: 8313

You could do a helper private function to do the actual work:

class A {
private:
  SomethingA before;
  Format formatter;
  SomethingB after;
public:
  A(const std::map<std::string, std::string>& info) :
    formatter(extractFormat(info))
  {
    // ...
  }
private:
  string extractFormat(const std::map<std::string, std::string>& info)
  {
    // Be careful here! The object is not constructed yet, you can't fully use it
    // this->before => ok
    // this->after => not ok
    // you could make this function static just to be safe
  }
};

Upvotes: 2

Related Questions