Reputation: 1386
edit Possibly can't be done, see Clean implementation of function template taking function pointer although answer 1 there has a C macro work-around https://stackoverflow.com/a/18706623/2332068
I'm passing a function into a template to become a pre-supplied argument to the constructor, but also need to use decltype
on that function to pass the function type to unique_ptr<...>
template instantiator(? is that the right word)
It works if I pre-use decltype
as an extra template argument, but not if I invoke it inside the template on the function passed as a parameter.
I'm using g++ 4.9.2, and extending my explorations here Calling inherited template constructor of unique_ptr subclass where I subclass unique_ptr
to have a fixed destructor, I find that some destructor functions do not return void
, so I want a more generic template that does not need to specify the destructor function type.
My current code is:
void free_int(int* p) {
delete p;
}
template<typename T, void (*D)(T*)>
class unique_dptr : public std::unique_ptr<T, decltype(D)> {
public: unique_dptr(T* t) : std::unique_ptr<T, decltype(D)>(t, D) { };
};
using int_ptr = unique_dptr<int, ::free_int>;
int_ptr i(new int(2));
but note the void (*D)(T*)
calling signature to restrict the destructor to a void function that takes a pointer to T
Given normal use of unique_ptr
in this form:
unique_ptr<foo, decltype(&::free_foo)>
I want to have something like this:
template<typename T, typename D>
class unique_gptr : public std::unique_ptr<T, decltype(&D)> {
public: unique_gptr(T* t) : std::unique_ptr<T, decltype(&D)>(t, D) { };
};
using int_gptr = unique_gptr<int, ::free_int>;
int_gptr ig(new int(2));
but the compiler hates it:
error: template argument 2 is invalid
class unique_gptr : public std::unique_ptr<T, decltype(&D)> {
^
No doubt ancient C-macro style token pasting is what I am wrongly aiming at.
I have tried removing the &
from decltype(&D)
but that leaves the error:
error: argument to decltype must be an expression
however this is OK:
template<typename T, typename D, D F>
class unique_gptr : public std::unique_ptr<T, D> {
public: unique_gptr(T* t) : std::unique_ptr<T, D>(t, F) { };
};
using int_gptr = unique_gptr<int, decltype(&::free_int), ::free_int>;
int_gptr ig(new int(2));
but I wonder what I am doing wrong that I can't manage move the decltype(&::free_int)
into the template.
I realise that I can just write additional specialisations for other fixed free-function types, replacing void(*)(void*)
I realise that I can override the std::default_delete
for my type.
But this is really an exercise in template composition
Upvotes: 4
Views: 2116
Reputation: 1529
I think for c++11 it is impossible to achieve what you want.
template<typename T, typename D> class unique_gptr : public std::unique_ptr<T, decltype(&D)> { public: unique_gptr(T* t) : std::unique_ptr<T, decltype(&D)>(t, D) { }; }; using int_gptr = unique_gptr<int, ::free_int>; int_gptr ig(new int(2));
Notice that decltype
is applied on D
, which is declared as a typename
. So D
is a type. But decltype
can't be used on a type.
Firstly the code tries to get the address of a type (&
). Secondly, the argument of decltype
is expected to be an expression, but not a type or "the address of a type". To make it easier to understand, we can say that decltype
expects its argument to be a “variable”, like the following example.
int main()
{
int i = 10;
decltype(i) j;
decltype(int) k; /* Compiler error. */
decltype(&int) l; /* Compiler error. */
return 0;
}
You also want the compiler to replace D
with ::free_int
. And ::free_int
is passed in as a non-type template argument. However D
is a type template parameter. A non-type template parameter starts with an actual type (e.g. int
, struct a*
or a type template name). While a type template parameter starts with typename
or class
. An easier example for non-type template parameter,
template<int INIT>
void func2(void)
{
decltype(INIT) j = INIT;
}
int main()
{
func2<10>();
return 0;
}
When you pass in a function pointer like ::free_int
, you need a non-type template parameter, which must be preceded by a type. And you want the type of the function pointer to be "replaceable". So the type of the function pointer has to be a type template parameter. These make them two template parameters.
That's the reason you need three template parameters in template<typename T, typename D, D F>
, which is the best result you have.
Upvotes: 2
Reputation: 117641
You can use some macro magic:
template<class T, class D, D d>
struct UniqueDeleter : public std::unique_ptr<T, D> {
UniqueDeleter(T* t) : std::unique_ptr<T, D>(t, d) { };
};
template<class R, class T>
T decl_unique_deleter_ptr(R (*d)(T*));
#define DECL_UNIQUE_DELETER1(f) UniqueDeleter<decltype(decl_unique_deleter_ptr(&f)), decltype(&f), &f>
#define DECL_UNIQUE_DELETER2(T, f) UniqueDeleter<T, decltype(&f), &f>
#define DECL_UNIQUE_DELETER_GET_MACRO(_1, _2, NAME, ...) NAME
#define DECL_UNIQUE_DELETER_EXPAND(x) x
#define decl_unique_deleter(...) DECL_UNIQUE_DELETER_EXPAND(DECL_UNIQUE_DELETER_GET_MACRO( \
__VA_ARGS__, DECL_UNIQUE_DELETER2, DECL_UNIQUE_DELETER1, _)(__VA_ARGS__))
Now you can use it like such:
decl_unique_deleter(int, free_int) ig(new int(2));
Or with using
and some more magic:
using int_gptr = decl_unique_deleter(free_int); // If accepted pointer type matches.
int_gptr ig(new int(2));
I might also recommend an alternative solution:
auto i = new int(2);
BOOST_SCOPE_EXIT_ALL(&) { delete i; };
Upvotes: 1
Reputation: 3778
You cannot use decltype
with a type as the goal is to obtain the type of a variable. But in decltype(&D)
, D
is a type.
I would rather pass throught a template function:
template<typename T, typename D, D F>
class unique_gptr : public std::unique_ptr<T, D> {
public: unique_gptr(T* t) : std::unique_ptr<T, D>(t, F) { };
};
template <typename T, typename D>
unique_gptr<T, D F> make_unique_gptr(T pointer, D deleter)
{
return unique_gptr<T, D, deleter>(pointer);
}
auto ptr = make_unique(new int(2), ::free_int);
Upvotes: 2