2009-07-15 4 views
6

Je cherche essentiellement à générer un wrapper pour une fonction C générique sans avoir à spécifier manuellement les types. J'ai donc un callback avec un prototype fixe mais je vais avoir besoin de faire du code spécial dans le wrapper basé sur le type de la fonction wrappée ... Donc en gros je pense à utiliser une méthode statique dans un template de classe pour envelopper ma fonction à une interface conforme par exemple:Paramètre de modèle non-type ... c'est un modèle! (C++)

// this is what we want the wrapped function to look like 
typedef void (*callback)(int); 
void foobar(float x); // wrappee 

// doesn't compile 
template< T (*f)(S) > // non-type template param, it's a function ptr 
struct Wrapper 
{ 
    static void wrapped(int x) 
    { 
    // do a bunch of other stuff here 
    f(static_cast<S>(x)); // call wrapped function, ignore result 

    } 
} 

et puis je voudrais faire quelque chose comme:

AddCallback(Wrapper<foobar>::wrapped); 

Cependant, le problème est que je ne peux pas aller de l'avant et à l'utilisation un "S" dans le paramètre de la fonction dans le modèle Wrapper, je dois d'abord le lister comme un paramètre:

template< class T, class S, T (*f)(S) > 
struct Wrapper 
// ... 

Mais cela signifie qu'il est beaucoup plus pénible à utiliser (Wrapper<void,float,foobar>::wrapped), idéalement, je voudrais juste passer dans le pointeur de fonction là-bas et lui faire travailler les types de paramètres (et les types de retour) automatiquement. Pour être clair, à l'intérieur de la fonction enveloppée, je vais devoir me référer aux types de pointeur de fonction (donc j'ai besoin d'un équivalent de S ou T).

Existe-t-il une façon de procéder?

+0

@damndirtyape: J'ai réfléchi à votre question et je pense que j'ai fait quelque chose de similaire. Malheureusement, cela impliquait beaucoup de code. Fondamentalement, ma solution avait une classe de base qui surchargeait operator() et j'avais des fonctions d'usine qui construisaient des classes de base basées sur le type de fonction passé. Si vous aimez je peux poster le code dans un pastebin quelque part. –

Répondre

0

Si vous utilisez une fonction qui retourne le "wrapped" au lieu de s'y référer directement, le compilateur tentera de faire correspondre automatiquement les paramètres du template pour l'appel de la fonction.

edit: qu'en est-il de ça?

int foobar(float x); // wrappee 

template <typename T, typename S> 
struct Wrapper { 
    typedef T (*F)(S); 
    F f; 

    Wrapper(F f) : f(f) { } 

    void wrapped(S x) { 
     // do a bunch of other stuff here 
     f(x); // call wrapped function, ignore result 
    } 
}; 

template <typename T, typename S> 
Wrapper<T,S> getWrapper(T (*f)(S)) { 
    return Wrapper<T,S>(f); 
} 

... 
getWrapper(foobar).wrapped(7); 
+0

Cela ne résout pas le problème, car maintenant les paramètres de modèle pour cette fonction a le même problème. – damndirtyape

+0

Etes-vous sûr? Peut-être que je ne comprends tout simplement pas ce que vous essayez de faire ... –

+0

