HiveBrain v1.2.0
Get Started
← Back to all entries
patterncppModerate

Small improved std::list to manage doubloons and pointers

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
stddoubloonsimprovedmanagesmallpointersandlist

Problem

I am extending std::list in order to have access to convenience member functions that simplify my everyday developments (e.g. ways to deal with doubloons, delete all the stored pointers, etc.). I removed some of these member functions, but I kept the essential ones.

```
#ifndef __PJ_LIST_H__
#define __PJ_LIST_H__

#include
#include

// This class is a wrapper of std::list to provide some smarter functionalities
template
class PjList: public std::list
{
public:

typedef typename PjList::iterator PjIterator;

PjList() {};
virtual ~PjList() {};

// Helper functions to deal with doubloons
bool contains(const T& t);
bool containsDoubloons();
PjList removeDoubloons();

// Calls destructor of all list item and empty the list
void destroyContents();

// Accessing elements at index
T& at(int index);

// Inserting element around "ref"
bool insertItem(const T& item, const T& ref, bool before = true);

};

template
bool PjList::contains(const T& t)
{
PjIterator it;

for( it = std::list::begin(); it != std::list::end(); it++ )
{
if ( *it == t )
return true;
}
return false;
}

template
bool PjList::containsDoubloons()
{
PjIterator it;
PjIterator jt;
int nb_occurence;

for( it = std::list::begin(); it != std::list::end(); it++ )
{
nb_occurence = 0;
for( jt = std::list::begin(); jt != std::list::end(); jt++ )
{
if ( it == jt )
nb_occurence += 1;
}
if ( nb_occurence > 1 )
return true;
}
return false;
}

template
PjList PjList::removeDoubloons()
{
PjList output;

PjIterator it;
for( it = std::list::begin(); it != std::list::end(); it++ )
{
T& t = *it;
if ( !output.contains(t) )
output.push_back(t);
}

return output;
}

template
void PjList::destroyContents()
{
PjIterator

Solution

I see no reason to have this be an entirely new data structure that inherits from std::list. All of the new member functions you've defined are really just wrapper functions that could just be delegated to std::list functions or functions that could be implemented as regular procedural functions.

For example, the contains() and at() and insertItem() (which should really just be named insertItemBefore()) functions could simply be:

namespace list_util {

template 
bool contains(const std::list& l, const T& elem)
{
    return std::find(l.begin(), l.end(), elem) != l.end();
}

template 
T& at(const std::list& l, std::size_t index)
{
    assert(index 
bool insertItemBefore(std::list& l, const T& ref_item, const T& elem)
{
    auto itemItr = std::find(l.begin(), l.end(), ref_item);

    // if we found the item, itemItr != l.end()
    if (itemItr != l.end()) {
        l.insert(itemItr, elem);
        return true;
    }
    return false;
}

}


You'd have to #include and #include for this to work. Notice that the index parameter is now std::size_t to avoid having to check for negative indexes. They could then simply be called as: list_util::contains(the_list, elem); and list_util::at(the_list, the_index);.

Moreover, containsDoubloons() could be implemented using std::find() and destroyContents() is really just a call to std::list::clear():

namespace list_util {

template
bool containsDoubloons(const std::list& l)
{
    for (auto it = l.begin(); it != l.end(); ++it) {
        const auto beginItr = std::next(it);
        if (beginItr != l.end() && 
            std::find(beginItr, l.end(), *it) != l.end()) {
            return true;
        }
    }
    return false;
}

template
void destroyContents(std::list& l)
{
    l.clear();
}

}


Moreover, since the new functions we've written are completely generic and use the STL iterators for all operations, there's no reason to limit them only to std::list. We could allow any container of containing T to be a parameter. An example is the contains() function:

template  class C, class T>
bool contains(const C& cont, const T& elem)
{
    return std::find(cont.begin(), cont.end(), elem) != cont.end();
}

Code Snippets

namespace list_util {

template <class T>
bool contains(const std::list<T>& l, const T& elem)
{
    return std::find(l.begin(), l.end(), elem) != l.end();
}

template <class T>
T& at(const std::list<T>& l, std::size_t index)
{
    assert(index < l.size());
    auto begin = l.begin();
    std::advance(begin, index);
    return *begin;
}

template <class T>
bool insertItemBefore(std::list<T>& l, const T& ref_item, const T& elem)
{
    auto itemItr = std::find(l.begin(), l.end(), ref_item);

    // if we found the item, itemItr != l.end()
    if (itemItr != l.end()) {
        l.insert(itemItr, elem);
        return true;
    }
    return false;
}

}
namespace list_util {

template<class T>
bool containsDoubloons(const std::list<T>& l)
{
    for (auto it = l.begin(); it != l.end(); ++it) {
        const auto beginItr = std::next(it);
        if (beginItr != l.end() && 
            std::find(beginItr, l.end(), *it) != l.end()) {
            return true;
        }
    }
    return false;
}

template<class T>
void destroyContents(std::list<T>& l)
{
    l.clear();
}

}
template < template <class> class C, class T>
bool contains(const C<T>& cont, const T& elem)
{
    return std::find(cont.begin(), cont.end(), elem) != cont.end();
}

Context

StackExchange Code Review Q#124960, answer score: 10

Revisions (0)

No revisions yet.