Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can RAII be applied for class members that require extended initialization?

As far as I understand the RAII idiom when applied to resources needed by a class (and please correct me if I'm wrong), a class that requires a resource should define a member of the appropriate type, and its destructor will be called automatically when the using class instance is destroyed, like this:

class Skybox
{
    public:
        Skybox() : tex_(...) {}

    private:
        Texture tex_;
};

Apart from using a smart pointer to allocate the resource on the heap, how can this pattern be applied if the resource member requires some code to be executed in the Skybox constructor, before the initialisation of the resource? For example:

class Skybox
{
    public:
        Skybox(const std::string& fileName);

    private:
        Texture tex_;
}

Skybox::Skybox(const std::string& fileName)
{
    // read stuff from skybox initialization file
    // including various texture parameters such as texture file
    ...
    // initialize tex_ based on information read above
}

Update: the Texture class requires all initialization to be performed in its constructor (i.e. no Texture::Init() method is available)

like image 411
Dan Nestor Avatar asked Dec 17 '22 07:12

Dan Nestor


2 Answers

Wrap the initialization code into a function, and use that function (member or non-member, static or non-static, as appropriate) to initialize the member variable:

Texture Skybox::init_tex(std::string const& fileName) {
  // read stuff from file, including textureFile
  // initialize result
  return Texture(...);
}

Skybox::Skybox(std::string const& fileName):
  tex_(init_tex(fileName))
{ }

The initialization function should probably be a static function. If it isn't, be careful not to use any members that haven't been initialized yet — you're calling init_tex on a not-yet-fully initialized Skybox instance.

like image 112
Rob Kennedy Avatar answered Dec 31 '22 02:12

Rob Kennedy


Maybe you should encapsulate the creation of a texture into a free function as the reading of the file seems unrelated to the Skybox and could possibly useful somewhere else. I guess another name for this is Factory.

Tex tex_from_file(const std::string&) {
  // ...
}

class Skybox {
  Skybox(const std::string& s) : tex_(tex_from_file(s)) {}
};

Even nicer would be a Skybox constructible from a Tex object. However, this requires Tex to be copy or move constructible. If this isn't the case a proper workaround could be to return a std::unique_ptr<Tex>.

like image 31
pmr Avatar answered Dec 31 '22 01:12

pmr