简体   繁体   English

传递std :: vector <int> 具有可变功能的项目

[英]Passing std::vector<int> items to variadic function

I'm using gcc 4.6. 我正在使用gcc 4.6。 Assume that there is a vector v of parameters I have to pass to a variadic function f(const char* format, ...). 假设有一个参数向量v我必须传递给一个可变函数f(const char * format,...)。

One approach of doing this is: 这样做的一种方法是:

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

The problem is of course that it does not support an arbitrary number of items in the vector v. However, I believe to have understood how va_lists works in principle, ie by reading the arguments from the stack, starting at the address of the last named argument prior to "...", Now I thought it should be possible to copy the vector item values to a memory block (eg myMemBlock) and pass it's address as the second argument after 'format'. 问题当然是它不支持向量v中的任意数量的项。但是,我相信已经理解了va_lists原则上是如何工作的,即通过从最后一个命名的地址开始从栈中读取参数。在“...”之前的参数,现在我认为应该可以将矢量项值复制到内存块(例如myMemBlock)并将其地址作为'format'之后的第二个参数传递。 Obviously that would require that myMemBlock to be structured as expected by f(), ie like a stack. 显然,这需要myMemBlock按f()的预期结构,即像堆栈一样。

  1. Is such a thing doable? 这样的事情可行吗?
  2. Alternatively, is it possible to push the vector item values on the real stack with some inline assembler magic, call function f() and clean up the stack afterwards? 或者,是否可以使用内联汇编程序魔术推送实际堆栈上的向量项值,调用函数f()并在之后清理堆栈?

Finally, things I don't care about: 最后,我不关心的事情:

  1. The code might be not portable. 代码可能不可移植。 OK I'm just interested in gcc. 好的,我只对gcc感兴趣。
  2. There might be other approaches involving preprocessor hackery. 可能还有其他涉及预处理器hackery的方法。
  3. Usage of variadic function for formatting like printf() does, is discouraged for C++. 对于C ++,不鼓励使用variadic函数进行格式化,如printf()。
  4. Usage of variadic template functions. 使用可变参数模板函数。

There's a "Creating a fake va_list" section at http://cocoawithlove.com/2009/05/variable-argument-lists-in-cocoa.html . http://cocoawithlove.com/2009/05/variable-argument-lists-in-cocoa.html上有一个“创建虚假的va_list”部分。 It's for Cocoa, but you might be able to find something on the net for GCC. 它适用于Cocoa,但你可以在网上找到GCC的东西。

Then, I'm guessing you'd do something like this: 然后,我你会做这样的事情:

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

But, I have absolutely no clue. 但是,我完全没有头绪。 :) :)

Your reflexion is not at the right level of abstraction. 你的反思不是正确的抽象层次。

When you say you want to convert the vector to a variable arguments lists, it is because the function that takes the variable argument list does something of interest to you. 当你想要将向量转换为变量参数列表时,这是因为获取变量参数列表的函数会对你感兴趣。

The real question is therefore, how could I do the same thing than f , but starting from a vector ? 因此,真正的问题是,我怎么能做与f相同的事情,但是从vector开始?

It is possible than forwarding the call to f might end up begin the solution, but it is not obvious. 有可能转发到f的调用最终可能会开始解决方案,但这并不明显。

If it is just about printing: 如果它只是打印:

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

Or, if you have access to outside libraries: 或者,如果您可以访问外部库:

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

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

At which point the interest of f is not really evident any longer. 此时f的兴趣不再明显。

Okay, here is a partial solution! 好的,这是部分解决方案! Partial, because it does not apply to really variadic functions, but to those which accept a va_list as argument. 部分,因为它不适用于真正的可变函数,而是适用于接受va_list作为参数的函数。 But I think the full solution isn't far away. 但我认为完整的解决方案并不遥远。

It is based on the examples I found here: 它基于我在这里找到的例子:

  1. Dynamically create a va_list https://bbs.archlinux.org/viewtopic.php?pid=238721 动态创建一个va_list https://bbs.archlinux.org/viewtopic.php?pid=238721

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

This code is tested with gcc on linux and VC++2008 successfully, other platforms might be supported too, but that's up to you. 此代码在linux VC ++ 2008上使用gcc进行了成功测试,也可能支持其他平台,但这取决于您。

The important insight for me was that a va_list is basically nothing more than a packed array, which can be filled with data dynamically and can be passed to functions like vprintf, vfprintf, vsprintf which accept it as argument. 对我来说重要的见解是,va_list基本上只不过是一个打包数组,它可以动态填充数据,并可以传递给vprintf,vfprintf,vsprintf等函数,接受它作为参数。

So passing vector items to one of those functions can work by allocating enough memory for the vector items and copy them over prior to the call. 因此,通过为矢量项分配足够的内存并在调用之前复制它们,可以将向量项传递给其中一个函数。

Having said that, here is the dynamically allocating stack approach : 话虽如此,这里是动态分配堆栈方法

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

Guess what it does? 猜猜它有什么作用? It allows printf(..) style formatting with the parameters collected in a vector. 它允许使用向量中收集的参数进行printf(..)样式格式化。 Yes, it's not perfect but it does what I wanted. 是的,它并不完美,但它可以满足我的需求。 Furthermore, it covers two major platforms :D 此外,它涵盖两个主要平台:D

Additionally, have look at this article: va_pass http://www.codeproject.com/Articles/9968/va_list-va_start-va_pass-or-how-to-pass-variable-a 另外,看看这篇文章: va_pass http://www.codeproject.com/Articles/9968/va_list-va_start-va_pass-or-how-to-pass-variable-a

Judging by your own answer given, it sounds like you could make use of boost format . 根据您自己给出的答案判断,听起来您可以使用boost格式

Examples: 例子:

#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

Of course, none of that's necessary to just print out a vector. 当然,没有必要打印出一个矢量。

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM