It often happens that I need to iterate over a list of strings in my C++ code.
In languages like Perl, this is easy:
foreach my $x ("abc", "xyz", "123") {.... }
In the past, this is what I've done in C++
const char* strs[] = { "abc", "xyz", "123" };
for (int i=0; i<sizeof(strs)/sizeof(const char*); i++) {
const char *str = strs[i];
...
If I already have an STL container, I can use BOOST_FOREACH
std::vector<std::string> strs;
BOOST_FOREACH(std::string str, strs) {
...
I've tried to create a macro to combine all these concepts but without success.
I'd like to be able to write code like this:
SPECIAL_STRING_FOREACH(const char* str, {"abc", "xyz", "123"}) {
...
}
Surely someone's cooked this up before.
Here is my attempt at it. Sadly it relies on variadic macros which is a C99/C++1x feature. But works in GCC.
#include <boost/foreach.hpp>
#include <boost/type_traits.hpp>
#include <iostream>
#define SEQ_FOR_EACH(D, ...) \
if(bool c = false) ; else \
for(boost::remove_reference<boost::function_traits<void(D)> \
::arg1_type>::type _t[] = __VA_ARGS__; \
!c; c = true) \
BOOST_FOREACH(D, _t)
int main() {
SEQ_FOR_EACH(std::string &v, { "hello", "doctor" }) {
std::cout << v << std::endl;
}
}
Note that you can also iterate with a reference variable, to avoid useless copying. Here is one using boost.preprocessor
and the (a)(b)...
syntax, compiling down to the same code after pre-processing stage.
#define SEQ_FOR_EACH(D, SEQ) \
if(bool c = false) ; else \
for(boost::remove_reference<boost::function_traits<void(D)> \
::arg1_type>::type _t[] = { BOOST_PP_SEQ_ENUM(SEQ) }; \
!c; c = true) \
BOOST_FOREACH(D, _t)
int main() {
SEQ_FOR_EACH(std::string &v, ("hello")("doctor")) {
std::cout << v << std::endl;
}
}
The trick is to assemble a function type that has as parameter the enumeration variable, and getting the type of that parameter. Then boost::remove_reference
will remove any reference. First version used boost::decay
. But it would also convert arrays into pointers, which i found is not what is wanted sometimes. The resulting type is then used as the array element type.
For use in templates where the enumerator variable has a dependent type, you will have to use another macro which puts typename
before boost::remove_reference
and boost::function_traits
. Could name it SEQ_FOR_EACH_D
(D == dependent).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With