#ifndef RESOURCES_H_INCLUDED
#define RESOURCES_H_INCLUDED
#include <map>
#include <string>
#include <SDL/SDL.h>
#include <SDL/SDL_mixer.h>
#include <iostream>
/**
* The Resources class holds some specialized types of resources, allowing for
* easy maintaining.
*/
class Resources
{
private:
std::map<std::string, SDL_Surface*> surfaces;
std::map<std::string, Mix_Chunk*> sounds;
public:
template<class T> void load(std::string, std::string);
template<class T> void unload(std::string);
template<class T> T get(std::string);
};
template<> void Resources::load<SDL_Surface*>(std::string file, std::string id)
{
SDL_Surface* toLoad = SDL_LoadBMP(file.c_str());
if (toLoad) {
surfaces[id] = SDL_DisplayFormat(toLoad);
SDL_FreeSurface(toLoad);
} else {
std::cout << "Error loading image \"" << file << "\" (" << id << ")" << std::endl;
}
}
template<> void Resources::load<Mix_Chunk*>(std::string file, std::string id)
{
sounds[id] = Mix_LoadWAV(file.c_str());
if (!sounds[id]) {
std::cout << "Error loading sound \"" << file << "\" (" << id << ")" << std::endl;
}
}
template<> void Resources::unload<SDL_Surface*>(std::string id)
{
SDL_FreeSurface(surfaces[id]);
surfaces.erase(id);
}
template<> void Resources::unload<Mix_Chunk*>(std::string id)
{
Mix_FreeChunk(sounds[id]);
sounds.erase(id);
}
template<> SDL_Surface* Resources::get<SDL_Surface*>(std::string id)
{
std::map<std::string, SDL_Surface*>::iterator iter = surfaces.find(id);
return (iter == surfaces.end()) ? NULL : iter->second;
}
template<> Mix_Chunk* Resources::get<Mix_Chunk*>(std::string id)
{
std::map<std::string, Mix_Chunk*>::iterator iter = sounds.find(id);
return (iter == sounds.end()) ? NULL : iter->second;
}
#endif // RESOURCES_H_INCLUDED
Template specialization -- mutliple definition error -- C++
It's all pretty much in the title there. I created a class with a few template methods, and I get a load of multiple definition errors from the linker.
obj/Debug/main.o: In function `SDL_Surface* Resources::get<SDL_Surface*>(std::basic_string<char, std::char_traits<char>, std::allocator<char> >)':
/usr/lib/gcc/i686-pc-linux-gnu/4.3.3/../../../../include/c++/4.3.3/new:105: multiple definition of `SDL_Surface* Resources::get<SDL_Surface*>(std::basic_string<char, std::char_traits<char>, std::allocator<char> >)'
obj/Debug/game.o:/home/rob/Development/C++/Tetris/resources.hpp:68: first defined here
obj/Debug/main.o: In function `Mix_Chunk* Resources::get<Mix_Chunk*>(std::basic_string<char, std::char_traits<char>, std::allocator<char> >)':
/home/rob/Development/C++/Tetris/resources.hpp:74: multiple definition of `Mix_Chunk* Resources::get<Mix_Chunk*>(std::basic_string<char, std::char_traits<char>, std::allocator<char> >)'
obj/Debug/game.o:/home/rob/Development/C++/Tetris/resources.hpp:74: first defined here
obj/Debug/main.o: In function `void Resources::unload<Mix_Chunk*>(std::basic_string<char, std::char_traits<char>, std::allocator<char> >)':
/home/rob/Development/C++/Tetris/resources.hpp:61: multiple definition of `void Resources::unload<Mix_Chunk*>(std::basic_string<char, std::char_traits<char>, std::allocator<char> >)'
obj/Debug/game.o:/home/rob/Development/C++/Tetris/resources.hpp:61: first defined here
obj/Debug/main.o: In function `void Resources::unload<SDL_Surface*>(std::basic_string<char, std::char_traits<char>, std::allocator<char> >)':
/home/rob/Development/C++/Tetris/resources.hpp:55: multiple definition of `void Resources::unload<SDL_Surface*>(std::basic_string<char, std::char_traits<char>, std::allocator<char> >)'
obj/Debug/game.o:/home/rob/Development/C++/Tetris/resources.hpp:55: first defined here
obj/Debug/main.o: In function `void Resources::load<Mix_Chunk*>(std::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::basic_string<char, std::char_traits<char>, std::allocator<char> >)':
/home/rob/Development/C++/Tetris/resources.hpp:45: multiple definition of `void Resources::load<Mix_Chunk*>(std::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::basic_string<char, std::char_traits<char>, std::allocator<char> >)'
obj/Debug/game.o:/home/rob/Development/C++/Tetris/resources.hpp:45: first defined here
obj/Debug/main.o: In function `void Resources::load<SDL_Surface*>(std::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::basic_string<char, std::char_traits<char>, std::allocator<char> >)':
/home/rob/Development/C++/Tetris/resources.hpp:33: multiple definition of `void Resources::load<SDL_Surface*>(std::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::basic_string<char, std::char_traits<char>, std::allocator<char> >)'
obj/Debug/game.o:/home/rob/Development/C++/Tetris/resources.hpp:33: first defined here
obj/Debug/states/mainmenu.o: In function `SDL_Surface* Resources::get<SDL_Surface*>(std::basic_string<char, std::char_traits<char>, std::allocator<char> >)':
/home/rob/Development/C++/Tetris/states/../resources.hpp:68: multiple definition of `SDL_Surface* Resources::get<SDL_Surface*>(std::basic_string<char, std::char_traits<char>, std::allocator<char> >)'
obj/Debug/game.o:/home/rob/Development/C++/Tetris/resources.hpp:68: first defined here
obj/Debug/states/mainmenu.o: In function `Mix_Chunk* Resources::get<Mix_Chunk*>(std::basic_string<char, std::char_traits<char>, std::allocator<char> >)':
/home/rob/Development/C++/Tetris/states/../resources.hpp:74: multiple definition of `Mix_Chunk* Resources::get<Mix_Chunk*>(std::basic_string<char, std::char_traits<char>, std::allocator<char> >)'
obj/Debug/game.o:/home/rob/Development/C++/Tetris/resources.hpp:74: first defined here
obj/Debug/states/mainmenu.o: In function `void Resources::unload<Mix_Chunk*>(std::basic_string<char, std::char_traits<char>, std::allocator<char> >)':
/home/rob/Development/C++/Tetris/states/../resources.hpp:61: multiple definition of `void Resources::unload<Mix_Chunk*>(std::basic_string<char, std::char_traits<char>, std::allocator<char> >)'
obj/Debug/game.o:/home/rob/Development/C++/Tetris/resources.hpp:61: first defined here
obj/Debug/states/mainmenu.o: In function `void Resources::unload<SDL_Surface*>(std::basic_string<char, std::char_traits<char>, std::allocator<char> >)':
/home/rob/Development/C++/Tetris/states/../resources.hpp:55: multiple definition of `void Resources::unload<SDL_Surface*>(std::basic_string<char, std::char_traits<char>, std::allocator<char> >)'
obj/Debug/game.o:/home/rob/Development/C++/Tetris/resources.hpp:55: first defined here
obj/Debug/states/mainmenu.o: In function `void Resources::load<Mix_Chunk*>(std::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::basic_string<char, std::char_traits<char>, std::allocator<char> >)':
/home/rob/Development/C++/Tetris/states/../resources.hpp:45: multiple definition of `void Resources::load<Mix_Chunk*>(std::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::basic_string<char, std::char_traits<char>, std::allocator<char> >)'
obj/Debug/game.o:/home/rob/Development/C++/Tetris/resources.hpp:45: first defined here
obj/Debug/states/mainmenu.o: In function `void Resources::load<SDL_Surface*>(std::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::basic_string<char, std::char_traits<char>, std::allocator<char> >)':
/home/rob/Development/C++/Tetris/states/../resources.hpp:33: multiple definition of `void Resources::load<SDL_Surface*>(std::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::basic_string<char, std::char_traits<char>, std::allocator<char> >)'
obj/Debug/game.o:/home/rob/Development/C++/Tetris/resources.hpp:33: first defined here
collect2: ld returned 1 exit status
Process terminated with status 1 (0 minutes, 1 seconds)
24 errors, 0 warnings
Thanks for any help.
I believe you still have to prototype your specializations in the class:
Seems to be the case with VS2008 anyway from a quick test - I don't know what the standard has to say.
class Resources{private: std::map<std::string, SDL_Surface*> surfaces; std::map<std::string, Mix_Chunk*> sounds;public: template<class T> void load(std::string, std::string); template<class T> void unload(std::string); template<class T> T get(std::string); template<> void load<SDL_Surface*>(std::string,std::string); template<> void unload<SDL_Surface*>(std::string); template<> SDL_Surface *get<SDL_Surface*>(std::string);};// implementations as before
Seems to be the case with VS2008 anyway from a quick test - I don't know what the standard has to say.
#ifndef RESOURCES_H_INCLUDED#define RESOURCES_H_INCLUDED#include <map>#include <string>#include <SDL/SDL.h>#include <SDL/SDL_mixer.h>#include <iostream>/** * The Resources class holds some specialized types of resources, allowing for * easy maintaining. */class Resources{private: std::map<std::string, SDL_Surface*> surfaces; std::map<std::string, Mix_Chunk*> sounds;public: template<class T> void load(std::string, std::string); template<class T> void unload(std::string); template<class T> T get(std::string); template<> void load<SDL_Surface*>(std::string, std::string); template<> void unload<SDL_Surface*>(std::string); template<> SDL_Surface* get<SDL_Surface*>(std::string); template<> void load<Mix_Chunk*>(std::string, std::string); template<> void unload<Mix_Chunk*>(std::string); template<> Mix_Chunk* get<Mix_Chunk*>(std::string);};template<> void Resources::load<SDL_Surface*>(std::string file, std::string id){ SDL_Surface* toLoad = SDL_LoadBMP(file.c_str()); if (toLoad) { surfaces[id] = SDL_DisplayFormat(toLoad); SDL_FreeSurface(toLoad); } else { std::cout << "Error loading image \"" << file << "\" (" << id << ")" << std::endl; }}template<> void Resources::load<Mix_Chunk*>(std::string file, std::string id){ sounds[id] = Mix_LoadWAV(file.c_str()); if (!sounds[id]) { std::cout << "Error loading sound \"" << file << "\" (" << id << ")" << std::endl; }}template<> void Resources::unload<SDL_Surface*>(std::string id){ SDL_FreeSurface(surfaces[id]); surfaces.erase(id);}template<> void Resources::unload<Mix_Chunk*>(std::string id){ Mix_FreeChunk(sounds[id]); sounds.erase(id);}template<> SDL_Surface* Resources::get<SDL_Surface*>(std::string id){ std::map<std::string, SDL_Surface*>::iterator iter = surfaces.find(id); return (iter == surfaces.end()) ? NULL : iter->second;}template<> Mix_Chunk* Resources::get<Mix_Chunk*>(std::string id){ std::map<std::string, Mix_Chunk*>::iterator iter = sounds.find(id); return (iter == sounds.end()) ? NULL : iter->second;}#endif // RESOURCES_H_INCLUDED
-------------- Build: Debug in Tetris ---------------
Compiling: game.cpp
In file included from /home/rob/Development/C++/Tetris/game.hpp:13,
from /home/rob/Development/C++/Tetris/game.cpp:2:
/home/rob/Development/C++/Tetris/resources.hpp:29: error: explicit specialization in non-namespace scope ‘class Resources’
/home/rob/Development/C++/Tetris/resources.hpp:30: error: explicit specialization in non-namespace scope ‘class Resources’
/home/rob/Development/C++/Tetris/resources.hpp:31: error: explicit specialization in non-namespace scope ‘class Resources’
/home/rob/Development/C++/Tetris/resources.hpp:33: error: explicit specialization in non-namespace scope ‘class Resources’
/home/rob/Development/C++/Tetris/resources.hpp:34: error: explicit specialization in non-namespace scope ‘class Resources’
/home/rob/Development/C++/Tetris/resources.hpp:35: error: explicit specialization in non-namespace scope ‘class Resources’
Process terminated with status 1 (0 minutes, 0 seconds)
6 errors, 0 warnings
Doesn't seem to be the case with gnu gcc.
When I remove all the non-standard calls (and forward declare SDL_Surface and Mix_Chunk; I am at work right now and can't install third party libs), that header compiles fine.
What misses to give a better diagnostic is an actual sourcefile where you use class Resources, because due to lazy instantiation I could miss some errors.
Anyway, my guess is that your problem is the definition of non-inline member functions in the header file, and the ad-hoc solution would be to make your member functions inline, either by putting them into the class body, or by attributing them with inline.
Solution 2 would be to put the specialisations into an actual source file.
If that does not help, please provide a test case.
(not the solution, just the header in compilable form)
What misses to give a better diagnostic is an actual sourcefile where you use class Resources, because due to lazy instantiation I could miss some errors.
Anyway, my guess is that your problem is the definition of non-inline member functions in the header file, and the ad-hoc solution would be to make your member functions inline, either by putting them into the class body, or by attributing them with inline.
Solution 2 would be to put the specialisations into an actual source file.
If that does not help, please provide a test case.
(not the solution, just the header in compilable form)
#include <map>#include <string>#include <iostream>class SDL_Surface;class Mix_Chunk;/** * The Resources class holds some specialized types of resources, allowing for * easy maintaining. */class Resources{private: std::map<std::string, SDL_Surface*> surfaces; std::map<std::string, Mix_Chunk*> sounds;public: template<class T> void load(std::string, std::string); template<class T> void unload(std::string); template<class T> T get(std::string);};template<> void Resources::load<SDL_Surface*>(std::string file, std::string id){}template<> void Resources::load<Mix_Chunk*>(std::string file, std::string id){}template<> void Resources::unload<SDL_Surface*>(std::string id){ surfaces.erase(id);}template<> void Resources::unload<Mix_Chunk*>(std::string id){ sounds.erase(id);}template<> SDL_Surface* Resources::get<SDL_Surface*>(std::string id){ std::map<std::string, SDL_Surface*>::iterator iter = surfaces.find(id); return (iter == surfaces.end()) ? NULL : iter->second;}template<> Mix_Chunk* Resources::get<Mix_Chunk*>(std::string id){ std::map<std::string, Mix_Chunk*>::iterator iter = sounds.find(id); return (iter == sounds.end()) ? NULL : iter->second;}Resource res;
Perhaps another option would be not to use templates and specializations. After all the methods are only meant for two types and it is longer to type load<Image*> than load_image.
Indeed, moving the specializations to a seperate sourcefile was the solution. Thank you very much.
edit After some rethinking, I've come to the conclusion my approach really beats the purpose of templates here, because I end up redefining each function for each resource type anyway. I've changed it to Resources::get_image() and the likes.
[Edited by - c4c0d3m0n on April 8, 2009 2:10:19 PM]
edit After some rethinking, I've come to the conclusion my approach really beats the purpose of templates here, because I end up redefining each function for each resource type anyway. I've changed it to Resources::get_image() and the likes.
[Edited by - c4c0d3m0n on April 8, 2009 2:10:19 PM]
Only specialize what needs to be specialized. Refactor the common stuff. :)
This approach also makes it easier to do something similar for other resources in the future, if you find that there are others that require similar management.
[Edited by - Zahlman on April 8, 2009 3:59:01 PM]
// EDIT: Fixed, with stubs provided to demonstrate that it compiles.#ifndef RESOURCES_H_INCLUDED#define RESOURCES_H_INCLUDED#include <map>#include <string>#include <stdexcept>class SDL_Surface {};class Mix_Chunk {};SDL_Surface* SDL_LoadBMP(const char* const name) { return new SDL_Surface(); }SDL_Surface* SDL_DisplayFormat(SDL_Surface* old) { return new SDL_Surface(*old); }void SDL_FreeSurface(SDL_Surface* dead) { delete dead; }Mix_Chunk* Mix_LoadWAV(const char* const name) { return new Mix_Chunk(); }void Mix_FreeChunk(Mix_Chunk* dead) { delete dead; }//#include <SDL/SDL.h>//#include <SDL/SDL_mixer.h>#include <iostream>template <typename T>struct helpers { typedef T* (*loader)(const char* const); static loader load; typedef void (*unloader)(T*); static unloader unload; static const char* const resource_description;};SDL_Surface* load_optimized(const char* const filename) { SDL_Surface* toLoad = SDL_LoadBMP(filename); if (toLoad) { SDL_Surface* optimized = SDL_DisplayFormat(toLoad); SDL_FreeSurface(toLoad); toLoad = optimized; } return toLoad;}template<> helpers<SDL_Surface>::loader helpers<SDL_Surface>::load = load_optimized;template<> helpers<SDL_Surface>::unloader helpers<SDL_Surface>::unload = SDL_FreeSurface;template<> const char* const helpers<SDL_Surface>::resource_description = "image";template<> helpers<Mix_Chunk>::loader helpers<Mix_Chunk>::load = Mix_LoadWAV;template<> helpers<Mix_Chunk>::unloader helpers<Mix_Chunk>::unload = Mix_FreeChunk;template<> const char* const helpers<Mix_Chunk>::resource_description = "sound";template <typename T>class ResourceRegistry { // You'll have to make a separate one for each type now. Oh Well. std::map<std::string, T*> resources; public: void load(std::string filename, std::string id) { T* resource = helpers<T>::load(filename.c_str()); if (resource) { resources[id] = resource; } else { // Don't print the message here; throw an exception. // In general, let callers decide how to report errors. Otherwise you // lose huge amounts of flexibility. throw std::runtime_error( std::string("Could not load ") + helpers<T>::resource_description + " \"" + filename + "\" (" + id + ")" ); } } void unload(const std::string& id) { helpers<T>::unload(resources[id]); resources.erase(id); } T* get(const std::string& id) { typename std::map<std::string, T*>::iterator iter = resources.find(id); return (iter == resources.end()) ? 0 : iter->second; }};#endifint main() { ResourceRegistry<Mix_Chunk> mc; ResourceRegistry<SDL_Surface> ss; mc.load("foo", "bar"); mc.get("bar"); mc.unload("bar"); ss.load("foo", "bar"); ss.get("bar"); ss.unload("bar");}
This approach also makes it easier to do something similar for other resources in the future, if you find that there are others that require similar management.
[Edited by - Zahlman on April 8, 2009 3:59:01 PM]
This topic is closed to new replies.
Advertisement
Popular Topics
Advertisement