I'm trying to use a Boost MultiIndex container in my simulation. My knowledge of C++ syntax is very weak, and I'm concerned I'm not properly removing an element from the container or deleting it from memory. I also need to modify elements, and I was hoping to confirm the syntax and basic philosophy here too.
// main.cpp
...
#include <boost/multi_index_container.hpp>
#include <boost/multi_index/hashed_index.hpp>
#include <boost/multi_index/member.hpp>
#include <boost/multi_index/ordered_index.hpp>
#include <boost/multi_index/mem_fun.hpp>
#include <boost/tokenizer.hpp>
#include <boost/shared_ptr.hpp>
...
#include "Host.h" // class Host, all members private, using get fxns to access
using boost::multi_index_container;
using namespace boost::multi_index;
typedef multi_index_container<
boost::shared_ptr< Host >,
indexed_by<
hashed_unique< const_mem_fun<Host,int,&Host::getID> >
// ordered_non_unique< BOOST_MULTI_INDEX_MEM_FUN(Host,int,&Host::getAge) >
> // end indexed_by
> HostContainer;
typedef HostContainer::nth_index<0>::type HostsByID;
int main() {
...
HostContainer allHosts;
Host * newHostPtr;
newHostPtr = new Host( t, DOB, idCtr, 0, currentEvents );
allHosts.insert( boost::shared_ptr<Host>(newHostPtr) );
// allHosts gets filled up
int randomHostID = 4;
int newAge = 50;
modifyHost( randomHostID, allHosts, newAge );
killHost( randomHostID, allHosts );
}
void killHost( int id, HostContainer & hmap ){
HostsByID::iterator it = hmap.find( id );
cout << "Found host id " << (*it)->getID() << "Attempting to kill. hmap.size() before is " << hmap.size() << " and ";
hmap.erase( it ); // Is this really erasing (freeing from mem) the underlying Host object?
cout << hmap.size() << " after." << endl;
}
void modifyHost( int id, HostContainer & hmap, int newAge ){
HostsByID::iterator it = hmap.find( id );
(*it) -> setAge( newAge ); // Not actually the "modify" function for MultiIndex...
}
My questions are
In the MultiIndex container allHosts of shared_ptrs to Host objects, is calling allHosts.erase( it ) on an iterator to the object's shared_ptr enough to delete the object permanently and free it from memory? It appears to be removing the shared_ptr from the container.
The allhosts container currently has one functioning index that relies on the host's ID. If I introduce an ordered second index that calls on a member function (Host::getAge()), where the age changes over the course of the simulation, is the index always going to be updated when I refer to it?
What is the difference between using the MultiIndex's modify to modify the age of the underlying object versus the approach I show above?
I'm vaguely confused about what is assumed/required to be constant in MultiIndex.
Thanks in advance.
Update
Here's my attempt to get the modify syntax working, based on what I see in a related Boost example.
struct update_age {
update_age():(){} // have no idea what this really does... elicits error
void operator() (boost::shared_ptr<Host> ptr) {
ptr->incrementAge(); // incrementAge() is a member function of class Host
}
};
and then in modifyHost, I'd have hmap.modify(it,update_age). Even if by some miracle this turns out to be right, I'd love some kind of explanation of what's going on.
shared_ptr will remove actual Host object in its destructor (if there is no other instances of shared_ptr). All objects in MultiIndex are considered constant. To modify the object you should use method modify of MultiIndex. In that case indexes will be updates if necessary.
You could use the following functor to change age field:
struct change_age
{
change_age(int age) : age_(age) {}
void operator()(boost::shared_ptr<Host> h) // shared_ptr !!!
{
h->age = age_;
}
private:
int age_;
};
Then use it as follows:
testHosts.modify( it, Host::change_age( 22 ) ); // set age to 22
Related
I would like to store the value that is passed to the constructor in a static set returned by a static function.
It seems that the insertion is successful, but when it reach the end of the scope of the constructor it disappear.
I have reproduced it in a simple example:
// container.hh
#pragma once
#include <vector>
#include <set>
class container {
public:
container(const int& s);
static std::set<int, std::less<int>>& object_set_instance();
};
#include "container.hxx"
// container.hxx
#pragma once
#include "container.hh"
#include <iostream>
container::container(const int& s)
{
auto set = object_set_instance();
set.insert(s);
std::cout << "Size " << set.size() << "\n";
}
std::set<int, std::less<int>>& container::object_set_instance()
{
static std::set<int, std::less<int>> s;
return s;
}
#include "container.hh"
#include <iostream>
int main()
{
auto a = container(42);
auto b = container(21);
auto b1 = container(51);
auto b2 = container(65);
auto b3 = container(99);
}
Output :
Size 1
Size 1
Size 1 // Size never change
Size 1
Size 1
Why doesn't the set's size change ?
auto set = object_set_instance();
If you use your debugger to inspect what set is, you will discover that it's a std::set and not a std::set& reference. Effectively, a copy of the original std::set is made (object_set_instance() returns a reference, only to copy-construct a new object that has nothing to do with the referenced one), and the next line of code modifies the copy, and it gets thrown away immediately afterwards.
This should be:
auto &set = object_set_instance();
A debugger is a very useful tool for solving these kinds of Scooby-Doo mysteries, and it would clearly reveal what's going on here. If you haven't yet had the opportunity to learn how to use one, hopefully this will inspire you to take a look, and join Mystery, Inc. as a member in good standing.
I'm looking to remove one key - value element from an std::map and keep the value which was stored in this map. Just removing it is not enough, I need the key and value which was stored for other things.
My example is something like this:
std::map<const Class1 *, std::unique_ptr<Class 2>> myMap;
I would like to extract the key and value from the std::map. Simply moving it away is not an option, since this will put the std::map in an incorrect state.
I found the std::extract function, which I can use for an std::set, but not for an std::map. I cannot find any example online showing how I can extract the key and value from this map.
I would like to do something like this:
auto keyAndValue = myMap.extract(myMap.find(instanceOfClass1));
auto key = keyAndValue.someMethod();
auto value = std::move(keyAndValue.someOtherMethod());
I thought I could use key() and value(), as described in some examples. But this does not work, I get an error.
error C2039: 'value': is not a member of
'std::_Node_handle<std::_Tree_node<std::pair<const
_Kty,_Ty>,std::_Default_allocator_traits<_Alloc>::void_pointer>,_Alloc,std::_Node_handle_map_base,_Kty,_Ty>'
You answered the question by yourself.
I just want to add a complete example. Please see:
#include <iostream>
#include <map>
#include <functional>
#include <memory>
struct Class1
{
void someMethod() const { std::cout << "Some Method from class 1\n";}
};
struct Class2
{
Class2(const int i) : test(i) {}
void someOtherMethod() const { std::cout << "Some other Method from class 2. Index: " << test << '\n';}
int test{0};
};
int main()
{
Class1 cl1_1{}, cl1_2{}, cl1_3{};
std::map<const Class1 *, std::unique_ptr<Class2>> myMap;
// Populate map
myMap[&cl1_1] = std::make_unique<Class2>(1);
myMap[&cl1_2] = std::make_unique<Class2>(2);
myMap[&cl1_3] = std::make_unique<Class2>(3);
// Extract data
auto keyAndValue = myMap.extract(myMap.find(&cl1_1));
// Get key and value
auto key = keyAndValue.key();
auto value = std::move(keyAndValue.mapped());
// Call methods
key->someMethod();
value->someOtherMethod();
return 0;
}
The answer was suggested by Mark.
For an std::map, I need to use mapped() on the node I get, not value.
Thanks a lot!
So, like this
auto keyAndValue = myMap.extract(myMap.find(instanceOfClass1));
auto key = keyAndValue.key();
auto value = std::move(keyAndValue.mapped());
How do I get the position of an element inside a vector, where the elements are classes. Is there a way of doing this?
Example code:
class Object
{
public:
void Destroy()
{
// run some code to get remove self from vector
}
}
In main.cpp:
std::vector<Object> objects;
objects.push_back( <some instances of Object> );
// Some more code pushing back some more stuff
int n = 20;
objects.at(n).Destroy(); // Assuming I pushed back 20 items or more
So I guess I want to be able to write a method or something which is a member of the class which will return the location of itself inside the vector... Is this possible?
EDIT:
Due to confusion, I should explain better.
void Destroy(std::vector<Object>& container){
container.erase( ?...? );
}
The problem is, how can I find the number to do the erasing...? Apparently this isn't possible... I thought it might not be...
You can use std::find to find elements in vector (providing you implement a comparison operator (==) for Object. However, 2 big concerns:
If you need to find elements in a container then you will ger much better performance with using an ordered container such as std::map or std::set (find operations in O(log(N)) vs O(N)
Object should not be the one responsible of removing itself from the container. Object shouldn't know or be concerned with where it is, as that breaks encapsulation. Instead, the owner of the container should concern itself ith such tasks.
The object can erase itself thusly:
void Destroy(std::vector<Object>& container);
{
container.erase(container.begin() + (this - &container[0]));
}
This will work as you expect, but it strikes me as exceptionally bad design. Members should not have knowledge of their containers. They should exist (from their own perspective) in an unidentifiable limbo. Creation and destruction should be left to their creator.
Objects in a vector don't automatically know where they are in the vector.
You could supply each object with that information, but much easier: remove the object from the vector. Its destructor is then run automatically.
Then the objects can be used also in other containers.
Example:
#include <algorithm>
#include <iostream>
#include <vector>
class object_t
{
private:
int id_;
public:
int id() const { return id_; }
~object_t() {}
explicit object_t( int const id ): id_( id ) {}
};
int main()
{
using namespace std;
vector<object_t> objects;
for( int i = 0; i <= 33; ++i )
{
objects.emplace_back( i );
}
int const n = 20;
objects.erase( objects.begin() + n );
for( auto const& o : objects )
{
cout << o.id() << ' ';
}
cout << endl;
}
If you need to destroy the n'th item in a vector then the easiest way is to get an iterator from the beginning using std::begin() and call std::advance() to advance how ever many places you want, so something like:
std::vector<Object> objects;
const size_t n = 20;
auto erase_iter = std::advance(std::begin(objects), n);
objects.erase(erase_iter);
If you want to find the index of an item in a vector then use std::find to get the iterator and call std::distance from the beginning.
So something like:
Object object_to_find;
std::vector<Object> objects;
auto object_iter = std::find(std::begin(objects), std::end(objects), object_to_find);
const size_t n = std::distance(std::begin(objects), object_iter);
This does mean that you need to implement an equality operator for your object. Or you could try something like:
auto object_iter = std::find(std::begin(objects), std::end(objects),
[&object_to_find](const Object& object) -> bool { return &object_to_find == &object; });
Although for this to work the object_to_find needs to be the one from the actual list as it is just comparing addresses.
I have a priority queue of pointers to a struct city. I modify the objects pointed by these pointers outside the priority queue, and want to tell the priority queue to "reorder" itself according to the new values.
What should I do?
Example:
#include <iostream>
#include <queue>
using namespace std;
struct city {
int data;
city *previous;
};
struct Compare {
bool operator() ( city *lhs, city *rhs )
{
return ( ( lhs -> data ) >= ( rhs -> data ) );
}
};
typedef priority_queue< city *, vector< city * >, Compare > pqueue;
int main()
{
pqueue cities;
city *city1 = new city;
city1 -> data = 5;
city1 -> previous = NULL;
cities.push( city1 );
city *city2 = new city;
city2 -> data = 3;
city2 -> previous = NULL;
cities.push( city2 );
city1 -> data = 2;
// Now how do I tell my priority_queue to reorder itself so that city1 is at the top now?
cout << ( cities.top() -> data ) << "\n";
// 3 is printed :(
return 0;
}
This is a bit hackish, but nothing illegal about it, and it gets the job done.
std::make_heap(const_cast<city**>(&cities.top()),
const_cast<city**>(&cities.top()) + cities.size(),
Compare());
Update:
Do not use this hack if:
The underlying container is not vector.
The Compare functor has behavior that would cause your external copy to order differently than the copy of Compare stored inside the priority_queue.
You don't fully understand what these warnings mean.
You can always write your own container adaptor which wraps the heap algorithms. priority_queue is nothing but a simple wrapper around make/push/pop_heap.
If you need to keep an ordered collection you may consider the following solution: Use std::set and to update values remove the item, update its value and place it back into the set. This will give you O(log n) complexity for updating one item, which is the best you can in a usual heap structure (Assuming you had access to its internals to mass with the sift-up sift-down procedures).
The only downside to std::set will be the time for initializing a set with n items. (O(n log n) instead of O(n)).
Based on http://www.sgi.com/tech/stl/priority_queue.html it does not look like there is a way to do that, without emptying and re-inserting.
If you are willing to move away from priority_queue (but still want a heap), then you can use a vector, along with the make_heap, push_heap and pop_heap. See the Notes section in the page for priority_queue.
This is an old question but I wasn't fully satisfied with any of the answers when I wanted to do this myself. There is no need for any hacks. std::priority_queue contains all the machinery to do this legally and idiomatically.
std::priority_queue has two very helpful data members, c (the underlying container) and comp (the comparison predicate).
Equally helpfully, the standard mandates that the Container template type must be a model of SequenceContainer who's iterators are models of RandomAccessIterator.
This is helpful because the Iter argument type of std::make_heap have the same RandomAccessIterator model requirement.
This is a longwinded way of saying that std::priority_queue is a wrapper around a heap and that therefore std::make_heap(std::begin(c), std::end(c), comp) must be a valid expression.
The 'bad' news is that c and comp are protected. This is actually good news for two reasons:
You can't destroy the heap accidentally.
If you derive from std::priority_queue you can modify the heap intentionally.
So the trick is to derive your priority queue from std::priority_queue, in a member function, mutate the internal heap c any way you like and then call std::make_heap(std::begin(c), std::end(c), comp); to turn it back into a valid heap.
Is it not, generally, a bad idea to inherit from STL containers
Well, yes, but...
There are two reasons that this could be a bad idea for the young and/or unwary. Lack of polymorphic destructors and the risk of slicing.
Polymorphic destructors
There is actually no reasonable use case for owning a std container through a pointer to its base class. Containers are lightweight (when there is nothing in them) and cheaply moveable. You may be able to think of use cases, but I can guarantee that whatever you intended to do can be done better by encapsulating the container in another heap-allocated object. In well-designed code, this should never have become a concern. In any case, inheriting privately from the priority_queue template class removes this risk.
Slicing
Certainly there is a risk of slicing when we pass inherited objects around. The answer here is to inherit privately from the priority_queue base class, and then use using in the derived class to export only the parts of the base class's interface that we wish to share.
The example below has been updated to show this.
Below is an example from a real project.
Requirements:
Keep a queue of topics that a client must be notified changes to. Order the queue by the timestamp of the earliest time that this topic was notified. Do not allow duplicate topic names.
Here is a working demo:
#include <queue>
#include <string>
#include <chrono>
#include <cassert>
#include <iostream>
using topic_type = std::string;
using timestamp_clock = std::chrono::system_clock;
using timestamp_type = timestamp_clock::time_point;
struct notification {
topic_type topic;
timestamp_type timestamp;
};
bool topic_equals(const notification& l, const topic_type& r) {
return l.topic == r;
}
bool topic_equals(const topic_type& l, const notification& r) {
return l == r.topic;
}
bool age_after(const notification& l , const notification& r) {
return l.timestamp > r.timestamp;
}
bool age_after(const notification& l , const timestamp_type& r) {
return l.timestamp > r;
}
bool age_after(const timestamp_type& l , const notification& r) {
return l > r.timestamp;
}
struct greater_age
{
template<class T, class U>
bool operator()(const T& l, const U& r) const {
return age_after(l, r);
}
};
template<class T>
struct pending_queue_traits;
template<>
struct pending_queue_traits<notification>
{
using container_type = std::vector<notification>;
using predicate_type = greater_age;
using type = std::priority_queue<notification, container_type, predicate_type>;
};
class pending_notification_queue
: private pending_queue_traits<notification>::type
{
using traits_type = pending_queue_traits<notification>;
using base_class = traits_type::type;
public:
// export the constructor
using base_class::base_class;
// and any other members our clients will need
using base_class::top;
using base_class::pop;
using base_class::size;
bool conditional_add(topic_type topic, timestamp_type timestamp = timestamp_clock::now())
{
auto same_topic = [&topic](auto& x) { return topic_equals(topic, x); };
auto i = std::find_if(std::begin(c), std::end(c), same_topic);
if (i == std::end(c)) {
this->push(notification{std::move(topic), std::move(timestamp)});
return true;
}
else {
if (timestamp < i->timestamp) {
i->timestamp = std::move(timestamp);
reorder();
return true;
}
}
return false;
}
private:
void reorder() {
std::make_heap(std::begin(c), std::end(c), comp);
}
};
// attempt to steal only the base class...
void try_to_slice(pending_queue_traits<notification>::type naughty_slice)
{
// danger lurks here
}
int main()
{
using namespace std::literals;
auto pn = pending_notification_queue();
auto now = timestamp_clock::now();
pn.conditional_add("bar.baz", now);
pn.conditional_add("foo.bar", now + 5ms);
pn.conditional_add("foo.bar", now + 10ms);
pn.conditional_add("foo.bar", now - 10ms);
// assert that there are only 2 notifications
assert(pn.size() == 2);
assert(pn.top().topic == "foo.bar");
pn.pop();
assert(pn.top().topic == "bar.baz");
pn.pop();
// try to slice the container. these expressions won't compile.
// try_to_slice(pn);
// try_to_slice(std::move(pn));
}
The stl's containers don't provide as fast as possible updatable priority queues.
#Richard Hodges: make_heap takes O(n) complexity, and the push_heap function doesn't tell you where the provided element was stored, making a quick update of a single element impossible (you need O(n) to find it).
I have implemented a high-performance updatable priority queue (an update costs O(log n), twice as fast as using an std::set) and made it available on github.
This is how you would typically use it :
better_priority_queue::updatable_priority_queue<int,int> pQ;
pQ.push(0, 30); // or pQ.set(0, 30)
pQ.push(1, 20);
pQ.push(2, 10);
pQ.update(2, 25); // or pQ.set(2, 25)
while(!pQ.empty())
std::cout << pQ.pop_value().key << ' ';
// Outputs: 0 2 1
A program I'm making requires access pools of various types of resources, but only accessible to a few classes. The resource pool is also its own class. As such, I decided to use a static class for the resource pool and have others access it this way.
But I am running into a strange problem with adding resources to the pool. Each pool is represented as an std::map, and after insertion, the map still remains empty. I am guessing this has something to do with the map being inside a static class. Is this why maps don't work as intended here?
Relevant code of Resource Pool
class ResourcePool {
private:
static ResourcePool m_ResourcePool;
public:
ResourcePool();
~ResourcePool();
static ResourcePool* Instance() { return &m_ResourcePool; }
// Where textures are stored. ci_less is for case comparison of names
std::map <std::string, TextureResource, ci_less> Textures;
TextureResource* getTexture(std::string handle);
};
Relevant code of how it's used
Scene::Scene() {
Assets = ResourcePool::Instance();
}
TextureResource* Scene::add(std::string handle, TextureResource Texture) {
// Insertion fails
Assets->Textures.insert(std::make_pair(handle + "_tex", Texture));
// I use this line to debug, the map still shows up empty
unsigned size = Assets->Textures.size();
// look up that texture by its name
return Assets->getTexture(handle);
}
The result is, add returns NULL since nothing is found. This program doesn't crash since I'm not doing anything with the texture, just testing out the insertion for now.
Since the question doesn't contain a proper compilable program, I have created one:
#include <string>
#include <map>
#include <iostream>
class ResourcePool {
private:
static ResourcePool m_ResourcePool;
public:
static ResourcePool* Instance() { return &m_ResourcePool; }
std::map <std::string, std::string> Textures;
std::string getTexture(std::string handle) { return Textures[handle]; }
};
ResourcePool ResourcePool::m_ResourcePool;
int main()
{
ResourcePool* Assets = ResourcePool::Instance();
Assets->Textures.insert(std::make_pair("test_tex", "texture"));
std::cout << Assets->Textures.size() << std::endl;
std::cout << Assets->getTexture("test_tex") << std::endl;
return 0;
}
This works on my machine as expected using linux, g++ Debian 4.3.2-1.1.
I wonder if it works for the original poster.