将std :: vector <int>项传递给variadic函数</int>

时间:2012-02-14 12:31:28

标签: c++ gcc arguments stdvector variadic-functions

我正在使用gcc 4.6。 假设有一个参数向量v我必须传递给一个可变函数f(const char * format,...)。

这样做的一种方法是:

        void VectorToVarArgs(vector<int> &v)
        {
            switch(v.size())
            {
                case 1: f("%i",             v[0]);
                case 2: f("%i %i",          v[0], v[1]);
                case 3: f("%i %i %i",       v[0], v[1], v[2]);
                case 4: f("%i %i %i %i",    v[0], v[1], v[2], v[3]);

                // etc...
                default:
                    break;
            }
        }

        // where function f is
        void f(const char* format, ...)
        {
            va_list args;
            va_start (args, format);
            vprintf (format, args);
            va_end (args);
        }

问题当然是它不支持向量v中的任意数量的项目。 但是,我相信已理解va_lists原则上是如何工作的, 即通过从堆栈中读取参数,从“...”之前的最后一个命名参数的地址开始, 现在我认为应该可以将矢量项值复制到内存块(例如myMemBlock) 并将其地址作为'format'后的第二个参数传递。 显然,这需要myMemBlock按f()的预期结构化,即像堆栈一样。

  1. 这样的事情可行吗?
  2. 或者,是否可以使用一些内联汇编程序魔法在实际堆栈上推送矢量项值, 调用函数f()并在之后清理堆栈?
  3. 最后,我不关心的事情:

    1. 代码可能不可移植。好的,我只对gcc感兴趣。
    2. 可能还有其他涉及预处理器hackery的方法。
    3. 对于C ++,不鼓励使用variadic函数进行格式化,例如printf()。
    4. 使用可变参数模板函数。

5 个答案:

答案 0 :(得分:3)

有一个&#34;创建假的va_list&#34; http://cocoawithlove.com/2009/05/variable-argument-lists-in-cocoa.html部分。它适用于Cocoa,但你可能会在网上为GCC找到一些东西。

然后,我猜测你做了这样的事情:

#include <string>
#include <cstdio>
#include <vector>
#include <cstdarg>
using namespace std;

struct my_list {
   unsigned int gp_offset;
   unsigned int fp_offset;
   void *overflow_arg_area;
   void *reg_save_area;
};

void f(const char* format, ...) {
    va_list args;
    va_start (args, format);
    vprintf (format, args);
    va_end (args);
}

void test(const vector<int>& v) {
    string fs;
    for (auto i = v.cbegin(); i !=v.cend(); ++i) {
        if (i != v.cbegin()) {
            fs += ' ';
        }
        fs += "%i";
    }
    my_list x[1];
    // initialize the element in the list in the proper way
    // (however you do that for GCC)
    // where you add the contents of each element in the vector 
    // to the list's memory
    f(fs.c_str(), x);
    // Clean up my_list
}

int main() {
    const vector<int> x({1, 2, 3, 4, 5});
    test(x);
}

但是,我完全没有头绪。 :)

答案 1 :(得分:2)

你的反思不在适当的抽象层次。

当您说要将向量转换为变量参数列表时,这是因为采用变量参数列表的函数会对您感兴趣。

因此,真正的问题是,我怎样才能做到f,但是从vector开始?

除了将呼叫转发到f之外,最终可能会开始解决方案,但这并不明显。

如果只是打印:

void f(std::vector<int> const& vi) {
   bool first = true;
   for (int i: vi) {
     if (first) { first = false; } else { std::cout << ' '; }
     std::cout << i;
   }
}

或者,如果您可以访问外部库:

#include <boost/algorithm/string/join.hpp>

void f(std::vector<int> const& vi) {
  std::cout << boost::join(vi, " ");
}

此时f的兴趣不再明显。

答案 2 :(得分:2)

好的,这是部分解决方案! 部分,因为它不适用于真正的可变参数函数, 但是那些接受va_list作为参数的人。 但我认为完整的解决方案并不遥远。

