Передача std :: vector <int>элементов в переменную функцию - PullRequest
8 голосов
/ 14 февраля 2012

Я использую 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 () и последующей очистки стека?

Наконец, вещи, которые меня не волнуют:

  1. Код может быть не переносимым.Хорошо, я просто заинтересован в gcc.
  2. Могут быть и другие подходы, связанные с хакерской обработкой препроцессора.
  3. Использование функции форматирования с переменным числом, как это делает printf (), не рекомендуется для C ++.
  4. Использование функций шаблона с переменным числом аргументов.

Ответы [ 5 ]

3 голосов
/ 14 февраля 2012

Есть раздел "Создание поддельного va_list" в http://cocoawithlove.com/2009/05/variable-argument-lists-in-cocoa.html. Это для Какао, но вы можете найти что-то в сети для 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);
}

Но я понятия не имею.:)

2 голосов
/ 16 февраля 2012

Хорошо, вот частичное решение! Частично, потому что это не относится к действительно функциям с переменным числом но для тех, которые принимают 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

Этот код успешно протестирован с gcc на linux и VC ++ 2008, другие платформы также могут поддерживаться, но это зависит от вас.

Важным для меня было понимание того, что 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

2 голосов
/ 14 февраля 2012

Ваше отражение не на нужном уровне абстракции.

Когда вы говорите, что хотите преобразовать вектор в списки аргументов переменной, это потому, что функция, которая принимает список аргументов переменной, делает что-то интересное

Поэтому реальный вопрос заключается в том, как я могу сделать то же самое, что и 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 на самом деле уже не очевиден.

1 голос
/ 17 февраля 2012

Судя по вашему собственному ответу, звучит так, как будто вы можете использовать буст формат .

Примеры:

#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

Конечно, ничего из этогонеобходимо просто распечатать вектор.

0 голосов
/ 14 февраля 2012

Вы можете использовать алгоритм STL for_each для печати каждого элемента вектора.

...