简体   繁体   中英

How to forward an argument to a template member function to a member base class pointer in C++

I have a class Foo which uses CRTP to inherit a template method from a parent class and avoid having to provide literally dozens of individual member methods. Something like this:

class Foo : public SomeBarClass<Foo>
{
//..
//from SomeBarClass
public:
    template <class T> void onMsg(T* msg);

private:
    IFoxMod* foxMod_;
};

Now, in the implementation for onMsg , I would like something like this:

template <class T>
void Foo::onMsg(T* msg)
{
    if (foxMod_->shouldDoStuff(msg))
    {
        //do stuff
    }
}

and there can be many foxMod_ types (one of them instantiated in the Foo constructor by name given in config file) as long as they abide by the common interface of providing a bool shouldDoStuff method. The problem, is that this leads me to define the following:

struct IFoxMod
{
    virtual ~IFoxMod() {}
    template <class T> shouldDoStuff(T* msg) = 0;
};

for all of the FoxMods to implement (like, class redMountainLogic : public IFoxMod might have it's own way of discerning, when it is appropiate to do stuff).

This is illegal though because one cannot have virtual templates and I'm trying to find a workaround for it. Basically, I need to have dynamic dispatch, but the argument I am passing is a template. I can't think of a workaround.

Virtual function tables don't seem to get along well with template specializations. Not too surprising. VFTs are generally based on declaration order, which doesn't really exist with templates. One solution is to manually recreate VFTs.

Here's an example. It could probably be a little cleaner, but it works.

#include<iostream>
using namespace std;

// Message.h

template<int n>
struct MessageByInt {
  typedef int Msg;
};

struct MessageOfHope {
  int a;
  int b;
  static const int id = 0;
};
template<> struct MessageByInt<MessageOfHope::id> { typedef MessageOfHope Msg; };

struct MessageOfDoom {
  int b;
  int c;
  static const int id = 1;
};
template<> struct MessageByInt<MessageOfDoom::id> { typedef MessageOfDoom Msg; };

const int nMessages = 2;

// IFoxMod.h

typedef bool(*callback)(void*);

struct IFoxMod {
  callback vtable[nMessages];
  template<typename MSG>
  bool ShouldDoWork(MSG* msg) {
    return vtable[MSG::id](msg);
  }
};

template<typename TESTER, int n>
struct filler {
  typedef typename MessageByInt<n>::Msg MSG;
  typedef typename TESTER::template Tester<MSG> Tester;
  static void fill(IFoxMod* impl) {
    impl->vtable[n] = reinterpret_cast<callback>(&Tester::ReallyShouldDoWork);
    filler<TESTER,n-1>::fill(impl);
  }
};

template<typename TESTER>
struct filler<TESTER,-1>{
  static void fill(IFoxMod* impl) {
  }
};

// RedFox.h

struct RedFoxTester {
  template<typename MSG>
  struct Tester { // This struct exists to allow partial specialization
    static bool ReallyShouldDoWork(MSG* msg) {
      return msg->b == 2;
    }
  };
};

struct RedFoxMod : public IFoxMod {
  RedFoxMod() {
    filler<RedFoxTester,nMessages-1>::fill(this);
  }
};

//Main

main() {
  IFoxMod* fm = new RedFoxMod();
  MessageOfHope mohb2 = {1, 2};
  MessageOfDoom modb2 = {2, 3};
  MessageOfHope mohbn2 = {2, 3};
  MessageOfDoom modbn2 = {1, 2};
  cout << fm->ShouldDoWork(&mohb2) << ", " << fm->ShouldDoWork(&modb2) << endl;
  cout << fm->ShouldDoWork(&mohbn2) << ", " << fm->ShouldDoWork(&modbn2) << endl;
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM