Jump to content

  • Log In with Google      Sign In   
  • Create Account


#Actualfastcall22

Posted 19 December 2012 - 02:42 PM

I recently solved this problem with what I call a "doubly-opaque pointer" (is there a named pattern or idiom for this?). The idea is based on a service provider object, which uses (abuses) forward declarations, template functions, and the opaque pointer idiom to provide a strongly typed pointer to its users. Only the object that constructs the ServiceManager will need to construct (and/or setup) all of its services. When setup correctly, only the ServiceManager.cpp and the object that sets up the ServiceManager will need to be recompiled if a service is added to the ServiceManager.

Here's a sample code sample:
// ServiceManagerImpl.h
class ResourceManager;
class SceneManager;
class RenderManager;

struct ServiceManagerImpl {
	ResourceManager* resman;
	SceneManager* sceneman;
	RenderManager* renderman;
};
// ServiceManager.h
struct ServiceManagerImpl;

class ServiceManager {
public:
	ServiceManager(ServiceManagerImpl* impl);
	
public:
	template<class ServiceType>
	ServiceType* get();
	
private:
	ServiceManagerImpl* impl;
};
// ServiceManager.cpp
#include "ServiceManager.h"
#include "ServiceManagerImpl.h"

ServiceManager::ServiceManager(ServiceManagerImpl* impl) : impl(impl) {
}

template<> ResourceManager* ServiceManager::get() { return impl->resman; }
template<> SceneManager* ServiceManager::get() { return impl->sceneman; }
template<> RenderManager* ServiceManager::get() { return impl->renderman; }

And a sample demonstratation:
// Game.cpp
void Game::Setup() {
	ServiceManagerImpl* impl = new ServiceManagerImpl;
	impl->resman = new ResourceManager;
	impl->renderman = new RenderManager;
	impl->sceneman = new SceneManager;
	
	theServiceManager = new ServiceManager(impl);
}

void Game::Dothings() {
    for ( int idx = 0; idx < entities.size(); ++idx )
		entities[idx]->update(theServiceManager);
}
// MonsterEntity.cpp
#include "MonsterEntity.h" // "ServiceManager.h" (implied)
#include "RenderManager.h"

void MonsterEntity::Update(ServiceManager& services){
	services.get<RenderManager>()->renderThing(iDunno);
}
// PlayerEntity.cpp
#include "PlayerEntity.h" // "ServiceManager.h" (implied)
#include "SceneManager.h"

void PlayerEntity::Update(ServiceManager& services) {
	services.get<SceneManager>()->currentScene()->setCameraPosition(getPosition());
}

As you can see, a new service can be added to ServiceManagerImpl.h with an accompanying template specialization for ServiceManager::get in ServiceManager.cpp without affecting ServiceManager.h and none of the ServicesManager users need to be recompiled. Only Game.cpp and ServiceManager.cpp need to be recompiled (expensive for Game.cpp, relatively light for ServiceManager.cpp).

I'm not sure how this rates on the OOP-perversion scale, but I think this is pretty neat.

EDIT:
Here's an image illustrating such an approach as well
http://i.imgur.com/rbPhD.png

#4fastcall22

Posted 19 December 2012 - 02:17 PM

I recently solved this problem with what I call a "doubly-opaque pointer" (is there a named pattern or idiom for this?). The idea is based on a service provider object, which uses (abuses) forward declarations, template functions, and the opaque pointer idiom to provide a strongly typed pointer to its users. Only the object that constructs the ServiceManager will need to construct (and/or setup) all of its services. When setup correctly, only the ServiceManager.cpp and the object that sets up the ServiceManager will need to be recompiled if a service is added to the ServiceManager.

Here's a sample code sample:
// ServiceManagerImpl.h
class ResourceManager;
class SceneManager;
class RenderManager;

struct ServiceManagerImpl {
	ResourceManager* resman;
	SceneManager* sceneman;
	RenderManager* renderman;
};
// ServiceManager.h
struct ServiceManagerImpl;

class ServiceManager {
public:
	ServiceManager(ServiceManagerImpl* impl);
	
public:
	template<class ServiceType>
	ServiceType* get();
	
private:
	ServiceManagerImpl* impl;
};
// ServiceManager.cpp
#include "ServiceManager.h"
#include "ServiceManagerImpl.h"

ServiceManager::ServiceManager(ServiceManagerImpl* impl) : impl(impl) {
}

template<> ResourceManager* ServiceManager::get() { return impl->resman; }
template<> SceneManager* ServiceManager::get() { return impl->sceneman; }
template<> RenderManager* ServiceManager::get() { return impl->renderman; }

And a sample demonstratation:
// Game.cpp
void Game::Setup() {
	ServiceManagerImpl* impl = new ServiceManagerImpl;
	impl->resman = new ResourceManager;
	impl->renderman = new RenderManager;
	impl->sceneman = new SceneManager;
	
	theServiceManager = new ServiceManager(impl);
}

