2013-04-02 36 views
6

Tôi đang cố triển khai trình bao bọc C++ 11 quanh thư viện SQL được viết bằng C. Thư viện C có các hàm riêng biệt để nhận các kiểu dữ liệu khác nhau từ một câu lệnh SQL yêu cầu chỉ mục cột. Một cách tiếp cận đơn giản được tạo mẫu dưới đây nhưng có một lỗ hổng quan trọng: nó dựa vào thứ tự thực thi đối số, không an toàn (cũng có thể có lỗi trình biên dịch, chưa thử nghiệm nó).Cách an toàn để tăng thêm một int trong bản mở rộng mẫu variadic là gì?

Câu hỏi: Cách nền tảng độc lập để tăng một biến an toàn trong bản mở rộng mẫu variadic là gì?

template< typename... ColumnTypes > 
void SQLStatement::execute(std::function< void(ColumnTypes...) > rowCallback){ 
    while(this->nextRow()){ 
     int column = 0; 
     rowCallback(this->getColumn<ColumnTypes>(column++)...); 
     //       unreliable increment^
    } 
} 

template< typename T > 
T SQLStatement::getColumn(const int columnIdx){} 

template<> 
inline int SQLStatement::getColumn<int>(const int columnIdx){ 
    return sql_library_column_int(this->nativeHandle, columnIdx); 
} 

// Other getColumn specializations here... 
+0

Bạn đang đặt cột thành 0 ở mọi thời điểm. – jthill

+0

@jthill: Đúng, trên mỗi hàng tôi khởi động lại từ cột số 0 khi trích xuất các giá trị từ hàng. –

+0

Tại sao không thực hiện một hàm đơn giản có tham chiếu, gia số tham chiếu và trả về giá trị cũ? –

Trả lời

4

Trong khi giải pháp của mfontanini hoạt động và tốt vì nó thực hiện việc tính toán chỉ số cột gia tăng tại thời gian biên dịch, tôi nghĩ rằng nó đáng giá chỉ ra rằng cũng có câu trả lời trực tiếp cho câu hỏi cách tăng int trong variadic gói mở rộng. (Thật không may nó dường như không hoạt động trên GCC do lỗi, xem báo trước ở cuối.)

Câu trả lời được dựa trên thực tế là trong khi các đánh giá đối số trong một cuộc gọi hàm không được kết thúc, các đánh giá của lập luận trong một danh sách khởi không:

(§8.5.4/4) trong thời hạn initializer-list của một chuẩn bị tinh thần-init-list, initializer-khoản, bao gồm bất kỳ mà kết quả từ việc mở rộng gói (14.5.3), được đánh giá theo thứ tự xuất hiện của chúng. Tức là, mọi tính toán giá trị và hiệu ứng phụ liên quan đến mệnh đề initializer-được sắp xếp trước khi tính toán giá trị và tác dụng phụ liên quan đến bất kỳ mệnh đề initializer nào theo sau trong danh sách được tạo bằng dấu phẩy của danh sách khởi tạo.
[Lưu ý: Thứ tự đánh giá này không phụ thuộc vào ngữ nghĩa của việc khởi tạo; ví dụ, nó áp dụng khi các phần tử của danh sách khởi tạo được hiểu là các đối số của một cuộc gọi hàm tạo, mặc dù thông thường không có ràng buộc trình tự trên các đối số của một cuộc gọi. - cuối note]

Do đó, nếu bạn chuyển đổi cuộc gọi chức năng của bạn vào một cái gì đó dựa trên một cú đúp-init-list, bạn sẽ nhận được hiệu quả mong muốn:

rowCallback(std::tuple<ColumnTypes...> { getColumn<ColumnTypes>(column++)... }); 

này khởi một std::tuple sử dụng danh sách -initialization (chú ý các dấu ngoặc đơn { ... }), do đó hiệu ứng phụ column++ sẽ được thực hiện theo thứ tự từ trái sang phải.

