views:

282

answers:

6

Hi,

I'm currently working on a C++ project that needs to have as few external dependencies as possible, and thus I'm pretty much sticking to STL and Boost. Until now, I've been almost exclusively living in Qt-land when it comes to C++. In general I tend to use C# and Python when I can.

Today I wanted to check whether a std::vector contained a certain item. With Qt, I'd do this like so:

QList< int > list;
list.append( 1 );
list.append( 2 );
list.append( 3 );

if ( list.contains( 2 ) )
{
    // do something
}

Nice and readable. But std::vector has no contains method, which was a surprise. Ok... what would the STL idiom for something like that be? Searching around, it seems to be this:

std::vector< int > list;
list.push_back( 1 );
list.push_back( 2 );
list.push_back( 3 );

std::vector< int >::const_iterator result =
    std::find( list.begin(), list.end(), 2 );

if ( result != list.end() )
{
    // do something
}

That (to me) is hardly readable and much too verbose. So I found myself writing a utility function that takes a vector and a value and returns bool depending on whether the value was found or not. Basically, a templated contains() method; a wrapper for the above std::find call. I can then use that in a way that is similar to the Qt example.

I have several similar utility functions in mind that would wrap other STL idioms for no other reason but a (perceived) increase in readability. What I want to know is... is this a bad idea? Do other people do the same? Am I missing something crucial? The code will be OSS at one point, and I'd rather not do something idiosyncratic that other C++ devs would find strange.

+6  A: 

There is nothing wrong in writing the utility functions that will help you and will make your code cleaner. Other people do the same. Boost library is the biggest set of such utility functions and classes.

More to say C++ Standard explicitly proposes to extend the Standard Library (17.3.1.2/1):

The library can be extended by a C++ program. Each clause, as applicable, describes the requirements that such extensions must meet. Such extensions are generally one of the following:

  • Template arguments
  • Derived classes
  • Containers, iterators, and/or algorithms that meet an interface convention
Kirill V. Lyadvinsky
+3  A: 

I'd say it's definitely a good idea. The C++ STL is missing a lot of what Python/C# programmers have come to expect from a standard library. If you can make your code more readable by taking that 2-3 line STL approach and making it a single function, go ahead!

Here's another example of a very similar issue: I often want to convert an int to a std::string. To my surprise, there's no concise way of doing this using the STL. So, I wrote a toStr function that runs the 2-3 lines required to put an int into a stringstream and return the resulting string.

Edit: To clarify, I recommend looking for boost solutions before creating your own. My example was intended to demonstrate the limitations of the STL, but has the alternative interpretation of, "whatever the STL is missing, boost has."

Justin Ardini
boost has lexical_cast<>() that will convert anything streamable into a string.
Martin York
boost::lexical_cast<int>(str) will also do the reverse and parse the string and return an int.
bradgonesurfing
Indeed, that's what I now use. I was just giving an example. :)
Justin Ardini
C++0x has `std::to_string()` that will convert any numeric type into a string. g++ supports it (along with `std::stoi()`, `std::stol()`, etc, for the reverse)
Cubbi
@Cubbi: Now that I did not know. Thanks!
Justin Ardini
+9  A: 

boost makes it much neater. I never use the STL iterator based algorithms anymore. The range based algorithms are a much neater abstraction and result in much cleaner code.

#include <boost/range/algorithm/find.hpp>

void foo(){
    std::vector<int> list;
    ...
    ...
    boost::find(list, 2) != list.end()
}
bradgonesurfing
+1  A: 

Similar enough, in my current project, we have a file called: stlutils.h, which contains some methods, such as contains(). Implemented as:

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

There are more functions, but I think you get the point

Edison Gustavo Muenz
+1  A: 

The other answers have made the point that you can write utility functions to do this for you, and that's a good idea where you need it. But I thought I'd point out an important point: the STL is designed around algorithmic efficiency. Nearly all operations with the STL have a standard-mandated big-O efficiency requirement.

If vector had a contains() member, it would certainly be O(n) to call, since vector is a simple contiguous list. Since it is also convenient, it might encourage programmers to use it regularly, even on large datasets, encouraging the design of applications with poor algorithmic performance. In the case of contains(), if it's important to look up if a container holds a certain element, with the added guarantee that all the elements are unique, std::set is almost certainly a better choice, with O(log n) efficiency lookups, or even O(1) for std::unordered_set.

So my personal view: learn all the containers and features the STL offers, and you'll find while it is terse, it encourages a more efficient programming style. You ask in the question if you're missing something, and I would say yes - you want to think more carefully about the container you use. I use set instead of a vector regularly these days.

AshleysBrain
A: 

I'm not a big fan of wrappers, but if they help you out, go for it. I think you'll find over time that you'll want to use your utility function with other containers besides std::vector. Eventually your utility function becomes so generic that you might as well use std::find directly.

But are you sure you're using the right container? std::set has a method count(), which is essentially equivalent to contains(). And it is O(log(n)), rather than O(n).

Ben