8
votes

C ++ - est-il possible d'extraire des types de classe et d'argumentation d'un type de fonction de membre dans un modèle?

Je voudrais envelopper les fonctions des membres conformes au type 'VIDY (CLASTTYPE: FOCKE) (argtype)' avec une classe modélisée. Plus tard, je veux passer une instance de Classeype à une instance de ce modèle et demandez-la d'appeler la méthode enveloppée:

class MyPropertyCollection {
 public:
  void set_oink(double value) { oink_ = value; }
  void set_bar(int value) { bar_ = value; }
  void set_squee(bool value) { squee_ = value; }
 private:
  double oink_;
  int bar_;
  bool squee_;
};

// elsewhere
WrapperCollection wrapper_collection;  // a simple set of wrapper objects, accessed by id
MyPropertyCollection property_collection;
wrapper_collection.add(PROPERTY_OINK_ID, new Wrapper<double, MyPropertySet, &MyPropertySet::set_oink>(&property_collection);
wrapper_collection.add(PROPERTY_BAR_ID, new Wrapper<int, MyPropertySet, &MyPropertySet::set_bar>(&property_collection);
wrapper_collection.add(PROPERTY_SQUEE_ID, new Wrapper<bool, MyPropertySet, &MyPropertySet::set_squee>(&property_collection);
// +300 more


1 commentaires

Je suis sûr que ce sera possible avec une spécialisation de classe. Étant donné que la déduction fonctionne en sens inverse, vous pouvez exploser le type de déclaration de méthode en 3 types de modèles, à partir de la déclaration de modèle de base qui n'en a qu'un. Et poof, extrait. Je vais essayer cela dans GCC tout de suite.


4 Réponses :


1
votes

Ceci est une mauvaise ré-implémentation de :: std :: mem_fn code> + :: std :: lid code>, qui sont des constructions C ++ 11. Voici comment vous pourriez faire cela en utilisant ceux:

#include <functional>

int main() {
   Foo foo;
   auto wrapper = ::std::bind(::std::mem_fn(&Foo::set), ::std::ref(foo), _1);
   wrapper(5); // Calls foo.set(5)
}


3 commentaires

Merci, je vais jeter un coup d'œil aux variations de TR1 et / ou de Boost. La raison pour laquelle j'ai rendu la fonction de fonction La partie de la signature de type est parce que je souhaite étendre ceci pour correspondre aux fonctions d'autres types et que le match de spécialisation correct est automatiquement. Cependant, maintenant, je pense que je me demande si cela serait possible avec l'argumentation correspondant à ce type de fonction - mais je devrais toujours spécifier CLASSTYPE et ARGTYPE en tant que paramètres de modèle afin que le type d'argument puisse être spécifié correctement.


@meowsQuak: J'en ai pensé un peu et aussi longtemps que vous avez la valeur du pointeur sous forme d'argument de modèle, vous ne pouvez en aucun cas résoudre votre problème avec la correspondance de l'argument de fonction. Le problème est que l'argument du modèle doit être une expression constante. Et les arguments de fonction ne sont pas des expressions constantes. Ainsi, alors que vous pouvez utiliser la fonction de fonction de la fonction de modèle pour tirer l'argument et le type de classe d'une valeur de pointeur aléatoire, vous ne pouvez ensuite pas utiliser cette valeur de pointeur comme l'un des arguments de modèle pour le type de retour. Si cela fait du tout de sens.


Je pense que cela a du sens. J'ai changé d'approche maintenant - au lieu d'utiliser le pointeur de fonction membre en tant que paramètre de modèle, je le transmettais comme un argument normal. Ensuite, j'ai un petit ensemble de modèles pour faire face à chaque signature que j'ai besoin d'envelopper. Cela signifie que la signature de la fonction emballée (y compris le nom) ne fait plus partie du type. Jusqu'à présent, ça fonctionne bien.



1
votes

lire sur ce que vous avez fait de me faire penser à quelques options:

1) envelopper l'instanciation dans l'héritage. Cela déplace les trucs effrayants à votre définition. P>

class Foo {
 public:
  Foo() : f_(0.0) {}
  void set(double v) { f_ = v * 2.1; }
  double get() { return f_; }
 private:
  double f_;
};


template <typename ArgType, typename ClassType, void (ClassType::*Method)(ArgType)>
class Wrapper {
 public:
  explicit Wrapper(ClassType *cls) : cls_(cls) {}

  void do_something(ArgType value) {
    (cls_->*Method)(value);
  }

 private:
  ClassType *cls_;
};


class FooWrapper : public Wrapper< double, Foo, &Foo::set >, public Foo
{
public:
    FooWrapper() : Wrapper(this){}
};


template<typename argType1, class classType1>
class FooWrapper2 : public Wrapper<argType1, classType1, &classType1::set>, public classType1
{
public:
    FooWrapper2()
        : classType1(),
          Wrapper<argType1, classType1, &classType1::set>(this)
    {

    }
};

template<typename argType1>
class FooWrapper3 : public FooWrapper2<argType1, Foo>
{
public:
    FooWrapper3()
    {

    }
};

template <typename ArgType>
class Do_something {
 public:

  virtual void do_something(ArgType value) = 0;

};

template<typename ArgType>
class FooWrapper4 : public Foo, public Do_something<ArgType>
{
public:
    virtual void do_something(ArgType value)
    {
        set(1.0);
    }
};

#include <iostream>
int main(int argc, char ** argv) {
  Foo foo;
  Wrapper<double, Foo, &Foo::set> wrapper(&foo);

  wrapper.do_something(1.0);
  std::cout << foo.get() << std::endl;

  FooWrapper fooWrapper;
  fooWrapper.do_something(1.0);
  std::cout << fooWrapper.get() << std::endl;
  // outputs "2.1"

  FooWrapper2<double, Foo> fooWrapper2;
  fooWrapper2.do_something(1.0);
  std::cout << fooWrapper2.get() << std::endl;

  FooWrapper3<double> fooWrapper3;
  fooWrapper3.do_something(1.0);
  std::cout << fooWrapper3.get() << std::endl;

  FooWrapper4<double> fooWrapper4;
  fooWrapper4.do_something(1.0);
  std::cout << fooWrapper4.get() << std::endl;

  return 0;
}


6 commentaires

Chers personnes du futur, je ne peux pas sembler avoir le formatage pour travailler correctement sur l'un des segments de code, mon excuse.


Merci d'avoir répondu. En ce qui concerne le point 1, je dois étendre ceci pour envelopper plus de 300 fonctions de membre (une partie d'une interface de configuration d'une tierce partie), sachant qu'il n'y a que six signatures de méthodes différentes. Emballage dans l'héritage pour chacun impliquera d'écrire 300 classes distinctes, à moins que je ne les modèle (ce qui ne me gagne rien à la fin).


@meowsQuak, puisque c'est le cas, la méthode 2 devrait vous donner ce que vous recherchez. Vous ne devriez avoir à définir que 6 de ceux-ci.


Basé sur ce que j'ai dit, la méthode 2 semble appropriée, mais je me suis trompé dans ce que je voulais dire par "Signature" - je me rends compte maintenant que le nom de la méthode fait partie de la signature et que cela change aussi (SET_FOO, SET_BAR, etc.). Je dispose donc de 300 signatures uniques, qui sont malheureuses. Je devra peut-être repenser cette interface d'abord.


Si les noms de fonction commencent à changer, je ne connais pas un bon moyen de le faire. Si vous avez une liste de tous vos appels de fonction, vous pouvez écrire un REG-EX pour générer le code pour vous, mais ce n'est pas aussi amusant que le modèle.


J'ai changé d'approche maintenant - j'ai déplacé le type de fonction de la liste des paramètres de modèle; C'est maintenant un argument normal. Cela me permet d'écrire des spécialisations distinctes pour chaque «type» de la signature que j'ai besoin d'envelopper. Jusqu'à présent, cela fonctionne bien (bien que j'ai eu des problèmes de surcharge ne fonctionnant pas sur des types de retour, soupir). Merci de votre aide.



3
votes

en C ++ 11 Vous pouvez utiliser Lambdas, comme:

#include <iostream>
#include <functional>

struct A {
    virtual void a(int i) { std::cout << "A: " << i << std::endl; }
};

struct B {
    virtual void b(int i) { std::cout << "B: " << i << std::endl; }
};

template <typename X, typename ARG>
std::function<void(X*, ARG)> wrapper(void (X::*mfp)(ARG)) {
    return [=](X *x, ARG arg) { (x->*mfp)(arg); };
}

int main()
{
    auto g = wrapper(&B::b);
    B b;
    g(&b, 3);
    auto h = wrapper(&A::a);
    A a;
    h(&a, 4);
    return 0;
}


0 commentaires

4
votes
struct MyClass
{
};

typedef int (MyClass::*MethodT) (bool);
typedef void (MyClass::*VV) ();
typedef void (MyClass::*IL) (int, long);

template< typename T >
struct ExtractType : std::false_type
{
};

template< typename R, typename C, class...A >
struct ExtractType< R (C::*)(A...) >
{
    typedef C type;
    typedef R returntype;
};

static_assert( std::is_same< ExtractType< MethodT >::type, MyClass >::value, "oops" );
static_assert( std::is_same< ExtractType< VV >::type, MyClass >::value, "oops" );
static_assert( std::is_same< ExtractType< IL >::type, MyClass >::value, "oops" );

static_assert( std::is_same< ExtractType< MethodT >::returntype, int >::value, "oops" );
static_assert( std::is_same< ExtractType< VV >::returntype, void >::value, "oops" );
static_assert( std::is_same< ExtractType< IL >::returntype, void >::value, "oops" );

0 commentaires