0

I want to store an array of Member Functions of different Classes. Just a repetition here:

Requirements:

  • TypeOf Class
  • Instance of Class containing the Function
  • AddressOf the Member Function
  • Member Function parameters

What I can store:

  • Instance of Class containing the Function
  • AddressOf the Member Function.
  • Member Function parameters

Normally, you don't need to store the Class type whatsoever, since you can make an array pointer to the Class type. The reason I can't do that is because the Class types I take in are unknown and different. The Class is going to be used in many different projects where Class types are unknown.

I need to store different types of Classes into the Array/List, in which case I just stored the addresses of the classes into an array pointer.

My Problem: When I'm about to call the Member Function, I need to cast the Class address to the Class type, but I don't know what the type is to cast to.

Example Code (Not tested - written real quickly):

class A
{
    public:
        void test1(int i);
};

class B
{
    public:
        void test2(char c);
};

class Container
{
    long* objects;
    long* funcs;
    int index;

    public:

        Container()
        {
            objects = new long[5];
            funcs = new long[5];
            index = 0;
        }

        template <class C, typename Types ...>
        void Add(C *obj, void (C::*func)(Types ...))
        {
            objects[index++] = (long)obj;
            funcs[index++] = (long)func;
        }

        typename <Types ...>
        void Call(int inx, Types ... values)
        {
            void (*func)(Types ...) = (void (*)(Types ...))funcs[inx];

            // This is where I've got trouble. I don't store the Class 
            // types, so I don't know what pointer Class type to cast 
            // the Class Instance address to.
            (((*???)objects[inx])->*func)(values ...);
        }
};

Thanks in advance. Ask ahead if there are any holes or any questions.

2
  • What makes you think funcs[index++] = (long)func; is valid? If I'm not wrong you're casting a pointer-to-member function to a long. That's illegal. Commented Sep 17, 2015 at 10:12
  • On ubuntu-15.04, gcc 4.9, the pointer for a class method is 10 bytes. I was very surprised. So, neither void* nor long* are adequate to point to a method. Found confirmation that it can be bigger that void* in SO. Commented Sep 17, 2015 at 14:11

2 Answers 2

3

Can you constrain this a little bit on the signature of the member functions? If so, instead of storing pointers to objects and member functions separately, you can store bound functions.

template<typename... Args>
class Container {
public:
    typedef std::function<void (Args...)> F;

    template <class C>
    void Add(C* obj, void (C::*func)(Args ...))
    {
        functions.push_back( [=](Args... args) {(obj->*func)(args...);} );
    }

    void call(size_t i, Args... args)
    {
        functions[i](args...);
    }

private:
    std::vector<F> functions;
};
Sign up to request clarification or add additional context in comments.

2 Comments

You miss C:: for C::*func.
This is a very clean and straightforward solution to my issue, something I probably wouldn't have concluded to alone. The Lambda Expression was something very new to me which is very interesting. I would recommend this solution for people using C++11 and above. This is the most straight forward solution to my question, but I'll be exploring DOUGLAS O. MOEN's answer further. Thank you very much, Jens! PS: C::*func instead of C*::func
1

IMHO, your posting reads like it might be an interesting polymorphic programming challenge, but with the added requirement, "no polymorphism" ... in which case you are to learn the more difficult approach.

What I have to address your stated problem:

When I'm about to call the Member Function, I need to cast the Class address to the Class type, but I don't know what the type is to cast to.

used to be called a Thunk (back when I first encountered it). In a (relatively) recent search, I have not found this idea under that name (and did find several other things called a thunk). The explanation for the name I once read was something like because "it encapsulates things that I already thunk".

Note that with a thunk, NO casting is required (because you already thunked it)

Use the thunk as the object in your Container.

Oh, and since you marked this C++, you really should use std::vector < PVThunk_t >.

  // ////////////////////////////////////////////////////////////////////
  // Pure Virtual Thunk_t: an abstract base class
  class PVThunk_t
  {
  public:
     virtual ~PVThunk_t(){}

     virtual void operator()(void* v) = 0;
     virtual void exec      (void* v) = 0;
  };
  // pure-virtual requires derived objects to implement both methods

// ///////////////////////////////////////////////////////////////////////
// template class - make it easy to create thunk for any class T
template <class T>
class Thunk_t : PVThunk_t
{
public:
   // constructor - takes pointer to an object and pointer to a member and stores
   // them in two private variables
   Thunk_t( T*    anInstance,        // pointer to an instance of class T
            void* (T::*aMethod)()) : // pointer to a  method   of class T, no parameter, returns void
      m_instance (anInstance),
      m_method   (aMethod)
      {
         assert  (m_instance);
         asssert (m_method);
      }

   Thunk_t( T*  anInstance,        // pointer to an instance of class T
            T*  (T::*aMethod)()) :  // pointer to a  method   of class T, no parameter, returns T*
      m_instance (anInstance),
      m_method   (aMethod)
      {
         assert  (m_instance);
         asssert (m_method);
      }

   virtual ~Thunk_t() { }

   // override operator "()"
   virtual void* operator()(void* v) { return((*m_instance.*m_method)(v)); }

   // override function "exec"
   virtual void* exec(void* v) { return((*m_instance.*m_method)(v)); }

private:
   T*        m_instance;         // pointer to object of T
   void (T::*m_method)(void*);   // pointer to method attribute of T with void* param

}; // template <class T>  class Thunk_t : public PVThunk_t

Note the proper mechanism to declare m_instance and m_method pointers.


If you need to use methods with a variable number of parameters, I suggest passing a struct pointer (with appropriate adjustments or additions to the Thunk. The single method parameter would always be a pointer to the struct, and its type or content can then be inferred by the method invoked.

I have not used this Thunk for a long time, because, well, polymorphism is superior in every way. But Thunk still compiles, so it will probably work.

update - noticed that the virtual methods didn't match. fixed

3 Comments

Very nice and interesting method, something I didn't consider whatsoever. My concern is that when I try to push the Thunk_t<> element to the std::vector<PVThunk_t> object, I get the error "cannot allocate an object of abstract type 'PVThunk_t'." I would also appreciate your own brief explanation of virtual classes and functions if possible, because I seem to lack some knowledge.
@AlexanderVassiliou - I've made the same mistake. The error means that you can only instantiate derived classes that implement all methods, which the template makes easier. The C++ rules simply do not allow an instance of a class with pure virtual components (i.e. unimplemented). Your effort is to create T, and the method to which m_method points.
I've re-read your code multiple times and understood exactly what the virtual keyword does and how the Thunk method works. I've managed to implement your method exactly the way I wanted it to work and I'm very pleased with the results. Thank you very much, DOUGLAS! You have no idea how you had made my day that day things I finished my class and things finally worked the way I had initially wished for! I flagged Jens response as the Answer due to the fact that it's a great and very straightforward, and is probably what people will look for, but yours is exactly what I wanted.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.