0

I am trying to make a class ShapeManager which is instantiated only once, and have a base class Shape(and all of its children) have access to it. This would result in a structure that looks like this:

ShapeManager
Shape
Square
Triangle
Circle


#include "Shape.h" // so that i can use Shape in the vector

// This is instantiated once in main
class ShapeManager {
public:
    typedef std::vector<std::unique_ptr<Shape>> VecShape;
    typedef std::default_random_engine          DRE;

private:
    VecShape&   m_shapes;
    b2World&    m_world;
    DRE&        m_gen;

public:
    ShapeManager(VecShape& vectorShapes, b2World& world, DRE& gen) 
        : m_shapes(vectorShapes), m_world(world), m_gen(gen) {}

    VecShape& getShapeList() { return m_shapes; }
    b2World& getWorld() { return m_world; }
    DRE& getDRE() { return m_gen; }
};

#include "ShapeManager.h" // so that i can access it's member functions

class Shape {
protected:
    std::string m_name;

public:
    Shape() : m_name("Shape") {}
    std::string getName() { return m_name; }
};

I simplified my code a lot to keep it relevant to my question. If it's unclear what i'm asking i will try to explain it further! Basically i just want to have a class that manages my shape objects & holds data that Shape's children will use such as m_gen and m_world.

EDIT 1: My main motivation to creating a ShapeManager is to store a reference to m_shapes, m_world, and m_gen, but i don't want every shape object to have those 3 reference variables. That would mean creating a Circle would have to have those 3 parameters in the ctor, along with the parameters relevant to the circle.

EDIT 2: Singleton and/or Dependency Injection are the two design patterns that, when implemented, solve my issue. Here is some info on it. Thank you all!