void Game::Dothings() {
    for ( int idx = 0; idx < entities.size(); ++idx )
		entities[idx]->update(theServiceManager);
}
// MonsterEntity.cpp
#include "MonsterEntity.h" // "ServiceManager.h" (implied)
#include "RenderManager.h"

void MonsterEntity::Update(ServiceManager& services){
	services.get<RenderManager>()->renderThing(iDunno);
}
// PlayerEntity.cpp
#include "PlayerEntity.h" // "ServiceManager.h" (implied)
#include "SceneManager.h"

void PlayerEntity::Update(ServiceManager& services) {
	services.get<SceneManager>()->currentScene()->setCameraPosition(getPosition());
}

As you can see, a new service can be added to ServiceManagerImpl.h with an accompanying template specialization for ServiceManager::get in ServiceManager.cpp without affecting ServiceManager.h and none of the ServicesManager users need to be recompiled. Only Game.cpp and ServiceManager.cpp need to be recompiled (expensive for Game.cpp, relatively light for ServiceManager.cpp).

I'm not sure how this rates on the OOP-perversion scale, but I think this is pretty neat.

#3fastcall22

Posted 19 December 2012 - 02:16 PM

I recently solved this problem with what I call a "doubly-opaque pointer" (is there a named pattern or idiom for this?). The idea is based on a service provider object, which uses (abuses) forward declarations, template functions, and the opaque pointer idiom to provide a strongly typed pointer to its users. Only the object that constructs the ServiceManager will need to construct (and/or setup) all of its services. When setup correctly, only the ServiceManager.cpp and the object that sets up the ServiceManager will need to be recompiled if a service is added to the ServiceManager.

Here's a sample code sample:

// ServiceManagerImpl.h

class ResourceManager;

class SceneManager;

class RenderManager;



struct ServiceManagerImpl {

	ResourceManager* resman;

	SceneManager* sceneman;

	RenderManager* renderman;

};



// ServiceManager.h

struct ServiceManagerImpl;



class ServiceManager {

public:

	ServiceManager(ServiceManagerImpl* impl);

	

public:

	template<class ServiceType>

	ServiceType* get();

	

private:

	ServiceManagerImpl* impl;

};



// ServiceManager.cpp

#include "ServiceManager.h"

#include "ServiceManagerImpl.h"



ServiceManager::ServiceManager(ServiceManagerImpl* impl) : impl(impl) {

}



template<> ResourceManager* ServiceManager::get() { return impl->resman; }

template<> SceneManager* ServiceManager::get() { return impl->sceneman; }

template<> RenderManager* ServiceManager::get() { return impl->renderman; }



And a sample demonstratation:

// Game.cpp

void Game::Setup() {

	ServiceManagerImpl* impl = new ServiceManagerImpl;

	impl->resman = new ResourceManager;

	impl->renderman = new RenderManager;

	impl->sceneman = new SceneManager;

	

	theServiceManager = new ServiceManager(impl);

}



void Game::Dothings() {

    for ( int idx = 0; idx < entities.size(); ++idx )

		entities[idx]->update(theServiceManager);

}



// MonsterEntity.cpp

#include "MonsterEntity.h" // "ServiceManager.h" (implied)

#include "RenderManager.h"



void MonsterEntity::Update(ServiceManager& services){

	services.get<RenderManager>()->renderThing(iDunno);

}



// PlayerEntity.cpp

#include "PlayerEntity.h" // "ServiceManager.h" (implied)

#include "SceneManager.h"



void PlayerEntity::Update(ServiceManager& services) {

	services.get<SceneManager>()->currentScene()->setCameraPosition(getPosition());

}



As you can see, a new service can be added to ServiceManagerImpl.h with an accompanying template specialization for ServiceManager::get in ServiceManager.cpp without affecting ServiceManager.h and none of the ServicesManager users need to be recompiled. Only Game.cpp and ServiceManager.cpp need to be recompiled (expensive for Game.cpp, relatively light for ServiceManager.cpp).

I'm not sure how this rates on the OOP-perversion scale, but I think this is pretty neat.

#2fastcall22

Posted 19 December 2012 - 02:16 PM

I recently solved this problem with what I call a "doubly-opaque pointer" (is there a named pattern or idiom for this?). The idea is based on a service provider object, which uses (abuses) forward declarations, template functions, and the opaque pointer idiom to provide a strongly typed pointer to its users. Only the object that constructs the ServiceManager will need to construct (and/or setup) all of its services. When setup correctly, only the ServiceManager.cpp and the object that sets up the ServiceManager will need to be recompiled if a service is added to the ServiceManager.

Here's a sample code sample:
// ServiceManagerImpl.h
class ResourceManager;
class SceneManager;
class RenderManager;

struct ServiceManagerImpl {
	ResourceManager* resman;
	SceneManager* sceneman;
	RenderManager* renderman;
};
// ServiceManager.h
struct ServiceManagerImpl;

