I would like to pass a function as a template argument to another function so that it can be stored and called back later. In some cases, I want to pass NULL for the call back, but I'm having trouble. Here's an example of what I'd like to be able to do:
#include <iostream>
struct Foo {
int i;
};
template <typename T>
T* T_new() {
return new T();
}
Foo* Foo_new() {
return new Foo();
}
template <typename T, T* (*func)()>
T* T_new() {
if (func)
return func();
else
return NULL;
}
int main(void) {
// Works
Foo* f1 = T_new<Foo>();
std::cout << f1 << std::endl;
// Works
Foo* f2 = T_new<Foo, Foo_new>();
std::cout << f2 << std::endl;
// fails to compile, "no matching function for call to ‘T_new()’"
// Foo* f3 = T_new<Foo, NULL>();
// std::cout << f3 << std::endl;
return 0;
}
I found this similar question, but that deals with passing null as an argument to the constructor, not passing null as a template argument, and the trick there (using (Foo*)0
) doesn't work as a template argument.
Is there a way to work around this or do some tricky template specialisation or some other clever thing to get the desired effect?
EDIT:
The above was a simplified example that illustrated the problem I was having, but here's the concrete problem I'm trying to solve. I have this project I'm working on. This is a set of functions that make mixing C++ and Lua simpler for me (For various reasons I don't want to use LuaBind or the other existing functions I've found out there). The important function to this question is luaW_register<T>
, near the bottom. This is a slightly out of date version, but it works in almost all cases. It doesn't work, however, if the constructor is private, which has just come when I tried mixing this with Box2D's b2Body
(which needs to be made from a b2World
). luaW_defaultallocator<T>()
(and luaW_defaultdeallocator<T>()
) still gets created since I'm using it as the default argument in luaW_register<T>()
.
My proposed solution was to pull the allocator
parameter out into template parameters of luaW_Register
. Then, if I want to use some other function to get my objects for a specific type, luaW_defaultallocator
will not even be created. In cases like b2Body
s, where they can't create themselves at all, I would like to be able to just pass in NULL
as a template argument (which seems perfectly reasonable, but the compiler is choking on it for reasons that are still unclear to me, it seems like if I can set a value to NULL
anywhere else in code I ought to be able to for templates as well). A hack I initially implemented was to pass in a boolean argument to my function which would disable the ability to call Foo.new
from my Lua code, but that doesn't stop defaultallocator
from compiling, and if I can use the null check in and working the way I would like it has the nice side effect of letting me simply check if there's an allocator and use that to control whether or not the new
function gets added to the lua table.
tl;dr: my goal was to go from this:
template <typename T>
void luaW_register(lua_State* L, const char* classname, const luaL_reg* table, const luaL_reg* metatable, const char** extends = NULL, bool disablenew = false, T* (*allocator)() = luaW_defaultallocator<T>, void (*deallocator)(T*) = luaW_defaultdeallocator<T>)
to this:
template <typename T, T* (*allocator)() = luaW_defaultallocator<T>, void (*deallocator)(T*) = luaW_defaultdeallocator<T> >
void luaW_register(lua_State* L, const char* classname, const luaL_reg* table, const luaL_reg* metatable, const char** extends = NULL)
to avoid instantiation of luaW_defaultallocator in some cases, but it's looking like it might not be possible.
The closest solution I've seen so far is to provide a function like luaW_cannotalloc<T>(lua_State*)
which returns NULL and can be checked for in my luaW_register function instead of null. I suppose that would work, but it means more typing and needing to remember that function name, and NULL seems much cleaner.
This can be solved by using template overloads. Instead of having just one ´T_new` signature you´ll have one signature for the NULL case and one for the other:
// Unused signature, no implementation so using this will result in link error
template<typename T, typename F>
T* T_new();
// NULL overload (NULL is an int)
template<typename T, int func>
T* T_new()
{
assert(func == 0 && "Signature should only be used with NULL");
return NULL;
}
// Valid function pointer overload
template<typename T, T* (*func)()>
T* T_new()
{
// I don´t think it´s possible with NULL functions now, but if it is
// we'll handle that too
if (func)
return func();
return NULL;
}
The trick is to realize that NULL is actually an int and use this to handle the NULL case in a different overload.
The problem for null-pointer is that a template pointer argument must have external linkage. And null doesn't have linkage.
How to make things work: It seems that you have selected the wrong tool for whatever it is you're trying to achieve.
Cheers & hth.,
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With