В C ++ 03 у вас есть разные возможности:
- генерирует перегрузки для 0-N аргументов (например, с использованием Boost.Preprocessor)
- использовать Cons-Lists (
cons(1)("some string")(foo)
)
- использовать объект и перегружать некоторый оператор (например,
operator()
или operator%
, как Boost.Format)
Мне кажется, что первый вариант немного сложен, потому что не все могут легко понять макросы, поэтому я бы оставил его только для краткосрочных решений, если вы планируете в ближайшее время перейти на C ++ 0x.
Третий вариант может обеспечить приятное настраиваемое прикосновение (форматирование выполняется с помощью знака %
на многих языках), но это также означает, что необходимо помнить, как эта конкретная «переменная» функция работает каждый раз.
Мое личное предпочтение - это подход cons
, потому что он решает обе проблемы:
- определение включает только шаблоны, поэтому оно более читабельно и доступно для обработки, чем 1.
- Вы один раз определяете аппарат-механизм, а затем можете повторно использовать его для любой «вариадической» функции (и они остаются функциями), так что она более согласованна и экономит вашу работу
Например, вот как это может работать:
Включения, которые будет использовать этот пример:
#include <cassert>
#include <iostream>
#include <string>
Помощник для типа результата добавления значения (это может быть более эффективным при добавлении, но это будет означать передачу аргументов в обратном порядке, что нелогично):
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;
};
Сам шаблон Cons
, с волшебным operator()
для добавления значения. Обратите внимание, что создается новый элемент другого типа:
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());
}
Повторно VarPrint
с ним:
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;
}
И демоверсия:
int main() {
VarPrint(std::cout, "integer %i\n", cons(1));
VarPrint(std::cout, "mix of %i and %s\n", cons(2)("foo"));
}
Выход:
integer 1
mix of 2 and foo