Convert list iterator to pointer doesn't work? - c++

I want to implement a function which iterates over a list and returns a pointer in case of a match. I wrote:
std::list<JobEntry> jobs;
JobsList::JobEntry *JobsList::getJobById(int jobId) {
for (auto const& job : jobs) {
if (job.pid==jobId) {
return std::addressof(*job);
}
}
return nullptr;
}
But this doesn't work, how can I do it?

getJobById() is declared to return a pointer to a non-const JobEntry object, but job is a reference to a const JobEntry object. You can't assign a pointer-to-const to a pointer-to-non-const. So drop the const.
Also, a range-based for loop handles iterators internally, so job is the actual object you want to return a pointer to, not an iterator to the object, so don't try to dereference it.
JobsList::JobEntry* JobsList::getJobById(int jobId) {
for (auto &job : jobs) {
if (job.pid == jobId) {
return std::addressof(job);
}
}
return nullptr;
}
That being said, consider using the standard std::find_if() algorithm instead of a manual loop:
#include <algorithm>
JobsList::JobEntry* JobsList::getJobById(int jobId) {
auto iter = std::find_if(jobs.begin(), jobs.end(),
[=](const JobEntry &job) { return job.pid == jobId; }
);
if (iter != jobs.end())
return std::addressof(*iter);
return nullptr;
}

Related

How can i return unique_ptr with pointer function?

i don't understand fully this problem. This is my function and i need return class with unique_ptr how can i this?
my list:
std::list<std::unique_ptr<Maths> > l_MathsList;
my function
Maths* Maths2::FindMathsID(int iID)
{
auto mpClass= std::unique_ptr<Maths>();
for (auto&& it : l_MathsList)
{
if (iter->IsMy(iID)) {
mpClass = std::move(it);
}
break;
}
}
return mpClass; // compiler error
You probably just want to return a non-owning pointer to the collection element right?
In that case just use .get():
Maths* Maths2::FindMathsID(int iID)
{
for (auto& it : l_MathsList)
{
if (it->IsMy(iID))
return it.get();
}
return nullptr;
}

Returning C++ const item, depending whether exists, safely

I have an std::unordered_map<uint64_t, Object> _map and I'd like to provide a read-only getter for clients.
Originally I thought of this:
bool foundItem(const uint64_t key, Object& object) const
{
if(_map.find(key) != _map.end())
{
object = _map.at(key);
return true;
}
else
{
return false;
}
}
but obviously that isn't read-only, so I changed the signature to return the object and the bool can be passed by reference:
const Object& foundItem(const uint64_t key, bool& found) const
{
if(_map.find(key) != _map.end())
{
found = true;
return _map.at(key);
}
else
{
found = false;
// What can I return here??
}
}
but now I have no type to return if the key is not found.
What is the best way to allow the user read-only access to the (maybe) returned object?
You could return a pointer:
// Returns nullptr if not found
const Object* getItem(const uint64_t key) const
{
auto it = _map.find(key);
if (it == _map.end())
return nullptr;
return &it->second;
}
Or you could return a std::optional, or you could return const Object& but throw an exception on lookup failure.
In all cases, be careful to document the expected lifetime of the result. In particular, you should comment which member functions will result in the result of this function being invalidated. Usually that'll be "any non-const function", like how std::string::c_str() is valid until you do mutatey things on the string.

C++ custom lazy iterator

