I'm trying to write an RAII-compliant resource wrapper, and I'm getting stuck on how to form the semantics of the template parameters.
For example, I could write a function to delete my resource:
void int_cleaner(int val) {
std::cout << "Value of " << val << " has been cleaned up." << std::endl;
}
Or I could write it as a Functor:
struct int_deleter {
void operator()(int val) const {
std::cout << "Value of " << val << " has been cleaned up." << std::endl;
}
};
But here's where I get stuck: if I want to pass this to my resource wrapper, I have to change how the template parameter is defined.
If I write resource
like this:
template<typename T, typename Deleter>
class resource {
};
This works fine with a functor, but not with the function itself.
int main() {
resource<int, int_deleter> res; //Compiles fine
//resource<int, int_cleaner> res2; //Does Not Compile
return 0;
}
Conversely, if I write the template parameters like this:
template<typename T>
using deleter_t = void(*)(T);
template<typename T, deleter_t<T> Deleter>
class resource {
};
int main() {
//resource<int, int_deleter> res; //Does Not Compile
resource<int, int_cleaner> res2; //Compiles fine
return 0;
}
Now, I could write both versions of the code, but there's two reasons I don't want to do that:
- I'd just be duplicating the definition for
resource
, and if I need to make a change to one, I need to make changes to the other as well. - The version that accepts function pointers won't accept a version like
void cleaner(T const&)
, because that won't bind tovoid(*)(T)
. So I'd also need to make two or three more versions so that I can handleT
,T&
,T const&
, andT&&
.
How can I write the resource wrapper in such a way that minimizes code duplication, especially given that the deletion mechanism is going to vary between the functor version and the function pointer version?
//example:
template<typename T>
using deleter_t = void(*)(T);
template<typename T, deleter_t<T> Deleter>
class resource {
~resource() {Deleter(val);}
};
template<typename T, typename Deleter>
class resource {
~resource() {Deleter{}(val);}//Note the subtle syntax change
};
std::unique_ptr
et al. have the same problem. They support passing the deleter object by value, so that you can pass the appropriate function pointer. Don't know if this is the best solution though. – Unicefresource<int, int(&)(int)>
to useint_cleaner
as a deleter. – Pronouncementtemplate <auto FUNCTION> struct wrapper
, and pass this as template parameter to resource:resource<int, wrapper<int_cleaner>>
. You have to make some compromise I think, becauseint_cleaner
is function, butint_deleter
is a type (so they cannot be mapped to the same template parameter at the same time). – Nikos