I'm currently using a C library that defines a number of data types, all of which need to have their lifetimes managed by the user. There are a number of functions defined in this fashion:
int* create() {
return new int();
}
void destroy(int* i) {
delete i;
}
Most of these don't need to be accessed after creation. They simply need to exist. Because of this, I'm trying to manage them using unique_ptr's declared in the scope of which I need them to live.
This is how such a declaration looks like:
// Note that I'm avoiding writing the type's name manually.
auto a = std::unique_ptr<std::decay_t<decltype(*create())>, decltype(&destroy)>{create(), &destroy};
But this is overly verbose, so I encapsulated it in a utility function template:
template<typename T>
auto make_unique_ptr(T* p, void (*f)(T*)) {
return std::unique_ptr<T, decltype(f)>(p, f);
}
Which is used this way:
auto b = make_unique_ptr(create(), &destroy);
This looks nice, but introduces a non-standard function that has no real purpose other than being syntactic sugar for some declarations. My colleagues may not even know it exists and end up creating other versions of it with different names.
c++17 Introduced class template argument deduction. I thought this is the perfect solution to my problem: A standard way to deduce all those types without resorting to user-defined wrappers. So I tried this:
auto c = std::unique_ptr{create(), &destroy};
As is the rule with C++ compilers and templates, this fails with a several lines long error message. Here are the relevant parts:
(...): error: class template argument deduction failed:
auto c = std::unique_ptr{create(), &destroy};
^
(...): note: candidate: 'template<class _Tp, class _Dp>
unique_ptr(std::unique_ptr<_Tp, _Dp>::pointer, typename std::remove_reference<_Dp>::type&&)-> std::unique_ptr<_Tp, _Dp>'
unique_ptr(pointer __p,
^~~~~~~~~~
(...): note: template argument deduction/substitution failed:
(...): note: couldn't deduce template parameter '_Tp'
auto c = std::unique_ptr{create(), &destroy};
^
Theoretically, I could add a deduction guide to handle this:
namespace std {
template<typename T>
unique_ptr(T* p, void (*f)(T*)) -> unique_ptr<T, decltype(f)>;
}
And it does work at least on my version of gcc, but the standard doesn't like it very much:
[namespace.std]
1 Unless otherwise specified, the behavior of a C++ program is undefined if it adds declarations or definitions to namespace std or to a namespace within namespace std.
4 The behavior of a C++ program is undefined if it declares
(...)
4.4 - a deduction guide for any standard library class template.
There's also some problems related to differentiating between pointers and arrays, but let's ignore that.
Finally, the question(s): Is there any other way to 'help' std::unique_ptr (or maybe std::make_unique) to deduce the correct type when using custom deleters? Just in case this is an XY problem, is there any solutions I didn't think of for lifetime management of those types (perhaps std::shared_ptr)? If both of those answers are a negative, is there any improvements on c++20 I should be looking forward to, that solve this issue?
Feel free to test the above examples at Coliru.
I would recommend writing custom deleters rather than using function pointers. Using the function pointer would make all the unique_ptrs twice the size for no reason.
Instead, write a deleter templated on a function pointer:
template <auto deleter_f>
struct Deleter {
template <typename T>
void operator()(T* ptr) const
{
deleter_f(ptr);
}
};
Or, as Yakk - Adam Nevraumont mentioned in the comments:
template <auto deleter_f>
using Deleter = std::integral_constant<std::decay_t<decltype(deleter_f)>, deleter_f>;
Using it becomes pretty clean:
auto a = std::unique_ptr<int, Deleter<destroy>>{create()};
Although you might want to combine this with your make_unique_ptr function:
template <auto deleter_f, typename T>
auto create_unique_ptr(T* ptr)
{
return std::unique_ptr<T, Deleter<deleter_f>>{ptr};
}
// Usage:
auto a = create_unique_ptr<destroy>(create());
A type as a value:
template<class T>
struct tag_t {};
template<class T>
constexpr tag_t<T> tag{};
A value as a type:
template<auto f>
using val_t = std::integral_constant<std::decay_t<decltype(f)>, f>;
template<auto f>
constexpr val_t<f> val{};
Note that val<some_function> is an empty type that can be invoked in a constexpr context with () and it will call some_function. It can also store ints or whatever, but we are going to use it to store function pointers statelessly.
Now let's have fun:
namespace factory {
// This is an ADL helper that takes a tag of a type
// and returns a function object that can be used
// to allocate an object of type T.
template<class T>
constexpr auto creator( tag_t<T> ) {
return [](auto&&...args){
return new T{ decltype(args)(args)... };
};
}
// this is an ADL helper that takes a tag of a type
// and returns a function object that can be used
// to destroy that type
template<class T>
constexpr auto destroyer( tag_t<T> ) {
return std::default_delete<T>{};
}
// This is a replacement for `std::unique_ptr`
// that automatically finds the destroying function
// object using ADL-lookup of `destroyer(tag<T>)`.
template<class T>
using unique_ptr = std::unique_ptr< T, decltype(destroyer(tag<T>)) >; // ADL magic here
// This is a replacement for std::make_unique
// that uses `creator` and `destroyer` to find
// function objects to allocate and clean up
// instances of T.
template<class T, class...Args>
unique_ptr<T> make_unique(Args&&...args) {
// ADL magic here:
return unique_ptr<T>( creator( tag<T> )(std::forward<Args>(args)...) );
}
}
ok, that is a framework.
Now let's imagine you have some library. It has a type in it. It needs super secret special sauce to create and destroy instances of it here:
namespace some_ns {
struct some_type {
int x;
};
some_type* create( int a, int b ) {
return new some_type{ a+b }; // ooo secret
}
void destroy( some_type* foo ) {
delete foo; // ooo special
}
}
and we want to connect it up. You reopen the namespace:
namespace some_ns {
constexpr auto creator( tag_t<some_type> ) {
return val<create>;
}
constexpr auto destoyer( tag_t<some_type> ) {
return val<destroy>;
}
}
and we are done.
factory::unique_ptr<some_ns::some_type> is the right type to store a unique ptr to some_type. To create it you just factory::make_unique<some_ns::some_type>( 7, 2 ) and you get a unique ptr of the correct type with a destroyer lined up, with zero memory overhead, and the call to the destroyer function involving no indirection.
Basically sweep std::unique_ptr and std::make_unique for factory::unique_ptr and factory::make_unique, then line up the creator/destroyer ADL helpers to make all unique ptrs to a given type just do the right thing.
Test code:
auto ptr = factory::make_unique<some_ns::some_type>( 2, 3 );
std::cout << ptr->x << "=5\n";
std::cout << sizeof(ptr) << "=" << sizeof(std::unique_ptr<some_ns::some_type>) << "\n";
live example.
This has zero runtime overhead over writing custom unique ptr types. If you don't overload creator or destroyer for a tag_t<X> in X's namespace (or in namespace factory), factory::make_unique returns a bog standard std::unique_ptr<X>. If you do, it injects compile-time information how to destroy it.
By default factory::make_unique<X> uses {} initialization so it works with aggregates.
The tag_t system is to enable ADL-based customization of factory::make_unique and factory::unique_ptr. It is useful elsewhere too. End users don't have to know about it, they just need to know you use factory::unique_ptr and factory::make_unique always.
Searching for std::make_unique and std::unique_ptr should find you cases where someone is violating this rule. Eventually (you hope) they'll notice all unique pointers are factory::unique_ptr instead of std.
The magic to add a type to the system is short enough and easy enough to copy that people can do it without having to understand ADL. I'd include a paragraph marked "you don't need to know this, but this is how this works" in comments somewhere.
This is possibly overdone; I was just thinking how I'd handle distributed traits and destruction/creation in c++17 with some of the new features. I haven't tried this in production, so there may be unrevealed problems.
One thing you could do is use a using statement to introduce an alias for std::unique_ptr<T, void (*)(T*)> like
template<typename T>
using uptr = std::unique_ptr<T, void (*)(T*)>;
Then you could use it like
auto u = uptr<int>{create(), &destroy};
You are overcomplicating things. Simply specialize std::default_delete for your custom types, and you can use a vanilla std::unique_ptr.
A shame that std::shared_ptr doesn't use the same customization-point, there you will have to supply the deleter explicitly.
Related
I would like to call a template function even if std::enable_if_t doesn't resolve to true. It would allow me to reuse one of my existent templates in other context.
Minimal example:
#include <iostream>
#include <type_traits>
template<typename T,
typename = typename std::enable_if<std::is_same<T, int>::value>::type>
auto foo(T t) {
std::cout << t << std::endl;
return t;
}
template<typename T>
auto bar(T t) {
foo(t);
}
int main() {
int i = 42;
foo(i); // ok already
// foo("42"); // shouldn't
bar("42"); // should
}
I tried to solve it bydeclaring a bool in template argument list of foo and specify it when calling foo in bar, but I didn't quite manage to compile it without changing how foo is getting called in already existent code.
Is there any way to accomplish this? Maybe std::disjunction?
What you want doesn't make sense. Either a call to foo is valid or it isn't. It shouldn't matter who does it. enable_if is used to ward templates from cases where a particular parameter cannot work (or you have an alternate definition where it can). For example, if the definition is going to use a copy constructor, but the type doesn't have one. What you're using it for here doesn't fit that use case.
You can pull out the logic of foo into a separate function that both foo and bar call, if you really need to do something like this.
Using this SFINAE technique, you can call foo for “forbidden” types by not using the default template argument (and therefore not using deduction):
template<typename T>
auto bar(T t) {
return foo<T,void>(t);
}
This is of course sometimes cited as a weakness of default-type-template-argument SFINAE, along with its inability to constrain multiple, otherwise-equivalent overloads.
In general, how would you request vector<T> as a type in a template?
template<?vector<T>?>
void allocate() {
vector<T> vector;
}
To be more specific (the example above is a bit dumb), I'm using a service_locator<T> from this library and all of its members are static. The constructor and the destructor of that struct are deleted, therefore there is no such thing as an instance of a service_locator<T>. I'd like to write a function that does something, given some service_locator<T>. Something like the following.
template<?service_locator<T>?> // What do I write here?
T& assertedRef() {
assert(!service_locator<T>::empty(), "Value not initialized for that type!");
return service_locator<T>::ref();
}
My current workaround is to ask for T instead of service_locator<T>. This is a hassle for the callers because it's on them to figure out the underlying type the service_locator is operating on (most of them are aliased to something more readable).
You can use partial specialization for this. Since that's not supported for functions, you have to forward the call to a partially specialized class instance.
template <typename T>
struct AssertedRefFunctor {
static_assert(sizeof(T) == -1, "Not a service locator");
};
template <typename T>
struct AssertedRefFunctor<entt::service_locator<T>> {
T& operator()() {
if (entt::service_locator<T>::empty()) {
throw std::runtime_error("Value not initialized for that type!");
}
return entt::service_locator<T>::ref();
}
};
template <typename T>
auto assertedRef() -> decltype(std::declval<AssertedRefFunctor<T>>()()) {
return AssertedRefFunctor<T>{}();
}
Usage:
assertedRef<entt::service_locator<int>>();
Demo: https://godbolt.org/z/f-oUpY
C++ (at least C++17) does not support template parameter constraints like C# does (they were proposed for C++0x as "concepts" but withdrawn). But you can have a static assert using std::is_base_of.
And as a hint to whoever consumes your library, I'd name the template parameter as TVector and TVectorItem to be clear that it expects a vector<TVectorItem> derivative.
(And I agree with the (now deleted) comment that the Service Locator pattern is an anti-pattern. There are almost always better alternatives to using the Service Locator pattern - but we'd need to know more about your application before suggesting anything).
(And if you're using Visual Studio or MSBuild and want to build a statically-evaluated or constexpr DI container builder, consider using T4 for codegen instead of C++ templates).
#include <type_traits>
template<typename TVector, typename TVectorItem>
void allocate() {
static_assert( std::is_base_of<vector<TVectorItem>, TVector>::value, "TVector must derive from vector<TVectorItem>." );
TVector vector;
}
There are no direct way to do that.
In your case, you might do:
template <typename ServiceLocatorT>
auto assertedRef() -> decltype(ServiceLocatorT::ref())
{
assert(!ServiceLocatorT::empty(), "Value not initialized for that type!");
return ServiceLocatorT::ref();
}
In generic case, template classes might have aliases of there template (as done with STL std::vector<T>::value_type) that you can use.
At worst, you might create type_traits:
template <typename T> struct value_type;
template <typename T> struct value_type<std::vector<T>>
{
using type = T;
};
// ...
Can the auto keyword in C++11 replace function templates and specializations? If yes, what are the advantages of using template functions and specializations over simply typing a function parameter as auto?
template <typename T>
void myFunction(T &arg)
{
// ~
}
vs.
void myFunction(auto &arg)
{
// ~
}
In a nutshell, auto cannot be used in an effort to omit the actual types of function arguments, so stick with function templates and/or overloads. auto is legally used to automatically deduce the types of variables:
auto i=5;
Be very careful to understand the difference between the following, however:
auto x=...
auto &x=...
const auto &x=...
auto *px=...; // vs auto px=... (They are equivalent assuming what is being
// assigned can be deduced to an actual pointer.)
// etc...
It is also used for suffix return types:
template <typename T, typename U>
auto sum(const T &t, const U &u) -> decltype(t+u)
{
return t+u;
}
Can the auto keyword in C++11 replace function templates and specializations?
No. There are proposals to use the keyword for this purpose, but it's not in C++11, and I think C++14 will only allow it for polymorphic lambdas, not function templates.
If yes, What are the advantages of using template functions and specializations over simply typing a function parameter as auto.
You might still want a named template parameter if you want to refer to the type; that would be more convenient than std::remove_reference<decltype(arg)>::type or whatever.
The only thing which makes it the auto keyword different from template that is you cannot make a generic class using the auto keyword.
class B { auto a; auto b; }
When you create an object of the above class it will give you an error.
B b; // Give you an error because compiler cannot decide type so it can not be assigned default value to properties
Whereas using template you can make a generic class like this:
template <class T>
class B {
T a;
};
void main() {
B<int> b; //No Error
}
I want to change default_deleter for std::unique_ptr. This is quite easy to achieve, but there is one inconvenient - I have to declare variables using 2 template parameters instead of one, something like this:
std::unique_ptr<MyType, MyDeleter<MyType>> myVar;
As you may see the declaration is long and I have a feeling I can use a shorter version, but I don't know how:)
Is it possible to declare some kind of MyUniquePtr<T> that will be the same as std::unique_ptr<T, MyDeleter<T>>?
EDIT: Matthieu M. already answered, but unfortunately I can't use this feature in Visual Studio as it's not implemented. Is there other way to have this behavior?
Actually it is, using template aliases:
template <typename T>
using MyUniquePtr = std::unique_ptr<T, MyDeleter<T>>;
If your compiler doesn't do template aliases yet, here's the C++03 idiom:
template <typename T>
struct MyUniquePtr {
typedef std::unique_ptr<T, MyDeleter<T> > type;
};
MyUniquePtr<MyType>::type var;
The setup is uglier, but the resulting usage is almost as short: you just need to add ::type.
Does your deleter need to be templated on the type of the object being deleted, or is it sufficient for the function call operator to be templated on the type of object being deleted?
Instead of:
template<typename T>
struct MyDeleter
{
void operator()(T* p) const { /* ... */ }
};
Can you write:
struct MyDeleter
{
template<typename T>
void operator()(T* p) const { /* ... */ }
};
This, of course, depends on what state MyDeleter has to maintain.
Another way conforming to c++03 is a subclass, which unfortunately requires you to reproduce the constructors. (This can be done easier, if your compiler supports variadic template arguments, which may be the case for current MSVC.)
template <class T>
class MyUniquePtr : public std::unique_ptr< T, MyDeleter<T> > {
public:
MyUniquePtr(args...) : std::unique_ptr< T, MyDeleter<T> >(args...) { }
};
You might want to consider writing your own version of a make_unique style function instead specialized for your custom allocation / deletion strategy. This also has the advantage that you can perform any specialized resource acquisition / allocation in an exception safe way. You can then declare your unique_ptr as auto and let type deduction work for you.
I currently have a function template, taking a reference, that does something in essence equivalent to:
template <typename T>
void f(T& t)
{
t = T();
}
Now, I can call:
int a;
f(a);
To initialize my variable a.
I can even do:
std::vector<int> a(10);
f(a[5]);
However, this will fail:
std::vector<bool> a(10);
f(a[5]);
The reason being a[5] returns an object with reference semantic, but not a reference. So I need to be able to write:
template <typename T>
void f(T a)
{
a = T();
}
But if I add this new template and try to compile the first example (with int), I obtain the following error:
test_multi_tmpl.cc: In function ‘int main()’:
test_multi_tmpl.cc:20: error: call of overloaded ‘f(int&)’ is ambiguous
test_multi_tmpl.cc:6: note: candidates are: void f(T&) [with T = int]
test_multi_tmpl.cc:12: note: void f(T) [with T = int]
Any ideas how to solve this? I wouldn't like to overload f just for std::vector<bool>::reference as this construct might appears in other places ...
I think specialising f for std::vector<bool>::reference is your only option.
Note that using std::vector<bool> is probably a bad idea in the first place (the std::vector<bool> specialisation is deprecated for future versions of the c++ language) so you could just use std::deque<bool> instead.
I'm not sure whether you already know about this...
The specialization std::vector is not really a STL container because it does not meet the necessary requirements. In particular, it's not possible to created proxied containers that satisfy the STL concepts because of the reference semantics (you can't fake a reference). Check this article for more information. (Also, as Autopulated mentioned there should be a compiler directive to provide control over std::vector in the future C++ Standard.)
A simple workaround that could solve your problem is by overloading function f for this particular type (and others if they appear and are not many). Notice that this is an overload and not an specialization. You might also wanna check this for why not specialize function templates.
void f(std::vector<bool>::reference t)
{
/* ... */
}
There are two ways to do this, one is, as you suggest, specialize for std::vector<bool>::reference. The other is by using type-traits dispatching.
template <class T>
void f (T& t) {
f_impl(t, is_reference_wrapper_type<T>());
}
template <class T>
void f_impl(T& t, mpi::false_) {
t = T();
}
template <class T>
void f_impl(T& t, mpi::true_) {
// do nothing, or voodoo here
}
Note the code above is un-tested and there would be more complex means of dispatching based on a trait -- or a set of traits -- in this situation.
This would also mean that you would need to implement is_reference_wrapper_type like so:
template <class T>
struct is_reference_wrapper_type : mpi::false_ {};
template <>
struct is_reference_wrapper_type<std::vector<bool>::reference> : mpi::true_ {};
Using traits or template specialization would make it work.