Implement a list of constant objects - c++

Suppose I have a basic class Object, which has some ints and std::strings (so not really big).
Now, I need some sort of list of Objects that wouldn't change, but I should still be able to search it (using a function for example). This "list" is created only at compile time and it will have approx. 60 objects. I currently have 2 solutions:
Create a class Objects that has a std::vector<Object> and which gets initialized in the constructor (along with the search functions).
Create a namespace which contains the Objects as constants and a std::vector<Object> for search (and the like) functions (which will be defined in the namespace).
One disadvantage of 1 is that it has to create the 60+ objects every time I need to access them. 2 is probably ok, but one must not forget to put a new object to the std::vector as well as in the top "list".
1 might be implemented like this:
class Objects
{
public:
Objects()
{
list.push_back(Object{ 0 });
list.push_back(Object{ 1 });
};
bool hasValue(int value)
{
for (auto& obj: list)
{
if (obj.getValue() == value)
return true;
}
return false;
}
private:
std::vector<Object> list;
};
2 like so:
namespace Objects
{
const Object obj1{ 0 };
const Object obj2{ 1 };
const std::vector<Object> list{ obj1, obj2 };
bool hasValue(int value)
{
for (auto& obj: list)
{
if (obj.getValue() == value)
return true;
}
return false;
}
}
Which way would be the best in terms of coding practice and/or usability/maintainability? Maybe there is another way that I haven't thought of?

20 Objects that never change and need to always be available?
Sounds like a job for a function that returns a reference to a static array. All standard algorithms (in this case find_if) work with a std::array.
#include <iostream>
#include <array>
struct Object
{
Object(int i) : _i(i) {};
int _i;
};
const std::array<Object, 20>& objects()
{
static std::array<Object, 20> os = {
Object(1),
Object(2),
Object(3),
Object(4),
Object(5),
Object(6),
Object(7),
Object(8),
Object(9),
Object(10),
Object(11),
Object(12),
Object(13),
Object(14),
Object(15),
Object(16),
Object(17),
Object(18),
Object(19),
Object(20)
};
return os;
}
int main()
{
using namespace std;
auto const& os = objects();
auto i_ten = std::find_if(std::begin(os),
std::end(os),
[](auto const& o) {
return o._i == 10; });
cout << i_ten->_i << std::endl;
return 0;
}

Related

std::find return a class that I can't acesses functions