Chris
  • 40
  • 6
  • you can store a global instance of `ShapeManager` (or anything like that) and use it. – apple apple Jan 31 '18 at 02:11
  • Warning: ShapeManager.h includes Shape.h and Shape.h includes ShapeManager.h. This is a circular dependency. Reading [Resolve build errors due to circular dependency amongst classes](https://stackoverflow.com/questions/625799/resolve-build-errors-due-to-circular-dependency-amongst-classes) may head off future problems. – user4581301 Jan 31 '18 at 02:11
  • Why would the shapes need to interact with the shape manager? I would expect the other way around. You may be conflating management and services. – user4581301 Jan 31 '18 at 02:19
  • I've tried to declare a global ShapeManager right above main() but it still isn't accessible by Shape's children. – Chris Jan 31 '18 at 02:22
  • The Shapes need to access the references stored in ShapeManager – Chris Jan 31 '18 at 02:23
  • What is your question? – user253751 Jan 31 '18 at 02:43
  • @Lindens You probably forgot to declare the global variable in the header file. – user253751 Jan 31 '18 at 02:46
  • @immibis How can i do that if the type of the global variable is `std::unique_ptr`? I would need to include the shapemanager class in my shape.h which is a circular dependancy because shapemanager also needs shape. I think i'm missing some real fundamentals on code structure/design – Chris Jan 31 '18 at 02:54
  • @Lindens use a "forward declaration" - `class ShapeManager;` - before that declaration. But you'll also find that your shape.h *shouldn't need* to include shapemanager.h. If it does need that (because all your member functions are defined in shape.h) then to resolve it you'll need to not define all your member functions in shape.h – user253751 Jan 31 '18 at 02:56
  • 1
    You're asking how to make a **singleton** object. This is a well-known (although not always liked) pattern, which you will have no problem using now that you know what word to search for. – Ben Voigt Jan 31 '18 at 03:10
  • See https://stackoverflow.com/a/45550373/2785528 - You can pass a reference of a non-global object (ngo) to any/every object instance that can use the ngo, by creating the ngo in main, prior to where the ngo is needed, and simply passing a reference to ngo through the ctors. You can even collect the (possibly n) would-be-globals into a single ngo container and pass that reference. – 2785528 Jan 31 '18 at 03:19
  • @BenVoigt I think this is what i'm looking for. Learning how to implement the singleton design would clear up a lot of confusion i have (and perhaps lead me to using another design since its so looked down upon). Not sure how to make this the answer – Chris Jan 31 '18 at 03:25

2 Answers2

1

Don't try and make a global variable accessible through all your code. Make a certain object accessible only to who needs it. Thus, make a private ShapeManager in Shape and pass it through Shapes constructor.

But it's still strange that Shape has to have access to ShapeManager.

EDIT - Comment by the author: My main motivation to creating a ShapeManager is to store a reference to m_shapes, m_world, and m_gen, but i didn't want every shape object to have those 3 reference variables.

You can have a struct that stores your 3 reference variables and each object that needs access to them would have a private struct.

Some other file:

struct myManagers {
ShapeManager *shapeMan;
WorldManager *worldMan;
GenManager *genMan;
}

Shape.h:

#ifndef H_SHAPE // Include guards to avoid circular dependency
#define H_SHAPE
#include "ShapeManager.h" // so that i can access it's member functions

class Shape {
protected:
    std::string m_name;
    myManagers *myMan = nullptr;

public:
    Shape(myManagers *myManager) : m_name("Shape"), myMan(myManager) {}
    std::string getName() { return m_name; }
};

#endif

and usage would be something like:

int main() {
myManagers GlobalManager;
GlobalManager.shapeMan = &ShapeManager;
// ... fill in other 2 managers

Shape myBall(&GlobalManager);
// this way myBall has access to all 3 references
}

ps: include guards are pretty much the only occasion where macros are acceptable

Pedro
  • 74
  • 1
  • 11
  • 1
    use `pragma once;` – Jake Freeman Jan 31 '18 at 02:59
  • My main motivation to creating a `ShapeManager` is to store a reference to m_shapes, m_world, and m_gen, but i didn't want every shape object to have those 3 reference variables. That would mean creating a `Circle` would have to have those 3 parameters in the ctor, along with the parameters relevant to the circle. – Chris Jan 31 '18 at 03:03
  • `pragma once;` is non standard and **probably** works on any modern compiler, but it might not. [Pragma once or ifndef?](https://stackoverflow.com/questions/787533/is-pragma-once-a-safe-include-guard) – Pedro Jan 31 '18 at 03:17
  • Would be nice to know the reason of the downvote :) – Pedro Jan 31 '18 at 03:18
  • This is a good way to go about it although i really want my shape ctors to only have parameters specific to themselves such as `Circle(float radius, sf::Vector2f pos);` and simply have access to those common variables. If i can't find a way to do this ill probably use your method! _ps: i brought you back to 0 lol_ – Chris Jan 31 '18 at 03:40
  • @Lindens it is possible to have a global variable but it's a **terrible** programming practice. Another way I think might be possible is having a class with the 3 managers `static` and include this class header in the classes you want access to, but I don't have enough experience to say if it's a good approach. [Static Reference](http://en.cppreference.com/w/cpp/language/static) – Pedro Jan 31 '18 at 20:33
1

What you are looking for is Singleton Design Pattern. See the code below:

class ShapeManager
{
public:
static ShapeManager& instance()
{
    static ShapeManager *instance = new ShapeManager();
    return *instance;
}

private:
    ShapeManager(){}

/*
    use this space to fill in your code
    */
}

You can access the class members as:

ShapeManager::instance().getDRE();

This limits to a single instance of the class. There's no need to instantiate an object of this class, in fact, you can not and can be accessed from anywhere.

Mahesh Nepal
  • 1,385
  • 11
  • 16
  • 3
    Dependency injection is better than singletons, unless your object is so lightweight that you can't afford an extra pointer. It allows mocking and prepares for the day when you discover you need *multiple* singletons. – Mark Ransom Jan 31 '18 at 04:07