• Advertisement
Sign in to follow this  

x.begin(), x.end()

This topic is 3510 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

When using STL algorithms, I often find myself typing stuff like
accumulate(a.begin(), a.end(), 0);
for_each(b.begin(), b.end(), foo);
remove_if(c.begin(), c.end(), bar);
Today I thought to myself: why not use the preprocessor to make things clearer?
#define ALL(x) x.begin(), x.end()

accumulate(ALL(a), 0);
for_each(ALL(b), foo);
remove_if(ALL(c), bar);
As long as x is just a variable, I don't see any problems with it. Is this a reasonable use of the evil preprocessor? Maybe there's even a real solution I don't know about? Discuss!

Share this post


Link to post
Share on other sites
Advertisement
Let's be honest, you are making your code more unclear as well as sacrificing type safeness in order to save a little extra typing.

Share this post


Link to post
Share on other sites
Using the preprocessor to obscure and de-idiomize code just because you are lazy is not generally considered to be a good idea.

Share this post


Link to post
Share on other sites
No, it's not. If you really think it's worth it, you should be able to make templated forms that do the same thing while being infinitely more robust.

Share this post


Link to post
Share on other sites
Quote:
Today I thought to myself: why not use the preprocessor to make things clearer?


Why not use language itself? C++ in general has no overhead for syntactic sugar, especially when dealing with templates.


template < class Container, class T >
void accumulate(Container & c, T t)
{
accumulate(c.begin(), c.end(), t);
};


accumulate(a, 0);

Or something....

Share this post


Link to post
Share on other sites
Taking for_each(ALL(b), foo) as an example, wouldn't this be better?:

template <class InputIterableContainer, class UnaryFunction>
inline UnaryFunction for_each(InputIterableContainer & c, UnaryFunction f)
{
return std::for_each(c.begin(), c.end(), f);
}

Share this post


Link to post
Share on other sites

#define ALL(x) x.begin(), x.end()

class Foo
{
public:
void AddValue(int i)
{
m_values.push_back(i);
}

std::vector<int> GetValues() const
{
return m_values;
}

private:
std::vector<int> m_values;
};


void printFoo(Foo f)
{
std::copy(ALL(f.GetValues()), std::ostream_iterator<int>(std::cout));
}




Spot the bug.


Also, check out boost::range.

Share this post


Link to post
Share on other sites
Quote:
Original post by Sneftel
*** Source Snippet Removed ***
Spot the bug.


getValues() returns a copy & the pre-processor makes x == f.getValues() instead of just the vector, so the .begin() and the .end() are iterators from different vectors. ZOMG, what do i win? [smile]

-me

Share this post


Link to post
Share on other sites
Quote:
Original post by dmatter
I believe the problem can be generalised by saying it's an ill-behaved macro because it evaluates its arguments more than once.


Writing macros in such a way that these issues can be avoided can be a headache, especially since at first glance, it looks just fine. The reality is that there's no reason for the riskier macro when saver solutions exist, so why even take the risk? A macro is an absolute last resort for me. Especially when you consider that to avoid clashes, a better name than "ALL" is required. You might need something like:


#define STL_ALGORITHM_HELPER_ALL(x) x.begin(), x.end()

...

accumulate(STL_ALGORITHM_HELPER_ALL(a), 0);



Once you do that, the supposed reduced typing of the macro is proven to be a fallacy and the macro loses on all accounts.

Share this post


Link to post
Share on other sites
Quote:
Original post by Palidine
Quote:
Original post by Sneftel
*** Source Snippet Removed ***
Spot the bug.


getValues() returns a copy & the pre-processor makes x == f.getValues() instead of just the vector, so the .begin() and the .end() are iterators from different vectors. ZOMG, what do i win? [smile]

-me


Yep. You win a cookie.

The unfortunate thing from a pedagogical perspective is that changing to return-by-const-reference - which should probably be done anyway - would fix the bug (but not the design problem).

Share this post


Link to post
Share on other sites
Quote:
Original post by Zahlman
Quote:
Original post by Palidine
Quote:
Original post by Sneftel
*** Source Snippet Removed ***
Spot the bug.


getValues() returns a copy & the pre-processor makes x == f.getValues() instead of just the vector, so the .begin() and the .end() are iterators from different vectors. ZOMG, what do i win? [smile]

-me


Yep. You win a cookie.

The unfortunate thing from a pedagogical perspective is that changing to return-by-const-reference - which should probably be done anyway - would fix the bug (but not the design problem).

I'd be more terrified by the reverse happening - the original code returning by const reference and everything working fine, then an unrelated change happens to switch to return by value and all of a sudden we've introduced a really obscure bug in an area of the code we apparently havn't touched. [tears]