I come from C/C# language and now I'm trying to learn about C++ and his standards functions.
Now, I'm creating a class called IMonsterDead. I will have a std::vector<IMonsterDead*> with N monsters.
Example:
class IMonsterDead {
public:
IMonsterDead(int Id)
{
this->_Id = Id;
}
virtual void OnDead() = 0;
int Id() const {
return _Id;
}
private:
int _Id;
};
One class which implements that class:
class MonsterTest : public IMonsterDead {
public:
MonsterTest(int generId)
: IMonsterDead(generId)
{
}
virtual void OnDead()
{
std::cout << "MonsterTesd died" << std::endl;
}
};
Ok, if I access directly everything works fine. But I'm trying to use std::find.
Full program test:
int main()
{
std::vector<IMonsterDead*> monsters;
for (int i = 0; i < 1000; i++)
{
monsters.emplace_back(new MonsterTest(1000 + i));
}
int id = 1033;
std::vector<IMonsterDead*>::iterator result = std::find(monsters.begin(), monsters.end(), [id]( IMonsterDead const* l) {
return l->Id() == id;
});
if (result == monsters.end())
std::cout << "Not found" << std::endl;
else
{
// Here I want to access OnDead function from result
}
return 0;
}
So I need to access OnDead function from result but I can't. Intellisense doesn't show anything for me. The result exists.
How can I access that function? Have another better way to do that?
You need to use std::find_if() instead of std::find(). std::find() is for finding an element with a specific value, so you have to pass it the actual value to find, not a user_defined predicate. std::find_if() is for finding an element based on a predicate.
Either way, if a match is found, dereferencing the returned iterator will give you a IMonsterDead* pointer (more accurately, it will give you a IMonsterDead*& reference-to-pointer). You need to then dereference that pointer in order to access any members, like OnDead().
You are also leaking memory. You are not delete'ing the objects you new. And when dealing with polymorphic types that get deleted via a pointer to a base class, the base class needs a virtual destructor to ensure all derived destructors get called properly.
With that said, you are clearly using C++11 or later (by the fact that you are using vector::emplace_back()), so you should use C++11 features to help you manage your code better:
You should use std::unique_ptr to wrap your monster objects so you don't need to delete them manually.
You should always use the override keyword when overriding a virtual method, to ensure you override it properly. The compiler can catch more syntax errors when using override than without it.
You should use auto whenever you declare a variable that the compiler can deduce its type for you. Especially useful when dealing with templated code.
Try something more like this:
#include <iostream>
#include <vector>
#include <memory>
#include <algorithm>
class IMonsterDead {
public:
IMonsterDead(int Id)
: m_Id(Id)
{
}
virtual ~IMonsterDead() {}
virtual void OnDead() = 0;
int Id() const {
return m_Id;
}
private:
int m_Id;
};
class MonsterTest : public IMonsterDead {
public:
MonsterTest(int generId)
: IMonsterDead(generId)
{
}
void OnDead() override
{
std::cout << "MonsterTest died" << std::endl;
}
};
int main()
{
std::vector<std::unique_ptr<IMonsterDead>> monsters;
for (int i = 0; i < 1000; i++)
{
// using emplace_back() with a raw pointer risks leaking memory
// if the emplacement fails, so push a fully-constructed
// std::unique_ptr instead, to maintain ownership at all times...
monsters.push_back(std::unique_ptr<IMonsterDead>(new MonsterTest(1000 + i)));
// or:
// std::unique_ptr<IMonsterDead> monster(new MonsterTest(1000 + i));
// monsters.push_back(std::move(monster));
// or, if you are using C++14 or later:
// monsters.push_back(std::make_unique<MonsterTest>(1000 + i));
}
int id = 1033;
auto result = std::find_if(monsters.begin(), monsters.end(),
[id](decltype(monsters)::value_type &l) // or: (decltype(*monsters.begin()) l)
{
return (l->Id() == id);
}
// or, if you are using C++14 or later:
// [id](auto &l) { return (l->Id() == id); }
);
if (result == monsters.end())
std::cout << "Not found" << std::endl;
else
{
auto &monster = *result; // monster is 'std::unique_ptr<IMonsterDead>&'
monster->OnDead();
}
return 0;
}
Iterators are an interesting abstraction, in this case to be reduced to pointers.
Either you receive the pointer to the element or you get an invalid end.
You can use it as a pointer: (*result)->func();
You can also use it to create a new variable:
IMonsterDead &m = **result;
m.func();
This should give the same assembly, both possible.

Strange issue when iterating a STL set<CStudent> in C++