I have a somewhat simple text file parser. The text I parse is split into blocks denoted by { block data }.
My parser has a string read() function, which gets tokens back, such that in the example above the first token is { followed by block followed by data followed by }.
To make things less repetitive, I want to write a generator-like iterator that will allow me to write something similar to this JavaScript code:
* readBlock() {
this.read(); // {
let token = this.read();
while (token !== '}') {
yield token;
token = this.read();
}
}
which in turn allows me to use simple for-of syntax:
for (let token of parser.readBlock()) {
// block
// data
}
For C++ I would like something similar:
for (string token : reader.read_block())
{
// block
// data
}
I googled around to see if this can be done with an iterator, but I couldn't figure if I can have a lazy iterator like this which has no defined beginning or end. That is, its beginning is the current position of the reader (an integer offset into a vector of characters), and its end is when the token } is found.
I don't need to construct arbitrary iterators, or to iterate in reverse, or to see if two iterators are equal, since it's purely to make linear iteration less repetitive.
Currently every time I want to read a block, I need to re-write the following:
stream.skip(); // {
while ((token = stream.read()) != "}")
{
// block
// data
}
This becomes very messy, especially when I have blocks inside blocks. To support blocks inside blocks, the iterators would have to all reference the same reader's offset, such that an inner block will advance the offset, and the outer block will re-start iterating (after the inner is finished) from that advanced offset.
Is this possible to achieve in C++?
In order to be usable in a for-range loop, a class has to have member functions begin() and end() which return iterators.
What is an iterator? Any object fulfilling a set of requirements. There are several kind of iterators, depending on which operations allow you. I suggest to implement an input iterator, which is the simplest: https://en.cppreference.com/w/cpp/named_req/InputIterator
class Stream
{
public:
std::string read() { /**/ }
bool valid() const { /* return true while more tokens are available */ }
};
class FileParser
{
std::string current_;
Stream* stream_;
public:
class iterator
{
FileParser* obj_;
public:
using value_type = std::string;
using reference = const std::string&;
using pointer = const std::string*;
using iterator_category = std::input_iterator_tag;
iterator(FileParser* obj=nullptr): obj_ {obj} {}
reference operator*() const { return obj_->current_; }
iterator& operator++() { increment(); return *this; }
iterator operator++(int) { increment(); return *this; }
bool operator==(iterator rhs) const { return obj_ == rhs.obj_; }
bool operator!=(iterator rhs) const { return !(rhs==*this); }
protected:
void increment()
{
obj_->next();
if (!obj_->valid())
obj_ = nullptr;
}
};
FileParser(Stream& stream): stream_ {&stream} {};
iterator begin() { return iterator{this}; }
iterator end() { return iterator{}; }
void next() { current_ = stream_->read(); }
bool valid() const { return stream_->valid(); }
};
So your end-of-file iterator is represented by an iterator pointing to no object.
Then you can use it like this:
int main()
{
Stream s; // Initialize it as needed
FileParser parser {s};
for (const std::string& token: parser)
{
std::cout << token << std::endl;
}
}

return empty vector by reference

I'm returning a vector by reference as shown below and it is getting bit ugly when I want to return an empty vector when there is no item in the map. The following gives warning (returning address of local variable) and to fix it, I have another private member variable vector<ClassA> empty_ and I could return it to avoid this.
I am wondering if there is elegant way to implement this.
const std::vector<ClassA>& GeVector(const std::string& class_id) {
auto iter = class_map_.find(class_id);
if (iter != class_map_.end())
return iter->second;
return {}; // return empty_;
}
private:
std::unordered_map<std::string, std::vector<ClassA>> class_map_;
vector<ClassA> empty_;
You could use a static variable:
static const std::vector<ClassA> empty;
return empty;
If your method support the option of failure you could throw a exception instead of returning an empty vector.
const std::vector<ClassA>& GeVector(const std::string& class_id) {
auto iter = class_map_.find(class_id);
if (iter != class_map_.end())
return iter->second;
throw std::exception("Element not found"); // or similar
}

Get class pointer from a vector via function argument

I have a class
class MyClass {
public:
string name;
};
I have a class manager, with a vector of said classes.
class MyManager {
public:
vector<MyClass> classes;
};
I want to pull out a class from the vector by looping and matching with the name
int MyManager::FindMyClass(string name, MyClass *clazz){
vector<MyClass>::iterator it;
for(it = classes.begin(); it != classes.end(); ++it){
if(it->name == name){
// if i cout here, i see the cout, so i did find it..
*clazz = *it;
return 1;
}
}
return 0;
}
int main() {
MyClass *myClass;
int result = myManager.FindMyClass("bob", *myClass);
}
I know for a fact that the object is matched properly (see the comment). I've tried every combination of pointers, double pointers, references, and get every error from unknown conversions to invalid pointers. I am just not sure what I have to do to get a reference to the class stored in the vector.
Change the argument type to MyClass*& so you can return a pointer to the object found in the vector.
int MyManager::FindMyClass(string name, MyClass* &foundPointer)
MyClass *myClass;
int result = myManager.FindMyClass("bob", myClass);
Once you find the match, you can get a pointer to the object using
foundPointer = &(*it);
I would also recommend changing the return type to bool.
Here's an updated version of the function:
bool MyManager::FindMyClass(string name, MyClass* &foundPointer)
{
vector<MyClass>::iterator it = classes.begin();
for(; it != classes.end(); ++it)
{
if(it->name == name)
{
foundPointer = &(*it);
return true;
}
}
return false;
}
Suggested second improvement:
Change the return type to MyClass*. Return a pointer to the found object or nullptr if none is found.
MyClass* MyManager::FindMyClass(string name)
{
...
}
Suggested third improvement:
Use the standard library function std::find_if and a lambda expression to simplify your function.
auto it = std::find_if(classes.begin(),
classes.end(),
[&](const MyClass &c) { return (c.name == name); });
The updated function:
MyClass* MyManager::FindMyClass(string name)
{
auto it = std::find_if(classes.begin(),
classes.end(),
[&](const MyClass &c) { return (c.name == name); });
if ( it != classes.end() )
{
return &(*it);
}
return nullptr;
}