Sign in to follow this  
Azzazelus_13

Structure issue

Recommended Posts

Hy.. Ive done some simple framework on my DirectX application with simple classes with public members. But now ive started over again to arange things more eficient. I first atepted to make a big class Device and other classes in that class like:
class Device {
public:
   LPDIRECT3DDEVICE9   g_pd3dDevice;

   class TextureManager {
         //blah blah, this class needs Device data;
   }

};

But then will be a really chaos as i want to put every class in a separate file. Ive tryed some stuff with the inheritance. BUT.. Lets say i have the Device class and TextureManager class inherited form Device I declare an object from the Device class (device1) and one from the TextureManager; After that i construct the object device1.createDevice(width,height etc...) How the TextureManager object will have acces to device1 object members? From inheritage i only make the Texture Class have the same members as the Device. But im not gonna create the device every time a object is inherited from Device class. I hope i was crealy :p Yhanks, bye.

Share this post


Link to post
Share on other sites
Why are you deriving TextureManager from Device?

If you want your texture manager to have access to the device, you can pass it a reference to the device as a constructor parameter:


Device device = new Device(...);
TextureManager texMgr = new TextureManager(device, ...);

Share this post


Link to post
Share on other sites
This is generally a sign that your design is not detailed enough: what do the classes actually do?

I can only assume that the Device class is some representation of an abstract rendering device (either an actual hardware device, or a software emulation of one). RAII dictates that the program should connect to an underlying device upon instantiation of the Device class, and should disconnect upon destruction.

Other typical responsibilities of a device include setting up the rendering pipeline (generally represented as a state machine), manipulating textures and vertices, and so on. Since all this functionality is already present in IDirect3DDevice, the simplest way to achieve this is to encapsulate it in a class, and overload operator* and operator-> to forward any calls to the device:


// Acts as a "smart pointer" to a device. Usable in
// a boost::shared_ptr, for example.
class device {
public:
typedef IDirect3DDevice9 device_type;

private:
device_type *d3d_device;

public:
device() : d3d_device(0) {
// Set up the device with default parameters here,
// throw an exception upon failure.
assert (d3d_device);
}
~device() {
assert (d3d_device);
// Release the device here
}

device_type* operator->() {
assert (d3d_device);
return d3d_device;
}

device_type& operator*() {
assert (d3d_device);
return *d3d_device;
}
};



Of course, the same thing could be done for textures (but this time, instead of default initialization, we create a texture on a destination device from a set of bytes representing the contents in a predefined format, such as an argb pixel buffer).

class texture {
public:
typedef IDirect3DTexture9 texture_type;
typedef std::vector<argb_pixel> data_type;

private:
texture_type *d3d_texture;
device & destination;

public:
texture(const data_type & pixel_data,
device & destination) :
d3d_texture(0), destination(destination)
{
// Set up the texture by copying from the data buffer
// to the texture. Use the destination device.
assert (d3d_texture);
}

~texture() {
assert (d3d_texture);
// Release the texture and clean up here
}

texture_type* operator->() {
assert (d3d_texture);
return d3d_texture;
}

texture_type& operator*() {
assert (d3d_texture);
return *d3d_texture;
}
};