I have class CStudent and class CStudentGroup which has one member set<CStudent>. I populate the set of an object from the class CStudentGroup. I want to iterate this set and print via the getter of the CStudent class the points of all the students in the set. I do this by assigning the set to a new one. Then I iterate the set with an iterator it. However the compiler gives an error *the object has type qualifiers that are not compatible with the member function CStudent::getP; object type is const CStudent* I would like to ask how can I do this? Thank you in advance.
#include <iostream>
#include <string>
#include <set>
using namespace std;
class CStudent {
string m_strFN;
int m_iPoints;
public:
void setP(int p) {
m_iPoints = p;
}
void setFN(string f) {
m_strFN = f;
}
int getP() {
return m_iPoints;
}
string getFN() {
return m_strFN;
}
CStudent() {
m_strFN = "123456789";
m_iPoints = 70;
}
CStudent(const CStudent& stud) {
m_strFN = stud.m_strFN;
m_iPoints = stud.m_iPoints;
};
CStudent(int p) {
m_iPoints = p;
}
};
class CStudentGroup {
set<CStudent> m_setStudents;
public:
CStudentGroup(const CStudentGroup& grp) {
m_setStudents = grp.m_setStudents;
};
CStudentGroup(set<CStudent> st) {
m_setStudents = st;
}
CStudentGroup() {
CStudent s1(50), s2, s3(s2);
m_setStudents.insert(s1);
m_setStudents.insert(s2);
m_setStudents.insert(s3);
}
set<CStudent> gets() {
return m_setStudents;
}
};
int main()
{
CStudentGroup group;
set<CStudent> stt = group.gets();
for (set<CStudent>::iterator it = stt.begin(); it != stt.end(); it++) {
cout << it->getP() << endl;
}
}
std::set stores keys as constant value, as a change of a key can be a cause of change to its position in red-black tree (typical std::set implementation).
In other words, your CStudent object are considered const or unchangeable.
It's possible to problem here using std::set::const_iterator as a type of iterator inside the loop in combination with std::set::cbegin() and std::set::cend() calls.
Another possible solution is to use foreach-loop:
for (CStudent const& student : stt)
std::cout << student.getP() << '\n';
Moreover, you would need to change CStudent::getP() declaration to be a constant method.
Objects inside a std::set are always const. That is to protect them, in case you decide you change any key field, the sorting order changes and the set invariant is broken.
So basically the set<CStudent>::iterator is a const_iterator and you get a const CStudent& reference. Since your CStudent::getP is not a const member function, you cannot use it.
Solution, make it const:
int getP() const {
return m_iPoints;
}
Naturally, you want to mark as const any function that does not change the contents of your object, not only the ones std::set requires you to do so. This is sometimes called const-correctness and is always a good practice.

Passing function and operator calls in object

I am wanting to make a class which allows me to lock an object from being modified. It would essentially be a template with a boolean specifying the lock state. Since it is a template, I won't know all the methods that can be called on the internal object, so I need a method to pass calls through...
template<class T>
class const_lock
{
public:
const_lock() : my_lock(false) {}
void set_const_lock(bool state) {my_lock = state;}
// HOW TO IMPLEMENT SOMETHING LIKE THESE????
//
template<typename...Args >
auto operatorANY_OPERATOR (Args...args)
{
if(my_lock != false)
throw std::exception("Objected locked to modification");
return my_value.ANY_OPERATOR(args);
}
template<typename...Args >
auto operatorANY_CONST_OPERATOR (Args...args) const
{
return my_value.ANY_CONST_OPERATOR(args);
}
template<typename...Args >
auto ANY_METHOD(Args...args)
{
if(my_lock != false)
throw std::exception("Objected locked to modification");
return my_value.ANY_METHOD(args);
}
template<typename...Args >
auto ANY_CONST_METHOD(Args...args) const
{
return my_value.ANY_CONST_METHOD(args);
}
private:
bool my_lock;
T my_value;
}
int main()
{
const_lock<std::vector<int>> v;
v.push_back(5);
v.push_back(7);
v.set_const_lock(true);
v.push_back(9); // fails compilation
std::cout << v.at(1) << std::endl; // ok
}
Any help would be appreciated. Thanks!
Edit: changed static assert to throw and exception
What you're trying to do looks rather difficult, but more importantly is over-complicated and unnecessary for what you're trying to do.
Essentially what you're trying to do (correct me if I'm wrong) is create a compile time check of whether you are supposed to able to modify an object at a given time. However, c++ already has a built in way of doing this. Simply declare or pass your object as const or const&, and the compiler will not allow you to modify non-mutable parts of the object. When you want to be able to modify it pass it without const. You can even cast it from const& to regular & when you want to go from code where you can't modify it directly to code where you can, though I don't recommend it.
edit: just saw a comment on the question about no reference arrays. Don't worry about that! The standard library has support for reference wrappers which allow you to essentially store references in arrays or anywhere else.
You can make a generic wrapper class that you can forward the function to using a lambda that captures a reference to the internal member. In this example I am just using an if statement to check if it is "locked" and if it is then we just modify a copy.
template<class T>
class const_lock
{
private:
bool my_lock;
mutable T my_value;
public:
const_lock() : my_lock(false) {}
void set_const_lock() { my_lock = true; }
template<typename F>
auto operator()(F f) const -> decltype(f(my_value))
{
if (my_lock)
{
T temp{my_value}; // make a copy
return f(temp);
}
else
return f(my_value); // modify wrraped value
}
};
int main()
{
const_lock<std::string> cl;
cl([](std::string& s) {
s = "foobar";
});
cl([](std::string& s) {
std::cout << s << std::endl;
});
cl.set_const_lock();
cl([](std::string& s) {
s = "we should still be foobar";
});
cl([](std::string& s) {
std::cout << s;
});
}
This is completely unimplementable. A trivial modification of your source code shows why this won't work.
int main()
{
const_lock<std::vector<int>> v;
v.push_back(5);
v.push_back(7);
if (rand() % 2)
v.set_const_lock(true);
v.push_back(9); // fails compilation
std::cout << v.at(1) << std::endl; // ok
}
You need to completely rethink your approach.
Below is an example illustrating what I would be trying to protect against
class Node
{
public:
Node(int id) : my_id(id) {}
// . . .
int id() {return my_id;}
private:
int my_id;
// . . .
};
class Grid
{
public:
Grid() {}
// . . .
void associate(Node* n) { my_nodes.push_back(n); }
private:
// . . .
std::vector<Node*> my_nodes;
};
Node* find(std::vector<Node>& Nodes, int ID)
{
for(auto i=Nodes.begin(); i!=Nodes.end(); ++i)
{
if (i->id() == ID)
{
return &*i;
}
}
}
main()
{
std::vector<Node> Nodes;
// fill Nodes with data
Grid Array;
Array.associate( find(Nodes,14325) );
Array.associate( find(Nodes,51384) );
Array.associate( find(Nodes,321684) );
// . . .
Nodes.push_back(Node(21616)); // this can invalidate my pointers in Array
}
If I was able to make my Nodes vairable be
const_lock<std::vector<Node>> Nodes;
then call
Nodes.set_const_lock(true);
after populating the data, I wouldn't need to worry about my pointers in Array getting messed up.

