Trong C++ 03, bạn có khả năng khác nhau:
- tạo ra quá tải cho 0-N đối số (sử dụng Boost.Preprocessor ví dụ)
- sử dụng Nhược điểm-Lists (
cons(1)("some string")(foo)
)
- sử dụng đối tượng và quá tải một số toán tử (ví dụ:
operator()
chẳng hạn hoặc operator%
như Boost.Format)
Tùy chọn đầu tiên hơi phức tạp, tôi cảm thấy, bởi vì không phải mọi người đều có thể hiểu macro một cách dễ dàng, vì vậy tôi sẽ chỉ dành cho các giải pháp ngắn hạn nếu bạn dự định chuyển sang C++ 0x sớm.
Tùy chọn thứ ba có thể cung cấp một chạm tùy chỉnh đẹp (định dạng được thực hiện bằng nhiều ngôn ngữ), nhưng điều này cũng có nghĩa là bạn cần nhớ chức năng "variadic" cụ thể này hoạt động mỗi lần như thế nào.
sở thích cá nhân của tôi là cách tiếp cận cons
vì nó giải quyết cả hai vấn đề:
- định nghĩa chỉ liên quan đến các mẫu, vì vậy nó dễ đọc hơn và maintanable hơn 1.
- bạn xác định các khuyết điểm-máy móc thiết bị cùng một lúc, và bạn có thể sau đó tái sử dụng nó cho bất kỳ chức năng "variadic" (và họ vẫn chức năng), vì vậy nó là phù hợp hơn, và giúp bạn tiết kiệm làm việc
Ví dụ, đây là cách nó có thể làm việc:
bao gồm rằng ví dụ này sẽ sử dụng:
#include <cassert>
#include <iostream>
#include <string>
một helper cho các loại hình kết quả của phụ thêm một giá trị (nó có thể hiệu quả hơn với thêm vào trước, nhưng điều đó có nghĩa đi qua các đối số theo thứ tự ngược đó là phản trực giác):
template <typename T, typename Next> struct Cons;
struct ConsEmpty;
template <typename Cons, typename U>
struct cons_result;
template <typename U>
struct cons_result<ConsEmpty, U> {
typedef Cons<U, ConsEmpty> type;
};
template <typename T, typename U>
struct cons_result<Cons<T, ConsEmpty>, U> {
typedef Cons<T, Cons<U, ConsEmpty> > type;
};
template <typename T, typename Next, typename U>
struct cons_result<Cons<T, Next>, U> {
typedef Cons<T, typename cons_result<Next, U>::type> type;
};
Các Cons
mẫu riêng của mình, với một ma thuật operator()
để thêm giá trị. Lưu ý rằng nó tạo ra một mục mới với một kiểu khác nhau:
template <typename T, typename Next>
struct Cons {
Cons(T t, Next n): value(t), next(n) {}
T value;
Next next;
template <typename U>
typename cons_result<Cons, U>::type operator()(U u) {
typedef typename cons_result<Cons, U>::type Result;
return Result(value, next(u));
}
};
struct ConsEmpty {
template <typename U>
Cons<U, ConsEmpty> operator()(U u) {
return Cons<U, ConsEmpty>(u, ConsEmpty());
}
};
template <typename T>
Cons<T, ConsEmpty> cons(T t) {
return Cons<T, ConsEmpty>(t, ConsEmpty());
}
Một revisited VarPrint
với nó:
bool VarPrint(std::ostream& out, const std::string& s, ConsEmpty) {
std::string::size_type offset = 0;
if((offset = s.find("%")) != std::string::npos) {
if(offset == s.size() - 1 || s[offset + 1] != '%') {
assert(0 && "Missing Arguments!");
return false;
}
}
out << s;
return true;
}
template<typename T, typename Next>
bool VarPrint(std::ostream& out,
std::string const& s,
Cons<T, Next> const& cons)
{
std::string::size_type prev_offset = 0, curr_offset = 0;
while((curr_offset = s.find("%", prev_offset)) != std::string::npos) {
out << s.substr(prev_offset, curr_offset);
if(curr_offset == s.size() - 1 || s[curr_offset + 1] != '%') {
out << cons.value;
if(curr_offset + 2 < s.length())
return VarPrint(out, s.substr(curr_offset + 2), cons.next);
return true;
}
prev_offset = curr_offset + 2;
if(prev_offset >= s.length())
break;
}
assert(0 && "Extra Argument Provided!");
return false;
}
Và demo
int main() {
VarPrint(std::cout, "integer %i\n", cons(1));
VarPrint(std::cout, "mix of %i and %s\n", cons(2)("foo"));
}
Bạn có thể kiểm tra kết quả trên ideone:
integer 1
mix of 2 and foo
Không thể nếu bạn muốn bất kỳ số lượng đối số tùy ý nào. Tất cả những gì bạn có thể làm là cung cấp việc triển khai cho một số lượng đối số bị ràng buộc, như 1-10 đối số. – Dani
Biên dịch ví dụ của bạn: 'prev_offset> = s.length' -> lưu ý thiếu'() 'để gọi hàm' length' :) –
Cảm ơn bạn. Tôi đã bỏ lỡ '()', trong khi gõ trực tiếp. hah: D – winnerrrr