Nếu được viết như trên, điều này có nghĩa là bạn cần thay đổi rowCallback() để làm cho nó chấp nhận std::tuple thay vì danh sách đối số. Nếu bạn không thích điều này, bạn có thể tạo một hàm mẫu riêng biệt call_on_tuple(fun,tup), gọi hàm bất kỳ fun trên các đối số phát sinh từ việc mở rộng tuple tup. Tôi đã từng mô tả cách thực hiện điều này here hoặc nếu bạn thích, bạn có thể sử dụng rlxutil::call_on_tuple từ my GitHub repository.

chức năng execute của bạn sau đó trông như thế này:

template <typename... ColumnTypes> 
void execute(function<void(ColumnTypes...)> rowCallback) 
{ 
    using std::tuple; 
    using rlxutil::call_on_tuple; 

    int column = 0; 
    call_on_tuple(rowCallback, 
       tuple<ColumnTypes...> { getColumn<ColumnTypes>(column++)... }); 
} 

Nên biết trước: Đây là không hoạt động như mong đợi với GCC. Tôi tin rằng đây là do lỗi được báo cáo ở đây: http://gcc.gnu.org/bugzilla/show_bug.cgi?id=51253.

+1

Ah, đó thực sự là một sự kiện thú vị. Tôi không thích dựa vào nó chính xác bởi vì nó khác với ngữ nghĩa gọi hàm thông thường, và nó quá dễ dàng để trao đổi với một cuộc gọi hàm và đột nhiên nhận được thứ tự sai, nhưng đó là một sự khác biệt thú vị thực sự ... –

+0

Tôi đã xem xét sử dụng một danh sách khởi tạo chính xác theo cách bạn giải thích, nhưng tôi không nghĩ rằng bạn có thể tạo một bộ tuple từ một danh sách khởi tạo. Tôi đang sử dụng GCC, vì vậy tôi sẽ phải đi với câu trả lời của @ mfontanini trong thực hiện, nhưng tôi sẽ chấp nhận câu trả lời của bạn ở đó chính xác hơn câu hỏi. –

5

Điều này có vẻ hiệu quả. Bạn chỉ cần phải điều chỉnh một vài thứ:

#include <functional> 
#include <iostream> 
#include <cstddef> 

void foo(int a, float b, int c) { 
    std::cout << a << ", " << b << ", " << c << std::endl; 
} 

template<typename T> 
T getColumn(int index) { 
    return T(index); 
} 

template<size_t... indexes> 
struct index_tuple {}; 

template<size_t head, size_t... indexes> 
struct index_tuple<head, indexes...> { 
    typedef typename index_tuple<head-1, head-1, indexes...>::type type; 
}; 

template<size_t... indexes> 
struct index_tuple<0, indexes...> { 
    typedef index_tuple<indexes...> type; 
}; 

template<typename... Args> 
struct make_index_tuple { 
    typedef typename index_tuple<sizeof...(Args)>::type type; 
}; 

template<typename... ColumnTypes, size_t... indexes> 
void execute(const std::function<void(ColumnTypes...)> &callback, index_tuple<indexes...>) { 
    // this should be done for every row in your query result 
    callback(getColumn<ColumnTypes>(indexes)...); 
} 

template<typename... ColumnTypes> 
void execute(const std::function<void(ColumnTypes...)> &callback) { 
    execute(
     callback, 
     typename make_index_tuple<ColumnTypes...>::type() 
    ); 
} 

int main() { 
    std::function<void(int, float, int)> fun(foo); 
    execute(fun); 
} 

Demo here. Lưu ý rằng hàm foo chỉ được sử dụng để cho thấy rằng các chỉ mục được tăng lên chính xác, giống như return T(index); trong getColumn.

+0

Wow, đây là cả hai câu trả lời hay, và tôi sẽ thực hiện câu trả lời của bạn do lỗi GCC đã đề cập đến câu trả lời của @ jogojapan, nhưng câu trả lời chính xác của anh ấy chính xác hơn khi được hỏi. –

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