Reputation: 1003
Is there a way to call the parent constructor with different arguments depending on the value of an argument that the child constructor has?
I have the following parent class:
class Rectangle
{
public:
Rectangle(std::string name, glm::vec3 top_left_corner, float height, float width, glm::vec3 color, bool fill);
~Rectangle();
//...
}
And the child class:
class Wall :
public Rectangle
{
public:
Wall(std::string name, Position position, float scene_height, float scene_width, float thickness, glm::vec3 color);
~Wall();
//...
}
Where Position
is an enum
that should dictate what arguments the parent constructor should be called with:
enum Position { UP, DOWN, LEFT, RIGHT };
So basically, I would like to have something like this in the child constructor:
Wall::Wall(std::string name, Position position, float window_height, float window_width, float thickness, glm::vec3 color) {
switch(position) {
case UP:
Rectangle(name, glm::vec3(0, window_height, 0), thickness, window_height, color, true);
break;
case DOWN:
Rectangle(name, glm::vec3(0, thickness, 0), thickness, window_width, color, true);
break;
case LEFT:
Rectangle(name, glm::vec3(0, window_height, 0), window_height, thickness, color, true);
break;
case RIGHT:
Rectangle(name, glm::vec3(0, window_width - thickness, window_height), window_height, thickness, color, true);
break;
}
}
But as you know, I have to call the parent constructor first, like:
Wall::Wall(std::string name, Position position, float window_height, float window_width, float thickness, glm::vec3 color)
: Rectangle(name, glm::vec3(0, window_width - thickness, window_height), window_height, thickness, color, true) {}
And that doesn't give me much leeway. What would a good, object-oriented approach be?
Upvotes: 5
Views: 1004
Reputation: 2748
I don't know what is a good OOP approach, but I do know how to do this in C++
at compile-time, if this is what you want.
template <class T, T val>
struct constant
{
#if __cplusplus >= 201703L
constexpr const static inline T value = val;
#else
constexpr const static T value = val;
#endif
};
class Wall: public Rectangular
{
Wall(..., constant<Position, Position::UP>, ...):
Rectangular(...) {}
// Providing other overload to initialize your base code differently
};
Edit:
Actually, in your case, you can replace the class constant
with std::integral_constant
for convenience.
Upvotes: 0
Reputation: 574
In the constructor You are sending duplicate data inside parent class top_left_corner
can be obtained from other arguments inside parent constructor.
You can change the top_left_corner
with your enum
and then calculate it inside the constructor.
if you cannot change parent constructor, I think You can use lambda in parent initialization and use same switch case You wrote
Upvotes: -1
Reputation: 217235
Create a factory method:
Rectangle MakeRectangle(const std::string& name,
const Position& position,
float window_height, float window_width,
float thickness,
const glm::vec3& color)
{
switch(position) {
case UP:
return Rectangle(name,
glm::vec3(0, window_height, 0),
thickness,
window_height,
color,
true);
case DOWN:
return Rectangle(name,
glm::vec3(0, thickness, 0),
thickness,
window_width,
color,
true);
case LEFT:
return Rectangle(name,
glm::vec3(0, window_height, 0),
window_height,
thickness,
color,
true);
case RIGHT:
return Rectangle(name,
glm::vec3(0, window_width - thickness, window_height),
window_height,
thickness,
color,
true);
}
throw std::runtime_error("Invalid position");
}
Then
Wall::Wall(std::string name,
Position position,
float window_height, float window_width,
float thickness,
glm::vec3 color)
: Rectangle(MakeRectangle(name, position, window_height, window_width, thickness, color)){
// ...
}
Upvotes: 5
Reputation: 21779
If I read correctly, in all cases you call the same constructor of the parent class, just the arguments are different. In this setting you can "inject" arbitrary code with a help of a function. For example
class Wall {
private:
static glm::vec3 top_left_corner(Position position, float window_height, float window_width, float thickness) {
switch (position) {
case UP: return glm::vec3(0, window_height, 0);
case DOWN: return glm::vec3(0, thickness, 0);
case LEFT: return glm::vec3(0, window_height, 0);
case RIGHT: return glm::vec3(0, window_width - thickness, window_height);
}
}
// similary for other arguments
And then you can invoke your constructor like this:
Wall::Wall(std::string name, Position position, float window_height, float window_width, float thickness, glm::vec3 color)
: Rectangle(name,
top_left_corner(position, window_height, window_width, thikness),
....... /* other arguments */
) {}
Upvotes: 2