Reputation: 181
I'm trying to detect if a type is a shared_ptr<T>
and if it is, dispatch to a specific function template or override.
Here's a simplified version of what I'm actually attempting:
#include <type_traits>
#include <memory>
#include <cstdio>
template <class T> struct is_shared_ptr : std::false_type {};
template <class T> struct is_shared_ptr<std::shared_ptr<T> > : std::true_type {};
class Foo { };
typedef std::shared_ptr<Foo> SharedFoo;
template<class T> void getValue();
template<class T, typename std::enable_if<is_shared_ptr<T>::value>::type = 0>
void getValue()
{
printf("shared!\n");
}
template<class T, typename std::enable_if<!is_shared_ptr<T>::value>::type = 0>
void getValue()
{
printf("not shared!\n");
}
int main(int, char **)
{
getValue<SharedFoo>();
getValue<Foo>();
return 0;
}
It compiles just fine, but it seems the actual functions were never actually generated because the code doesn't link with the following errors:
/tmp/ccjAKSBE.o: In function `main':
shared_test.cpp:(.text+0x10): undefined reference to `void getValue<std::shared_ptr<Foo>>()'
shared_test.cpp:(.text+0x15): undefined reference to `void getValue<Foo>()'
collect2: error: ld returned 1 exit status
I would think that those would be covered by the two function templates. But they aren't.
Given that, it seems I am seriously misunderstanding something.
So maybe it would help if I explain what I'm /trying/ to do rather than what I'm actually doing.
I have some "magic" code using a bunch of new (to me) C++11 features to bind C++ code to lua (can be seen here: https://github.com/Tomasu/LuaGlue). someone has recently asked for support for binding to classes wrapped in shared_ptr
's. which is not something that works at the moment, because it binds at compile time using templates and tuple unwrapping to generate code to call functions on either the C++ or lua side. In the "magic" unwrapping code, I have a bunch of overridden and "specialized" functions that handle various variable types. Some for basic types, one for static objects, and another for pointer to objects. A shared_ptr
can't be handled in the same way as either a static or pointer object, so I need to add some extra handling just for them.
For example:
template<typename T>
T getValue(LuaGlue &, lua_State *, unsigned int);
template<>
int getValue<int>(LuaGlue &, lua_State *state, unsigned int idx)
{
return luaL_checkint(state, idx);
}
template<class T>
T getValue(LuaGlue &g, lua_State *state, unsigned int idx)
{
return getValue_<T>(g, state, idx, std::is_pointer<T>());
}
That's the actual code (notice the hairy template/override via function argument :-x).
I had thought it'd be as simple as adding another addValue
function, along the lines of the code in my earlier example, via enable_if
.
Upvotes: 5
Views: 5466
Reputation: 20523
Here is another solution which is basically a fix for the OP rather than alternatives (using a class and its constructor as in zennehoy's post or tag dispatch as in Casey's).
// Do not provide this declaration:
// template<class T> void getValue();
template<class T>
typename std::enable_if<is_shared_ptr<T>::value>::type
getValue()
{
printf("shared!\n");
}
template<class T>
typename std::enable_if<!is_shared_ptr<T>::value>::type
getValue()
{
printf("not shared!\n");
}
I must say that, as Casey has put it, I also "prefer the simplicity of tag dispatch". Hence, I would go for his solution.
I also found zennehoy's idea interesting but, unfortunately, it doesn't allow your function to return something. (I believe a workaround is possible but it adds complexity.) With the solution here you just need to provide the return type as the 2nd parameter of std::enable_if
.
Upvotes: 0
Reputation: 42554
Your program declares template<class T> void getValue()
but doesn't define it. template <typename, typename> void getValue()
is a different function. The compiler is picking template<class T> void getValue()
as a better match with your invocations, which of course fails at link time as there is no definition of that function.
I prefer the simplicity of tag dispatch when it's applicable (live at Coliru):
template<class T>
void getValue(std::true_type)
{
printf("shared!\n");
}
template<class T>
void getValue(std::false_type)
{
printf("not shared!\n");
}
template<class T>
void getValue() {
return getValue<T>(is_shared_ptr<T>{});
}
Upvotes: 2
Reputation: 6856
Any reason not to simply use partial specialization?
#include <type_traits>
#include <memory>
#include <cstdio>
class Foo { };
typedef std::shared_ptr<Foo> SharedFoo;
template <class T>
struct getValue {
getValue() {
printf("not shared!\n");
}
};
template <class T>
struct getValue<std::shared_ptr<T> > {
getValue() {
printf("shared!\n");
}
};
int main(int, char **)
{
getValue<SharedFoo>();
getValue<Foo>();
return 0;
}
Upvotes: 7