objecttemplatesc++14template-specializationpredefined-variables

Class template instances limited to predefined objects


I want a class template template<std::size_t N> Shape, where the template parameter N represents the dimension of the Shape. There should be a limited number of predefined Shapes, such as Shape<2> SQUARE, Shape<3> CUBE and Shape<3> SPHERE. I might add more predefined Shapes in the future.

I want Shape objects only to be constructible as any of the predefined Shapes. Since the properties of these predefined Shapes remain the same at all time, it would be optimal to have them stored only once, and to have new Shape objects refer to them.

At this moment, I have the following implementation:

// Flag for the possible shapes
enum class Tag
{
    SPHERE,
    CUBE,
    SQUARE
};

template<std::size_t N>
class Shape
{
public:
    // Predefined shapes.
    static const Shape<3> SPHERE;
    static const Shape<3> CUBE;
    static const Shape<2> SQUARE;
    // Information stored about the given shapes
    const Tag tag; // tag specifying the shape
    const double v; // Shape volume/area
    const std::array<double, 2*N> surrounding_box; // Storing intervals for a surrounding box
    //... Some other information that depends on template parameter N
private:
    // Private constructor. This prevents other, unintended shapes from being created
    Shape(Tag tag, double v, const std::array<double, 2*N> surrounding_box):
            tag{tag}, v {v}, surrounding_box {surrounding_box} {};
};

// Initialization of predefined shape: SPHERE
template<std::size_t N>
const Shape<3> Shape<N>::SPHERE(Tag::SPHERE, 3.0,{{0.0,2.7,0.0,2.7,0.0,2.7}});

// Initialization of predefined shape: CUBE
template<std::size_t N>
const Shape<3> Shape<N>::CUBE(Tag::CUBE, 1.0,{{0.0,1.0,0.0,1.0,0.0,1.0}});

// Initialization of predefined shape: SQUARE
template<std::size_t N>
const Shape<2> Shape<N>::SQUARE(Tag::SQUARE, 1.0,{{0.0,1.0,0.0,1.0}});

This implementation has a few problems:

I would like to know what would be a better design pattern to achieve the above described goals. I was thinking of using the Tag as a constructor parameter and using some kind of factory. However, I have problems to get the implementation details right due to the template complications and the fact that I only want predefined Shapes to be constructible.


Solution

  • Factory pattern is what you need. It delegates the creation of the instances to another class.

    There are multiple way to implement it, you can choose the level of abstraction depending on the complexity of your problem.

    Here's a basic example

    template<std::size_t N>
    class Shape
    {
        friend class ShapeFactory;
    public:
        // Information stored about the given shapes
        const Tag tag; // tag specifying the shape
        const double v; // Shape volume/area
        const std::array<double, 2*N> surrounding_box; // Storing intervals for a surrounding box
        //... Some other information that depends on template parameter N
    private:
        // Private constructor. This prevents other, unintended shapes from being created
        Shape(Tag tag, double v, const std::array<double, 2*N> surrounding_box):
                tag{tag}, v {v}, surrounding_box {surrounding_box} {};
    };
    
    class ShapeFactory
    {
    public:
        // return value optimization
        static Shape<3> createSphere()
        {
            return Shape<3>(Tag::SPHERE, 3.0,{{0.0,2.7,0.0,2.7,0.0,2.7}});
        }
    
        static Shape<3> createCube()
        {
            return Shape<3>(Tag::CUBE, 1.0,{{0.0,1.0,0.0,1.0,0.0,1.0}});
        }
    
        static Shape<2> createSquare()
        {
            return Shape<2>(Tag::SQUARE, 1.0,{{0.0,1.0,0.0,1.0}});
        }
    };
    

    Since the properties of these predefined Shapes remain the same at all time, it would be optimal to have them stored only once, and to have new Shape objects refer to them.

    if you strictly want to use this approach, you can refer to Prototype Pattern