How to get rid of weak_ptrs in a container

I have a class that stores weak_ptrs in a container and later does something if the weak_ptr is not expired:
class Example
{
public:
void fill(std::shared_ptr<int> thing)
{
member.push_back(thing);
}
void dosomething() const
{
for (const auto& i : member)
if (!i.expired())
;// do something. the weak_ptr will not be locked
}
private:
std::vector<std::weak_ptr<int>> member;
};
If Example is an object that lives forever and fill is used regularily, the vector allocates memory for elements continously, but they are never removed after they expired.
Is there any automatic C++ way to get rid of the expired weak_ptrs in the container or is there a better way to store a variable number of them?
My naive way would be to iterate over the container each time fill is called and remove all the expired weak_ptrs. In scenarios where Example has many elements in the container and fill is frequently called this seems to be very inefficient.
Since you clarified that you are actually using a std::map and not a std::vector, it might be easiest to remove the expired elements on-the-fly in doSomething(). Switch back from a range-based for loop to a normal iterator based design:
void dosomething() const
{
auto i = member.begin();
while( i != member.end() ) {
if( i->expired() ) { i = member.erase( i ); continue; }
;// do something. the weak_ptr will not be locked
++i;
}
}
Does the shared_ptr<int> have to be a shared_ptr<int>?
How about a shared_ptr<IntWrapper>?
#include <iostream>
#include <forward_list>
using namespace std;
class IntWrapper {
public:
int i;
static forward_list<IntWrapper*>& all() {
static forward_list<IntWrapper*> intWrappers;
return intWrappers;
}
IntWrapper(int i) : i(i) {
all().push_front(this);
}
~IntWrapper() {
all().remove(this);
}
};
void DoSomething() {
for(auto iw : IntWrapper::all()) {
cout << iw->i << endl;
}
}
int main(int argc, char *argv[]) {
shared_ptr<IntWrapper> a = make_shared<IntWrapper>(1);
shared_ptr<IntWrapper> b = make_shared<IntWrapper>(2);
shared_ptr<IntWrapper> c = make_shared<IntWrapper>(3);
DoSomething();
return 0;
}
I would rather use a custom deleter for the shared_ptr. But this implies here to change the interface of the Example class. The advantage using custom deleter is that there is no need to check for expired objects in the collection. The collection is directly maintained by the custom deleter.
Quick implementation :
#include <memory>
#include <iostream>
#include <set>
template <typename Container>
// requires Container to be an associative container type with key type
// a raw pointer type
class Deleter {
Container* c;
public:
Deleter(Container& c) : c(&c) {}
using key_type = typename Container::key_type;
void operator()(key_type ptr) {
c->erase(ptr);
delete ptr;
}
};
class Example {
public:
// cannot change the custom deleter of an existing shared_ptr
// so i changed the interface here to take a unique_ptr instead
std::shared_ptr<int> fill(std::unique_ptr<int> thing) {
std::shared_ptr<int> managed_thing(thing.release(), Deleter<containter_type>(member));
member.insert(managed_thing.get());
return managed_thing;
}
void dosomething() const {
// we don't need to check for expired pointers
for (const auto & i : member)
std::cout << *i << ", ";
std::cout << std::endl;
}
using containter_type = std::set<int*>;
private:
containter_type member;
};
int main()
{
Example example;
auto one = example.fill(std::unique_ptr<int>(new int(1)));
auto two = example.fill(std::unique_ptr<int>(new int(2)));
auto three = example.fill(std::unique_ptr<int>(new int(3)));
example.dosomething();
three.reset();
example.dosomething();
}