Ne fonctionne pas parce que vous avez changé la fonction d'une méthode statique à une méthode d'instance, mais le but était de rendre la fonction enveloppée conforme une signature de rappel existante (ce qui signifie qu'elle doit être statique). – damndirtyape

0

EDIT: complètement nouvelle réponse

OK, je l'ai complètement repensé la question et je crois que je reçois ce que vous voulez. J'ai déjà fait ça avant :-P.

Voici l'idée, j'ai une classe de base qui surcharge l'opérateur(), alors j'ai une sous-classe pour chaque "arité" de fonctions. Enfin j'ai une fonction d'usine qui retournera une de ces choses. Le code est gros (et probablement un peu exagéré) mais fonctionne bien. La plupart des surcharges library_function doivent prendre en charge différentes syntaxes, la plupart du temps inutiles. Il prend également en charge les fonctions boost::bind, les fonctions membres, etc., beaucoup plus que ce dont vous avez probablement besoin.

http://pastebin.com/m35af190

Exemple, l'utilisation:

// map of library functions which will return an int. 
std::map<std::string, LibraryFunction<int> > functions; 

// function to register stuff in the map 
void registerFunction(const std::string &name, LibraryFunction<int> func) { 
    functions.insert(std::make_pair(name, func)); 
} 

plus tard vous pouvez le faire:

// the this param is so the function has access to the scripting engine and can pop off the parameters, you can easily chop it out 

// register 2 functions, one with no params, one with 1 param 
registerFunction("my_function", library_function1(*this, call_my_function)); 
registerFunction("my_function2", library_function0(*this, call_my_function2)); 

functions["my_function"](); 
functions["my_function2"](); 
+0

Cela ne me laisse pas envelopper une fonction existante, je dois écrire un nouvel objet ce qui signifie que je dois encore spécifier les types à l'avance plutôt que de les déduire? – damndirtyape

+0

Je ne vois pas comment cela m'aide. Fondamentalement, j'ai une signature de rappel spécifique (sur laquelle je n'ai aucun contrôle), j'ai besoin d'emballer un grand nombre de fonctions C existantes pour se conformer à cette signature de rappel (c'est une action de script). 3 valeurs hors de la pile, passez-les au wrappee, et poussez le résultat sur la pile - mais à l'extérieur, chaque fonction a exactement la même signature). – damndirtyape

+0

Encore une fois, cela ne m'aide pas. Je dois le transformer en un pointeur de fonction compatible avec une signature de rappel fixe. Cela ne fait pas ça. Je dois activer une fonction qui prend une chaîne (disons) à une fonction qui correspond au rappel, en faisant un peu de magie (en poussant la chaîne vers une pile etc.) dans la fonction, mais la signature de cette fonction enveloppée * doit * correspondent au rappel. – damndirtyape

0

Je regarde coup de pouce. À la première lecture de votre question, il me semble que <boost/function_types/parameter_types.hpp> répond à vos besoins.

+0

Pas vraiment une bonne réponse: sans au moins pointer vers la bibliothèque de boost concernée, c'est comme dire "Recherche sur Google, je pense que la réponse est là". De plus, je ne vois rien dans Boost qui puisse répondre aux exigences du PO. Pourriez-vous être plus précis dans votre réponse? –

+0

J'avais. J'avais utilisé directement <et> au lieu des entités HTML et le nom de l'en-tête était caché. J'ai édité ma réponse pour corriger cela. – AProgrammer

+0

Un dans les commentaires, on n'a pas besoin des entités et < and > fonctionne ... eh bien, j'apprends toujours l'interface. – AProgrammer

5

Une chose que vous pourriez envisager est d'utiliser LLVM ou similaire pour générer une fonction de trampoline appropriée à l'exécution.Ou voici une solution statique:

#include <iostream> 

void f(float f) { std::cout << f << std::endl; } 

template<typename T, typename S> struct static_function_adapter { 
     template<T(*f)(S)> struct adapt_container { 
       static void callback(int v) { 
         f(static_cast<S>(v)); 
       } 
     }; 

     template<T(*f)(S)> adapt_container<f> adapt() const { 
       return adapt_container<f>(); 
     } 
}; 

template<typename T, typename S> struct static_function_adapter<T, S> get_adapter(T (*)(S)) { 
     return static_function_adapter<T, S>(); 
} 

#define ADAPTED_FUNCTION(f) (&get_adapter(f).adapt<f>().callback) 

int main() { 
     void (*adapted)(int) = ADAPTED_FUNCTION(f); 
     adapted(42); 
     return 0; 
} 

La fonction get_adapter permet d'inférer l'argument et le type de retour; adapter() convertit ensuite ceci en un type paramétré sur la fonction actuelle, et finalement nous obtenons une fonction statique en callback.

+0

Je n'ai aucune idée de ce que ce code est censé faire et du problème qu'il est supposé résoudre: O – Virus721

Questions connexes