2011-10-07 6 views
9

Używam programu Visual Studio 2008 i chcę zaimplementować funkcję formatowania ciągów znaków bez zmiennej listy argumentów.Jak zaimplementować "Variadic Template" z pre-C++ 0x (VS2008)?

Jak zaimplementować "Variadic Template" z pre-C++ 0x (VS2008)?

Czy istnieje jakaś biblioteka, która implementuje to jako wzmocnienie?

Lub inny sposób realizacji tego?

Oto mój przykładowy kod. (oczywiście, to nie mogą być zachowane, ponieważ używam VS2008.)

bool VarPrint(std::ostringstream& out, const std::string& s) 
{ 
    std::string::size_type offset = 0; 
    if((offset = s.find("%")) != std::string::npos) 
    { 
     if(!(offset != s.size() - 1 && s[offset + 1] == '%')) 
     { 
      ASSERT(!"Missing Arguments!"); 
      return false; 
     } 
    } 
    out << s; 
    return true; 
} 

template<typename T, typename... Args> 
bool VarPrint(std::ostringstream& out, const std::string& s, const T& value, const Args&... args) 
{ 
    std::string::size_type prev_offset = 0; 
    std::string::size_type 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 << value; 
      if(curr_offset + 2 < s.length()) 
       return VarPrint(out, s.substr(curr_offset + 2), args...);     return true; 
     } 

     prev_offset = curr_offset + 2; 
     if(prev_offset >= s.length) 
      break; 
    } 
    ASSERT(!"Extra Argument Provided!"); 
    return false; 
} 
+0

Jest niemożliwe, jeśli chcesz dowolną ilość argumentów. Wszystko, co możesz zrobić, to dostarczyć implementacje dla określonej liczby argumentów, takich jak 1-10 argumentów. – Dani

+0

Kompilowanie twojego przykładu: 'prev_offset> = s.length' -> zanotuj brakujące'() ', aby wywołać funkcję' length' :) –

+0

Dziękuję. Brakowało mi '()', podczas pisania bezpośrednio. hah: D – winnerrrr

Odpowiedz

18

w C++ 03, masz różne możliwości:

  1. generują przeciążeń 0-N argumentów (na przykład z wykorzystaniem Boost.Preprocessor)
  2. użytku minusy wykazach (cons(1)("some string")(foo))
  3. użytku Przedmiot i pewne przeciążenia operatora (operator() na przykład, czy operator% jak Boost.Format)

pierwszą opcją jest nieco skomplikowane, czuję dlatego nie każdy może łatwo zrozumieć makra, więc zarezerwowałbym go tylko dla rozwiązań krótkoterminowych, jeśli wkrótce planujesz migrację do C++ 0x.

Trzecia opcja może zapewnić miły niestandardowy dotyk (formatowanie odbywa się za pomocą znaku % w wielu językach), ale oznacza to również, że należy pamiętać, że ta konkretna funkcja "variadic" działa za każdym razem.

Moje osobiste preferencje jest podejście cons ponieważ rozwiązuje oba problemy:

  • definicja obejmuje tylko szablony, więc jest bardziej czytelny i maintanable niż 1.
  • zdefiniować minusy-Machinery raz, i można ponownie wykorzystać go w dowolnym funkcji „o zmiennej liczbie argumentów” (i pozostają funkcji), więc jest bardziej spójna i zapisuje pracy

na przykład, oto jak to może działać:

Układ składa, że ​​ten przykład użyje:

#include <cassert> 
#include <iostream> 
#include <string> 

pomocnik dla podać wynik dołączanie wartość (może to być bardziej efektywne z poprzedzenie, ale Oznaczałoby to, że przekazując argumenty w odwrotnej kolejności, co jest sprzeczne z intuicją):

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

Szablon sam Cons, z magicznym operator() dołączyć wartość. Zauważ, że tworzy nowy element z innego typu:

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

zrewidowana VarPrint z nim:

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

a demo

int main() { 
    VarPrint(std::cout, "integer %i\n", cons(1)); 
    VarPrint(std::cout, "mix of %i and %s\n", cons(2)("foo")); 
} 

można sprawdzić wyjście na ideone:

integer 1 
mix of 2 and foo 
+0

Cool ~ Doceniam twój szczegół! – winnerrrr

6

Nie ma o zmiennej liczbie argumentów funkcjonalność szablonu w C++ 03. Boost i inne dobrze zaprojektowane biblioteki działają w różny sposób. W przypadku funkcji można mieć wiele przeciążeń N + 1, w których każde przeciążenie przyjmuje od 0 do N argumentów. W przypadku klas można mieć jedną definicję z maksymalnie N argumentami, które domyślnie są niepoprawnym typem. Te wyższe limity są zwykle konfigurowalne za pomocą niektórych makr; ponieważ ustawienie go na wysoki spowoduje nałożenie narzutu w czasach kompilacji, a ustawienie go na niski spowoduje, że użytkownicy nie będą w stanie przekazać wystarczającej liczby argumentów.

Dla twojego konkretnego przypadku, będę implementował VarPrint w rekursywny sposób. Każdy krok w rekursji przetwarza pojedynczy argument i wywołuje rekursywne wywołanie ze zmodyfikowanym łańcuchem formatu, a wszystkie lewe wartości przesunięte są o jedną pozycję w lewo.

+0

Dziękuję. Myślę, że nie mam innego wyjścia, jak zaimplementować funkcje przeciążenia N. :] – winnerrrr

+1

@winnerrrr: Boost.Format po prostu używa przeciążenia operatora i leniwe oceny, aby to zrobić. Musisz wstawić '%' między argumentami zamiast ',' s, i nie wchodzą one w nawias. Ale działa. –