Reputation: 2531
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)
Upvotes: 4
Views: 1480
Reputation: 308206
If the Texture
class has a default constructor and supports swapping, you can initialize the resource with a local variable and swap it at the end of the constructor.
Skybox::Skybox(const std::string& fileName)
{
Texture localTex(fileName);
//...
tex_.swap(localTex);
}
Upvotes: 0
Reputation: 163287
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.
Upvotes: 10
Reputation: 59811
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>
.
Upvotes: 2
Reputation: 1
Using C++11 features (variadic templates and perfect forwarding), this can be achieved thanks to a template constructor :
#include <utility>
template<class T>
class raii_wrapper
{
public:
template<typename... Arg>
raii_wrapper(Arg&&... args) : obj(std::forward<Arg>(args)...) {}
private:
T obj;
};
struct foo
{
foo(){}
};
struct foo_1
{
foo_1(int){}
};
struct foo_2
{
foo_2(int,int&){}
};
int main()
{
raii_wrapper<foo> f;
raii_wrapper<foo_1> f1(1);
int i(3);
raii_wrapper<foo_2> f2(1,i);
return 0;
}
In C++03/98, template constructor is still the solution (but boost shall help for variadic template and argument passing). See implementations of functions like make_share_ptr.
Upvotes: 0