1

In class A I have one member that needs to be accessed hundreds or thousand times per n milliseconds, e.g. a LPDIRECT3DDEVICE3D needs to be accessed to draw anything.

Used to have a global but that doesn't seem right. On my research I found this answer to the OP's question:

jalf: "That's painful, and so it's a wake-up call: I am doing something wrong. That many object shouldn't need to know about the screen buffer. How can I fix this?`)" @ this answer alinea 8

Basically I want to know how to treat such a global variable, what other options are there?

Community
  • 1
  • 1
  • Your options are pretty much to use a giant for loop, or to create some sort of disseminator class. – Wug Aug 14 '12 at 20:06
  • As class A could serve as one, how would a disseminator class look like? That is actually my problem. –  Aug 14 '12 at 20:11
  • You could probably accomplish it with static methods of the class itself. You would need to maintain a list of "registered" instances of the object (it may be beneficial to be able to exclude objects) and the methods would simply take the arguments to supply to the functions and call them for every instance of the class in the list. There's no way to avoid looping, you have to do it somewhere. – Wug Aug 14 '12 at 20:19

3 Answers3

0

One good approach is to have objects that you all want to use A::member to register themselves with a datastructure (vector of weak or raw pointers, map of pointers, etc..) upon construction and then when you want to apply A::member to each object you can loop through all registered objects and process the request on each. Note it's easy to fall into a trap with using globals here -- try to avoid it.

If you have objects of varying types (no inheritance hierarchy) you can use type erasure to store the function pointer (or function object like boost::function) for each object which will take A::member and apply it to that object. This would allow any abstract object to use A::member without needing to know the types of your x number of objects. Getting the functions to match could be as simple as adding a similar formatted member function to each object or doing a boost::bind to wrap on the call that does the work into a format which matches.

This approach eliminates a lot of boiler-plate code and give you easy extensiblity with various new object types to all use A::member at some specified notification time.

Pyrce
  • 8,296
  • 3
  • 31
  • 46
  • 1
    quote: "using globals here -- try to avoid it", the reason why I asked :D –  Aug 14 '12 at 20:31
  • @JohnSmith Have a shared resource object (non global) that you pass to objects which share the given state you care about. You can make an Application object which holds shared state for anything dealing with applications. It's usually easy to move global state into shared state. The hard part is passing that holder among objects which need to use it. Usually this involves adding a parameter to object constructors which tells the object where shared state exists. – Pyrce Aug 14 '12 at 20:33
  • Thus far I am using a template class that maintains the life cycle of the objects. Considering to pass a pointer or reference (researching that) of A::member to that container class. Then it should be easy enough to pass a pointer or reference to the constructors of the new created objects. Looks awkward too I think. –  Aug 14 '12 at 20:51
  • @JohnSmith It's also possible that the functionality you're trying to achieve is overly complicated. Think about why those objects all need to be given A::member and when they use that variable. It might be easier for each object to just ask for A::member when it needs it, or to only be creatable once A::member is available. – Pyrce Aug 14 '12 at 21:42
  • @all: "ask for A::member when it needs it", that's my problem which I can't figure out how to do that without having A global. How does any other object know about A without having A global? –  Aug 15 '12 at 07:19
  • @JohnSmith Have each object that might use A::member be passed during construction a pointer or reference to the shared object which holds that data. That container doesn't need to be global either, it just has to be passed to each object -- so you can make the shared data container first, then pass it along to each object which uses that data. This achieves a similar effect as globals, but without a lot of the nasty side effects (mainly state resets of tests). The cost comes in the fact that you now have to pass this container around in constructors. – Pyrce Aug 15 '12 at 16:41
0

One thing I can think of is to pass that A::member to all functions that need access to it, however it would be a ridiculous amount.

there are a lot of approaches -- they all over programs you use every day.

  • you could hold a reference
  • pass a reference (as you mentioned)
  • use multiple instances in some cases
  • use methods to abstract the fact that 3 calls are actually required to call the method (Principle of Least Knowledge)
  • use smaller, more specialized objects
  • consider other forms of abstraction and/or wrapping
  • consider other design patterns in some places

just begin writing the program -- not blindly because you could end up with high coupling -- observe usage patterns and refactor and restructure accordingly. this is one reason globals are bad -- they are very difficult to unroot, but this is pretty much the process one must go through when removing one which has a lot of references.

justin
  • 104,054
  • 14
  • 179
  • 226
0

Your choices boil down to this: if a function needs data, that data must:

  1. be passed to the function as a parameter, or
  2. be in a place where the function can effectively query for it.

(I'll ignore 3. or be generated by the function itself.)

If you want to pass A::member, you have several options. You can put the data in a context object and pass it around (this can be effective if you have a handful of these members that you want to pass around, and happens to works well in the presence of unit tests). You can pass the data directly into the function. You can pass the data to something that will in turn pass it to the function. You can use message passing. You can use more indirect mechanisms like publish/subscribe queues. You can choose how much knowledge such intermediaries have about the type of data they are passing. All of these imply various kinds of coupling between the source, transport, and destination as you pass the data from one place to another. At best this coupling can be irritating at scale; at worst this can be a serious design or security flaw.

If you want to put A::member somewhere where it can be queried, you also have several options. A global variable is one. A data store that can be accessed (e.g. a file, a database, a service, a cache, an HTTP resource) is another. Each of these has implications: you need to consider who can access the data and how. If access is easy (like in global variables) you have the problem of how to evolve your system without breaking clients of your data. You may also have problems when it comes time to test your code. At best this coupling can be irritating at scale; at worst it can be a serious design flaw and lead to practically untestable code.

If you'd like to pass the data, but you don't like the number of places you need to pass it to, your other angle of attack is to limit the number of functions that need your data. I.e. consolidate your code so that fewer pieces of code actually need to know about A::member. Various patterns like Facade, Bridge, Observer, and Abstract Base Class may help you at the language level. Architectural patterns such as Publish-Subscribe and Callback may also be able to help. Research the topics of decoupling, refactoring, and dependency elimination for inspiration.

So which of these is the right way? There is no one right way. You'll need to look at your particular case, weigh the options and tradeoffs for that case, and pick the one you like best.

Owen S.
  • 7,665
  • 1
  • 28
  • 44