Share this post


Link to post
Share on other sites
The scary part is how often macros are employed. I have coworkers who tended to do the same thing. I've slowly had them using small functions and template functions where they would've used macros in the past. But it is a tough habit to break, it seems.

Of course what took the cake was when I saw an STL-like linked list class implemented as a single macro back at my prior company. Pretty brutal.

Share this post


Link to post
Share on other sites
Nothing beats DO_THE_FUNKY_STUFF macro I saw mentioned somewhere once. I believe it had about 400 lines of code in it.

Share this post


Link to post
Share on other sites
I've come up with a more general solution. I had to use a class template cause function templates wouldn't let me do this.

template <class T, class Iter = typename T::iterator>
class range
{
public:
// function type to send to algorithm
typedef void (*Function) (typename T::reference);

// algorithm function type
typedef Function (*Algorithm) (typename Iter, typename Iter, Function);

// constructor acts like a function
inline range (Algorithm algorithm, T& container, Function function)
{
algorithm (container.begin(), container.end(), function);
}
};

// Prints the number sent by for_each
void print_nums (int& number)
{
printf ("%d\n", number);
}

int main ()
{
// create a vector of ints
std::vector <int> nums;
nums.push_back (1);
nums.push_back (2);
nums.push_back (3);

// call the for_each algorithm on the vector
range <std::vector <int>> (std::for_each, nums, print_nums);
return 0;
}

Share this post


Link to post
Share on other sites
For extra irony points, I'd prefer BOOST_FOREACH over that:

#include <boost/foreach.hpp>

void print_nums (int& number)
{
printf ("%d\n", number);
}

int main ()
{
// create a vector of ints
std::vector <int> nums;
nums.push_back (1);
nums.push_back (2);
nums.push_back (3);

BOOST_FOREACH( int& number, nums ) {
print_nums(number);
}
return 0;
}



Yes, BOOST_FOREACH is a macro. But no, it's not just a macro. It's implementation is worthy of it's own article. Please note the actual implementation (for portability reasons, I'm presuming) is even more complex than the article would suggest.

Share this post


Link to post
Share on other sites
Quote:
Original post by Almindor
Nothing beats DO_THE_FUNKY_STUFF macro I saw mentioned somewhere once. I believe it had about 400 lines of code in it.


I'd not actually seen it, but at a company I used to work for the word was that the entirety of the intrusion detection engine was written in macro. It took about 8 hours to build and by all accounts, inscrutable. (but very, very, very fast)

Share this post


Link to post
Share on other sites
Quote:
Original post by Rydinare
Of course what took the cake was when I saw an STL-like linked list class implemented as a single macro back at my prior company. Pretty brutal.

That's long been a common C idiom, actually. Gives tighter code than void*, and actual type-safety. Terrifying to have to work with, of course. The existence of that idiom is pretty much the reason templates exist.

Share this post


Link to post
Share on other sites
Quote:
Original post by Telastyn
I'd not actually seen it, but at a company I used to work for the word was that the entirety of the intrusion detection engine was written in macro. It took about 8 hours to build and by all accounts, inscrutable. (but very, very, very fast)


"intrusion detection engine"?

Share this post


Link to post
Share on other sites
Quote:
Original post by Sneftel
Quote:
Original post by Rydinare
Of course what took the cake was when I saw an STL-like linked list class implemented as a single macro back at my prior company. Pretty brutal.

That's long been a common C idiom, actually. Gives tighter code than void*, and actual type-safety. Terrifying to have to work with, of course. The existence of that idiom is pretty much the reason templates exist.


Oh sure, I hear you on that. It actually wasn't so bad until I had to debug in it. At that point, I was ready to cry (well, not literally). [smile]

The scary part was is that I don't think it was legacy. I think it was just one developer on that project wasn't aware of templates. There were other things like that that were done with templates by other developers, so might just've been that guy.

Hmm, of course I've seen much scarier things in code, but that's a story for another day. [grin]

Share this post


Link to post
Share on other sites
Quote:
Original post by Zahlman
Quote:
Original post by Telastyn
I'd not actually seen it, but at a company I used to work for the word was that the entirety of the intrusion detection engine was written in macro. It took about 8 hours to build and by all accounts, inscrutable. (but very, very, very fast)


"intrusion detection engine"?


The software was network intrusion detection. It would monitor a stream of network traffic, and toss flags if the traffic was 'bad'. Most systems just use some signature engine or state machine to process the traffic. This one used a heuristic process, but the goal is the same:

a bit of code to process 1GB/s of network traffic and trigger some event if a remote exploit or similarly malicious bit of traffic is seen.

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement