2010-12-10 32 views
2

J'ai un Visual Studio 2008 C++ DLL qui exporte une fonction qui accepte un paramètre variadique comme ceci:En utilisant métaprogrammation modèle pour envelopper des arguments variadique C style

__declspec(dllexport) void DLL_Foo(int count, ...) 
{ 
    va_list list; 
    va_start(list, count); 

    for(; count; --count) 
    { 
     const wchar_t* item = va_arg(list, const wchar_t*); 
     if(count % 2 == 0) 
      printfW(L"[%s] ", item); 
     else 
      printfW(L"%s ", item); 
    } 

    va_end(list); 

    printfW(L"\r\n"); 
} 

L'utilisation attendue est quelque chose comme ceci:

DLL_Foo(4, L"int", L"1", L"const wchar_t*", L"Hello"); 

Lorsque la sortie serait:

[int] 1, [const wchar_t*] Hello 

Pour simplifier l'utilisation de cette fonct ion, je l'intention d'inclure une fonction de modèle de C++ comme ceci:

template< class T1, class T2 > 
void Foo(T1 p1, T2 p2) 
{ 
    std::wstringstream t1W; 
    t1W << typeid(p1).name(); 
    std::wstringstream p1W; 
    p1W << p1; 

    std::wstringstream t2W; 
    t2W << typeid(p2).name(); 
    std::wstringstream p2W; 
    p2W << p2; 

    ::DLL_Foo(4, t1W.str().c_str(), p1W.str().c_str(), t2W.str().c_str(), p2W.str().c_str()); 
}; 

Lorsque l'utilisation prévue est comme ceci:

int a = 1; 
const wchar_t* b = L"Hello"; 
Foo(a, b); 

Avec le même résultat escompté comme précédemment.

Existe-t-il une méthode de récurrence de gabarit que je peux utiliser pour ne pas avoir à implémenter une autre fonction template<> Foo() pour les paramètres 0..n?

template<> void Foo(); 
template< class T1 > void Foo(T1 p1); 
template< class T1, ..., class N > void Foo(T1 p1, ..., N n); 

S'il vous plaît, pas de solutions impliquant des modèles variadic ou d'autres fonctionnalités C++ 0x. Je réalise qu'ils sont merveilleux, mais j'utilise VS2008. Aussi, juste pour être plus difficile, je ne peux pas utiliser la fonctionnalité boost comme boost :: MPL.

Merci, PaulH


Edit: Oui, la fonction DLL dll réelle ne plus imprimer les informations de type et la valeur. La fonction DLL réelle ressemble un peu plus à ceci:

__declspec(dllexport) void DLL_Foo(MYHANDLE handle, int count, ...) 
{ 
    CMyObject* obj = reinterpret_cast< CMyObject* >(handle); 

    va_list list; 
    for(va_start(list, count); count; --count) 
    { 
     const wchar_t* item = va_arg(list, const wchar_t*); 
     if(count % 2 == 0) 
     { 
      obj->AddTypeInfo(item); 
     } 
     else 
     { 
      obj->AddValueInfo(item); 
     } 
    } 

    va_end(list); 
} 
+0

Si c'est la source de la fonction, pourquoi ne pas l'utiliser et mettre la fonctionnalité dans Foo() vous-même? La moitié de la fonction est juste la gestion des arguments variables. – Puppy

+0

@DeadMG - Ce n'est pas la fonction complète. DLL_Foo() traitera les informations et utilisera pour modifier l'état d'un objet qui est passé en tant que handle, puis le retourner comme un handle. Si ce n'est pas en contradiction avec votre suggestion, pouvez-vous en dire plus sur ce que vous voulez dire? – PaulH

Répondre

1

Ceci est uniquement possible en utilisant les fonctionnalités C++ 0x. Si vous ne pouvez pas utiliser ses modèles variadiques, vous ne pouvez pas créer de fonction ... euh, cela prend un nombre variable de modèles. D'autre part, peut créer plusieurs surcharges de la même méthode, chacune avec son propre nombre d'arguments (1 ... une limite supérieure). C'est bien sûr beaucoup de travail.

+2

Pas tout à fait. S'il avait accès à Boost.PP et Boost.MPL, il pourrait le faire aussi. Techniquement, vous pourriez juste regarder comment Boost.MPL implémente 'vector' et code le vôtre. Vous devrez essentiellement ré-implémenter des parties de MPL et PP vous-même. – KitsuneYMG

+0

Il ne doit pas nécessairement être vraiment variadique. Une solution limite supérieure fixe est acceptable. Les paramètres 0..6, par exemple, devraient être corrects. – PaulH

1

La réponse de Konrad est juste.

Cependant, vous pouvez éviter d'écrire les multiples surcharges au prix d'un léger inconvénient pour vos utilisateurs en ayant Foo accepter un tuple, quelque chose comme:

template<class TupleT> 
void Foo(const TupleT& Args) 

Et demander à l'appelant d'envelopper les arguments dans un tuple lorsque vous appelez Foo:

//Foo(an_int, a_bool, a_whatever); 
Foo(boost::make_tuple(an_int, a_bool, a_whatever)); 

... et maintenant, droite comme je suis sur le point de cliquer sur le bouton « postez votre réponse », je vois que vous ne pouvez pas utiliser Boost. Pouvez-vous utiliser le pack de fonctionnalités? Je pense qu'il a std :: tr1 :: tuple.

+0

@ Éric-std :: TR1 ne semble pas être dans notre SDK. Pour clarifier, je peux utiliser la bibliothèque standard et les fonctionnalités du langage SDK et C++ de Windows disponibles pour le compilateur VS2008. Je crois que boost va compiler et fonctionner. Je ne peux tout simplement pas l'utiliser (oui, je déteste cette limitation, aussi). Donc, je vais probablement avoir besoin de mettre en œuvre une petite partie du voodo qui est boost.MPL pour y arriver. Je ne sais pas ce que c'est. – PaulH

0

La dernière fois que j'ai eu besoin de faire quelque chose comme ça, j'ai écrit un script perl qui génèrerait les templates pour moi.Le côté négatif est que vous vous retrouvez avec beaucoup de code à compiler, mais cela dépend de ce que votre max est.