这是基于我在这里找到的例子:

  1. 动态创建va_list https://bbs.archlinux.org/viewtopic.php?pid=238721

  2. 伪造va_list http://confuseddevelopment.blogspot.com/2006/04/dynamically-creating-valist-in-c.html

  3. 此代码已成功通过linux VC ++ 2008上的gcc进行测试, 其他平台也可能受到支持,但这取决于你。

    对我来说重要的见解是va_list基本上只不过是一个打包数组, 它可以动态填充数据,也可以传递给像 vprintf,vfprintf,vsprintf,接受它作为参数。

    因此,通过分配足够的内存,可以将向量项传递给其中一个函数 对于矢量项目,并在通话之前复制它们。

    话虽如此,这里是动态分配堆栈方法

    #include <iostream>
    #include <stdio.h>
    #include <stdarg.h>
    #include <string>
    #include <vector>
    #include <alloca.h>
    
    using namespace std;
    
    
    class Format
    {
        typedef vector<unsigned long> ULVector;
        ULVector _args;
        string _format;
    
        public:
            Format(const char* format) : _format(format)
            {}
    
            Format &operator<<(int arg) {
                _args.push_back((unsigned long)arg);
                return *this;
            }
    
            Format &operator<<(const char* arg) {
                _args.push_back((unsigned long)arg);
                return *this;
            }
    
            string format() {
                union {
                    va_list varargs;
                    unsigned long* packedArray;
                } fake_va_list;
    
                // malloc would do it as well!
                // but alloca frees the mem after leaving this method
                unsigned long *p = (unsigned long*)alloca(_args.size() * sizeof(unsigned long));
                fake_va_list.packedArray = p;
    
                ULVector::iterator i = _args.begin();
                for (int n=0; i != _args.end(); i++, n++) {
                    p[n] = *i;
                }
    
                char buffer[512];
                const char* fmt = _format.c_str();
                vsprintf(buffer, fmt, fake_va_list.varargs);
    
                // place a free(p) here if you used malloc
                return string(buffer);
            }
    };
    
    
    ostream& operator <<=(ostream &os, Format &obj) {
          os << obj.format();
          return os;
    }
    
    
    int main()
    {
        // we use '<<=' operator here which has lower precedence than '<<'
        // otherwise we have to write
        // cout << ( Format("\n%x %s %x %c\n") <<  etc. );
        cout <<= Format("\n%x %s %x %c\n") << 0x11223344 << "VectorToVarArg" << 0xAABBCCDD << '!';
        return 0;
    }
    

    猜猜它是做什么的? 它允许使用向量中收集的参数进行printf(..)样式格式化。 是的,它并不完美,但它可以满足我的需求。 此外,它涵盖了两个主要平台:D

    另外,看看这篇文章: 的 va_pass http://www.codeproject.com/Articles/9968/va_list-va_start-va_pass-or-how-to-pass-variable-a

答案 3 :(得分:1)

根据您自己给出的答案判断,听起来您可以使用boost format

示例:

#include <iostream>
#include <string>
#include <sstream>
#include <boost/format.hpp>
using namespace std;
using namespace boost;

template <typename T>
string formatted_str_from_vec(const T& v) {
    ostringstream fs;
    size_t count = 1;
    for (const auto& i : v) {
        if (&i != &v[0]) {
            fs << " ";
        }
        fs << '%' << count << '%';
        ++count;
    }
    format fmtr(fs.str());
    for (const auto& i : v) {
        fmtr % i;
    }
    // looks like fmtr("%1% %2% %3% %4%") % v[0] % v[1] etc.
    return fmtr.str();
}

int main() {
    cout << formatted_str_from_vec(vector<int>({1, 2, 3, 4, 5, 6, 7, 8, 8, 10, 11, 12})) << endl;
    cout << formatted_str_from_vec(vector<string>({"a", "b", "c"})) << endl;
    format test1("%1% %2% %3%");
    test1 % 1 % "2" % '3';
    cout << test1.str() << endl;
    format test2("%i %s %c");
    test2 % 1 % "2" % '3';
    cout << test2.str() << endl;
    format test3("%1% %2%");
    test3.exceptions(io::no_error_bits);
    test3 % 'g';
    cout << test3.str() << endl;
    format test4("%%1%% = %1%");
    test4 % "zipzambam";
    cout << test4.str() << endl;
}

// g++ -Wall -Wextra printvector.cc -o printvector -O3 -s -std=c++0x

当然,打印出一个矢量并不是必需的。

答案 4 :(得分:0)

您可以使用STL算法for_each打印矢量的每个项目。