In older C++98, I don't believe there was any good way to reuse a temporary result in an initializer list to initialize multiple members of an object.
Has this changed at all in newer versions of C++ (11, 14, 17)?
Consider the following code:
//
// compileShaders()
//
// Takes a string containing the source code to possibly hundreds of shaders
// for an effect (In this case, there are only 2 shaders for this effect)
//
// Returns a vector of compiled bytecode for each shader in the effect
//
std::vector<std::string> compileShaders(
const std::string& sourceCode,
const RenderTargetLayout& layout);
//
// class ScaleDownEffect
//
// Scales image to 1/4 in each dimension by using two 1/2 passes
//
class ScaleDownEffect
{
public:
ScaleDownEffect(const TargetImage& targetImage)
: m_renderTarget(targetImage),
m_firstPassShader(
compileShaders(
ScaleDownEffectShaderSource,
m_renderTarget.getLayout()
)[0],
m_renderTarget.getWidth() / 2,
m_renderTarget.getHeight() / 2
),
m_secondPassShader(
compileShaders(
ScaleDownEffectShaderSource,
m_renderTarget.getLayout()
)[1],
m_renderTarget.getWidth() / 4,
m_renderTarget.getHeight() / 4
)
{
}
private:
RenderTarget m_renderTarget;
Shader m_firstPassShader;
Shader m_secondPassShader;
};
compileShaders()
is a very heavyweight call, and I don't really think it's a good idea to call it twice when I don't really have to.
Note None of the three objects being initialized have default ctors, so doing it in the body of the function is not really an option.
What do we think? Is there any good way for me to do this, or should I switch to smart pointers and dynamically allocate the objects I contain?
You can use delegating constructor since C++11:
class ScaleDownEffect
{
ScaleDownEffect(const TargetImage& targetImage,
const std::vector<std::string>& shaders)
: m_renderTarget(targetImage),
m_firstPassShader(shaders[0],
m_renderTarget.getWidth() / 2,
m_renderTarget.getHeight() / 2),
m_secondPassShader(shaders[1],
m_renderTarget.getWidth() / 4,
m_renderTarget.getHeight() / 4)
{}
public:
ScaleDownEffect(const TargetImage& targetImage)
: ScaleDownEffect(targetImage,
compileShaders(ScaleDownEffectShaderSource,
targetImage.getLayout()))
{
}
private:
RenderTarget m_renderTarget;
Shader m_firstPassShader;
Shader m_secondPassShader;
};
You can use a nested type:
class ScaleDownEffect
{
struct Shaders
{
explicit Shaders(const std::vector<std::string>& s, unsigned int w, unsigned int h)
: m_firstPassShader(
s[0],
w / 2,
h / 2
),
m_secondPassShader(
s[1],
w / 4,
h / 4
)
{}
Shader m_firstPassShader;
Shader m_secondPassShader;
};
RenderTarget m_renderTarget;
Shaders m_shaders;
public:
ScaleDownEffect(const TargetImage& targetImage)
: m_renderTarget(targetImage),
, m_shaders(compileShaders(ScaleDownEffectShaderSource,
m_renderTarget.getLayout()),
m_renderTarget.getWidth(),
m_renderTarget.getHeight())
{
}
};
You could also pass a reference to m_renderTarget
to the nested type's c'tor instead of width and height (which I just assumed to be of type unsigned int
, btw).
There is one disadvantage in that you now need to access the first pass shader e.g. as m_shaders.m_firstPassShader
. I recommend adding (inline) getter methods, even if they return references, and even if they are private, to isolate code using the shaders from changes in the way they are stored.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With