How can I create a switch for class members?

Say I have a class with a couple of data members, and I want a class method that returns one, and the next time it is called returns the value of the other. Something like:
class MyClass
{
public:
MyClass():switch(0){};
int get();
private:
int intA, intB;
int sw;
};
int MyClass::get()
{
if ( (++sw)%2 )
return intA;
else
return intB;
}
What would a more elegant way of doing it be? I don't like the if...else statement very much. It's fine for something like return, but if I'm actually using more complex operations, I end up duplicating a ton of code. Or having to create a second method within each method that is called after I resolve what element I'm pointing to.
What I'd prefer to do, ideally, is to use some form of pointer, so I can do
class MyClass
{
public:
MyClass():switch(&intA){};
int get();
void toggleSwitch();
private:
int intA, intB;
int * sw;
};
int MyClass::get()
{
return *sw;
}
void MyClass::toggleSwitch()
{
if ( sw == &intA )
sw = &intB;
else
sw = &intA;
}
Or something to that effect. I could call toggleSwitch(), and have my class operate on either one or the other value easily.
I still don't like it though. I prefer to avoid if's when possible, and I shouldn't need one in this case. This use of a naked pointer should be pretty safe, but I was thinking I could have something like std::unique_ptr holding each element and then std::swap them. But then the pointers would own the elements, and they'd be dynamic memory instead.
So is there a better way to do it?
Well, switch is a keyword, but I'll roll with it. How about an array of pointers?
int *fields[] = {&intA, &intB};
int MyClass::get()
{
return *fields[++switch % 2];
}
This would expand nicely if you could have additional variables later.
Or maybe:
int MyClass::get()
{
return *fields[switch = 1 - switch];
}
If you return a reference then you could use get() internally.
int &MyClass::get()
{
return *fields[switch = 1 - switch];
}
I would encapsulate the concept of a toggling value:
template<typename T>
class Toggleable {
T first;
T second;
T* current;
T* other;
public:
Toggleable(const T& first, const T& second)
: first(first),
second(second),
current(&first),
other(&second) {
}
bool toggle() {
std::swap(current, other);
}
const T& get() const {
return *current;
}
}
Then use as:
class MyClass
{
Toggleable<int> value;
public:
MyClass()
: value(42, 1729)
{
}
const int& get() {
value.toggle();
return value.get();
}
};