Now, the objective of the TextureManager is to manage textures, but that description is woefully lacking details. So, for the sake of the example, I'll decide that "managing textures" means "load textures from files on demand, but cache them in memory". Which makes it obvious that the class has two responsibilities: on the one hand, it handles texture loading, and on the other hand it handles texture caching. Since caching isn't something exclusive to textures (it can be done for about any data that's difficult to create), it's possible to move it to its own class, a Cache, and move the texture loading facilities into a TextureLoader object.

The texture loader simply loads the specified textures from the named files. Where does it put them? On a device. Which? The device which is specified as a constructor argument, for instance.

class texture_loader 
{
device& destination;
public:

typename std::string key_type;
typename texture value_type;

texture_loader(device& destination)
: destination(destination) {}

// The loading function returns a pointer to a heap-allocated
// freshly loaded resource. It is never null, but an exception
// may be thrown if a loading error is encountered.
value_type* load(const key_type & key) {
value_type::data_type pixel_data;
// fill in data from file 'key'
return new value_type(pixel_data,destination);
}
};



Then, a Cache works in conjunction with a loader (which can be of various types, so it is provided as a template argument, with the constraint that it's copy-constructible) to store objects. As is typical with cache systems, a means of flushing unused data is necessary, so the cache can manipulate its data by a smart pointer system, such as boost::shared_ptr.

template<typename loader_t>
class cache {
public:
typedef loader_t loader_type;
typedef loader_type::key_type key_type;
typedef loader_type::value_type value_type;
typedef boost::shared_ptr<value_type> pointer_type;
typedef std::map<key_type,pointer_type> storage_type;
typedef storage_type::size_type size_type;

private:
storage_type storage;
loader_type loader;

public:

// Construction
cache(const loader_type & loader) : loader(loader) {}

// Acquires ownership of value and stores it in the map
void add(const key_type & key, const value_type* value)
{
assert (value);

storage_type::iterator it = storage.find(key);
if (it != storage.end())
throw std::exception("Element already exists in cache");

storage[key] = value;
}

// Removes a value from the cache (silently does nothing if it
// does not exist)
void remove(const key_type & key)
{
storage_type::iterator it = storage.find(key);
if (it != storage.end())
storage.erase(it);
}

// Retrieves a value from the cache, or creates it if it does
// not exist.
pointer_type get(const key_type & key)
{
storage_type::iterator it = storage.find(key);
if (it != storage.end())
return it->second;

pointer_type ptr = loader.load(key);
storage[key] = ptr;
return ptr;
}

// Clean subscript operator sugar
pointer_type operator[](const key_type & key)
{
return get(key);
}

// The size of the cache
size_type size() const {
return storage.size();
}

// Flush up to N values from the cache.
void flush(unsigned N) {
// Traverse cache, determine if unused outside, remove.
}
};



With the obvious:
typedef cache<texture_loader> texture_cache;

device my_device = device();
texture_cache my_texture_cache = texture_cache(texture_loader(my_device));

Share this post


Link to post
Share on other sites
Wow, nice example..thanks.

So I will do separate classes instead a main class and classes inside of it ?
The main goal was to make objects not depend on other objects (like the example texture_manager(device). Ive tryed to avoid but its ok that way..

Share this post


Link to post
Share on other sites
Quote:
Original post by Azzazelus_13
Hy..

Ive done some simple framework on my DirectX application with simple classes with public members. But now ive started over again to arange things more eficient.

I first atepted to make a big class Device and other classes in that class like:

*** Source Snippet Removed ***

But then will be a really chaos as i want to put every class in a separate file.


Ive tryed some stuff with the inheritance. BUT..

Lets say i have the Device class and TextureManager class inherited form Device

I declare an object from the Device class (device1) and one from the TextureManager;

After that i construct the object
device1.createDevice(width,height etc...)

How the TextureManager object will have acces to device1 object members? From inheritage i only make the Texture Class have the same members as the Device. But im not gonna create the device every time a object is inherited from Device class.

I hope i was crealy :p
Yhanks, bye.


I second Gage64.. TextureManager should not be derived from Device.
You could pass the device a an argument to TextureManager's constructor...

OR a better idea, since you probably only need 1 device (I'm not sure though, never used DirectX) in that case, you could make Device a Singleton and call the static function Device::getInstance() from TextureManager.

Share this post


Link to post
Share on other sites
Quote:
OR a better idea, since you probably only need 1 device (I'm not sure though, never used DirectX) in that case, you could make Device a Singleton and call the static function Device::getInstance() from TextureManager.


That is not a better idea.

Share this post


Link to post
Share on other sites
Multiple devices are a possibility, and one that is occasionally useful.

Furthermore, "probably only need one" is not a good reason to use a singleton. As the device has no implementation restrictions that make it impossible to implement without a singleton, electing to employ a singleton would consistute a poor design decision.

It would also allow the OP to completely bypass the problem he is struggling with in favor of fixing a symptom.

EDIT:
Beaten to my own link. Bah.

Share this post


Link to post
Share on other sites
Quote:
Original post by Azzazelus_13
The main goal was to make objects not depend on other objects (like the example texture_manager(device). Ive tryed to avoid but its ok that way..


The fact is that a texture cannot exist without being associated with a device, and by consequence anything which might create a texture will also need to be associated, directly or indirectly, with one or more devices. This is an element of the reality you are trying to model with your program—you may choose to make the approximation that the texture creator does not explicitly depend on a device in your program (as would be the case with a global device, including a singleton), but that dependence will remain anyway, only it will be implicit instead of explicit. There's nothing you can do about that, because you can only control your program, not the reality that the program models. And, of course, there's nothing worse than an implicit dependency, because these are the most easily overlooked and thus the most dangerous to have.

Share this post


Link to post
Share on other sites
Quote:
Original post by jpetrie
Multiple devices are a possibility, and one that is occasionally useful.

Furthermore, "probably only need one" is not a good reason to use a singleton. As the device has no implementation restrictions that make it impossible to implement without a singleton, electing to employ a singleton would consistute a poor design decision.

It would also allow the OP to completely bypass the problem he is struggling with in favor of fixing a symptom.

EDIT:
Beaten to my own link. Bah.


You seem to have a very strong stance against Singletons.

I personally don't like to use singletons where they're not needed, but they're still useful in some cases. According to your article they're never useful...

although they do have some advantages

Though I admit you are right about this case: using a singleton is a bad idea... but the article you gave is really biased against singletons and it doesn't really provide good explanations...

Anyway that was off-topic now, cheers

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this