Browse Source

Better fix to ensure a single static ResourceManager instance.

I'm not exactly sure where was the actual problem, but it seems to
be related to definition of templated function with static member
variables in header file in combination with dynamic libraries, causing
multiple definitions of a symbol which did or did not lead to compile
error and/or things breaking in horribly unpredictable way.

Previously it was done via (undocumented) macros, depending on include
order, which is great until it breaks horribly after random unrelated
changes. Currently it is done via black template magic and I'm
still afraid of including it in the public documentation.

There should be no visible change to the end user except for possibly
one less randomly weird bug (#79? couldn't reproduce).

... well, looking at the diff, the solution is still an ugly **********,
but at least it seems to work.
pull/94/head
Vladimír Vondruš 11 years ago
parent
commit
7e8d90a436
  1. 1
      src/Magnum/CMakeLists.txt
  2. 11
      src/Magnum/DebugTools/ResourceManager.cpp
  3. 13
      src/Magnum/DebugTools/ResourceManager.h
  4. 74
      src/Magnum/ResourceManager.h
  5. 46
      src/Magnum/ResourceManager.hpp

1
src/Magnum/CMakeLists.txt

@ -112,6 +112,7 @@ set(Magnum_HEADERS
Renderer.h
Resource.h
ResourceManager.h
ResourceManager.hpp
SampleQuery.h
Sampler.h
Shader.h

11
src/Magnum/DebugTools/ResourceManager.cpp

@ -23,25 +23,22 @@
DEALINGS IN THE SOFTWARE.
*/
#define MAGNUM_RESOURCEMANAGER_DEFINE_INTERNALINSTANCE
#include "ResourceManager.h"
#include "Magnum/AbstractShaderProgram.h"
#include "Magnum/Buffer.h"
#include "Magnum/Mesh.h"
#include "Magnum/MeshView.h"
#include "Magnum/ResourceManager.hpp"
#include "Magnum/DebugTools/ForceRenderer.h"
#include "Magnum/DebugTools/ObjectRenderer.h"
#include "Magnum/DebugTools/ShapeRenderer.h"
namespace Magnum {
template class
#if defined(CORRADE_TARGET_WINDOWS) && _MSC_VER
MAGNUM_DEBUGTOOLS_EXPORT
#endif
ResourceManager<AbstractShaderProgram, Buffer, Mesh, MeshView, DebugTools::ForceRendererOptions, DebugTools::ObjectRendererOptions, DebugTools::ShapeRendererOptions>;
namespace Implementation {
template MAGNUM_DEBUGTOOLS_EXPORT ResourceManager<Implementation::ResourceManagerLocalInstance, AbstractShaderProgram, Buffer, Mesh, MeshView, DebugTools::ForceRendererOptions, DebugTools::ObjectRendererOptions, DebugTools::ShapeRendererOptions>*& ResourceManagerLocalInstanceImplementation<ResourceManagerLocalInstance, AbstractShaderProgram, Buffer, Mesh, MeshView, DebugTools::ForceRendererOptions, DebugTools::ObjectRendererOptions, DebugTools::ShapeRendererOptions>::internalInstance();
}
namespace DebugTools {

13
src/Magnum/DebugTools/ResourceManager.h

@ -29,9 +29,6 @@
* @brief Class @ref Magnum::DebugTools::ResourceManager
*/
#ifndef MAGNUM_RESOURCEMANAGER_DEFINE_INTERNALINSTANCE
#define MAGNUM_RESOURCEMANAGER_DONT_DEFINE_INTERNALINSTANCE
#endif
#include "Magnum/ResourceManager.h"
#include "Magnum/Magnum.h"
@ -42,14 +39,6 @@
namespace Magnum {
/** @todo Do the listing in one place, not five thousand! */
#ifndef CORRADE_TARGET_WINDOWS
extern template ResourceManager<AbstractShaderProgram, Buffer, Mesh, MeshView, DebugTools::ForceRendererOptions, DebugTools::ObjectRendererOptions, DebugTools::ShapeRendererOptions> MAGNUM_DEBUGTOOLS_EXPORT *& ResourceManager<AbstractShaderProgram, Buffer, Mesh, MeshView, DebugTools::ForceRendererOptions, DebugTools::ObjectRendererOptions, DebugTools::ShapeRendererOptions>::internalInstance();
#else
extern template class MAGNUM_DEBUGTOOLS_EXPORT ResourceManager<AbstractShaderProgram, Buffer, Mesh, MeshView, DebugTools::ForceRendererOptions, DebugTools::ObjectRendererOptions, DebugTools::ShapeRendererOptions>;
#endif
namespace DebugTools {
/**
@ -58,7 +47,7 @@ namespace DebugTools {
Stores various data used by debug renderers. See @ref debug-tools for more
information.
*/
class MAGNUM_DEBUGTOOLS_EXPORT ResourceManager: public Magnum::ResourceManager<AbstractShaderProgram, Buffer, Mesh, MeshView, DebugTools::ForceRendererOptions, DebugTools::ObjectRendererOptions, DebugTools::ShapeRendererOptions> {
class MAGNUM_DEBUGTOOLS_EXPORT ResourceManager: public Magnum::ResourceManager<Magnum::Implementation::ResourceManagerLocalInstance, AbstractShaderProgram, Buffer, Mesh, MeshView, DebugTools::ForceRendererOptions, DebugTools::ObjectRendererOptions, DebugTools::ShapeRendererOptions> {
public:
explicit ResourceManager();
~ResourceManager();

74
src/Magnum/ResourceManager.h

@ -155,6 +155,29 @@ template<class T> class ResourceManagerData {
std::size_t _lastChange;
};
/* Helper class for defining which real types are in the type pack */
template<class...> struct ResourceTypePack {};
/* Common resource manager implementation with inline internal instance (for
use in user code), definition of internalInstance() is in this header */
template<class... Types> struct ResourceManagerInlineInstanceImplementation {
static ResourceManager<Types...>*& internalInstance();
};
template<class ...Types> struct ResourceManagerImplementation: ResourceManagerInlineInstanceImplementation<Types...>, ResourceManagerData<Types>... {
typedef ResourceTypePack<Types...> TypePack;
};
/* Resource manager implementation with file-local internal instance (for use
in code where the manager is used across library boundaries), definition
of internalInstance() is in ResourceManager.hpp, see it for usage details */
template<class... Types> struct ResourceManagerLocalInstanceImplementation {
static ResourceManager<Types...>*& internalInstance();
};
struct ResourceManagerLocalInstance;
template<class ...Types> struct ResourceManagerImplementation<ResourceManagerLocalInstance, Types...>: ResourceManagerLocalInstanceImplementation<ResourceManagerLocalInstance, Types...>, ResourceManagerData<Types>... {
typedef ResourceTypePack<Types...> TypePack;
};
}
/**
@ -224,7 +247,7 @@ cube->draw(*shader);
/* Due to too much work involved with explicit template instantiation (all
Resource combinations, all ResourceManagerData...), this class doesn't have
template implementation file. */
template<class... Types> class ResourceManager: private Implementation::ResourceManagerData<Types>... {
template<class... Types> class ResourceManager: private Implementation::ResourceManagerImplementation<Types>... {
public:
/**
* @brief Global instance
@ -366,7 +389,7 @@ template<class... Types> class ResourceManager: private Implementation::Resource
* @return Reference to self (for method chaining)
*/
ResourceManager<Types...>& free() {
freeInternal<Types...>();
freeInternal(typename Implementation::ResourceManagerImplementation<Types...>::TypePack{});
return *this;
}
@ -390,7 +413,7 @@ template<class... Types> class ResourceManager: private Implementation::Resource
* referenced.
*/
ResourceManager<Types...>& clear() {
clearInternal<Types...>();
clearInternal(typename Implementation::ResourceManagerImplementation<Types...>::TypePack{});
return *this;
}
@ -419,35 +442,31 @@ template<class... Types> class ResourceManager: private Implementation::Resource
}
private:
template<class FirstType, class ...NextTypes> void freeInternal() {
template<class FirstType, class ...NextTypes> void freeInternal(Implementation::ResourceTypePack<FirstType, NextTypes...>) {
free<FirstType>();
freeInternal<NextTypes...>();
freeInternal(Implementation::ResourceTypePack<NextTypes...>{});
}
template<class...> void freeInternal() const {}
void freeInternal(Implementation::ResourceTypePack<>) const {}
template<class FirstType, class ...NextTypes> void clearInternal() {
template<class FirstType, class ...NextTypes> void clearInternal(Implementation::ResourceTypePack<FirstType, NextTypes...>) {
clear<FirstType>();
clearInternal<NextTypes...>();
clearInternal(Implementation::ResourceTypePack<NextTypes...>{});
}
template<class...> void clearInternal() const {}
void clearInternal(Implementation::ResourceTypePack<>) const {}
template<class FirstType, class ...NextTypes> void freeLoaders() {
template<class FirstType, class ...NextTypes> void freeLoaders(Implementation::ResourceTypePack<FirstType, NextTypes...>) {
Implementation::ResourceManagerData<FirstType>::freeLoader();
freeLoaders<NextTypes...>();
freeLoaders(Implementation::ResourceTypePack<NextTypes...>{});
}
template<class...> void freeLoaders() const {}
static ResourceManager<Types...>*& internalInstance();
void freeLoaders(Implementation::ResourceTypePack<>) const {}
};
#ifndef MAGNUM_RESOURCEMANAGER_DONT_DEFINE_INTERNALINSTANCE
template<class ...Types> ResourceManager<Types...>*& ResourceManager<Types...>::internalInstance() {
namespace Implementation {
template<class ...Types> ResourceManager<Types...>*& ResourceManagerInlineInstanceImplementation<Types...>::internalInstance() {
static ResourceManager<Types...>* _instance(nullptr);
return _instance;
}
#endif
namespace Implementation {
template<class T> void safeDelete(T* data) {
static_assert(sizeof(T) > 0, "Cannot delete pointer to incomplete type");
@ -588,19 +607,22 @@ template<class T> inline ResourceManagerData<T>::Data::~Data() {
}
template<class ...Types> ResourceManager<Types...>& ResourceManager<Types...>::instance() {
CORRADE_ASSERT(internalInstance(), "ResourceManager::instance(): no instance exists", *internalInstance());
return *internalInstance();
CORRADE_ASSERT(Implementation::ResourceManagerImplementation<Types...>::internalInstance(),
"ResourceManager::instance(): no instance exists",
static_cast<ResourceManager<Types...>&>(*Implementation::ResourceManagerImplementation<Types...>::internalInstance()));
return static_cast<ResourceManager<Types...>&>(*Implementation::ResourceManagerImplementation<Types...>::internalInstance());
}
template<class ...Types> ResourceManager<Types...>::ResourceManager() {
CORRADE_ASSERT(!internalInstance(), "ResourceManager::ResourceManager(): another instance is already created", );
internalInstance() = this;
CORRADE_ASSERT(!Implementation::ResourceManagerImplementation<Types...>::internalInstance(),
"ResourceManager::ResourceManager(): another instance is already created", );
Implementation::ResourceManagerImplementation<Types...>::internalInstance() = this;
}
template<class ...Types> ResourceManager<Types...>::~ResourceManager() {
freeLoaders<Types...>();
CORRADE_INTERNAL_ASSERT(internalInstance() == this);
internalInstance() = nullptr;
freeLoaders(typename Implementation::ResourceManagerImplementation<Types...>::TypePack{});
CORRADE_INTERNAL_ASSERT(Implementation::ResourceManagerImplementation<Types...>::internalInstance() == this);
Implementation::ResourceManagerImplementation<Types...>::internalInstance() = nullptr;
}
}

46
src/Magnum/ResourceManager.hpp

@ -0,0 +1,46 @@
/*
This file is part of Magnum.
Copyright © 2010, 2011, 2012, 2013, 2014, 2015
Vladimír Vondruš <mosra@centrum.cz>
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
*/
#include "ResourceManager.h"
/*
File-local definition of ResourceManager instance holder for use in cases
where the class is used across library boundaries, in which case additional
care must be done to ensure a single static instance.
Usage: typedef the resource manager with Implementation::ResourceManagerLocalInstance
as a first type and then include this file in a _single_ *.cpp file.
This symbol is always exported.
*/
namespace Magnum { namespace Implementation {
template<class ...Types> CORRADE_VISIBILITY_EXPORT ResourceManager<Types...>*& ResourceManagerLocalInstanceImplementation<Types...>::internalInstance() {
static ResourceManager<Types...>* _instance(nullptr);
return _instance;
}
}}
Loading…
Cancel
Save