有没有一种简单的方法来迭代C ++中的静态字符串列表

时间:2009-05-22 18:19:20

标签: c++ string iterator

我经常需要迭代C ++代码中的字符串列表。

在像Perl这样的语言中,这很简单:

foreach my $x ("abc", "xyz", "123") {.... }

过去,这就是我在C ++中所做的事情

const char* strs[] = { "abc", "xyz", "123" };
for (int i=0; i<sizeof(strs)/sizeof(const char*); i++) {
   const char *str = strs[i];
   ...

如果我已经拥有STL容器,我可以使用BOOST_FOREACH

std::vector<std::string> strs;
BOOST_FOREACH(std::string str, strs) {
   ...

我尝试创建一个宏来组合所有这些概念,但没有成功。

我希望能够编写这样的代码:

SPECIAL_STRING_FOREACH(const char* str, {"abc", "xyz", "123"}) {
   ...
}

肯定有人以前做过这个。

8 个答案:

答案 0 :(得分:9)

这是我的尝试。遗憾的是,它依赖于可变宏,这是一个C99 / C ++ 1x功能。但是在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;
    }
}

请注意,您还可以使用引用变量进行迭代,以避免无用的复制。这是一个使用boost.preprocessor(a)(b)...语法的语法,在预处理阶段后编译为相同的代码。

#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;
    }
}

技巧是组合一个函数类型,该函数类型具有枚举变量作为参数,并获取该参数的类型。然后boost::remove_reference将删除任何引用。第一个版本使用boost::decay。但它也会将数组转换为指针,我发现它有时并不是我想要的。然后将结果类型用作数组元素类型。

要在枚举器变量具有依赖类型的模板中使用,您必须使用另一个将typename放在boost::remove_referenceboost::function_traits之前的宏。可以命名为SEQ_FOR_EACH_D(D ==依赖)。

答案 1 :(得分:6)

请注意,如果标记数组的末尾,则更容易处理字符串的C数组:

const char* strs[] = { "abc", "xyz", "123", NULL };
for (int i=0; strs[i] != NULL  i++) {
  ...
}

答案 2 :(得分:4)

这样的事情:


void func( const char* s ) { /* ... */ }

const char* array[] = { "abc", "xyz", "123" };
std::for_each( array, array + 3, func );

您可能还想查看boost::array

答案 3 :(得分:2)

Cou可以使用va_arg hack创建一个返回可迭代集合的函数(注意它真的是一个黑客!)

新的C ++ - 标准(C ++ 0x)将提供更方便的初始化方法(initializer lists

另一种可能性是将boost::assignmentFOREACH结合使用。

请注意,BOOST::FOREACH也适用于数组!

答案 4 :(得分:1)

制作一个返回数组大小的宏有助于此。

#define N_ELEMS(a) (sizeof(a) / sizeof((a)[0]))

然后你的原始代码看起来不那么糟糕。

for(int i = 0; i < N_ELEMS(strs); ++i) {
  ...
}

这是迭代任何静态数组的好习惯,而不仅仅是字符串数组。

答案 5 :(得分:0)

而不是:

for (int i=0; i<sizeof(strs)/sizeof(const char*); i++)
{
    const char *str = strs[i];
    ...

考虑一下:

for (const char *ptr = strs[0],
    *end = strs[sizeof(strs)/sizeof(const char*)];
    ptr < end; ++ptr)
{
    ...

您可能会发现此表单更易于宏观化;在任何情况下,ptr变量都模拟迭代器。

  

肯定有人以前做过这个。

我怀疑他们应该这样做。 for循环是惯用且易于阅读的(特别是如果你知道数组的大小),用户定义的宏是非标准的。

答案 6 :(得分:0)

这应该这样做(我没有测试它,所以可能会有一些拼写错误)

#define STR_ARRAY_FOREACH(I) const char* Items[] = I; for( const char *item = Items[0], *end = strs[ sizeof( Items ) / sizeof( const char* ) ]; item < end; ++item )

然后在循环中使用item:

STR_ARRAY_FOREACH({ "abc", "xyz", "123" })
{
    cout << item << "\n";
}

答案 7 :(得分:0)

我会尝试直接在数组上使用BOOST_FOREACH。 documentation似乎认为它会起作用。

事实上确实如此。它必须是两行,但它有效:

const char * myarray[] = {"abc", "xyz", "123"};
BOOST_FOREACH (const char *str, myarray) {
  std::cout << "Hello " << str << std::endl;
}