I'm trying to make a manager, which provides access via templates to containers that store different types of data.
First of all, i made base manager class for storing different type of data
template <typename T>
class BaseMapManager{
public:
T add(T element, std::string name);
T remove(T element);
T remove(std::string name);
T remove(unsigned int id);
T get(std::string name);
T get(unsigned int id);
BaseMapManager() { id = 0; };
~BaseMapManager() { nameMap.clear(); idMap.clear(); };
protected:
std::map<std::string, T> nameMap;
std::map<unsigned int, T> idMap;
//each element of type T gets new unique id
unsigned int id;
//hide it
BaseMapManager(const BaseMapManager&) {};
BaseMapManager& operator=(const BaseMapManager&) {};
};
Then i made my concrete manager, which had to store 3 types of baseMapManagers:
class ResourceManager{
public:
//creates singleton
static ResourceManager* init(){
static ResourceManager singleton;
return &singleton;
}
template <typename T>
void load_resource(std::string path, std::string name){
get_map<std::shared_ptr<T>>().add(std::shared_ptr<T>(new T(path, name), name);
}
template <typename T>
std::shared_ptr<T> get_resource(std::string name){
get_map<T>().get(name);
}
template <typename T>
std::shared_ptr<T> get_resource(unsigned int id){
get_map<T>().get(id);
}
private:
BaseMapManager<std::shared_ptr<AnimationResource> > animationMap;
BaseMapManager<std::shared_ptr<ImageResource> > imageMap;
BaseMapManager<std::shared_ptr<FontResource> > fontMap;
template <typename T>
BaseMapManager<T>& get_map(){
if (std::is_same<T, std::shared_ptr<AnimationResource> >() == true) return animationMap;
if (std::is_same<T, std::shared_ptr<ImageResource> >() == true) return imageMap;
if (std::is_same<T, std::shared_ptr<FontResource> >() == true) return fontMap;
};
};
and now i got this:
1>------ Build started: Project: BOSS, Configuration: Debug Win32 ------
1> main.cpp
1>d:\programming\github projects\boss\boss\new\resourcemanager\resourcemanager.h(43):
error C2440: 'return' : cannot convert from 'BaseMapManager<T>' to 'BaseMapManager<T> &'
1> with
1> [
1> T=std::shared_ptr<ImageResource>
1> ]
1> and
1> [
1> T=std::shared_ptr<AnimationResource>
1> ]
1> d:\programming\github projects\boss\boss\new\resourcemanager\resourcemanager.h(21) : see reference to function template instantiation 'BaseMapManager<T> &ResourceManager::get_map<std::shared_ptr<_Ty>>(void)' being compiled
1> with
1> [
1> T=std::shared_ptr<AnimationResource>,
1> _Ty=AnimationResource
1> ]
1> d:\programming\github projects\boss\boss\new\main.cpp(17) : see reference to function template instantiation 'void ResourceManager::load_resource<AnimationResource>(std::string,std::string)' being compiled
1>d:\programming\github projects\boss\boss\new\resourcemanager\resourcemanager.h(44): error C2440: 'return' : cannot convert from 'BaseMapManager<T>' to 'BaseMapManager<T> &'
1> with
1> [
1> T=std::shared_ptr<FontResource>
1> ]
1> and
1> [
1> T=std::shared_ptr<AnimationResource>
1> ]
========== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ==========
The goal is - providing access to different types on data using template functions like
manager->get_resource<AnimationResource>(itsName) //get it or
manager->load_resource<FontResource>(itsPath, itsName) //add it
Is there a better way to handle this maybe? Thanks!
edit
The source of your problem seams to be the is_same function not resolving correctly at compile time.
/edit
You can always have template functions that only work with the specified types.
For example:
template<>
BaseMapManager<AnimationResource> get_map<AnimationResource>()
{
return animationMap;
}
template<>
BaseMapManager<ImageResource> get_map<ImageResource>()
{
return imageMap;
}
template<>
BaseMapManager<FontResource> get_map<FontResource>()
{
return fontMap;
}
Related
I tried one here. Code as following:
#include <iostream>
#include <string>
#include <vector>
#include <map>
#include <new>
#include <climits>
namespace test {
template <class T>
inline T* _allocate(ptrdiff_t size, T*) {
std::cout << "_allocate called" << std::endl;
T* tmp = (T*)(::operator new((size_t)(size * sizeof(T))));
if (NULL == tmp) {
std::cerr << "out of memory" << std::endl;
exit(0);
}
return tmp;
}
template <class T>
inline void _deallocate(T* p) {
::operator delete(p);
}
template <class T1, class T2>
inline void _construct(T1* p, const T2& value) {
::new (p) T1(value);
}
template <class T>
inline void _destroy(T* p) {
p->~T();
}
template <class T>
class Allocator {
public:
typedef T value_type;
typedef T* pointer;
typedef const T* const_pointer;
typedef T& reference;
typedef const T& const_reference;
typedef size_t size_type;
typedef ptrdiff_t difference_type;
template <class U>
struct rebind {
typedef Allocator<U> other;
};
pointer allocate(size_type n, const void* hint = 0) { return _allocate((difference_type)n, (pointer)0); }
void deallocate(pointer p, size_type n) { return _deallocate(p); }
void construct(pointer p, const T& value) { _construct(p, value); }
void destroy(pointer p) { _destroy(p); }
pointer address(reference x) { return (pointer)&x; }
const_pointer address(const_reference x) { return (const_pointer)&x; }
size_type max_size() const { return size_type(UINT_MAX / sizeof(T)); }
};
} // namespace test
static std::map<void*, uint64_t, std::less<void*>, test::Allocator<std::pair<void* const, uint64_t>>> global_map;
int main()
{
std::vector<std::string> vec = {
"Hello", "from", "GCC", "!"
};
std::cout << "xxxx " << global_map.size() << std::endl;
}
But it leads to compiling fail on Visual Studio 2019 x86:
Build started...
1>------ Build started: Project: ConsoleApplication1, Configuration: Debug Win32 ------
1>ConsoleApplication1.cpp
1>C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Tools\MSVC\14.29.30037\include\xtree(1096,27): error C2440: 'static_cast': cannot convert from 'test::Allocator<U>' to 'test::Allocator<U>'
1> with
1> [
1> U=std::_Tree_node<std::pair<void *const ,uint64_t>,void *>
1> ]
1> and
1> [
1> U=std::_Container_proxy
1> ]
1>C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Tools\MSVC\14.29.30037\include\xtree(1096,27): message : No constructor could take the source type, or constructor overload resolution was ambiguous
1>C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Tools\MSVC\14.29.30037\include\xtree(1092): message : while compiling class template member function 'std::_Tree<std::_Tmap_traits<_Kty,_Ty,_Pr,_Alloc,false>>::~_Tree(void) noexcept'
1> with
1> [
1> _Kty=void *,
1> _Ty=uint64_t,
1> _Pr=std::less<void *>,
1> _Alloc=test::Allocator<std::pair<void *const ,uint64_t>>
1> ]
1>C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Tools\MSVC\14.29.30037\include\map(348): message : see reference to function template instantiation 'std::_Tree<std::_Tmap_traits<_Kty,_Ty,_Pr,_Alloc,false>>::~_Tree(void) noexcept' being compiled
1> with
1> [
1> _Kty=void *,
1> _Ty=uint64_t,
1> _Pr=std::less<void *>,
1> _Alloc=test::Allocator<std::pair<void *const ,uint64_t>>
1> ]
1>C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Tools\MSVC\14.29.30037\include\map(75): message : see reference to class template instantiation 'std::_Tree<std::_Tmap_traits<_Kty,_Ty,_Pr,_Alloc,false>>' being compiled
1> with
1> [
1> _Kty=void *,
1> _Ty=uint64_t,
1> _Pr=std::less<void *>,
1> _Alloc=test::Allocator<std::pair<void *const ,uint64_t>>
1> ]
How to fix it?
Edit:
Found another solution. But do not know why. Code as https://godbolt.org/z/jvscsMx7T
This problem appeard in Visual Studio Debug mode , but solved when switching to Release mode. If it still bothers you, you can report problems to Developer Community.
I have a base class and a templated derived class as below
class ComponentArray_Base
{
public:
virtual ~ComponentArray_Base() = default;
virtual void EntityDestroyed(Entity entity) = 0;
};
template <typename cType>
class ComponentArray : public ComponentArray_Base
{
//Derived class implementation...
}
and later in the code, outside of the two classes I attempt to add the derived class to an unordered map with
mComponentArrays.insert({ TypeName, std::make_shared<ComponentArray<cType>> });
which gives me the error
C:\Users\davis\source\repos\ECS\ECS\Component.h(91,1): error C2664: 'void std::_Hash<std::_Umap_traits<_Kty,_Ty,std::_Uhash_compare<_Kty,_Hasher,_Keyeq>,_Alloc,false>>::insert(std::initializer_list<std::pair<const char *const ,std::shared_ptr<ComponentArray_Base>>>)': cannot convert argument 1 from 'initializer list' to 'std::initializer_list<_Ty>'
1> with
1> [
1> _Kty=const char *,
1> _Ty=std::shared_ptr<ComponentArray_Base>,
1> _Hasher=std::hash<const char *>,
1> _Keyeq=std::equal_to<const char *>,
1> _Alloc=std::allocator<std::pair<const char *const ,std::shared_ptr<ComponentArray_Base>>>
1> ]
1> and
1> [
1> _Ty=std::pair<const char *const ,std::shared_ptr<ComponentArray_Base>>
1> ]
I do not seem to be having this problem when using a raw pointer and cannot figure out what the problem is as the hash function should be defined for const char*
Using std::static_ptr_cast as below also gives an error,
mComponentArrays.insert({ TypeName, std::static_pointer_cast<ComponentArray_Base>(std::make_shared<ComponentArray<cType>>) });
gives me
'std::static_pointer_cast': none of the 2 overloads could convert all the argument types
1>C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Tools\MSVC\14.28.29910\include\memory(2095,29): message : could be 'std::shared_ptr<ComponentArray_Base> std::static_pointer_cast<ComponentArray_Base,ComponentArray<cType>>(std::shared_ptr<ComponentArray<cType>> &&) noexcept'
1> with
1> [
1> cType=Renderable
1> ]
I successfully compile below example. Did I miss sth?
#include <unordered_map>
#include <string>
#include <memory>
class ComponentArray_Base
{
public:
virtual ~ComponentArray_Base() = default;
virtual void EntityDestroyed(int entity) = 0;
};
template <typename cType>
class ComponentArray : public ComponentArray_Base
{
void EntityDestroyed(int entity) override
{
return;
}
//Derived class implementation...
};
std::unordered_map<std::string, std::shared_ptr<ComponentArray_Base>> mComponentArrays;
int main()
{
mComponentArrays.insert({ "Sth", std::make_shared<ComponentArray<int>>() });
return 0;
}
I learned a bit about variadic templates and searched over the Internet for some samples and now trying to write some tricky code to call member a method of a variadic class template with one of its fields. I can't understand why it doesn't work. Please, help.
Here is sample classes:
class BarBase
{
public:
BarBase() = default;
virtual void call() = 0;
};
template<typename O, typename M, typename... A>
class Bar
: public BarBase
{
public:
Bar(O* o, M m, A&&... a)
: BarBase()
, m_o(o), m_m(m), m_a(std::forward<A>(a)...)
{ }
void call() override final
{
callInnerWithArgsInside();
}
private:
void callInnerWithArgsInside()
{
(m_o->*m_m)(m_a); // Some errors happends here
}
O* m_o;
M m_m;
std::tuple<typename std::remove_reference<A>::type...> m_a;
};
template<typename O, typename M, typename... A>
BarBase* crateBar(O* o, M m, A&&... a)
{
return new Bar<O, M, A...>(o, m, std::forward<A>(a)...);
}
And call from main:
struct Foo
{
void foo(int ii, float ff, std::string ss)
{
std::cout << "called" << std::endl;
}
};
int main()
{
Foo f;
int i = 10;
float ff = 20.2f;
std::string s = "Hello";
BarBase* bar = crateBar(&f, &Foo::foo, i, ff, s);
bar->call();
}
Errors:
main.cpp
1>d:\drafts_tests\main.cpp(203): error C2198: 'void (__thiscall Foo::* )(int,float,std::string)' : too few arguments for call
1> d:\drafts_tests\main.cpp(202) : while compiling class template member function 'void Bar::callInnerWithArgsInside(void)'
1> with
1> [
1> O=Foo
1> , M=void (__thiscall Foo::* )(int,float,std::string)
1> ]
1> d:\drafts_tests\main.cpp(197) : see reference to function template instantiation 'void Bar::callInnerWithArgsInside(void)' being compiled
1> with
1> [
1> O=Foo
1> , M=void (__thiscall Foo::* )(int,float,std::string)
1> ]
1> d:\drafts_tests\main.cpp(214) : see reference to class template instantiation 'Bar' being compiled
1> with
1> [
1> O=Foo
1> , M=void (__thiscall Foo::* )(int,float,std::string)
1> ]
1> d:\drafts_tests\main.cpp(225) : see reference to function template instantiation 'BarBase *crateBar(O *,M,int &,float &,std::string &)' being compiled
1> with
1> [
1> O=Foo
1> , M=void (__thiscall Foo::* )(int,float,std::string)
1> ]
========== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ==========
you are passing a tuple to the function, rather than the individual type arguments. The following will pass the required type args to the call:
template<std::size_t... I>
void callInnerWithArgsInside2(std::index_sequence<I...>)
{
(m_o->*m_m)(std::get<I>(m_a)...);
}
void callInnerWithArgsInside()
{
return callInnerWithArgsInside2( std::make_index_sequence<sizeof...(A)>());
}
live demo
EDIT1: C++11 version
I have implemented a C++11 version, see updated live demo
I am trying following code from book C++ Template The Complete Guide , but it is failing to compile with Visual Studio 2010.
template < typename T>
class IsClassType {
private:
typedef char One;
typedef struct { char a[2]; } Two;
template<typename C> static One test(int C::*);
template<typename C> static Two test(...);
public:
enum { Yes = sizeof ( IsClassType<T>::test<T>(0)) == 1 };
enum { No = !Yes };
};
template < typename T>
void check() {
if ( IsClassType<T>::Yes ) {
std::cout << " IsClassType Yes " << std::endl;
} else {
std::cout << " IsClassType No " << std::endl;
}
}
int main() {
std::cout << "Int: ";
check<int>();
}
Following is the compiler error.
1>------ Build started: Project: test123, Configuration: Debug Win32 ------
1> test123.cpp
1>e:\svn\office_expt\test123\test123\test123.cpp(45): error C2783: 'IsClassType<T>::Two IsClassType<T>::test(...)' : could not deduce template argument for 'C'
1> with
1> [
1> T=int
1> ]
1> e:\svn\office_expt\test123\test123\test123.cpp(42) : see declaration of 'IsClassType<T>::test'
1> with
1> [
1> T=int
1> ]
1> e:\svn\office_expt\test123\test123\test123.cpp(51) : see reference to class template instantiation 'IsClassType<T>' being compiled
1> with
1> [
1> T=int
1> ]
1> e:\svn\office_expt\test123\test123\test123.cpp(60) : see reference to function template instantiation 'void check<int>(void)' being compiled
1>e:\svn\office_expt\test123\test123\test123.cpp(45): error C2784: 'IsClassType<T>::One IsClassType<T>::test(int C::* )' : could not deduce template argument for 'int C::* ' from 'int'
1> with
1> [
1> T=int
1> ]
1> e:\svn\office_expt\test123\test123\test123.cpp(41) : see declaration of 'IsClassType<T>::test'
1> with
1> [
1> T=int
1> ]
1>e:\svn\office_expt\test123\test123\test123.cpp(45): error C2056: illegal expression
========== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ==========
Is it this line:
template<typename C> static Two test(...);
The typename cannot be inferred from this. I think it should read:
static Two test(...);
I'm using Boost.PropertyTree for a project and I want to use user-defined types for Key and Data instead of the std::string that Boost uses in the ptree typedef.
However when I typedef basic_ptree myself I get the following compiler errors:
1> main.cpp
1>c:\boost_1_49_0\boost\property_tree\ptree.hpp(82): error C2027: use of undefined type 'boost::property_tree::path_of<Key>'
1> with
1> [
1> Key=int
1> ]
1> c:\users\mathias\documents\visual studio 2012\projects\testappern\testappern\main.cpp(10) : see reference to class template instantiation 'boost::property_tree::basic_ptree<Key,Data>' being compiled
1> with
1> [
1> Key=int,
1> Data=int
1> ]
1>c:\users\mathias\documents\visual studio 2012\projects\testappern\testappern\main.cpp(13): error C2664: 'boost::property_tree::basic_ptree<Key,Data>::add_child' : cannot convert parameter 1 from 'int' to 'const boost::type &'
1> with
1> [
1> Key=int,
1> Data=int
1> ]
1> Reason: cannot convert from 'int' to 'const boost::type'
1> The target type has no constructors
========== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ==========
The following snippet shows and example how I typedef basic_tree and get the compiler errors:
#include <iostream>
#include <boost\property_tree\ptree.hpp>
using namespace boost::property_tree;
typedef basic_ptree<int, int> IntTree;
int main(int argc, char* argv[])
{
IntTree tree;
IntTree child;
int index = 42;
tree.add_child(index, child);
return 0;
}
So my question is, how do I typedef it correctly?
If it's any interest I'm running MSVC 2012.
Thanks in advance!
According to the contents of boost/property_tree/ptree_fwd.hpp, if you want to use a custom type as a key, you should follow this:
/// If you want to use a custom key type, specialize this struct for it
/// and give it a 'type' typedef that specifies your path type. The path
/// type must conform to the Path concept described in the documentation.
/// This is already specialized for std::basic_string.
So you can not simply use int. Refer to the documentation for more information, as the code says.
Here is an example of basic_ptree <int, int>
//path class must conform Path concept
template<> class boost::property_tree::path_of < int >
{
public:
typedef int key_type;
typedef boost::property_tree::path_of < int > type;
boost::property_tree::path_of<int>(vector<int>& vals)
{
std::transform(vals.begin(), vals.end(), back_inserter(_impl),
[&](int v) -> int{ return v; });
}
key_type reduce()
{
key_type res = *_impl.begin();
_impl.pop_front();
return res;
}
bool empty() const
{
return _impl.empty();
}
bool single() const
{
return _impl.size() == 1;
}
std::string dump() const
{
std::string res;
std::for_each(_impl.begin(),
_impl.end(), [&res](key_type k) -> void
{
res.append(".");
res.append(boost::lexical_cast<std::string>(k));
});
return res;
}
private:
deque<key_type> _impl;
};
//usage:
typedef boost::property_tree::basic_ptree < int, int> custom_int_tree_t
//or
typedef boost::property_tree::basic_ptree < int, void*> custom_int_tree2_t