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
Related
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 have a C++ template function being used in VS2013 without any problem. But when I upgrade to VS2017 the VC compiler complains it cannot match the argument list. Anyone can help me how to fix the code?
A simplified code snippet that demonstrates the problem here:
#include "stdafx.h"
#include <functional>
#include <memory>
class FS
{
};
typedef std::shared_ptr<FS> FSPtr;
class FSM
{
public:
FSM() : m_pFs(new FS()) {}
template <typename CALLABLE, typename... ARGS>
typename std::enable_if<std::is_same<bool, std::result_of_t<CALLABLE(ARGS&&...)>>::value, std::result_of_t<CALLABLE(ARGS&&...)>>::type
All(CALLABLE fn, ARGS&&... args) const // line 21
{
std::function<bool()> rFunc = std::bind(fn, m_pFs, args...);
bool bSuccess = rFunc();
return bSuccess;
}
private:
FSPtr m_pFs;
};
class SFF
{
public:
SFF() : m_pFsm(new FSM()) {}
bool VF(FSPtr pFs)
{
return nullptr != pFs;
}
bool Do()
{
return m_pFsm->All(std::bind(&SFF::VF, this, std::placeholders::_1)); // line 41
}
bool TF(FSPtr pFs, int n)
{
return nullptr != pFs && 0 != n;
}
bool Do1(int n)
{
return m_pFsm->All(std::bind(&SFF::TF, this, std::placeholders::_1, std::placeholders::_2), n); // line 49
}
private:
std::shared_ptr<FSM> m_pFsm;
};
int _tmain(int argc, _TCHAR* argv[])
{
SFF oSff;
bool bOk1 = oSff.Do();
bool bOk2 = oSff.Do1(4);
int rc = (bOk1 && bOk2) ? 0 : 1;
return rc;
}
And the errors VS2017 VC compiler output is:
1>------ Build started: Project: ConsoleApplication1, Configuration: Debug Win32 ------
1>ConsoleApplication1.cpp
1>c:\program files (x86)\microsoft visual studio\2017\professional\vc\tools\msvc\14.11.25503\include\utility(486): error C2338: tuple index out of bounds
1>c:\program files (x86)\microsoft visual studio\2017\professional\vc\tools\msvc\14.11.25503\include\functional(887): note: see reference to class template instantiation 'std::tuple_element<0,std::tuple<>>' being compiled
1>c:\program files (x86)\microsoft visual studio\2017\professional\vc\tools\msvc\14.11.25503\include\tuple(793): note: see reference to function template instantiation 'const tuple_element<_Index,_Tuple>::type &&std::get(const std::tuple<_Rest...> &&) noexcept' being compiled
1> with
1> [
1> _Tuple=std::tuple<_Rest...>
1> ]
1>c:\users\s.chan\source\repos\consoleapplication1\consoleapplication1\consoleapplication1.cpp(41): note: see reference to class template instantiation 'std::result_of<std::_Binder<std::_Unforced,bool (__thiscall SFF::* )(FSPtr),SFF *,const std::_Ph<1> &> (void)>' being compiled
1>c:\users\s.chan\source\repos\consoleapplication1\consoleapplication1\consoleapplication1.cpp(21): note: while compiling class template member function 'std::enable_if<std::is_same<bool,result_of<_Ty>::type>::value,result_of<_Ty>::type>::type FSM::All(CALLABLE,ARGS &&...) const'
1> with
1> [
1> _Ty=CALLABLE (ARGS &&...)
1> ]
1>c:\users\s.chan\source\repos\consoleapplication1\consoleapplication1\consoleapplication1.cpp(41): error C2672: 'FSM::All': no matching overloaded function found
1>c:\users\s.chan\source\repos\consoleapplication1\consoleapplication1\consoleapplication1.cpp(41): error C2893: Failed to specialize function template 'std::enable_if<std::is_same<bool,result_of<_Ty>::type>::value,result_of<_Ty>::type>::type FSM::All(CALLABLE,ARGS &&...) const'
1> with
1> [
1> _Ty=CALLABLE (ARGS &&...)
1> ]
1>c:\users\s.chan\source\repos\consoleapplication1\consoleapplication1\consoleapplication1.cpp(41): note: With the following template arguments:
1>c:\users\s.chan\source\repos\consoleapplication1\consoleapplication1\consoleapplication1.cpp(41): note: 'CALLABLE=std::_Binder<std::_Unforced,bool (__thiscall SFF::* )(FSPtr),SFF *,const std::_Ph<1> &>'
1>c:\users\s.chan\source\repos\consoleapplication1\consoleapplication1\consoleapplication1.cpp(41): note: 'ARGS={}'
1>c:\users\s.chan\source\repos\consoleapplication1\consoleapplication1\consoleapplication1.cpp(49): error C2672: 'FSM::All': no matching overloaded function found
1>c:\users\s.chan\source\repos\consoleapplication1\consoleapplication1\consoleapplication1.cpp(49): error C2893: Failed to specialize function template 'std::enable_if<std::is_same<bool,result_of<_Ty>::type>::value,result_of<_Ty>::type>::type FSM::All(CALLABLE,ARGS &&...) const'
1> with
1> [
1> _Ty=CALLABLE (ARGS &&...)
1> ]
1>c:\users\s.chan\source\repos\consoleapplication1\consoleapplication1\consoleapplication1.cpp(49): note: With the following template arguments:
1>c:\users\s.chan\source\repos\consoleapplication1\consoleapplication1\consoleapplication1.cpp(49): note: 'CALLABLE=std::_Binder<std::_Unforced,bool (__thiscall SFF::* )(FSPtr,int),SFF *,const std::_Ph<1> &,const std::_Ph<2> &>'
1>c:\users\s.chan\source\repos\consoleapplication1\consoleapplication1\consoleapplication1.cpp(49): note: 'ARGS={int &}'
1>Done building project "ConsoleApplication1.vcxproj" -- FAILED.
========== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ==========
Any help is much appreciated.
Apparently fn (CALLABLE) in FSM::All() is supposed to be called as fn(m_pFs, args...), not fn(args...)
So your SFINAE is wrong:
std::result_of_t<CALLABLE(ARGS&&...)> is missing the m_pFs argument:
std::result_of_t<CALLABLE(FSPtr, ARGS&&...)>
If you add FSPtr it should work. But keep in mind that result_of is deprecated. You can achieve the same effect simply with a trailing return type:
template <typename CALLABLE, typename... ARGS>
auto All(CALLABLE fn, ARGS&&... args)
-> std::enable_if_t<std::is_same_v<bool, decltype(fn(std::declval<FSPtr>(), args...))>, bool>
{
Note also that std::bind returns a lambda. Creating an std::function from that will be inefficient. Better to just use the returned type as-is:
auto rFunc = std::bind(fn, m_pFs, args...); // no need to cast to std::function
rFunc();
The reason why your code doesn't compile is the following:
bool Do()
{
return m_pFsm->All(std::bind(&SFF::VF, this, std::placeholders::_1));
}
Here you use wrapper function All to call VF function. But:
1) All function is supposed to get the function arguments:
template <typename CALLABLE, typename... ARGS>
typename std::enable_if<std::is_same<bool, std::result_of_t<CALLABLE(ARGS&&...)>>::value, std::result_of_t<CALLABLE(ARGS&&...)>>::type
All(CALLABLE fn, ARGS&&... args) const // line 21
{
std::function<bool()> rFunc = std::bind(fn, m_pFs, args...);
bool bSuccess = rFunc();
return bSuccess;
}
Args here should stand for single argument of FSPtr type, see the signature of SFF::VF.
So the correct code should be:
return m_pFsm->All(std::bind(&SFF::VF, this, std::placeholders::_1), somethingOfFSPtrType);
clang 3.4 accepts the following code; while vc++ NOV 2013 CTP rejects it with an error:
error C2668: 'AreEqual' : ambiguous call to overloaded function
template<class headT, class... tailTypes>
constexpr headT&& __GetFirst__(headT&& value, tailTypes&&...)
{
return static_cast<headT&&>(value);
};
template<class T>
constexpr bool AreEqual(const T& a, const T& b)
{
return a == b;
}
template<class headT, class... tailTypes>
constexpr bool AreEqual(const headT& head_value, const tailTypes&... tail_values)
{
return AreEqual(head_value, __GetFirst__(tail_values...))
&& AreEqual(tail_values...);
}
int main()
{
AreEqual(1, 1, 2, 1);
}
Which compiler is correct as per the C++14 standard?
Update: The full error message:
error C2668: 'AreEqual' : ambiguous call to overloaded function
1> d:\projects\ktl\test\main.cpp(20): could be 'bool AreEqual<headT,int>(const headT &,const int &)'
1> with
1> [
1> headT=int
1> ]
1> d:\projects\ktl\test\main.cpp(8): or 'bool AreEqual<headT>(const T &,const T &)'
1> with
1> [
1> headT=int
1> , T=int
1> ]
1> while trying to match the argument list '(const int, const int)'
1>
1>Build FAILED.
Clang (and GCC)'s behavior is correct. You can read ยง14.8.2.4 [temp.deduct.partial] of the standard for how the partial ordering for function templates is done, but the example given in p8 of that subclause directly covers this situation:
template<class... Args> void f(Args... args); // #1
template<class T1, class... Args> void f(T1 a1, Args... args); // #2
template<class T1, class T2> void f(T1 a1, T2 a2); // #3
f(); // calls #1
f(1, 2, 3); // calls #2
f(1, 2); // calls #3; non-variadic template #3 is more
// specialized than the variadic templates #1 and #2
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;
}
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(...);