class ServiceManager {
public:
	ServiceManager(ServiceManagerImpl* impl);
	
public:
	template<class ServiceType>
	ServiceType* get();
	
private:
	ServiceManagerImpl* impl;
};
// ServiceManager.cpp
#include "ServiceManager.h"
#include "ServiceManagerImpl.h"

ServiceManager::ServiceManager(ServiceManagerImpl* impl) : impl(impl) {
}

template<> ResourceManager* ServiceManager::get() { return impl->resman; }
template<> SceneManager* ServiceManager::get() { return impl->sceneman; }
template<> RenderManager* ServiceManager::get() { return impl->renderman; }

And a sample demonstratation:
// Game.cpp
void Game::Setup() {
	ServiceManagerImpl* impl = new ServiceManagerImpl;
	impl->resman = new ResourceManager;
	impl->renderman = new RenderManager;
	impl->sceneman = new SceneManager;
	
	theServiceManager = new ServiceManager(impl);
}

void Game::Dothings() {
    for ( int idx = 0; idx < entities.size(); ++idx )
		entities[idx]->update(theServiceManager);
}
// MonsterEntity.cpp
#include "MonsterEntity.h" // "ServiceManager.h" (implied)
#include "RenderManager.h"

void MonsterEntity::Update(ServiceManager& services){
	services.get<RenderManager>()->renderThing(iDunno);
}
// PlayerEntity.cpp
#include "PlayerEntity.h" // "ServiceManager.h" (implied)
#include "SceneManager.h"

void PlayerEntity::Update(ServiceManager& services) {
	services.get<SceneManager>()->currentScene()->setCameraPosition(getPosition());
}

As you can see, a new service can be added to ServiceManagerImpl.h with an accompanying template specialization for ServiceManager::get in ServiceManager.cpp without affecting ServiceManager.h and none of the ServicesManager users need to be recompiled. Only Game.cpp and ServiceManager.cpp need to be recompiled (expensive for Game.cpp, relatively light for ServiceManager.cpp).

I'm not sure how this rates on the OOP-perversion scale, but I think this is pretty neat.

#1fastcall22

Posted 19 December 2012 - 02:15 PM

I recently solved this problem with what I call a "doubly-opaque pointer" (is there a named pattern or idiom for this?). The idea is based on a service provider object, which uses (abuses) forward declarations, template functions, and the opaque pointer idiom to provide a strongly typed pointer to its users. Only the object that constructs the ServiceManager will need to construct (and/or setup) all of its services. When setup correctly, only the ServiceManager.cpp and the object that sets up the ServiceManager will need to be recompiled if a service is added to the ServiceManager.

Here's a sample code sample:
// ServiceManagerImpl.h
class ResourceManager;
class SceneManager;
class RenderManager;

struct ServiceManagerImpl {
	ResourceManager* resman;
	SceneManager* sceneman;
	RenderManager* renderman;
};
// ServiceManager.h
struct ServiceManagerImpl;

class ServiceManager {
public:
	ServiceManager(ServiceManagerImpl* impl);
	
public:
	template<class ServiceType>
	ServiceType* get();
	
private:
	ServiceManagerImpl* impl;
};
// ServiceManager.cpp
#include "ServiceManager.h"
#include "ServiceManagerImpl.h"

ServiceManager::ServiceManager(ServiceManagerImpl* impl) : impl(impl) {
}

template<> ResourceManager* ServiceManager::get() { return impl->resman; }
template<> SceneManager* ServiceManager::get() { return impl->sceneman; }
template<> RenderManager* ServiceManager::get() { return impl->renderman; }

And a sample demonstratation:
// Game.cpp
void Game::Setup() {
	ServiceManagerImpl* impl = new ServiceManagerImpl;
	impl->resman = new ResourceManager;
	impl->renderman = new RenderManager;
	impl->sceneman = new SceneManager;
	
	theServiceManager = new ServiceManager(impl);
}

void Game::Dothings() {
    for ( int idx = 0; idx < entities.size(); ++idx )
		entities[idx]->update(theServiceManager);
}
// MonsterEntity.cpp
#include "MonsterEntity.h" // "ServiceManager.h" (implied)
#include "RenderManager.h"

void MonsterEntity::Update(ServiceManager& services){
	services.get<RenderManager>()->renderThing(iDunno);
}
// PlayerEntity.cpp
#include "PlayerEntity.h" // "ServiceManager.h" (implied)
#include "SceneManager.h"

void PlayerEntity::Update(ServiceManager& services) {
	services.get<SceneManager>->currentScene()->setCameraPosition(getPosition());
}

As you can see, a new service can be added to ServiceManagerImpl.h with an accompanying template specialization for ServiceManager::get in ServiceManager.cpp without affecting ServiceManager.h and none of the ServicesManager users need to be recompiled. Only Game.cpp and ServiceManager.cpp need to be recompiled (expensive for Game.cpp, relatively light for ServiceManager.cpp).

I'm not sure how this rates on the OOP-perversion scale, but I think this is pretty neat.

PARTNERS