2012-02-14 54 views
8

Tôi đang sử dụng gcc 4.6. Giả sử rằng có một v vectơ tham số tôi phải chuyển đến hàm variadic f (const char * format, ...).Chuyển std :: vector <int> mục sang hàm variadic

Một cách tiếp cận để làm điều này là:.

 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ấn đề là tất nhiên nó không hỗ trợ một số tùy ý của các mục trong vector v Tuy nhiên, tôi tin rằng đã hiểu thế nào va_lists công trình nguyên tắc, tức là bằng cách đọc các đối số từ ngăn xếp, bắt đầu từ địa chỉ của đối số cuối cùng được đặt tên trước "...", Bây giờ tôi nghĩ rằng có thể sao chép giá trị mục vectơ vào khối bộ nhớ (ví dụ: myMemBlock) và chuyển địa chỉ của nó làm đối số thứ hai sau 'định dạng'. Rõ ràng điều đó sẽ yêu cầu myMemBlock phải được cấu trúc như mong đợi bởi f(), tức là giống như một chồng.

  1. Điều đó có thể thực hiện được không?
  2. Cách khác, có thể đẩy giá trị mục vectơ lên ​​ngăn xếp thực bằng một số ma thuật lắp ráp nội tuyến, chức năng gọi f() và dọn dẹp ngăn xếp sau đó không?

Cuối cùng, điều tôi không quan tâm đến:

  1. Mã này có thể là không cầm tay. OK Tôi chỉ quan tâm đến gcc.
  2. Có thể có các cách tiếp cận khác liên quan đến hackery tiền xử lý.
  3. Sử dụng hàm variadic để định dạng như printf(), không được khuyến khích cho C++.
  4. Cách sử dụng các hàm mẫu của hàm VARdic.
+1

Từ những gì tôi biết, không thể "giả mạo" danh sách va_arg của riêng bạn. Những gì bạn có thể làm là truyền trực tiếp véc-tơ bằng cách quá tải hàm f() của bạn. – Gui13

+0

Tôi không muốn thực hiện lại va_arg, tôi chỉ muốn bắt chước một ngăn xếp cho va_arg để hoạt động. – user1142580

Trả lời

2

Được rồi, đây là giải pháp từng phần! Một phần, bởi vì nó không áp dụng cho thực sự hàm variadic, nhưng đối với những người chấp nhận va_list làm đối số. Nhưng tôi nghĩ rằng giải pháp đầy đủ không phải là xa.

Nó được dựa trên các ví dụ tôi tìm thấy ở đây:

  1. động tạo ra một va_list https://bbs.archlinux.org/viewtopic.php?pid=238721

  2. rèn một va_list http://confuseddevelopment.blogspot.com/2006/04/dynamically-creating-valist-in-c.html

Mã này được kiểm tra với gcc trên linux VC++ 2008 thành công, các nền tảng khác có thể được hỗ trợ, nhưng điều đó tùy thuộc vào bạn.

Sự thấu hiểu quan trọng đối với tôi là một va_list về cơ bản là không có gì hơn một mảng đóng gói, mà có thể được lấp đầy với các dữ liệu tự động và có thể được thông qua với các chức năng như vprintf, vfprintf, vsprintf mà chấp nhận nó như là đối số.

Vì vậy, việc chuyển các mục vectơ tới một trong các chức năng đó có thể hoạt động bằng cách phân bổ đủ bộ nhớ cho các mục vectơ và sao chép chúng trước cuộc gọi.

Có nói rằng, đây là cách tiếp cận chồng tự động phân bổ:

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

Đoán những gì nó? Nó cho phép định dạng kiểu printf (..) với các tham số được thu thập trong một vectơ. Vâng, nó không hoàn hảo nhưng nó làm những gì tôi muốn. Hơn nữa, nó bao gồm hai nền tảng chính: D

Bên cạnh đó, có xem bài viết này: va_pass http://www.codeproject.com/Articles/9968/va_list-va_start-va_pass-or-how-to-pass-variable-a

+0

Ok, câu trả lời hoàn chỉnh có thể đạt được bằng cách sử dụng cách tiếp cận được trình bày ở đây: http://www.codeproject.com/Articles/9968/va_list-va_start-va_pass-or-how-to-pass-variable-a Tuy nhiên, do với một giấy phép khác, tôi không thể sao chép mã ở đây một cách đơn giản. – user1142580

0

Bạn có thể sử dụng thuật toán STL for_each để in mọi mục của vectơ.

3

Có phần "Tạo danh sách giả và danh sách" tại http://cocoawithlove.com/2009/05/variable-argument-lists-in-cocoa.html. Đó là cho Cocoa, nhưng bạn có thể tìm thấy một cái gì đó trên mạng cho GCC.

Sau đó, tôi đoán bạn muốn làm một cái gì đó như thế này:

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

Nhưng, tôi đã hoàn toàn không có đầu mối. :)

+0

Trông rất thú vị! Cảm ơn bạn! Tôi sẽ báo cáo kết quả. – user1142580

+1

Cảm ơn gợi ý hữu ích của bạn! – user1142580

2

Phản xạ của bạn không ở mức trừu tượng phù hợp.

Khi bạn nói bạn muốn chuyển đổi vectơ thành một danh sách đối số biến, đó là vì hàm lấy danh sách đối số biến sẽ làm điều gì đó bạn quan tâm.

Câu hỏi thực sự là do đó, làm cách nào tôi có thể làm điều tương tự hơn f, nhưng bắt đầu từ một số vector?

Có thể chuyển tiếp cuộc gọi đến f có thể sẽ bắt đầu giải pháp, nhưng không rõ ràng.

Nếu nó chỉ là về in ấn:

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

Hoặc, nếu bạn có quyền truy cập vào các thư viện bên ngoài:

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

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

Tại thời điểm đó vì lợi ích của f là không thực sự rõ ràng nữa.

+0

Giả sử nó chỉ là về in ấn. Vì sự đơn giản, tôi đã xác định v là vectơ . Tuy nhiên điều này đáng lẽ phải là điểm khởi đầu cho bản thân tôi. Trong trường hợp v sẽ được thay đổi để chứa các loại POD khác nhau, tôi muốn sử dụng định dạng vprintf(). – user1142580

1

Đánh giá bởi câu trả lời của riêng bạn đưa ra, có vẻ như bạn có thể tận dụng boost format.

Ví dụ:

#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 

Tất nhiên, không ai trong số đó là cần thiết để chỉ in ra một vector.

+0

Tôi đã đề cập rằng đây là một giải pháp một phần. Các giải pháp hoàn chỉnh giải quyết các câu hỏi như tôi đã viết nó, có thể đạt được bằng cách sử dụng va_pass mà tôi nhận xét theo câu trả lời của riêng tôi. Đề xuất của bạn có thể hữu ích trong nhiều trường hợp (ví dụ: trong trường hợp quảng cáo có sẵn hoặc được đánh giá cao), do đó "bỏ phiếu". – user1142580

Các vấn đề liên quan