Assume I have a set of unique_ptr:
std::unordered_set <std::unique_ptr <MyClass>> my_set;
I'm not sure what's the safe way to check if a given pointer exists in the set. The normal way to do it may be to call my_set.find (), but what do I pass as a parameter?
All I have from the outside is a raw pointer. So I have to create another unique_ptr from the pointer, pass it to find() and then release() that pointer, otherwise the object would get destructed (twice). Of course, this process can be done in a function, so the caller can pass the raw pointer and I do the conversions.
Is this method safe? Is there a better way to work with a set of unique_ptr?
You can also use a deleter that optionally doesn't do anything.
template<class T>
struct maybe_deleter{
bool _delete;
explicit maybe_deleter(bool doit = true) : _delete(doit){}
void operator()(T* p) const{
if(_delete) delete p;
}
};
template<class T>
using set_unique_ptr = std::unique_ptr<T, maybe_deleter<T>>;
template<class T>
set_unique_ptr<T> make_find_ptr(T* raw){
return set_unique_ptr<T>(raw, maybe_deleter<T>(false));
}
// ...
int* raw = new int(42);
std::unordered_set<set_unique_ptr<int>> myset;
myset.insert(set_unique_ptr<int>(raw));
auto it = myset.find(make_find_ptr(raw));
Live example.
Note that the ability to do heterogenous lookups on standard containers is subject of some proposals.
http://cplusplus.github.io/LWG/lwg-proposal-status.html lists
N3465 Adding heterogeneous comparison lookup to associative containers for TR2 (Rev 2) [Handle with N3573]
N2882 id.
N3573 Heterogenous extensions to unordered containers [Handle with N3465]
Especially the latter looks like it would cover your use case.
For now, here is an IMO not very pretty but working alternative workaround (O(n)):
#include <iterator>
#include <iostream>
#include <algorithm>
#include <unordered_set>
#include <memory>
#include <cassert>
struct MyClass {};
template <typename T>
struct RawEqualTo
{
RawEqualTo(T const* raw) : raw(raw) {}
bool operator()(T const* p) const
{ return raw == p; }
bool operator()(std::unique_ptr<T> const& up) const
{ return raw == up.get(); }
private:
T const* raw;
};
using namespace std;
int main()
{
std::unordered_set <std::unique_ptr <MyClass>> my_set;
my_set.insert(std::unique_ptr<MyClass>(new MyClass));
my_set.insert(std::unique_ptr<MyClass>(new MyClass));
auto raw = my_set.begin()->get();
bool found = end(my_set) != std::find_if(begin(my_set), end(my_set), RawEqualTo<MyClass>(raw));
assert(found);
raw = new MyClass;
found = end(my_set) != std::find_if(begin(my_set), end(my_set), RawEqualTo<MyClass>(raw));
assert(!found);
delete raw;
}
Warning It's also very inefficient, of course.
You can use a std::map<MyClass*, std::unique_ptr<MyClass>> instead of a set. Then you can add elements like this:
std::unique_ptr<MyClass> instance(new MyClass);
map.emplace(instance.get(), std::move(instance));
If the goal is constant time for the look up, I don't think that
there is a solution.
std::unordered_set<std::unique_ptr<MyClass>>::find requires an
std::unique_ptr<MyClass> as argument. You will have to either change
the container, or change the contained type.
One possibility might be to replace std::unique_ptr with
std::shared_ptr, and change the rest of the code so that all
MyClass are put into a shared_ptr as soon as they are created,
and are only manipulated through shared pointers. Logically,
this is probably more coherent anyway: unique_ptr pretty much
implies (by its name, as well as its semantics) that there
aren't other pointers to the object. On the other hand, you may
not be able to use shared_ptr, if e.g. MyClass has pointers to
other MyClass, which may build a cycle.
Otherwise, if you can accept O(lg n) access, rather than
constant access (the difference generally doesn't become
noticeable until the tables are fairly large), you can use an
std::vector<MyClass>, using std::lower_bound to keep it
sorted. Unlike std::unordered_set<>::find, std::lower_bound
does not require the target value to have the same type as the
value_type of the sequence; all you have to do is to ensure
that they are comparable, say by providing a Compare object
along the lines of:
class MyClassPtrCompare
{
std::less<MyClass const*> cmp;
public:
bool operator()( std::unique_ptr<MyClass> const& lhs,
std::unique_ptr<MyClass> const& rhs ) const
{
return cmp( lhs.get(), rhs.get() );
}
bool operator()( MyClass const* lhs,
std::unique_ptr<MyClass> const& rhs ) const
{
return cmp( lhs, rhs.get() );
}
bool operator()( std::unique_ptr<MyClass> const& lhs,
MyClass const* rhs ) const
{
return cmp( lhs.get(), rhs );
}
bool operator()( MyClass const* lhs,
MyClass const* rhs ) const
{
return cmp( lhs, rhs );
}
};
Insertion may involve a number of moves, but moving
a std::unique_ptr should be fairly cheap, and the improved
locality of this solution might offset the additional runtime
costs it otherwise imposes.
If you can use Abseil, do it:
absl::flat_hash_set<std::unique_ptr<MyClass>> my_set;
just works :)
Here is the proper way to do it in C++20 with "Heterogeneous lookup for unordered containers" available:
struct Hash {
using is_transparent = void;
template <class P>
size_t operator()(const P& p) const {
return std::hash<P>{}(p);
}
};
struct KeyEqual {
using is_transparent = void;
template <class P, class Q>
bool operator()(const P& lhs, const Q& rhs) const {
return std::to_address(lhs) == std::to_address(rhs);
}
};
std::unordered_set<std::unique_ptr<MyClass>, Hash, KeyEqual> my_set;
More on the topic (in Russian): https://www.coursera.org/learn/c-plus-plus-brown/supplement/TtrLN/unordered-set-unique-ptr
Related
Say I define a map with a custom comparator such as
struct Obj
{
int id;
std::string data;
std::vector<std::string> moreData;
};
struct Comparator
{
using is_transparent = std::true_type;
bool operator()(Obj const& obj1, Obj const& obj2) { return obj1.id < obj2.id; };
}
std::map<Obj,int,Comparator> compMap;
is there a good way to ensure that downstream users don't have to implement the comparator to use the map as a map?
for instance my compiler throws an error if I try to pass it to a function with a similar type.
template<class T>
inline void add(std::map<T, int>& theMap, T const & keyObj)
{
auto IT = theMap.find(keyObj);
if (IT != theMap.end())
IT->second++;
else
theMap[keyObj] = 1;
}
add(compMap,newObj); //type error here
EDIT:
I kinda over santitized this to make a generic case. and then overlooked the obvious
template<class T, class Comp, class Alloc>
inline void add(std::map<T, int, Comp, Alloc>& theMap, T const & keyObj)
still having issues with one use not being able to deduce T, but went from 80 erros to 1 so... progress
thanks everyone.
You can typedef the specialised type and use that type inplace of
std::map<...
typedef std::map<Obj,int,Comparator> compMap_t;
inline void add(compMap_t& theMap, Obj const & keyObj)
...
Downstream users either use the type declared by you
using my_important_map = std::map<Obj,int,Comparator>;
or better use functions which take a generic map type,
auto some_function(auto const& map_)
{
//do something with the map and don't care about the ordering
return map_.find(Obj(1));
}
I am storing the ownership of some objects inside an unordered_set, using unique_ptrs.
But I don't know a good way to erase one of them from the set, when the time comes.
Code looks something like this:
typedef unique_ptr<MyType> MyPtr;
unordered_set<MyPtr> owner;
MyPtr p = make_unique<MyType>("foo")
MyType *pRaw = p.get();
owner.insert(std::move(p));
// Later ...
// I want to do something like this (cannot be written as-is, of course):
// owner.erase(pRaw);
Is there a way to do this?
I can, of course, iterate the entire set with begin() and end(), but the whole point of putting them in the set is to make these lookups efficient.
Some things I have thought of already:
Use shared_ptr. This is the wrong abstraction for my case. Ownership is unique.
Use raw pointers, and forget about unique_ptr. This abandons all the advantages that unique_ptr provides.
Find the bucket with unordered_set::begin(key). As far as I know, there is no way for me to create a key that will match the unique_ptr I want to delete. But I'm happy to be proven wrong (:
(In truth, I solved this using eastl::unordered_set, with its find_as function for custom keys)
This is a tough case. erase has an overload that takes a const key_type& parameter, so we can try to create a "stale" unique_ptr to get the hash value of the element to be erased:
template <typename T>
auto erase(std::unordered_set<std::unique_ptr<T>>& set, T* ptr)
{
std::unique_ptr<T> stale_ptr{ptr};
auto ret = set.erase(stale_ptr);
stale_ptr.release();
return ret;
}
(live demo)
This version, however, is not exception safe in general, because release will not be called if set.erase throws an exception. This is not a problem in this case, since std::equal_to<std::unique_ptr<T>>::operator() never throws exception. In the general case, we can abuse unique_ptr (!) to enforce exception safety by ensuring that release is called regardless of whether the function is exited normally or exceptionally:
template <typename T>
auto erase(std::unordered_set<std::unique_ptr<T>>& set, T* ptr)
{
std::unique_ptr<T> stale_ptr{ptr};
auto release = [](std::unique_ptr<T>* p) { p->release(); };
std::unique_ptr<std::unique_ptr<T>, decltype(release)> release_helper{&stale_ptr, release};
return set.erase(stale_ptr);
}
(live demo)
In C++20, std::unordered_set::find can use equivalent key with transparent hash and KeyEqual, then you might do something similar to:
struct MyHash
{
using is_transparent = void;
auto operator()(MyType* p) const { return std::hash<MyType*>{}(p); }
auto operator()(const MyPtr& p) const { return std::hash<MyType*>{}(p.get()); }
};
struct MyEqual
{
using is_transparent = void;
template <typename LHS, typename RHS>
auto operator()(const LHS& lhs, const RHS& rhs) const
{
return AsPtr(lhs) == AsPtr(rhs);
}
private:
static const MyType* AsPtr(const MyType* p) { return p; }
static const MyType* AsPtr(const MyPtr& p) { return p.get(); }
};
int main()
{
std::unordered_set<MyPtr, MyHash, MyEqual> owner;
MyPtr p = std::make_unique<MyType>();
MyType *pRaw = p.get();
owner.insert(std::move(p));
auto it = owner.find(pRaw);
if (it != owner.end()) {
owner.erase(it);
}
}
template<typename T>
struct raster {
std::vector<T> v;
template<typename U, typename = std::enable_if_t<sizeof(T) == sizeof(U)>>
raster(raster<U>&& other){
// What code goes here?
}
}
Suppose we have raster<uint32_t> r such that r.v.size() is in the millions, and a guarantee that all its elements are within int32_t's range. Is it possible for raster<int32_t>::raster(raster<uint32_t>&& other) to avoid copying the memory backing other.v?
Or should I just do something like *reinterpret_cast<raster<int32_t>*>(&r) instead of calling that constructor?
There is no legal way to do this in C++; you can only move buffers from a std::vector to another std::vector of the exact same type.
There are a variety of ways you can hack this. The most illegal and evil would be
std::vector<uint32_t> evil_steal_memory( std::vector<int32_t>&& in ) {
return reinterpret_cast< std::vector<uint32_t>&& >(in);
}
or something similar.
A less evil way would be to forget it is a std::vector at all.
template<class T>
struct buffer {
template<class A>
buffer( std::vector<T,A> vec ):
m_begin( vec.data() ),
m_end( m_begin + vec.size() )
{
m_state = std::unique_ptr<void, void(*)(void*)>(
new std::vector<T,A>( std::move(vec) ),
[](void* ptr){
delete static_cast<std::vector<T,A>*>(ptr);
}
);
}
buffer(buffer&&)=default;
buffer& operator=(buffer&&)=default;
~buffer() = default;
T* begin() const { return m_begin; }
T* end() const { return m_end; }
std::size_t size() const { return begin()==end(); }
bool empty() const { return size()==0; }
T* data() const { return m_begin; }
T& operator[](std::size_t i) const {
return data()[i];
}
explicit operator bool() const { return (bool)m_state; }
template<class U>
using is_buffer_compatible = std::integral_constant<bool,
sizeof(U)==sizeof(T)
&& alignof(U)==alignof(T)
&& !std::is_pointer<T>{}
>;
template<class U,
std::enable_if_t< is_buffer_compatible<U>{}, bool > = true
>
buffer reinterpret( buffer<U> o ) {
return {std::move(o.m_state), reinterpret_cast<T*>(o.m_begin()),reinterpret_cast<T*>(o.m_end())};
}
private:
buffer(std::unique_ptr<void, void(*)(void*)> state, T* b, T* e):
m_state(std::move(state)),
m_begin(begin),
m_end(end)
{}
std::unique_ptr<void, void(*)(void*)> m_state;
T* m_begin = 0;
T* m_end = 0;
};
live example: this type erases a buffer of T.
template<class T>
struct raster {
buffer<T> v;
template<typename U, typename = std::enable_if_t<sizeof(T) == sizeof(U)>>
raster(raster<U>&& other):
v( buffer<T>::reinterpret( std::move(other).v ) )
{}
};
note that my buffer has a memory allocation in it; compared to millions of elements that is cheap. It is also move-only.
The memory allocation can be eliminated through careful use of the small buffer optimization.
I'd leave it move-only (who wants to accidentally copy a million elements?) and maybe write
buffer clone() const;
which creates a new buffer with the same contents.
Note that instead of a const buffer<int> you should use a buffer<const int> under the above design. You can change that by duplicating the begin() const methods to have const and non-const versions.
This solution relies on your belief that reinterpreting a buffer of int32_ts as a buffer of uint32_ts (or vice versa) doesn't mess with anything. Your compiler may provide this guarantee, but the C++ standard does not.
The problem is that the implementation of the vector template itself may be specialised for the type. For some weird whacky reason we don't understand today, the top-level vector might need an extra member not provided in vector, so simply reinterpret casting will not work safely.
Another evil approach might be to look at the Allocator of your two vectors.
If this was
a custom allocator you had written and both were a derived class from vector
and you wrote an overload in the class for swap and =&&
within which you created wrapped tmp vectors to swap with
and detected that the Allocator was being called within those temporary objects constructors/destructors, and on the same thread
to release and reallocate the same sized array
Then, perhaps you could legitimately pass the memory buffer between them without resetting the content.
But that is a lot of fragile work!
My situation:
I frequently need to have a vector of structures where one field can be thought of as a Key or ID, and rather than store it expensively in a map (memory usage is very important in this app) I want to store it in a flat vector but present a map-like interface for finding elements by key.
My first solution to this problem:
template <class T, class Key, class KeyFn>
class TKeyedVector : public std::vector<T>
{
public:
const_iterator find(const Key& key) const {return std::find_if(begin(), end(), [&](const T& entry) {return keyFn(entry)==key; }); }
KeyFn keyFn;
};
struct KeyedDataEntry
{
std::string key;
int value;
struct KeyExtractor {
const std::string& operator()(const KeyedDataEntry& e) const {return e.key; };
};
};
using KeyedDataArray = TKeyedVector<KeyedDataEntry, std::string, KeyedDataEntry::KeyExtractor>;
Now this all works, but I would like to be able to remove the need for the KeyExtractor type by using the pointer to the member variable embedded into the type:
template <class T, class Key, Key T::* keyFn>
class TKeyedVector : public std::vector<T>
{
public:
const_iterator find(const Key& key) const {return std::find_if(begin(), end(), [&](const T& entry) {return keyFn(entry)==key; }); }
};
using KeyedDataArray = TKeyedVector<KeyedDataEntry, std::string, &KeyedDataEntry::key>;
However I can't get this to work. I've been looking at the implementation of std::mem_fn for clues, but I can't work out how to do it. The error I get with is something like:
warning C4353: nonstandard extension used: constant 0 as function expression. Use '__noop' function intrinsic instead
Any clues?
EDIT: sample version at http://ideone.com/Qu6TEy
Here is the start of a working solution. You don't need a special extractor object.
Note that I have encapsulated the vector. In time, you'll regret not doing this.
#include <vector>
#include <string>
template <class T, class Key, const Key& (T::*Extractor)() const>
class TKeyedVector
{
using storage = std::vector<T>;
using const_iterator = typename storage::const_iterator;
public:
decltype(auto) begin() const
{
return storage_.begin();
}
decltype(auto) end() const
{
return storage_.end();
}
const_iterator find(const Key& key) const
{
return std::find_if(begin(),
end(),
[&](const T& entry)
{
return entry.*Extractor() == key;
});
}
storage storage_;
};
struct KeyedDataEntry
{
std::string key;
int value;
const std::string& get_key() const { return key; }
};
int main()
{
TKeyedVector<KeyedDataEntry, std::string, &KeyedDataEntry::get_key> mymap;
}
But there is a problem with this idea of yours.
In order for this structure to be a map, the keys must be immutable. This argues for only returning immutable objects. This then argues immediately for simply using an unordered_set or set.
If you're going to return references to mutable objects in the underlying vector, then you may as well simply use std::find_if with a predicate to find them.
A pointer to member requires the pointer to member call syntax. (entry.*keyFn)()
C++17 will come with a standard std::invoke function to make writing such templates a bit less tiresome (it will work for all callable objects). But in the meanwhile, this is how you need to do this.
This is what I have:
struct Foo {
int index;
}
std::set<std::shared_ptr<Foo>> bar;
I want to order bar's elements by their indices instead of by the default std::less<std::shared_ptr<T>> function, which relates the pointers.
I read I can type std::set<std::shared_ptr<Foo>, std::owner_less<std::shared_ptr<Foo>>> bar, but I'd prefer to stick to the previous syntax.
I tried defining std::less<std::shared_ptr<Foo>>, but it's not actually being used by the set functions. Is there a way I can achieve this?
If you want to compare by their indices, you'll have to write a comparator that checks by their indices. std::less<> will do the wrong thing (since it won't know about index) and std::owner_less<> will do the wrong thing (since it still won't compare the Foos, but rather has to do with ownership semantics of them).
You have to write:
struct SharedFooComparator {
bool operator()(const std::shared_ptr<Foo>& lhs,
const std::shared_ptr<Foo>& rhs) const
{
return lhs->index < rhs->index;
}
};
and use it:
std::set<std::shared_ptr<Foo>, SharedFooComparator> bar;
You could additionally generalize this to a generic comparator for shared_ptr's:
struct SharedComparator {
template <typename T>
bool operator()(const std::shared_ptr<T>& lhs,
const std::shared_ptr<T>& rhs) const
{
return (*lhs) < (*rhs);
}
};
and then simply make Foo comparable.
You can provide your own specialization of less<shared_ptr<Foo>> in the std namespace.
namespace std
{
template<>
class less<shared_ptr<Foo>>
{
public:
bool operator()(const shared_ptr<Event>& a, const shared_ptr<Event>& b)
{
// Compare *a and *b in some way
}
};
}
Then you can form a set<shared_ptr<Foo>> without a comparator. I needed this for a priority_queue<shared_ptr<Foo>>, where I didn't want to use a priority_queue<Foo*, vector<Foo*>, int (*)(const Foo*, const Foo*)>. I am not proud of it, but it works.